⚠️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: 4transactionsTransactions 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:
/v3/quote—destinationAddressis optional and never validated (used only for screening)./v3/swap— passdestinationAddress+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
Paymenttransactions 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:
Broadcast the returned
approvalTxand wait for it to confirm. The allowance is now0.Call
/v3/swapor/v3/limit/buildagain. With the allowance at0, the response now carries the realapprove(spender, sellAmount).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.
Do not stop after step 1. Abandoning the flow there leaves the user worse off than before — the pre-existing partial allowance is gone, and the swap or order reverts if broadcast. Either carry it through both approvals, or do not start it.
Last updated

