diff --git a/02_NodeService/readme.md b/02_NodeService/readme.md index da52548..fdda29c 100644 --- a/02_NodeService/readme.md +++ b/02_NodeService/readme.md @@ -4,13 +4,13 @@ 节点服务是 DApp 开发必不可少的服务。它是一个运行在区块链网络上的服务,它可以帮助你与区块链网络进行交互。在 DApp 开发中,我们需要通过节点服务来获取区块链的数据,发送交易等。 -在以太坊网络中,我们可以通过 [ZAN](https://zan.top/)、[Infura](https://infura.io/)、[Alchemy](https://www.alchemy.com/) 等服务来获取节点服务。这些服务都提供了免费的节点服务,当然,它们也提供了付费的服务,如果你的 DApp 需要更高的性能,你可以考虑使用它们的付费服务。 +在以太坊网络中,我们可以通过 [ZAN](https://zan.top?chInfo=wtf)、[Infura](https://infura.io/)、[Alchemy](https://www.alchemy.com/) 等服务来获取节点服务。这些服务都提供了免费的节点服务,当然,它们也提供了付费的服务,如果你的 DApp 需要更高的性能,你可以考虑使用它们的付费服务。 ## 配置节点服务 -这里以 [ZAN 的节点服务](https://zan.top/home/node-service)为例,指引你如何配置节点服务。 +这里以 [ZAN 的节点服务](https://zan.top/home/node-service?chInfo=wtf)为例,指引你如何配置节点服务。 -首先注册并登录 [https://zan.top](https://zan.top) 之后进入到节点服务的控制台 [https://zan.top/service/apikeys](https://zan.top/service/apikeys) 创建一个 Key,每个 Key 都有默认的免费额度,对于微型项目来说够用了,但是对于生产环境的项目来说,请结合实际情况购买节点服务。 +首先注册并登录 [https://zan.top](https://zan.top?chInfo=wtf) 之后进入到节点服务的控制台 [https://zan.top/service/apikeys](https://zan.top/service/apikeys?chInfo=wtf) 创建一个 Key,每个 Key 都有默认的免费额度,对于微型项目来说够用了,但是对于生产环境的项目来说,请结合实际情况购买节点服务。 创建成功后你会看到如下的页面: diff --git a/P101_InitContract/code/interfaces/IFactory.sol b/P101_ContractsDesign/code/IFactory.sol similarity index 100% rename from P101_InitContract/code/interfaces/IFactory.sol rename to P101_ContractsDesign/code/IFactory.sol diff --git a/P101_InitContract/code/interfaces/IPool.sol b/P101_ContractsDesign/code/IPool.sol similarity index 100% rename from P101_InitContract/code/interfaces/IPool.sol rename to P101_ContractsDesign/code/IPool.sol diff --git a/P101_InitContract/code/interfaces/IPoolManager.sol b/P101_ContractsDesign/code/IPoolManager.sol similarity index 100% rename from P101_InitContract/code/interfaces/IPoolManager.sol rename to P101_ContractsDesign/code/IPoolManager.sol diff --git a/P101_InitContract/code/interfaces/IPositionManager.sol b/P101_ContractsDesign/code/IPositionManager.sol similarity index 100% rename from P101_InitContract/code/interfaces/IPositionManager.sol rename to P101_ContractsDesign/code/IPositionManager.sol diff --git a/P101_InitContract/code/interfaces/ISwapRouter.sol b/P101_ContractsDesign/code/ISwapRouter.sol similarity index 100% rename from P101_InitContract/code/interfaces/ISwapRouter.sol rename to P101_ContractsDesign/code/ISwapRouter.sol diff --git a/P101_InitContract/readme.md b/P101_ContractsDesign/readme.md similarity index 97% rename from P101_InitContract/readme.md rename to P101_ContractsDesign/readme.md index 245e755..9fb5d58 100644 --- a/P101_InitContract/readme.md +++ b/P101_ContractsDesign/readme.md @@ -111,7 +111,7 @@ function createAndInitializePoolIfNecessary( ) external payable returns (address pool); ``` -完整的接口在 [IPoolManager](./code/interfaces/IPoolManager.sol) 中。 +完整的接口在 [IPoolManager](./code/IPoolManager.sol) 中。 #### PositionManager @@ -225,7 +225,7 @@ function collect( ) external returns (uint256 amount0, uint256 amount1); ``` -完整的接口在 [IPositionManager](./code/interfaces/IPositionManager.sol) 中。 +完整的接口在 [IPositionManager](./code/IPositionManager.sol) 中。 #### SwapRouter @@ -302,7 +302,7 @@ function exactOutput( ) external payable returns (uint256 amountIn); ``` -完整的接口在 [ISwapRouter](./code/interfaces/ISwapRouter.sol) 中。 +完整的接口在 [ISwapRouter](./code/ISwapRouter.sol) 中。 #### Factory @@ -340,7 +340,7 @@ function parameters() returns (address factory, address token0, address token1, uint24 fee); ``` -完整的接口在 [IFactory](./code/interfaces/IFactory.sol) 中。 +完整的接口在 [IFactory](./code/IFactory.sol) 中。 #### Pool @@ -474,4 +474,4 @@ interface ISwapCallback { } ``` -完整的接口在 [IPool.sol](./code/interfaces/IPool.sol) 中。 +完整的接口在 [IPool.sol](./code/IPool.sol) 中。 diff --git a/P101_InitContract/code/Factory.sol b/P102_InitContracts/code/Factory.sol similarity index 100% rename from P101_InitContract/code/Factory.sol rename to P102_InitContracts/code/Factory.sol diff --git a/P101_InitContract/code/Pool.sol b/P102_InitContracts/code/Pool.sol similarity index 95% rename from P101_InitContract/code/Pool.sol rename to P102_InitContracts/code/Pool.sol index 9527ad9..057dd8d 100644 --- a/P101_InitContract/code/Pool.sol +++ b/P102_InitContracts/code/Pool.sol @@ -32,9 +32,9 @@ contract Pool is IPool { {} function initialize( - uint160 sqrtPriceX96, - int24 tickLower, - int24 tickUpper + uint160 sqrtPriceX96_, + int24 tickLower_, + int24 tickUpper_ ) external override {} function mint( diff --git a/P101_InitContract/code/PoolManager.sol b/P102_InitContracts/code/PoolManager.sol similarity index 100% rename from P101_InitContract/code/PoolManager.sol rename to P102_InitContracts/code/PoolManager.sol diff --git a/P101_InitContract/code/PositionManager.sol b/P102_InitContracts/code/PositionManager.sol similarity index 100% rename from P101_InitContract/code/PositionManager.sol rename to P102_InitContracts/code/PositionManager.sol diff --git a/P101_InitContract/code/SwapRouter.sol b/P102_InitContracts/code/SwapRouter.sol similarity index 100% rename from P101_InitContract/code/SwapRouter.sol rename to P102_InitContracts/code/SwapRouter.sol diff --git a/P102_InitContracts/code/interfaces/IFactory.sol b/P102_InitContracts/code/interfaces/IFactory.sol new file mode 100644 index 0000000..9c74cbc --- /dev/null +++ b/P102_InitContracts/code/interfaces/IFactory.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.24; + +interface IFactory { + function parameters() + external + view + returns (address factory, address token0, address token1, uint24 fee); + + event PoolCreated( + address indexed token0, + address indexed token1, + uint24 indexed fee, + address pool + ); + + function getPool( + address tokenA, + address tokenB, + uint24 fee + ) external view returns (address pool); + + function createPool( + address tokenA, + address tokenB, + uint24 fee + ) external returns (address pool); +} diff --git a/P102_InitContracts/code/interfaces/IPool.sol b/P102_InitContracts/code/interfaces/IPool.sol new file mode 100644 index 0000000..9da3828 --- /dev/null +++ b/P102_InitContracts/code/interfaces/IPool.sol @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.24; + +interface IMintCallback { + function mintCallback( + uint256 amount0Owed, + uint256 amount1Owed, + bytes calldata data + ) external; +} + +interface ISwapCallback { + function swapCallback( + int256 amount0Delta, + int256 amount1Delta, + bytes calldata data + ) external; +} + +interface IPool { + function factory() external view returns (address); + + function token0() external view returns (address); + + function token1() external view returns (address); + + function fee() external view returns (uint24); + + function tickLower() external view returns (int24); + + function tickUpper() external view returns (int24); + + function sqrtPriceX96() external view returns (uint160); + + function tick() external view returns (int24); + + function liquidity() external view returns (uint128); + + function positions( + int8 positionType + ) + external + view + returns (uint128 _liquidity, uint128 tokensOwed0, uint128 tokensOwed1); + + function initialize( + uint160 sqrtPriceX96, + int24 tickLower, + int24 tickUpper + ) external; + + event Mint( + address sender, + address indexed owner, + int8 indexed positionType, + uint128 amount, + uint256 amount0, + uint256 amount1 + ); + + function mint( + address recipient, + int8 positionType, + uint128 amount, + bytes calldata data + ) external returns (uint256 amount0, uint256 amount1); + + event Collect( + address indexed owner, + address recipient, + int8 indexed positionType, + uint128 amount0, + uint128 amount1 + ); + + function collect( + address recipient, + int8 positionType + ) external returns (uint128 amount0, uint128 amount1); + + event Burn( + address indexed owner, + int8 indexed positionType, + uint128 amount, + uint256 amount0, + uint256 amount1 + ); + + function burn( + int8 positionType + ) external returns (uint256 amount0, uint256 amount1); + + event Swap( + address indexed sender, + address indexed recipient, + int256 amount0, + int256 amount1, + uint160 sqrtPriceX96, + uint128 liquidity, + int24 tick + ); + + function swap( + address recipient, + bool zeroForOne, + int256 amountSpecified, + uint160 sqrtPriceLimitX96, + bytes calldata data + ) external returns (int256 amount0, int256 amount1); +} diff --git a/P102_InitContracts/code/interfaces/IPoolManager.sol b/P102_InitContracts/code/interfaces/IPoolManager.sol new file mode 100644 index 0000000..83c65c3 --- /dev/null +++ b/P102_InitContracts/code/interfaces/IPoolManager.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.24; +pragma abicoder v2; + +interface IPoolManager { + struct PoolKey { + address token0; + address token1; + uint24 fee; + } + + function getPools() external view returns (PoolKey[] memory pools); + + function getTokens() external view returns (address[] memory tokens); + + function getTokenPools( + address token + ) external view returns (PoolKey[] memory pools); + + struct PoolInfo { + // the current protocol fee as a percentage of the swap fee taken on withdrawal + // represented as an integer denominator (1/x)% + uint8 feeProtocol; + // tick range + int24 tickLower; + int24 tickUpper; + // the current tick + int24 tick; + // the current price + uint160 sqrtPriceX96; + } + + function getPoolInfo( + address token0, + address token1, + uint24 fee + ) external view returns (PoolInfo memory poolInfo); + + struct CreateAndInitializeParams { + address token0; + address token1; + uint24 fee; + int24 tickLower; + int24 tickUpper; + uint160 sqrtPriceX96; + } + + function createAndInitializePoolIfNecessary( + CreateAndInitializeParams calldata params + ) external payable returns (address pool); +} diff --git a/P102_InitContracts/code/interfaces/IPositionManager.sol b/P102_InitContracts/code/interfaces/IPositionManager.sol new file mode 100644 index 0000000..aab68c5 --- /dev/null +++ b/P102_InitContracts/code/interfaces/IPositionManager.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +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 token0; + address token1; + uint24 fee; + int128 liquidity; + // tick range + int24 tickLower; + int24 tickUpper; + uint256 tokensOwed0; + uint256 tokensOwed1; + } + + function getPositionInfo( + uint256 positionId + ) external view returns (PositionInfo memory positionInfo); + + struct MintParams { + address token0; + address token1; + uint24 fee; + int8 positionType; // lower:-1; medium:0; upper:1 + uint256 amount0Desired; + uint256 amount1Desired; + address recipient; + uint256 deadline; + } + + function mint( + MintParams calldata params + ) + external + payable + returns ( + uint256 positionId, + uint128 liquidity, + uint256 amount0, + uint256 amount1 + ); + + function burn( + uint256 positionId + ) external returns (uint256 amount0, uint256 amount1); + + function collect( + uint256 positionId, + address recipient + ) external returns (uint256 amount0, uint256 amount1); +} diff --git a/P102_InitContracts/code/interfaces/ISwapRouter.sol b/P102_InitContracts/code/interfaces/ISwapRouter.sol new file mode 100644 index 0000000..bcddf12 --- /dev/null +++ b/P102_InitContracts/code/interfaces/ISwapRouter.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.24; +pragma abicoder v2; + +interface ISwapRouter { + struct ExactInputParams { + address tokenIn; + address tokenOut; + address recipient; + uint256 deadline; + uint256 amountIn; + uint256 amountOutMinimum; + uint160 sqrtPriceLimitX96; + } + + function exactInput( + ExactInputParams calldata params + ) external payable returns (uint256 amountOut); + + struct ExactOutputParams { + address tokenIn; + address tokenOut; + address recipient; + uint256 deadline; + uint256 amountOut; + uint256 amountInMaximum; + uint160 sqrtPriceLimitX96; + } + + function exactOutput( + ExactOutputParams calldata params + ) external payable returns (uint256 amountIn); + + struct QuoteExactInputParams { + address tokenIn; + address tokenOut; + uint256 amountIn; + uint160 sqrtPriceLimitX96; + } + + function quoteExactInput( + QuoteExactInputParams memory params + ) external returns (uint256 amountOut); + + struct QuoteExactOutputParams { + address tokenIn; + address tokenOut; + uint256 amount; + uint160 sqrtPriceLimitX96; + } + + function quoteExactOutput( + QuoteExactOutputParams memory params + ) external returns (uint256 amountIn); +} diff --git a/P102_InitContracts/img/deploy.png b/P102_InitContracts/img/deploy.png new file mode 100644 index 0000000..64981da Binary files /dev/null and b/P102_InitContracts/img/deploy.png differ diff --git a/P102_InitContracts/readme.md b/P102_InitContracts/readme.md new file mode 100644 index 0000000..a329faf --- /dev/null +++ b/P102_InitContracts/readme.md @@ -0,0 +1,127 @@ +这一讲我们将在本地开发环境中初始化合约,正式启动开发。 + +--- + +## 初始化合约 + +Wtfswap 的合约开发我们继续基于之前在[《合约本地开发和测试环境》](../14_LocalDev/readme.md)中搭建的本地开发环境开发,如果你还没有搭建过,请基于那一讲课程搭建。 + +我们结合在上一讲中接口的设计,我们新增一个 `contract/wtfswap` 的目录按照如下结构初始化合约: + +``` +- contracts + - wtfswap + - interfaces + - IFactory.sol + - IPool.sol + - IPoolManager.sol + - IPositionManager.sol + - ISwapRouter.sol + - Factory.sol + - Pool.sol + - PoolManager.sol + - PositionManager.sol + - SwapRouter.sol +``` + +每一个合约文件我们都对应初始化好一个基础的架子,以 `Pool.sol` 为例: + +```solidity +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.24; + +import "./interfaces/IPool.sol"; + +contract Pool is IPool { + function factory() external view override returns (address) {} + + function token0() external view override returns (address) {} + + function token1() external view override returns (address) {} + + function fee() external view override returns (uint24) {} + + function tickLower() external view override returns (int24) {} + + function tickUpper() external view override returns (int24) {} + + function sqrtPriceX96() external view override returns (uint160) {} + + function tick() external view override returns (int24) {} + + function liquidity() external view override returns (uint128) {} + + function positions( + int8 positionType + ) + external + view + override + returns (uint128 _liquidity, uint128 tokensOwed0, uint128 tokensOwed1) + {} + + function initialize( + uint160 sqrtPriceX96_, + int24 tickLower_, + int24 tickUpper_ + ) external override {} + + function mint( + address recipient, + int8 positionType, + uint128 amount, + bytes calldata data + ) external override returns (uint256 amount0, uint256 amount1) {} + + function collect( + address recipient, + int8 positionType + ) external override returns (uint128 amount0, uint128 amount1) {} + + function burn( + int8 positionType + ) external override returns (uint256 amount0, uint256 amount1) {} + + function swap( + address recipient, + bool zeroForOne, + int256 amountSpecified, + uint160 sqrtPriceLimitX96, + bytes calldata data + ) external override returns (int256 amount0, int256 amount1) {} +} +``` + +其它合约对应的代码可以参考 [code](./code/) 查看。 + +初始化完成后执行 `npx hardhat compile` 编译合约。 + +## 初始化部署脚本 + +结合之前[《合约本地开发和测试环境》](../14_LocalDev/readme.md)教程的内容,我们新建 `ignition/modules/Wtfswap.ts` 文件,编写部署脚本: + +```ts +import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; + +const WtfswapModule = buildModule("Wtfswap", (m) => { + const pool = m.contract("Pool"); + const factory = m.contract("Factory"); + const poolManager = m.contract("PoolManager"); + const swapRouter = m.contract("SwapRouter"); + const positionManager = m.contract("PositionManager"); + + return { pool, factory, poolManager, swapRouter, positionManager }; +}); + +export default WtfswapModule; +``` + +通过 `npx hardhat ignition deploy ./ignition/modules/Wtfswap.ts --network localhost` 启动本地的测试链。 + +然后执行 `npx hardhat ignition deploy ./ignition/modules/Wtfswap.ts --network localhost` 部署。 + +如果顺利你可以看到如下结果: + +![deploy](./img/deploy.png) + +接下来,从下一章开始,我们就可以愉快的进行开发了。🎉 diff --git a/README.md b/README.md index c93edbb..9ba684f 100644 --- a/README.md +++ b/README.md @@ -44,19 +44,21 @@ WTF Dapp 是一个 DApp 极简入门教程,帮助开发者入门去中心应 **第 P003 讲:Wtfswap 整体设计**:[教程](./P003_OverallDesign/readme.md) -**第 P101 讲:Wtfswap 合约设计**:[教程](./P101_InitContract/readme.md) | [代码](./P101_InitContract/code/) +**第 P101 讲:Wtfswap 合约设计**:[教程](./P101_ContractsDesign/readme.md) | [代码](./P101_ContractsDesign/code/) -**第 P102 讲:Pool 合约 LP 相关接口开发** +**第 P102 讲:初始化合约和开发环境**:[教程](./P102_InitContracts/readme.md) | [代码](./P102_InitContracts/code/) -**第 P103 讲:Factory 合约开发** +**第 P103 讲:Pool 合约 LP 相关接口开发** -**第 P104 讲:PoolManager 合约开发** +**第 P104 讲:Factory 合约开发** -**第 P105 讲:PositionManager 合约开发** +**第 P105 讲:PoolManager 合约开发** -**第 P106 讲:Pool 合约 swap 接口开发** +**第 P106 讲:PositionManager 合约开发** -**第 P107 讲:SwapRouter 合约开发** +**第 P107 讲:Pool 合约 swap 接口开发** + +**第 P108 讲:SwapRouter 合约开发** **第 P201 讲:初始化前端代码和技术分析**:[教程](./P201_InitFrontend/readme.md) | [代码](./P201_InitFrontend/code/) diff --git a/demo-contract/contracts/wtfswap/Pool.sol b/demo-contract/contracts/wtfswap/Pool.sol index 9527ad9..057dd8d 100644 --- a/demo-contract/contracts/wtfswap/Pool.sol +++ b/demo-contract/contracts/wtfswap/Pool.sol @@ -32,9 +32,9 @@ contract Pool is IPool { {} function initialize( - uint160 sqrtPriceX96, - int24 tickLower, - int24 tickUpper + uint160 sqrtPriceX96_, + int24 tickLower_, + int24 tickUpper_ ) external override {} function mint( diff --git a/demo-contract/ignition/modules/Wtfswap.ts b/demo-contract/ignition/modules/Wtfswap.ts new file mode 100644 index 0000000..fe8ef91 --- /dev/null +++ b/demo-contract/ignition/modules/Wtfswap.ts @@ -0,0 +1,13 @@ +import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; + +const WtfswapModule = buildModule("Wtfswap", (m) => { + const pool = m.contract("Pool"); + const factory = m.contract("Factory"); + const poolManager = m.contract("PoolManager"); + const swapRouter = m.contract("SwapRouter"); + const positionManager = m.contract("PositionManager"); + + return { pool, factory, poolManager, swapRouter, positionManager }; +}); + +export default WtfswapModule;