// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; /** * @title AereSwap — constant-product AMM * @notice Constant-product automated market maker for AERE Network * @dev Implements the x*y=k constant-product invariant, the canonical EVM * pool design. Each pair is its own contract; this factory uses a * simple single-pool design (token0/token1 paired against WAERE). * * Whitepaper alignment: * - "Ultra-High Performance DEX Engine" * - sub-second trade execution (matches AERE 100ms blocks) * - near-zero slippage (governed by liquidity depth, same as any AMM) */ interface IERC20 { function transfer(address to, uint256 amount) external returns (bool); function transferFrom(address from, address to, uint256 amount) external returns (bool); function balanceOf(address account) external view returns (uint256); function approve(address spender, uint256 amount) external returns (bool); } contract AereSwapPair { address public immutable token0; address public immutable token1; address public immutable factory; uint112 private reserve0; uint112 private reserve1; uint32 private blockTimestampLast; uint256 public constant MINIMUM_LIQUIDITY = 1000; uint256 public totalSupply; mapping(address => uint256) public balanceOf; event Mint(address indexed sender, uint256 amount0, uint256 amount1); event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to); event Swap( address indexed sender, uint256 amount0In, uint256 amount1In, uint256 amount0Out, uint256 amount1Out, address indexed to ); event Sync(uint112 reserve0, uint112 reserve1); constructor(address _token0, address _token1) { token0 = _token0; token1 = _token1; factory = msg.sender; } function getReserves() public view returns (uint112 r0, uint112 r1, uint32 ts) { return (reserve0, reserve1, blockTimestampLast); } function _update(uint256 balance0, uint256 balance1) private { require(balance0 <= type(uint112).max && balance1 <= type(uint112).max, "AereSwap: overflow"); reserve0 = uint112(balance0); reserve1 = uint112(balance1); blockTimestampLast = uint32(block.timestamp); emit Sync(reserve0, reserve1); } /** * @notice Mint LP tokens by depositing both tokens at the current ratio. * @dev Caller must have already transferred tokens to this contract. */ function mint(address to) external returns (uint256 liquidity) { (uint112 _r0, uint112 _r1, ) = getReserves(); uint256 balance0 = IERC20(token0).balanceOf(address(this)); uint256 balance1 = IERC20(token1).balanceOf(address(this)); uint256 amount0 = balance0 - _r0; uint256 amount1 = balance1 - _r1; uint256 _totalSupply = totalSupply; if (_totalSupply == 0) { liquidity = _sqrt(amount0 * amount1) - MINIMUM_LIQUIDITY; balanceOf[address(0)] = MINIMUM_LIQUIDITY; totalSupply += MINIMUM_LIQUIDITY; } else { uint256 a = (amount0 * _totalSupply) / _r0; uint256 b = (amount1 * _totalSupply) / _r1; liquidity = a < b ? a : b; } require(liquidity > 0, "AereSwap: zero liquidity"); balanceOf[to] += liquidity; totalSupply += liquidity; _update(balance0, balance1); emit Mint(msg.sender, amount0, amount1); } /** * @notice Burn LP tokens, withdraw both tokens. * @dev Caller must have transferred their LP tokens to this contract first. */ function burn(address to) external returns (uint256 amount0, uint256 amount1) { uint256 liquidity = balanceOf[address(this)]; uint256 balance0 = IERC20(token0).balanceOf(address(this)); uint256 balance1 = IERC20(token1).balanceOf(address(this)); uint256 _totalSupply = totalSupply; amount0 = (liquidity * balance0) / _totalSupply; amount1 = (liquidity * balance1) / _totalSupply; require(amount0 > 0 && amount1 > 0, "AereSwap: zero burn output"); balanceOf[address(this)] = 0; totalSupply -= liquidity; IERC20(token0).transfer(to, amount0); IERC20(token1).transfer(to, amount1); balance0 = IERC20(token0).balanceOf(address(this)); balance1 = IERC20(token1).balanceOf(address(this)); _update(balance0, balance1); emit Burn(msg.sender, amount0, amount1, to); } /** * @notice Swap with constant-product invariant. 0.3% fee retained in pool (LP earns). * @dev Caller must have transferred input tokens to this contract first. */ function swap(uint256 amount0Out, uint256 amount1Out, address to) external { require(amount0Out > 0 || amount1Out > 0, "AereSwap: zero output"); (uint112 _r0, uint112 _r1, ) = getReserves(); require(amount0Out < _r0 && amount1Out < _r1, "AereSwap: insufficient liquidity"); if (amount0Out > 0) IERC20(token0).transfer(to, amount0Out); if (amount1Out > 0) IERC20(token1).transfer(to, amount1Out); uint256 balance0 = IERC20(token0).balanceOf(address(this)); uint256 balance1 = IERC20(token1).balanceOf(address(this)); uint256 amount0In = balance0 > _r0 - amount0Out ? balance0 - (_r0 - amount0Out) : 0; uint256 amount1In = balance1 > _r1 - amount1Out ? balance1 - (_r1 - amount1Out) : 0; require(amount0In > 0 || amount1In > 0, "AereSwap: zero input"); // Fee: 0.3% taken from input. Adjusted balances must satisfy x*y >= k. uint256 balance0Adj = balance0 * 1000 - amount0In * 3; uint256 balance1Adj = balance1 * 1000 - amount1In * 3; require(balance0Adj * balance1Adj >= uint256(_r0) * _r1 * 1_000_000, "AereSwap: K invariant"); _update(balance0, balance1); emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to); } function _sqrt(uint256 x) private pure returns (uint256 r) { if (x == 0) return 0; uint256 z = (x + 1) / 2; r = x; while (z < r) { r = z; z = (x / z + z) / 2; } } } contract AereSwapFactory { address public immutable WAERE; address public feeTo; address public feeToSetter; mapping(address => mapping(address => address)) public getPair; address[] public allPairs; event PairCreated(address indexed token0, address indexed token1, address pair, uint256 index); constructor(address _WAERE, address _feeToSetter) { WAERE = _WAERE; feeToSetter = _feeToSetter; } function allPairsLength() external view returns (uint256) { return allPairs.length; } function createPair(address tokenA, address tokenB) external returns (address pair) { require(tokenA != tokenB, "AereSwap: identical tokens"); (address t0, address t1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); require(t0 != address(0), "AereSwap: zero address"); require(getPair[t0][t1] == address(0), "AereSwap: pair exists"); AereSwapPair p = new AereSwapPair(t0, t1); pair = address(p); getPair[t0][t1] = pair; getPair[t1][t0] = pair; allPairs.push(pair); emit PairCreated(t0, t1, pair, allPairs.length - 1); } function setFeeTo(address _feeTo) external { require(msg.sender == feeToSetter, "AereSwap: forbidden"); feeTo = _feeTo; } }