From 180130b992031121036831ace335567fefc1d44c Mon Sep 17 00:00:00 2001 From: "tingzhao.ytz" Date: Fri, 2 Aug 2024 16:26:51 +0800 Subject: [PATCH] feat: init contract code --- demo-contract/contracts/wtfswap/Factory.sol | 8 ++ demo-contract/contracts/wtfswap/Pool.sol | 45 ++++++++-- .../contracts/wtfswap/PositionManager.sol | 86 +++++++++++++++++-- .../contracts/wtfswap/SwapRouter.sol | 61 ++++++++++++- .../contracts/wtfswap/interfaces/IFactory.sol | 6 ++ .../contracts/wtfswap/interfaces/IPool.sol | 6 ++ .../wtfswap/interfaces/IPositionManager.sol | 16 ++-- .../wtfswap/interfaces/ISwapRouter.sol | 6 ++ 8 files changed, 209 insertions(+), 25 deletions(-) diff --git a/demo-contract/contracts/wtfswap/Factory.sol b/demo-contract/contracts/wtfswap/Factory.sol index 1391bf4..2b71405 100644 --- a/demo-contract/contracts/wtfswap/Factory.sol +++ b/demo-contract/contracts/wtfswap/Factory.sol @@ -31,6 +31,14 @@ contract Factory is IFactory { return pools[tokenA][tokenB]; } + function getPool( + address tokenA, + address tokenB, + uint32 index + ) external view override returns (address) { + return pools[tokenA][tokenB][index]; + } + function createPool( address tokenA, address tokenB, diff --git a/demo-contract/contracts/wtfswap/Pool.sol b/demo-contract/contracts/wtfswap/Pool.sol index 491338c..16167bc 100644 --- a/demo-contract/contracts/wtfswap/Pool.sol +++ b/demo-contract/contracts/wtfswap/Pool.sol @@ -2,10 +2,9 @@ pragma solidity ^0.8.24; import "./interfaces/IPool.sol"; -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "./interfaces/IFactory.sol"; -contract Pool is IPool, ERC20 { +contract Pool is IPool { /// @inheritdoc IPool address public immutable override factory; /// @inheritdoc IPool @@ -26,32 +25,62 @@ contract Pool is IPool, ERC20 { /// @inheritdoc IPool uint128 public override liquidity; + // 用一个 mapping 来存放所有 Position 的信息 + mapping(address => Position) public positions; + constructor() { - ERC20("Wtfswap", "WTF-SWAP"); // constructor 中初始化 immutable 的常量 // Factory 创建 Pool 时会通 new Pool{salt: salt}() 的方式创建 Pool 合约,通过 salt 指定 Pool 的地址,这样其他地方也可以推算出 Pool 的地址 // 参数通过读取 Factory 合约的 parameters 获取 // 不通过构造函数传入,因为 CREATE2 会根据 initcode 计算出新地址(new_address = hash(0xFF, sender, salt, bytecode)),带上参数就不能计算出稳定的地址了 - (factory, token0, token1, fee, tickLower, tickUpper) = IFactory( + (factory, token0, token1, tickLower, tickUpper, fee) = IFactory( msg.sender ).parameters(); } - function initialize(uint160 sqrtPriceX96_) external override {} + function initialize(uint160 sqrtPriceX96_) external override { + // 初始化 Pool 的 sqrtPriceX96 + sqrtPriceX96 = sqrtPriceX96_; + } function mint( address recipient, uint128 amount, bytes calldata data - ) external override returns (uint256 amount0, uint256 amount1) {} + ) external override returns (uint256 amount0, uint256 amount1) { + // 基于 amount 计算出当前需要多少 amount0 和 amount1 + // TODO 当前先写个假的 + (amount0, amount1) = (amount / 2, amount / 2); + // 把流动性记录到对应的 position 中 + positions[recipient].liquidity += amount; + // 回调 mintCallback + IMintCallback(recipient).mintCallback(amount0, amount1, data); + // TODO 检查钱到位了没有,如果到位了对应修改相关信息 + } function collect( address recipient - ) external override returns (uint128 amount0, uint128 amount1) {} + ) external override returns (uint128 amount0, uint128 amount1) { + // 获取当前用户的 position + Position storage position = positions[msg.sender]; + // TODO 把钱退给用户 recipient + + // 修改 position 中的信息 + position.tokensOwed0 -= amount0; + position.tokensOwed1 -= amount1; + } function burn( uint128 amount - ) external override returns (uint256 amount0, uint256 amount1) {} + ) external override returns (uint256 amount0, uint256 amount1) { + // 修改 positions 中的信息 + positions[msg.sender].liquidity -= amount; + // 获取燃烧后的 amount0 和 amount1 + // TODO 当前先写个假的 + (amount0, amount1) = (amount / 2, amount / 2); + positions[msg.sender].tokensOwed0 += amount0; + positions[msg.sender].tokensOwed1 += amount1; + } function swap( address recipient, diff --git a/demo-contract/contracts/wtfswap/PositionManager.sol b/demo-contract/contracts/wtfswap/PositionManager.sol index 58db3f2..befbdd5 100644 --- a/demo-contract/contracts/wtfswap/PositionManager.sol +++ b/demo-contract/contracts/wtfswap/PositionManager.sol @@ -2,13 +2,24 @@ pragma solidity ^0.8.24; pragma abicoder v2; +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "./interfaces/IPositionManager.sol"; +import "./interfaces/IPool.sol"; +import "./interfaces/IPoolManager.sol"; -contract PositionManager is IPositionManager { - function getPositions( - address owner - ) external view override returns (uint256[] memory positionIds) {} +contract PositionManager is IPositionManager, ERC721 { + // 保存 PoolManager 合约地址 + IPoolManager public poolManager; + constructor(address _poolManger) ERC721("WTFSwapPosition", "WTFP") { + poolManager = IPoolManager(_poolManger); + } + + // 用一个 mapping 来存放所有 Position 的信息 + mapping(uint256 => PositionInfo) public positions; + + // 通过 positionId 获取 Position 信息,positionId 就是 NFT 的 tokenId + // 如果要获得某个用户的所有的 Position 信息,需要自己遍历所有的 tokenId,可以通过 ZAN 的节点服务来获取 function getPositionInfo( uint256 positionId ) external view override returns (PositionInfo memory positionInfo) {} @@ -25,14 +36,75 @@ contract PositionManager is IPositionManager { uint256 amount0, uint256 amount1 ) - {} + { + // mint 一个 NFT 作为 position 发给 LP + // NFT 的 tokenId 就是 positionId + // 通过 MintParams 里面的 token0 和 token1 以及 index 获取对应的 Pool + // 调用 poolManager 的 getPool 方法获取 Pool 地址 + address _pool = poolManager.getPool( + params.token0, + params.token1, + params.index + ); + IPool pool = IPool(_pool); + // 通过获取 pool 相关信息,结合 params.amount0Desired 和 params.amount1Desired 计算这次要注入的流动性 + // TODO: 计算 _liquidity,这里只是随便写的 + uint128 _liquidity = uint128( + params.amount0Desired * params.amount1Desired + ); + // data 是 mint 后回调 PositionManager 会额外带的数据 + // 需要 PoistionManger 实现回调,在回调中给 Pool 打钱 + bytes memory data = abi.encode("todo"); + (amount0, amount1) = pool.mint(params.recipient, _liquidity, data); + positionId = 1; + liquidity = _liquidity; + // TODO 以 NFT 的形式把 Position 的所有权发给 LP + } function burn( uint256 positionId - ) external override returns (uint256 amount0, uint256 amount1) {} + ) external override returns (uint256 amount0, uint256 amount1) { + // TODO 检查 positionId 是否属于 msg.sender + // 移除流动性,但是 token 还是保留在 pool 中,需要再调用 collect 方法才能取回 token + // 通过 positionId 获取对应 LP 的流动性 + uint128 _liquidity = positions[positionId].liquidity; + // 调用 Pool 的方法给 LP 退流动性 + address _pool = poolManager.getPool( + positions[positionId].token0, + positions[positionId].token1, + positions[positionId].index + ); + IPool pool = IPool(_pool); + (amount0, amount1) = pool.burn(_liquidity); + // 修改 positionInfo 中的信息 + positions[positionId].liquidity = 0; + positions[positionId].tokensOwed0 = amount0; + positions[positionId].tokensOwed1 = amount1; + } function collect( uint256 positionId, address recipient - ) external override returns (uint256 amount0, uint256 amount1) {} + ) external override returns (uint256 amount0, uint256 amount1) { + // TODO 检查 positionId 是否属于 msg.sender + // 调用 Pool 的方法给 LP 退流动性 + address _pool = poolManager.getPool( + positions[positionId].token0, + positions[positionId].token1, + positions[positionId].index + ); + IPool pool = IPool(_pool); + (amount0, amount1) = pool.collect(recipient); + // 修改 positionInfo 中的信息 + positions[positionId].tokensOwed0 = 0; + positions[positionId].tokensOwed1 = 0; + } + + function mintCallback( + uint256 amount0, + uint256 amount1, + bytes calldata data + ) external override { + // 在这里给 Pool 打钱,需要用户先 approve 足够的金额,这里才会成功 + } } diff --git a/demo-contract/contracts/wtfswap/SwapRouter.sol b/demo-contract/contracts/wtfswap/SwapRouter.sol index 0e9cbb8..3476ff5 100644 --- a/demo-contract/contracts/wtfswap/SwapRouter.sol +++ b/demo-contract/contracts/wtfswap/SwapRouter.sol @@ -3,21 +3,76 @@ pragma solidity ^0.8.24; pragma abicoder v2; import "./interfaces/ISwapRouter.sol"; +import "./interfaces/IPool.sol"; +import "./interfaces/IPoolManager.sol"; contract SwapRouter is ISwapRouter { + IPoolManager public poolManager; + + constructor(address _poolManager) { + poolManager = IPoolManager(_poolManager); + } + + // 确定输入的 token 交易 function exactInput( ExactInputParams calldata params - ) external payable override returns (uint256 amountOut) {} + ) external payable override returns (uint256 amountOut) { + // 用 index 记录当前正在读取的 index + uint256 index = 0; + // while 循环遍历 indexPath,获取每个 pool 的价格 + while (index < params.indexPath.length) { + address _pool = poolManager.getPool( + params.tokenIn, + params.tokenOut, + params.indexPath[index] + ); + IPool pool = IPool(_pool); + // TODO 交易 + bytes memory data; + // 交易的钱统一转给本合约,最后都完成之后在 swapCallback 中打给用户 + pool.swap(msg.sender, true, 12, 12, data); + index++; + } + } + // 确定输出的 token 交易 function exactOutput( ExactOutputParams calldata params ) external payable override returns (uint256 amountIn) {} + // 确认输入的 token,估算可以获得多少输出的 token function quoteExactInput( QuoteExactInputParams memory params - ) external override returns (uint256 amountOut) {} + ) external view override returns (uint256 amountOut) { + // 用 index 记录当前正在读取的 index + uint256 index = 0; + // while 循环遍历 indexPath,获取每个 pool 的价格 + while (index < params.indexPath.length) { + address _pool = poolManager.getPool( + params.tokenIn, + params.tokenOut, + params.indexPath[index] + ); + IPool pool = IPool(_pool); + uint160 sqrtPriceX96 = pool.sqrtPriceX96(); + // TODO 计算 amountOut + amountOut = sqrtPriceX96; + // 更新 index + index++; + } + } + // 确认输出的 token,估算需要多少输入的 token function quoteExactOutput( QuoteExactOutputParams memory params - ) external override returns (uint256 amountIn) {} + ) external view override returns (uint256 amountIn) {} + + function swapCallback( + uint256 amount0In, + uint256 amount1In, + bytes calldata data + ) external override { + // 每次 swap 后 pool 会调用这个方法 + // 最后一次 swap 完成后这里统一把钱打给用户 + } } diff --git a/demo-contract/contracts/wtfswap/interfaces/IFactory.sol b/demo-contract/contracts/wtfswap/interfaces/IFactory.sol index 6719916..01d3bbe 100644 --- a/demo-contract/contracts/wtfswap/interfaces/IFactory.sol +++ b/demo-contract/contracts/wtfswap/interfaces/IFactory.sol @@ -28,6 +28,12 @@ interface IFactory { address tokenB ) external view returns (address[] memory pools); + function getPool( + address tokenA, + address tokenB, + uint32 index + ) external view returns (address pool); + function createPool( address tokenA, address tokenB, diff --git a/demo-contract/contracts/wtfswap/interfaces/IPool.sol b/demo-contract/contracts/wtfswap/interfaces/IPool.sol index aa48f46..149d986 100644 --- a/demo-contract/contracts/wtfswap/interfaces/IPool.sol +++ b/demo-contract/contracts/wtfswap/interfaces/IPool.sol @@ -18,6 +18,12 @@ interface ISwapCallback { } interface IPool { + struct Position { + uint128 liquidity; + uint256 tokensOwed0; + uint256 tokensOwed1; + } + function factory() external view returns (address); function token0() external view returns (address); diff --git a/demo-contract/contracts/wtfswap/interfaces/IPositionManager.sol b/demo-contract/contracts/wtfswap/interfaces/IPositionManager.sol index 42894db..a9c54b0 100644 --- a/demo-contract/contracts/wtfswap/interfaces/IPositionManager.sol +++ b/demo-contract/contracts/wtfswap/interfaces/IPositionManager.sol @@ -3,17 +3,13 @@ pragma solidity ^0.8.24; pragma abicoder v2; interface IPositionManager { - function getPositions( - address owner - ) external view returns (uint256[] memory positionIds); - struct PositionInfo { - // address owner; + address owner; address token0; address token1; + uint32 index; uint24 fee; - int128 liquidity; - // tick range + uint128 liquidity; int24 tickLower; int24 tickUpper; uint256 tokensOwed0; @@ -54,4 +50,10 @@ interface IPositionManager { uint256 positionId, address recipient ) external returns (uint256 amount0, uint256 amount1); + + function mintCallback( + uint256 amount0, + uint256 amount1, + bytes calldata data + ) external; } diff --git a/demo-contract/contracts/wtfswap/interfaces/ISwapRouter.sol b/demo-contract/contracts/wtfswap/interfaces/ISwapRouter.sol index 49c8f8e..1415c85 100644 --- a/demo-contract/contracts/wtfswap/interfaces/ISwapRouter.sol +++ b/demo-contract/contracts/wtfswap/interfaces/ISwapRouter.sol @@ -56,4 +56,10 @@ interface ISwapRouter { function quoteExactOutput( QuoteExactOutputParams memory params ) external returns (uint256 amountIn); + + function swapCallback( + uint256 amount0In, + uint256 amount1In, + bytes calldata data + ) external; }