For the complete documentation index, see llms.txt. This page is also available as Markdown.

⚠️Smart contract limitations & edge cases

Key corner cases to take into account when integrating with our cross-chain providers

When integrating with THORChain or Mayachain, certain limitations at the smart contract and transaction level must be taken into account. Failing to do so may result in failed swaps, rejected transactions, or permanent loss of funds.

EVM Transaction Types

THORChain and Mayachain do not support EVM Type 4 transactions introduced in EIP-7702, which involve account abstraction. This includes:

  • All type: 4 transactions

  • Transactions from wallets based on abstracted accounts

Any transaction using this format will be rejected and may lead to loss of funds if funds are transferred under an unsupported transaction structure.

The following EVM transaction types are supported:

  • type: 0 (Legacy)

  • type: 2 (EIP-1559)

The includeTx optional argument when requesting a quote returns a legacy transaction object.

Chainflip, by contrast, does not impose restrictions on transaction types. All EVM transaction types, including Type 4, are supported when depositing on a Chainflip channel.

Transaction Log Limit

For inbound transactions into THORChain and Mayachain, the maximum number of logs that a transaction can include is 50 logs.

This limitation is particularly important for integrators who use smart contract wrappers to:

  • Perform token swaps

  • Route through aggregators

  • Execute any custom logic before sending funds to THORChain or Mayachain

If the resulting transaction generates more than 50 logs (e.g., due to complex DeFi interactions), THORChain will not process it.

THORChain, Mayachain & Smart Contract Limitations

Thorchain has strict gas limits when sending assets to smart contracts. Wallet-emitting events require gas and might break compatibility with Thorchain normal swap flow. By default, SwapKit API checks if the sender and recipient addresses are smart contract and will filter out THORCHAIN, THORCHAIN_STREAMING, MAYACHAIN and MAYACHAIN_STREAMING providers. You may use the allowSmartContractReceiver after confirming with SwapKit developers that your wallet respects Thorchain gas requirements.

Solana multisig vaults & PDAs (off-curve addresses)

Ordinary Solana wallets are on-curve ed25519 public keys. Program Derived Addresses (PDAs) are off-curve by construction and have no private key, so they cannot sign transactions. This includes Squads multisig vaults, Associated token accounts (ATAs) and Program-owned state accounts.

These are valid, well-formed Solana addresses, so SwapKit treats them the same way it treats EVM smart-contract counterparties: valid, but only accepted when you explicitly opt in. SwapKit rejects them by default (invalidDestinationAddress / invalidSourceAddress), funds sent to one are unrecoverable. Never send to a token account (ATA); use the owner address.

To swap into a vault you control:

  1. /v3/quotedestinationAddress is optional and never validated (used only for screening).

  2. /v3/swap — pass destinationAddress + allowSmartContractReceiver: true.

allowSmartContractSender works the same way for off-curve source addresses, on /v3/swap only. (A PDA can't sign a plain transfer, so a multisig source needs your own signing flow regardless.)

Current limitations:

Chainflip → SOL token (via Jupiter) — requires on-curve owner

Exchange deposit addresses as source or refund address

Refunds return to sourceAddress — there is no separate refundAddress on /v3/swap. SwapKit derives it from sourceAddress, including Chainflip's refundParameters.refundAddress and the refund address in the Maya memo for Zcash.

Do not use a centralised exchange deposit address. It passes validation and screening, so nothing in the API rejects it, but the exchange may not credit the refund — some only credit while actively expecting a deposit, and some flag a provider's outbound address as risky and stop crediting anything from it. The refund succeeds on-chain while the user still cannot reach the funds, and recovery is up to the exchange's support process. The same applies to destinationAddress.

Use a self-custody address the user holds keys for, and surface this if your interface lets them paste an arbitrary address.

XRP through THORChain limitations

Integrators should be aware of if they are not using our includeTx optional argument to build transactions for XRP swaps through THORChain:

  • Transaction Type: Only Payment transactions are supported for both inbound and outbound transfers.

  • Address Format: Only classic XRP addresses (starting with r...) are supported. X-addresses (which encode both address and destination tag) are not supported and must be decoded before use.

  • Memo Field:

    • THORChain supports only the first entry in the Memos[] array.

    • Additional memos (if present) will be ignored.

    • Maximum memo length: 250 characters, any excess will be ignored.

For reliable behavior, always use classic addresses and ensure your memo data fits within the 250-character limit.

Litecoin MWEB addresses

No provider SwapKit routes Litecoin through can settle MWEB (Litecoin's Mimblewimble Extension Blocks). Two different cases follow from that, and only one of them is detectable.

An MWEB address is rejected. An ltcmweb1… address supplied as either sourceAddress or destinationAddress returns 400 litecoinMwebAddressUnsupported. It has its own error code rather than the generic invalidSourceAddress / invalidDestinationAddress so it's distinguishable from a typo. Note it is not waived by disableSecurityChecks, unlike the other address checks — that flag relaxes our heuristics, but no provider can settle MWEB, so waiving it would only permit a swap that cannot complete.

Funding from MWEB is not detectable, and can lose funds. A user can hold LTC inside MWEB, give you an ordinary ltc1… source address, and then fund the deposit with a peg-out. That request is byte-for-byte identical to a normal Litecoin swap, so nothing on our side can spot it — but the peg-out's sender cannot be identified, so if the swap needs to refund, there is nowhere to send the funds.

Because it can't be detected, it can only be warned about: every route selling LTC carries a litecoinMwebNotSupported entry in warnings[], on both /v3/quote and /v3/swap. The warning is unconditional on the sell side — its presence says nothing about whether this particular user is using MWEB. Surface it to anyone selling Litecoin, and tell them to fund from a regular Litecoin balance rather than an MWEB one.

Both /v3/quote and /v3/swap still return routes and a transaction; no route is filtered or reordered.

USDT on Ethereum needs two approval transactions

Ethereum mainnet USDT (0xdAC17F958D2ee523a2206206994597C13D831ec7) predates ERC-20 and its approve() reverts when an allowance moves from one non-zero value to another. Raising an existing allowance therefore takes two transactions: reset to 0, then approve the new amount. Ethereum mainnet USDT is the only token SwapKit treats this way — USDT on other chains is approved in one transaction like any other ERC-20.

Today the API hands you only the first of the two. When the sell asset is Ethereum USDT and the wallet already holds an allowance that is non-zero but smaller than the sell amount, the approvalTx returned by /v3/swap and /v3/limit/build encodes approve(spender, 0) — the reset — and nothing else in the response distinguishes it from a normal approval.

To complete the approval:

  1. Broadcast the returned approvalTx and wait for it to confirm. The allowance is now 0.

  2. Call /v3/swap or /v3/limit/build again. With the allowance at 0, the response now carries the real approve(spender, sellAmount).

  3. Broadcast that one, then proceed with the swap or order.

You can recognise a reset without making the second call: approvalTx.data is a standard approve(address,uint256) payload, so its final 32 bytes are the amount. All-zero means a reset rather than the approval itself.

Last updated