
In the first part of this article, I described a case where the swapping service rabbit.io tried to send a small amount of SOL to a client, but the transaction kept being rejected - even though, according to all the known rules of the Solana blockchain, everything seemed perfectly valid.
That first part of the article also covered situations where a transaction that is technically correct may still be rejected in networks such as XRP Ledger, Stellar, and the Lightning Network (Bitcoin's Layer 2). I also explained an implementation detail of blacklists in the USDC smart contract that can cause USDC transactions to fail unexpectedly, even when nothing appears to be wrong on the sender's side.
If you missed Part I, you can read it here.
Today I want to look at several similar examples involving other networks and tokens. In each of them, everything may appear correct at first glance - yet the transaction still fails.
This case is specific to developers and operators of DeFi protocols. Nevertheless, ordinary users can also feel its consequences.
Imagine the following situation. You are trying to deposit USDC, DAI, and USDT into some protocol. Deposits with USDC and DAI work fine, but the USDT transfer gets reverted. The address is correct, the network is correct, and there is enough gas. What is the reason?
The reason is that USDT on Ethereum does not fully comply with the ERC-20 standard. The standard requires the functions transfer and transferFrom to return a boolean value: true on success and false on failure. USDT returns nothing.
If a smart contract is written strictly according to the ERC-20 standard and expects to receive a boolean value in response to the transfer call, while USDT returns nothing, the EVM virtual machine (when using Solidity starting from version 0.4.22) interprets this as an error and reverts the transfer. Technically the transaction could have succeeded, but it is forcibly rolled back. And the sender receives no clear indication from either the network or the smart contract about what exactly went wrong.
This example has nothing to do with swaps on rabbit.io. Our system is organized in the simplest and most reliable way possible: you receive an address to which you send your tokens manually, and in return you receive the tokens you need from us. DeFi smart contracts are not involved.
Nevertheless, for readers who not only perform swaps on rabbit.io but also actively interact with DeFi - including developers - I can suggest a simple solution. Use the SafeERC20 library from OpenZeppelin, which correctly handles non-standard tokens.
Most professionally written DeFi protocols already do this. However, older or amateur smart contracts still break when interacting with USDT. This issue is so widespread that it has been included in the weird-erc20 database of known token quirks, where you can also find other tokens with similar behavior.
USDT on Ethereum has another peculiarity that regularly causes transactions with smart contracts to fail.
In the ERC-20 standard, if you want to allow a smart contract to spend your tokens, you call the function approve(spender, amount). For example, if you want to increase the allowance from 100 to 200 USDC, you simply call approve(spender, 200).
USDT works differently. Its code explicitly states that if the recipient address already has a non-zero allowance, it is not allowed to set a new non-zero value directly. A transaction that would be completely valid for almost any other token is rejected when used with USDT.
This behavior was introduced as protection against a double-spend attack involving allowances. The developers feared that an attacker might instantly spend both the old allowance and the new one.
The mandatory pattern for changing a USDT allowance is therefore the following:
approve(spender, 0) to reset the allowanceapprove(spender, new_value)For users of decentralized applications this can be confusing. The user previously granted an allowance to the application, now wants to set a different allowance, signs the transaction - yet nothing changes and there is no obvious explanation.
This example also has nothing to do with swaps on rabbit.io. Such rejected transactions cannot occur on our platform because rabbit.io does not require connecting a wallet and does not request permission to spend tokens from it.
However, if in some dApp, your approve transaction involving USDT fails, the solution is usually simple. Check the current allowance level. If it is not zero, first reset it to zero and only then set the new value.
In the Bitcoin network there is a concept called dust. It refers to a transaction output whose value is smaller than the amount required to spend it later. In other words, the coins technically exist in your wallet, but the fee required to send them would be larger than the coins themselves.
It is important to understand a fundamental distinction that is often confused: the difference between consensus rules and node policy.
A transaction whose outputs are smaller than the dust limit may technically be valid according to Bitcoin consensus rules. However, most nodes by default will refuse to relay such a transaction, and most miners will refuse to include it in a block.
The reason is simple: storing extremely small coins bleats the database without providing any real economic benefit.
The dust limit depends on the type of address and the minimum relay fee. Historically the dust limit is considered to be 546 satoshis. Transactions with smaller outputs would cost more in fees than the amount being sent if the minimum fee rate is 1 sat/vByte. But this applies only to legacy addresses that start with "1".
More modern address types create transactions that occupy less space in the blockchain, so their dust limits can be lower. For example, for regular bc1q... addresses the dust limit at a fee rate of 1 sat/vByte is 294 satoshis, and with the currently applied minimum relay fee (0.1 sat/vByte) the dust limit becomes ten times smaller.

Imagine the following situation.
You have 100,000 satoshis (0.001 BTC). You want to send 99,000 satoshis to someone. You construct a transaction, and the network fee is 980 satoshis. The remaining 20 satoshis should come back to you as change.
But that will not happen. You will not receive your change, and the main payment will not reach the recipient either. The network of relay nodes will silently reject the transaction because one of its outputs is below the dust limit.
There is also a related problem: spending dust later. If you receive many outputs smaller than 294 satoshis while fees are low, and later fees increase, any transaction attempting to spend those coins may become so expensive that the transferred amount cannot even cover the required fee.
Therefore, when exchanging other crypto assets for bitcoin, make sure that the outputs being created exceed the dust limit. Rabbit.io allows swaps in amounts several times higher than the current dust limit. However, if network fees increase, such small amounts may become difficult to send further.
In Ethereum and any EVM-compatible blockchains (such as BSC, HyperEVM and others), every transaction sent from a particular address has a sequential number called a nonce.
The network processes transactions strictly in order: first the transaction with nonce = 0, then nonce = 1, then 2, and so on. It is impossible to skip a number.
If one transaction becomes stuck in the mempool due to a gas fee that is too low, all subsequent transactions from the same address with higher nonces will also remain pending, even if they have normal fees.
Here is a very realistic scenario. A few weeks ago you sent a transaction with a low gas fee. It became stuck. You forgot about it. Now you try to send a new transaction. You specify the correct address and set an adequate fee, but the transaction keeps failing to be processed.
The problem is not the current transaction, but the old pending one.
The solution is to send a transaction with the same old nonce but with a higher gas fee. This will either accelerate the original transaction (speed up) or cancel it. The latter happens if you send a transfer to yourself with the same nonce and a higher gas fee.
However, note that not all wallets allow users to control the nonce value. For example, popular wallets such as Trust Wallet and Exodus do not provide this function. Meanwhile, MetaMask - which many people consider outdated and inconvenient - actually does.
The TRON network has an unusual resource model. Instead of a single transaction fee, transactions consume two types of resources:
Energy can be obtained by freezing TRX. If this is not done, the network automatically burns TRX to cover the cost.
And this is where an unexpected pricing issue appears:
If the sender has 15 TRX available for fees but the transfer is going to a new address, the transaction may fail with the error OUT OF ENERGY. The TRX spent in this attempt is not refunded.
In other words, the situation again looks similar to the other examples in this article: the address is correct, the network is correct, the sender has USDT, TRX was present - yet the transaction still fails.
The conclusion is simple: for stable work with USDT TRC-20, it is advisable to keep at least 27 TRX on the balance to cover possible transaction costs.
The story with the failed SOL transfer taught me a valuable lesson. Not in the sense that I misunderstood blockchains, but in the sense that each blockchain is its own ecosystem with hidden rules that are not always clearly documented and sometimes reveal themselves only in unusual situations.
Every confusing failure is not a reason for frustration, but an opportunity to learn something new. And the more new things I learn, the more interesting it becomes for me to continue working with cryptocurrencies.
If you feel the same way, document unusual cases and share your findings. The crypto industry becomes more reliable when practical experience spreads faster than official documentation can be updated.