Token Exchange in Sword Art Online

As Aincrad's economy grows, adventurers need a reliable way to trade currencies. The two primary currencies circulating in the floating castle are Yrd — the basic currency earned from defeating monsters — and Col — the advanced currency used for high-level gear and rare items.

Kayaba has decided to integrate a decentralized exchange powered by Uniswap V2 into the Aincrad system. This will allow players to freely swap between Yrd and Col at fair market rates determined by supply and demand, with no middleman taking a cut.

Your task is to implement the TokenExchange contract that wraps the Uniswap V2 Router so that players can add liquidity and swap tokens seamlessly.

Core Mechanics

The exchange operates on the Automated Market Maker (AMM) model used by Uniswap V2:

  • Liquidity Providers deposit equal value of both tokens into a pool and receive LP tokens in return.
  • Traders swap one token for another; the exchange rate is determined by the constant product formula x * y = k.

Functions

  1. function addLiquidity(uint amountA, uint amountB) external returns (uint liquidity)

    • Transfer amountA of Yrd and amountB of Col from the caller into the contract
    • Approve the Uniswap V2 Router to spend both tokens
    • Call router.addLiquidity with a deadline of block.timestamp
    • Emit a LiquidityAdded event with the actual amounts used and LP tokens received
  2. function swapTokens(address tokenIn, uint amountIn, uint amountOutMin) external returns (uint amountOut)

    • Transfer amountIn of tokenIn from the caller into the contract
    • Approve the Uniswap V2 Router to spend tokenIn
    • Build the swap path [tokenIn, tokenOut] (where tokenOut is the other token)
    • Call router.swapExactTokensForTokens with a deadline of block.timestamp
    • Transfer the received tokens back to the caller
    • Emit a TokensSwapped event
  3. function getTokenPrice(address token, uint amountIn) external view returns (uint amountOut)

    • Build the path [token, otherToken] where otherToken is the other token in the pair
    • Call router.getAmountsOut(amountIn, path) to query the current exchange rate
    • Return the expected output amount
  4. function getTokenEthPrice(address token, uint amountIn) external view returns (uint ethAmount)

    • Build the path [token, router.WETH()] to route through the WETH pair
    • Call router.getAmountsOut(amountIn, path) to query the current ETH price
    • Return the expected ETH amount

Events

event LiquidityAdded(address indexed provider, uint amountA, uint amountB, uint liquidity);

  • Emitted when a player provides liquidity to the pool

event TokensSwapped(address indexed swapper, address tokenIn, address tokenOut, uint amountIn, uint amountOut);

  • Emitted when a player swaps one token for another

Uniswap V2 API Reference

The Uniswap V2 Router interface is available via:

import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";

Key functions you will use:

function addLiquidity(
    address tokenA,
    address tokenB,
    uint amountADesired,
    uint amountBDesired,
    uint amountAMin,
    uint amountBMin,
    address to,
    uint deadline
) external returns (uint amountA, uint amountB, uint liquidity);

function swapExactTokensForTokens(
    uint amountIn,
    uint amountOutMin,
    address[] calldata path,
    address to,
    uint deadline
) external returns (uint[] memory amounts);

function getAmountsOut(
    uint amountIn,
    address[] calldata path
) external view returns (uint[] memory amounts);

function WETH() external pure returns (address);

Hints

  • Use IERC20(token).transferFrom(msg.sender, address(this), amount) to pull tokens from the caller.
  • Use IERC20(token).approve(address(router), amount) before calling the router.
  • The swap path is an array of token addresses: [tokenIn, tokenOut].
  • For amountAMin and amountBMin in addLiquidity, you can use 0 to accept any amount.
  • The deadline parameter can be set to block.timestamp to execute immediately.
  • Use router.getAmountsOut(amountIn, path) to query the expected output without executing a swap.
  • Use router.WETH() to get the WETH token address for ETH price queries.

Code

Was this page helpful?