Skip to content

Token Swap System

Introduction

Token swap systems enable decentralized token exchanges through automated market makers (AMM). This tutorial explains how to implement a basic token swap system with core AMM functionality.

Features

  • Constant product AMM
  • Liquidity provision
  • Token swapping
  • Fee collection
  • Price discovery

Contract Implementation

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

contract TokenSwap is ReentrancyGuard {
    using SafeMath for uint256;

    // Tokens in the pool
    IERC20 public immutable token0;
    IERC20 public immutable token1;

    // Pool reserves
    uint256 public reserve0;
    uint256 public reserve1;

    // Fee parameters
    uint256 private constant FEE_DENOMINATOR = 1000;
    uint256 private constant FEE = 3; // 0.3% fee

    // Events
    event Swap(
        address indexed sender,
        uint256 amount0In,
        uint256 amount1In,
        uint256 amount0Out,
        uint256 amount1Out,
        address indexed to
    );
    
    event AddLiquidity(
        address indexed provider,
        uint256 amount0,
        uint256 amount1
    );
    
    event RemoveLiquidity(
        address indexed provider,
        uint256 amount0,
        uint256 amount1
    );

    constructor(address _token0, address _token1) {
        require(_token0 != address(0), "Invalid token0");
        require(_token1 != address(0), "Invalid token1");
        require(_token0 != _token1, "Same tokens");
        token0 = IERC20(_token0);
        token1 = IERC20(_token1);
    }

    // Add liquidity
    function addLiquidity(uint256 amount0, uint256 amount1) 
        external 
        nonReentrant 
        returns (uint256 liquidity) 
    {
        require(amount0 > 0 && amount1 > 0, "Insufficient amounts");

        // Transfer tokens to contract
        token0.transferFrom(msg.sender, address(this), amount0);
        token1.transferFrom(msg.sender, address(this), amount1);

        // Update reserves
        reserve0 = reserve0.add(amount0);
        reserve1 = reserve1.add(amount1);

        emit AddLiquidity(msg.sender, amount0, amount1);
    }

    // Remove liquidity
    function removeLiquidity(uint256 amount0, uint256 amount1)
        external
        nonReentrant
        returns (uint256 amount0Out, uint256 amount1Out)
    {
        require(amount0 > 0 || amount1 > 0, "Insufficient amounts");
        require(amount0 <= reserve0 && amount1 <= reserve1, "Exceeds reserves");

        // Transfer tokens to user
        if (amount0 > 0) {
            token0.transfer(msg.sender, amount0);
        }
        if (amount1 > 0) {
            token1.transfer(msg.sender, amount1);
        }

        // Update reserves
        reserve0 = reserve0.sub(amount0);
        reserve1 = reserve1.sub(amount1);

        emit RemoveLiquidity(msg.sender, amount0, amount1);
        return (amount0, amount1);
    }

    // Swap tokens
    function swap(
        address tokenIn,
        uint256 amountIn,
        uint256 amountOutMin,
        address to
    ) external nonReentrant returns (uint256 amountOut) {
        require(amountIn > 0, "Insufficient input amount");
        require(to != address(0), "Invalid recipient");
        require(
            tokenIn == address(token0) || tokenIn == address(token1),
            "Invalid token"
        );

        bool isToken0 = tokenIn == address(token0);
        (IERC20 tokenInContract, IERC20 tokenOutContract) = isToken0 
            ? (token0, token1) 
            : (token1, token0);
        (uint256 reserveIn, uint256 reserveOut) = isToken0 
            ? (reserve0, reserve1) 
            : (reserve1, reserve0);

        // Transfer tokens to contract
        tokenInContract.transferFrom(msg.sender, address(this), amountIn);

        // Calculate amount out
        uint256 amountInWithFee = amountIn.mul(FEE_DENOMINATOR.sub(FEE)).div(FEE_DENOMINATOR);
        amountOut = getAmountOut(amountInWithFee, reserveIn, reserveOut);
        require(amountOut >= amountOutMin, "Insufficient output amount");

        // Transfer tokens to recipient
        tokenOutContract.transfer(to, amountOut);

        // Update reserves
        _update(
            token0.balanceOf(address(this)),
            token1.balanceOf(address(this))
        );

        emit Swap(
            msg.sender,
            isToken0 ? amountIn : 0,
            isToken0 ? 0 : amountIn,
            isToken0 ? 0 : amountOut,
            isToken0 ? amountOut : 0,
            to
        );
    }

    // Calculate output amount
    function getAmountOut(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut
    ) public pure returns (uint256) {
        require(amountIn > 0, "Insufficient input amount");
        require(reserveIn > 0 && reserveOut > 0, "Insufficient liquidity");

        uint256 numerator = amountIn.mul(reserveOut);
        uint256 denominator = reserveIn.add(amountIn);
        return numerator.div(denominator);
    }

    // Update reserves
    function _update(uint256 balance0, uint256 balance1) private {
        reserve0 = balance0;
        reserve1 = balance1;
    }

    // Get reserves
    function getReserves() public view returns (uint256, uint256) {
        return (reserve0, reserve1);
    }
}

Core Concepts

1. Automated Market Maker (AMM)

AMM is a decentralized trading mechanism that:

  • Uses mathematical formulas
  • Maintains liquidity pools
  • Enables permissionless trading
  • Provides continuous pricing

2. Constant Product Formula

The core formula x * y = k ensures:

  • Price stability
  • Liquidity availability
  • Slippage protection
  • Market efficiency

3. Liquidity Provision

Liquidity providers can:

  • Add token pairs
  • Earn trading fees
  • Remove liquidity
  • Track positions

4. Price Discovery

Price is determined by:

  • Pool reserves
  • Trade volumes
  • Market dynamics
  • Arbitrage activity

Security Considerations

  1. Access Control

    • Function permissions
    • Transfer validation
    • Balance checks
    • Reentrancy protection
  2. Price Protection

    • Slippage limits
    • Front-running prevention
    • Price manipulation guards
    • Reserve validation
  3. Token Safety

    • Address validation
    • Transfer success checks
    • Balance tracking
    • Fee calculation
  4. Pool Security

    • Reserve updates
    • Liquidity validation
    • Emergency controls
    • State consistency

Best Practices

  1. Implementation

    • Use SafeMath
    • Implement nonReentrant
    • Validate inputs
    • Handle edge cases
  2. Testing

    • Unit tests
    • Integration tests
    • Scenario testing
    • Security audits
  3. Deployment

    • Parameter verification
    • Initial liquidity
    • Fee settings
    • Emergency controls
  4. Monitoring

    • Price tracking
    • Volume monitoring
    • Reserve tracking
    • Event logging

Extended Features

  1. Advanced Trading

    • Multi-token swaps
    • Flash swaps
    • Price oracles
    • Route optimization
  2. Pool Management

    • Dynamic fees
    • Liquidity mining
    • Pool parameters
    • Emergency pause
  3. Integration

    • DEX aggregation
    • Yield farming
    • Lending protocols
    • Governance systems
  4. Analytics

    • Price feeds
    • Volume tracking
    • Pool metrics
    • User statistics

FAQ

General Questions

Q: What is a token swap?

A: A token swap system:

  • Enables decentralized trading
  • Uses AMM mechanism
  • Provides liquidity pools
  • Automates pricing

Q: Why use AMM?

A: Benefits include:

  • Continuous liquidity
  • Permissionless trading
  • Automated pricing
  • Low slippage

Technical Questions

Q: How to handle slippage?

A: Slippage control:

  • Set minimum output
  • Calculate price impact
  • Monitor reserves
  • Update frequently

Q: What about front-running?

A: Prevention measures:

  • Minimum output
  • Maximum slippage
  • Transaction ordering
  • MEV protection

Implementation Questions

Q: How to set parameters?

A: Consider:

  • Pool size
  • Token types
  • Fee structure
  • Market conditions

Q: What about upgrades?

A: Plan for:

  • Contract migration
  • State preservation
  • Parameter updates
  • Emergency procedures

Released under the MIT License by Vogeb.