Skip to main content
Rainbow supports atomic swaps - trustless, non-custodial token exchanges that execute completely or not at all.

What are Atomic Swaps?

Atomic Swaps are smart contract-based token exchanges that guarantee both parties receive their tokens or the transaction is completely reverted.

Key Properties

  • All-or-Nothing: Swap completes fully or reverts entirely
  • Trustless: No intermediary custody of funds
  • Non-Custodial: You always control your keys
  • On-Chain: Executed via smart contracts
  • Instant Settlement: No waiting periods (on same chain)

Atomic Guarantee

// Pseudocode of atomic swap
function atomicSwap(
  address tokenA,
  address tokenB,
  uint256 amountA,
  uint256 amountB
) external {
  // EITHER both transfers succeed
  require(transferFrom(msg.sender, counterparty, tokenA, amountA));
  require(transferFrom(counterparty, msg.sender, tokenB, amountB));
  
  // OR entire transaction reverts
  // No possibility of one-sided transfer
}

Atomic Swap Feature Flag

Rainbow has an experimental atomic swaps feature:
// From src/config/experimentalConfig.ts
import { ATOMIC_SWAPS, getExperimentalFlag } from '@/config';

// Check if atomic swaps enabled
const atomicSwapsEnabled = getExperimentalFlag(ATOMIC_SWAPS);

if (atomicSwapsEnabled) {
  // Use atomic swap execution path
  await executeAtomicSwap(swapParams);
} else {
  // Use standard swap execution
  await executeStandardSwap(swapParams);
}
Atomic swaps are currently an experimental feature in Rainbow. Enable in Settings > Advanced > Experimental Features.

How Atomic Swaps Work

Single Transaction Execution

1

Prepare Swap Data

Bundle all operations:
  • Token approval (if needed)
  • Swap execution
  • Slippage protection
  • Recipient verification
2

Smart Contract Call

Single contract interaction:
const atomicSwapTx = await swapContract.swap({
  tokenIn: inputToken.address,
  tokenOut: outputToken.address,
  amountIn: sellAmount,
  minAmountOut: minimumReceived,
  recipient: userAddress,
  deadline: currentTime + 1200, // 20 minutes
});
3

Atomic Execution

Contract executes atomically:
  • Validates input amount
  • Executes swap through DEX
  • Verifies output meets minimum
  • Transfers output to recipient
  • All steps succeed or all revert
4

Settlement

Transaction confirmed:
  • Input tokens debited from wallet
  • Output tokens credited to wallet
  • No intermediate state possible
  • Cannot partially execute

Atomic Swap vs Standard Swap

Single TransactionAdvantages:
  • Guaranteed atomicity
  • Lower gas (one transaction)
  • Faster execution
  • No approval + swap delay
  • MEV protection inherent
Disadvantages:
  • Requires contract support
  • Less flexibility
  • May need higher gas limit

Slippage Protection

Atomic swaps enforce slippage limits:
// Calculate minimum acceptable output
const minimumReceived = useDerivedValue(() => {
  if (!quote.value) return null;
  
  const { buyAmount } = quote.value;
  const slippageBips = swapSettings.slippage;
  
  // Subtract slippage tolerance
  const slippageMultiplier = divWorklet(
    subWorklet(10000, slippageBips),
    10000
  );
  
  return mulWorklet(buyAmount, slippageMultiplier);
});

// Swap will revert if actual output < minimum
if (actualOutput < minimumReceived) {
  revert("Slippage tolerance exceeded");
}

Slippage Scenarios

Scenario: Price moved 2%, slippage set to 5%Result:
  • Swap executes successfully
  • User receives slightly less than quoted
  • Still within acceptable range
  • Transaction confirms
Scenario: Price moved 7%, slippage set to 5%Result:
  • Swap would give too little output
  • Contract enforces minimum
  • Entire transaction reverts
  • User keeps original tokens
  • Gas still consumed (failed tx)
Scenario: Price moved favorably by 3%Result:
  • User receives more than quoted
  • No upper limit on positive slippage
  • Full benefit goes to user
  • Transaction succeeds

Deadline Protection

Swaps include deadline to prevent stale execution:
const SWAP_DEADLINE_MINUTES = 20;

const deadline = Math.floor(Date.now() / 1000) + (SWAP_DEADLINE_MINUTES * 60);

const swapParams = {
  // ... other params
  deadline, // Unix timestamp
};

// In smart contract:
require(block.timestamp <= deadline, "Transaction too old");
Prevents:
  • Execution at stale prices
  • MEV attacks from delaying transaction
  • Execution in different market conditions
  • Unfavorable price movement impact
If your transaction is pending longer than 20 minutes, it will fail. You’ll need to submit a new swap with updated quote and deadline.

MEV Protection

Atomic swaps have built-in MEV resistance:

How MEV Affects Swaps

Sandwich Attack:
  1. MEV bot sees your pending swap
  2. Bot front-runs: Buys token before you
  3. Price increases
  4. Your swap executes at worse price
  5. Bot back-runs: Sells token
  6. Bot profits, you lose

Atomic Swap Protection

// Atomic swap parameters include:
{
  minAmountOut: calculatedWithSlippage,
  deadline: currentTime + 1200,
  recipient: userAddress, // Verified
}

// Contract enforces:
// 1. Minimum output amount (slippage protection)
// 2. Deadline (prevents delayed execution)
// 3. Recipient verification (prevents redirection)
// 4. Single transaction (no front-run window)
Protections:
  • Slippage limit: Bot can’t profit beyond tolerance
  • Deadline: Limits time window for manipulation
  • Atomic execution: No approval + swap gap
  • Single transaction: Reduced attack surface

Cross-Chain Atomic Swaps

Cross-chain atomicity is more complex:

Hash Time-Locked Contracts (HTLCs)

interface HTLC {
  hashlock: bytes32;      // Hash of secret
  timelock: uint256;      // Expiration time
  sender: address;        // Initiator
  receiver: address;      // Counterparty
  amount: uint256;        // Token amount
  tokenAddress: address;  // Token contract
}

// Cross-chain atomic swap process:
// 1. Alice locks tokens on Chain A with hashlock H
// 2. Bob locks tokens on Chain B with same hashlock H
// 3. Alice reveals secret, claims Bob's tokens on Chain B
// 4. Bob uses same secret to claim Alice's tokens on Chain A
// 5. If timelock expires, both parties can reclaim

Cross-Chain Challenges

  • Different block times: Chains confirm at different speeds
  • Timelock coordination: Must account for both chains
  • Failure modes: One chain succeeds, other fails
  • Gas on both chains: Need native tokens on both
True cross-chain atomic swaps are rare in practice. Most “cross-chain swaps” in Rainbow use trusted bridge protocols, which are not atomic in the strict sense.

Swap Execution Flow

Detailed atomic swap execution:
// From src/__swaps__/screens/Swap/providers/swap-provider.tsx
const executeSwap = async () => {
  const { inputAsset, outputAsset, quote } = swapState;
  
  // 1. Validate swap can execute
  if (!quote || isQuoteError(quote)) {
    throw new Error('No valid quote');
  }
  
  // 2. Check atomic swap support
  const atomicEnabled = getExperimentalFlag(ATOMIC_SWAPS);
  
  if (atomicEnabled && supportsAtomicSwap(inputAsset, outputAsset)) {
    // 3a. Execute atomic swap (single transaction)
    await executeAtomicSwapTransaction({
      quote,
      inputAsset,
      outputAsset,
      minOutput: calculateMinimumOutput(quote, slippage),
      deadline: currentTimestamp() + SWAP_DEADLINE,
    });
  } else {
    // 3b. Execute standard swap (approval + swap)
    if (requiresApproval(inputAsset, quote)) {
      await approveToken(inputAsset, quote.allowanceTarget, quote.sellAmount);
    }
    
    await executeSwapTransaction(quote);
  }
  
  // 4. Track completion
  trackSwapEvent({ type: 'swap', success: true });
};

Gas Optimization

Atomic swaps can save gas:

Gas Comparison

// Standard swap (2 transactions)
const approvalGas = 45000;  // ERC-20 approve
const swapGas = 150000;     // Swap execution
const totalGas = 195000;    // Total

// Atomic swap (1 transaction)  
const atomicGas = 175000;   // Combined operation

// Savings
const gasSaved = totalGas - atomicGas; // 20000 units
const percentSaved = (gasSaved / totalGas) * 100; // ~10%
Additional benefits:
  • Only one transaction fee
  • No approval transaction overhead
  • Optimized contract call
  • Batch operations

Safety Features

Pre-Flight Checks

const validateSwap = async (params: SwapParams) => {
  // Check balances
  if (userBalance < params.sellAmount) {
    throw new Error('Insufficient balance');
  }
  
  // Validate slippage
  if (params.slippage > MAX_SLIPPAGE_BIPS) {
    warn('High slippage tolerance');
  }
  
  // Check deadline
  if (params.deadline < currentTimestamp()) {
    throw new Error('Deadline in past');
  }
  
  // Verify recipient
  if (params.recipient.toLowerCase() !== userAddress.toLowerCase()) {
    throw new Error('Recipient mismatch');
  }
  
  // Estimate gas
  const gasEstimate = await estimateGas(params);
  if (gasEstimate > GAS_LIMIT_MAX) {
    throw new Error('Gas estimate too high');
  }
};

Revert Protection

// Contract includes multiple revert conditions
function atomicSwap(SwapParams memory params) external {
  // Time check
  require(block.timestamp <= params.deadline, "Deadline passed");
  
  // Balance check
  require(
    IERC20(params.tokenIn).balanceOf(msg.sender) >= params.amountIn,
    "Insufficient balance"
  );
  
  // Allowance check
  require(
    IERC20(params.tokenIn).allowance(msg.sender, address(this)) >= params.amountIn,
    "Insufficient allowance"
  );
  
  // Execute swap
  uint256 amountOut = _executeSwap(params);
  
  // Slippage check
  require(amountOut >= params.minAmountOut, "Slippage exceeded");
  
  // Transfer output
  require(
    IERC20(params.tokenOut).transfer(params.recipient, amountOut),
    "Transfer failed"
  );
}

Limitations

Atomic swaps have constraints:
Current Limitations:
  • Not all DEXs support: Requires specific contract interface
  • Single liquidity source: Can’t split across multiple DEXs
  • Higher initial gas: More complex single transaction
  • Less common: Standard swaps more widely supported
  • Experimental: Feature still in testing phase

Enabling Atomic Swaps

1

Access Settings

Navigate to Settings > Advanced
2

Enable Feature Flag

Toggle “Experimental Features” > “Atomic Swaps”
3

Understand Risks

Read warning about experimental feature:
  • May have bugs
  • Limited DEX support
  • Higher gas if estimate wrong
  • Can revert to standard swaps
4

Confirm Enable

Accept risks and enable
5

Use Atomic Swaps

Swaps will use atomic execution when:
  • Feature enabled
  • Both tokens supported
  • DEX supports atomic interface
  • Otherwise falls back to standard
You can disable atomic swaps anytime. Existing pending swaps will complete with the method they started with.

Best Practices

When first using atomic swaps:
  • Test with small value
  • Verify execution works
  • Check gas costs
  • Confirm output received
  • Scale up after success
Atomic swaps gas:
  • Check estimate before confirming
  • Compare to standard swap
  • Ensure sufficient balance for gas
  • Account for gas price volatility
Slippage for atomic swaps:
  • Can’t adjust after submission
  • Set higher for volatile tokens
  • Too low = likely to revert
  • Too high = worse execution
  • 2-5% typical for most pairs
Before atomic swap:
  • Confirm token addresses
  • Check both tokens are legitimate
  • Verify you’re on correct chain
  • No recovery if sent to wrong token

Future Enhancements

Planned improvements:
  • Multi-DEX atomic swaps: Split across sources atomically
  • Better gas estimates: More accurate predictions
  • Wider DEX support: More liquidity sources
  • Cross-chain HTLCs: True atomic cross-chain swaps
  • Batched operations: Multiple swaps in one transaction

Swaps Overview

Complete swap functionality

Quote Fetching

How quotes are obtained

Gas Estimation

Gas calculation details

Build docs developers (and LLMs) love