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
-
function addLiquidity(uint amountA, uint amountB) external returns (uint liquidity)- Transfer
amountAof Yrd andamountBof Col from the caller into the contract - Approve the Uniswap V2 Router to spend both tokens
- Call
router.addLiquiditywith a deadline ofblock.timestamp - Emit a
LiquidityAddedevent with the actual amounts used and LP tokens received
- Transfer
-
function swapTokens(address tokenIn, uint amountIn, uint amountOutMin) external returns (uint amountOut)- Transfer
amountInoftokenInfrom the caller into the contract - Approve the Uniswap V2 Router to spend
tokenIn - Build the swap path
[tokenIn, tokenOut](wheretokenOutis the other token) - Call
router.swapExactTokensForTokenswith a deadline ofblock.timestamp - Transfer the received tokens back to the caller
- Emit a
TokensSwappedevent
- Transfer
-
function getTokenPrice(address token, uint amountIn) external view returns (uint amountOut)- Build the path
[token, otherToken]whereotherTokenis the other token in the pair - Call
router.getAmountsOut(amountIn, path)to query the current exchange rate - Return the expected output amount
- Build the path
-
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
- Build the path
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
amountAMinandamountBMininaddLiquidity, you can use0to accept any amount. - The
deadlineparameter can be set toblock.timestampto 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.