punkkit-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/.env +47 -0
  2. package/.gitmodules +3 -0
  3. package/README.md +158 -0
  4. package/config/auction.config.ts +40 -0
  5. package/config/env.config.ts +204 -0
  6. package/config/uniswap.config.ts +107 -0
  7. package/config/voucher.config.ts +10 -0
  8. package/contracts/MutiVoucher.sol +78 -0
  9. package/contracts/auction/ChainXAuction.sol +177 -0
  10. package/contracts/auction/ChainXAuctionV2.sol +672 -0
  11. package/contracts/auction/ChainXWrappedETH.sol +80 -0
  12. package/contracts/auction/ChainYLiquidityManager.sol +57 -0
  13. package/contracts/auction/ChainYShadowETH.sol +148 -0
  14. package/contracts/auction/ChainYVault.sol +195 -0
  15. package/contracts/auction/ChainYVaultCoinbase.sol +276 -0
  16. package/contracts/auction/ChainYVaultV2.sol +318 -0
  17. package/contracts/auction/coinbase-and-stake/README.md +55 -0
  18. package/contracts/auction/coinbase-and-stake/coinbase.sol +142 -0
  19. package/contracts/auction/coinbase-and-stake/invokeCoinbase.sol +159 -0
  20. package/contracts/auction/coinbase-and-stake/invokeStake.sol +82 -0
  21. package/contracts/auction/coinbase-and-stake/stake.sol +92 -0
  22. package/contracts/auction/interfaces/IUniswapV2Factory.sol +15 -0
  23. package/contracts/auction/interfaces/IUniswapV2Pair.sol +53 -0
  24. package/contracts/auction/interfaces/IUniswapV2Router02.sol +25 -0
  25. package/contracts/auction/interfaces/IUnlockStrategy.sol +18 -0
  26. package/contracts/auction/libraries/EventParser.sol +32 -0
  27. package/contracts/auction/libraries/TransactionParser.sol +70 -0
  28. package/contracts/auction/strategies/MatchResultWithdrawnStrategy.sol +33 -0
  29. package/contracts/auction/utils/BytesLib.sol +180 -0
  30. package/contracts/auction/utils/RLPReader.sol +355 -0
  31. package/contracts/uniswap/Create2.sol +80 -0
  32. package/contracts/uniswap/DynamicFee.sol +100 -0
  33. package/contracts/uniswap/Example.sol +35 -0
  34. package/contracts/uniswap/HookMiner.sol +52 -0
  35. package/contracts/uniswap/LimitOrder.sol +486 -0
  36. package/contracts/uniswap/LiquidPool.sol +179 -0
  37. package/contracts/uniswap/MockERC20.sol +20 -0
  38. package/hardhat.config.ts +35 -0
  39. package/ignition/modules/LimitOrder.ts +33 -0
  40. package/package.json +32 -0
  41. package/scripts/auction/deploy.ts +23 -0
  42. package/scripts/auction/deployCoinbase.ts +21 -0
  43. package/scripts/auction/deployXAuction.ts +23 -0
  44. package/scripts/auction/deployYVault.ts +22 -0
  45. package/scripts/deploy_voucher.ts +20 -0
  46. package/scripts/uniswap/deploy/deploy.ts +65 -0
  47. package/scripts/uniswap/deploy/deploy_create2.ts +11 -0
  48. package/scripts/uniswap/deploy/deploy_example.ts +35 -0
  49. package/scripts/uniswap/deploy/deploy_hooks.ts +74 -0
  50. package/scripts/uniswap/deploy/deploy_mockERC20.ts +42 -0
  51. package/scripts/uniswap/deploy/help.ts +96 -0
  52. package/scripts/uniswap/deploy/init.ts +70 -0
  53. package/src/auction/chainXAuction.ts +209 -0
  54. package/src/auction/chainYVault.ts +153 -0
  55. package/src/auction/event.ts +19 -0
  56. package/src/auction/serialize.ts +162 -0
  57. package/src/auction/type.ts +71 -0
  58. package/src/lib/signer.ts +20 -0
  59. package/src/lib/unlock.ts +14 -0
  60. package/src/uniswap/1-marketprice/addLiquidity.ts +80 -0
  61. package/src/uniswap/1-marketprice/removeLiquidity.ts +63 -0
  62. package/src/uniswap/1-marketprice/swap.ts +100 -0
  63. package/src/uniswap/2-limitorder/kill.ts +70 -0
  64. package/src/uniswap/2-limitorder/place.ts +93 -0
  65. package/src/uniswap/2-limitorder/withdraw.ts +78 -0
  66. package/src/uniswap/3-dynamicfee/dynamicfee.ts +321 -0
  67. package/src/uniswap/lib/ERC20.ts +49 -0
  68. package/src/uniswap/lib/contract.ts +18 -0
  69. package/src/uniswap/lib/limitOrder.ts +40 -0
  70. package/src/uniswap/lib/liqCalculation.ts +152 -0
  71. package/src/uniswap/lib/listen.ts +57 -0
  72. package/src/uniswap/lib/pool.ts +62 -0
  73. package/src/uniswap/lib/swap.ts +8 -0
  74. package/src/uniswap/lib/types.ts +21 -0
  75. package/src/uniswap/lib/utils.ts +26 -0
  76. package/src/uniswap/playgroud/abiencode.ts +21 -0
  77. package/src/uniswap/playgroud/amount0.ts +47 -0
  78. package/src/uniswap/playgroud/errordecode.ts +54 -0
  79. package/src/uniswap/playgroud/errorsigs.ts +86 -0
  80. package/src/voucher.ts +122 -0
  81. package/test/auction/ChainXAuctionV2.test.ts +265 -0
  82. package/test/auction/ChainYVaultV2.test.js +163 -0
  83. package/test/auction/ChainYVaultV2.test.ts +183 -0
  84. package/test/auction/auction.test.ts +106 -0
  85. package/test/connect_punk.test.ts +26 -0
  86. package/test/create2.test.ts +44 -0
  87. package/test/normal.ts +43 -0
  88. package/test/test-config.ts +18 -0
  89. package/test/uniswap/example.test.ts +62 -0
  90. package/test/uniswap/limitOrder.test.ts +184 -0
  91. package/test/uniswap/mockERC20.test.ts +142 -0
  92. package/test/voucher_hardhat.test.ts +120 -0
  93. package/test/voucher_punk.test.ts +83 -0
  94. package/tsconfig.json +11 -0
@@ -0,0 +1,100 @@
1
+ // SPDX-License-Identifier: UNLICENSED
2
+ // a uniswapV4 hook that allows the pool use dynamic fees
3
+ pragma solidity ^0.8.20;
4
+ import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
5
+ import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol";
6
+ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
7
+ import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol";
8
+ import {Pool} from "@uniswap/v4-core/src/libraries/Pool.sol";
9
+ import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol";
10
+ import {FullMath} from "@uniswap/v4-core/src/libraries/FullMath.sol";
11
+ import {SafeCast} from "@uniswap/v4-core/src/libraries/SafeCast.sol";
12
+ import {IERC20Minimal} from "@uniswap/v4-core/src/interfaces/external/IERC20Minimal.sol";
13
+ import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
14
+ import {Currency} from "@uniswap/v4-core/src/libraries/CurrencyDelta.sol";
15
+ import {BalanceDelta, toBalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol";
16
+ import {BeforeSwapDelta, toBeforeSwapDelta} from "@uniswap/v4-core/src/types/BeforeSwapDelta.sol";
17
+ import {ModifyLiquidityParams, SwapParams} from "@uniswap/v4-core/src/types/PoolOperation.sol";
18
+
19
+ /// @notice The dynamic fee manager determines fees for pools
20
+ /// @dev note that this pool is only called if the PoolKey fee value is equal to the DYNAMIC_FEE magic value
21
+ interface IDynamicFeeManager {
22
+ function getFee(PoolKey calldata key) external returns (uint24);
23
+ }
24
+
25
+ contract DynamicFee is IDynamicFeeManager{
26
+ using SafeCast for uint256;
27
+ using Pool for Pool.State;
28
+ using StateLibrary for IPoolManager;
29
+
30
+ uint160 public lastPrice;
31
+ uint160 public nowPrice;
32
+ uint256 public lastBlockNumber;
33
+ uint24 public feeNow;
34
+ IPoolManager public immutable poolManager;
35
+
36
+ constructor(IPoolManager _poolManager) {
37
+ poolManager = _poolManager;
38
+ }
39
+
40
+ function getHookPermissions() public pure returns (Hooks.Permissions memory) {
41
+ return Hooks.Permissions({
42
+ beforeInitialize: false,
43
+ afterInitialize: true,
44
+ beforeAddLiquidity: false,
45
+ afterAddLiquidity: false,
46
+ beforeRemoveLiquidity: false,
47
+ afterRemoveLiquidity: false,
48
+ beforeSwap: false,
49
+ afterSwap: true,
50
+ beforeDonate: false,
51
+ afterDonate: false,
52
+ beforeSwapReturnDelta: false,
53
+ afterSwapReturnDelta: false,
54
+ afterAddLiquidityReturnDelta: false,
55
+ afterRemoveLiquidityReturnDelta: false
56
+ });
57
+ }
58
+
59
+ function afterInitialize(address, PoolKey calldata key, uint160, int24)
60
+ external
61
+ virtual
62
+ returns (bytes4)
63
+ {
64
+ // Initializing the nowPrice during the initialization of the contract
65
+ (nowPrice,,,) = poolManager.getSlot0(PoolIdLibrary.toId(key));
66
+ lastBlockNumber = block.number;
67
+
68
+ return DynamicFee.afterInitialize.selector;
69
+ }
70
+
71
+ function getFee(PoolKey calldata key) external returns (uint24) {
72
+ if(block.number > lastBlockNumber) {
73
+ lastPrice = nowPrice;
74
+
75
+ // 调用function getSlot0(PoolId id), 获取价格信息存入price_now
76
+ (nowPrice,,,) = poolManager.getSlot0(PoolIdLibrary.toId(key));
77
+
78
+ // 比较price_now和price_last, 根据price波动率设定fee
79
+ if(nowPrice > lastPrice) {
80
+ feeNow = 30;
81
+ } else {
82
+ feeNow = 5;
83
+ }
84
+
85
+ lastBlockNumber = block.number; // Update the last block number
86
+ }
87
+ return feeNow;
88
+ }
89
+
90
+ function afterSwap(address, PoolKey calldata, SwapParams calldata, BalanceDelta, bytes calldata)
91
+ external
92
+ virtual
93
+ returns (bytes4, int128)
94
+ {
95
+ // Update lastBlockNumber after the swap
96
+ lastBlockNumber = block.number;
97
+ return (DynamicFee.afterSwap.selector, 0);
98
+ }
99
+
100
+ }
@@ -0,0 +1,35 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.19;
3
+
4
+ contract Example {
5
+ uint256 public value;
6
+
7
+ constructor(uint256 _value) {
8
+ value = _value;
9
+ }
10
+
11
+ function setValue(uint256 _value) external {
12
+ value = _value;
13
+ }
14
+
15
+ function getValue() external view returns (uint256) {
16
+ return value;
17
+ }
18
+ }
19
+
20
+
21
+ contract Example2{
22
+ string public name;
23
+
24
+ constructor() {
25
+ name = "Hello World!";
26
+ }
27
+
28
+ function setName(string calldata _name) public {
29
+ name = _name;
30
+ }
31
+
32
+ function getName() public view returns (string memory){
33
+ return name;
34
+ }
35
+ }
@@ -0,0 +1,52 @@
1
+ // SPDX-License-Identifier: GPL-2.0-or-later
2
+ pragma solidity ^0.8.21;
3
+
4
+ /// @title HookMiner - a library for mining hook addresses
5
+ /// @dev This library is intended for `forge test` environments. There may be gotchas when using salts in `forge script` or `forge create`
6
+ library HookMiner {
7
+ // mask to slice out the bottom 14 bit of the address
8
+ uint160 constant FLAG_MASK = 0x3FFF;
9
+
10
+ // Maximum number of iterations to find a salt, avoid infinite loops
11
+ uint256 constant MAX_LOOP = 100_000;
12
+
13
+ /// @notice Find a salt that produces a hook address with the desired `flags`
14
+ /// @param deployer The address that will deploy the hook. In `forge test`, this will be the test contract `address(this)` or the pranking address
15
+ /// In `forge script`, this should be `0x4e59b44847b379578588920cA78FbF26c0B4956C` (CREATE2 Deployer Proxy)
16
+ /// @param flags The desired flags for the hook address
17
+ /// @param creationCode The creation code of a hook contract. Example: `type(Counter).creationCode`
18
+ /// @param constructorArgs The encoded constructor arguments of a hook contract. Example: `abi.encode(address(manager))`
19
+ /// @return hookAddress salt and corresponding address that was found. The salt can be used in `new Hook{salt: salt}(<constructor arguments>)`
20
+ function find(address deployer, uint160 flags, bytes memory creationCode, bytes memory constructorArgs)
21
+ internal
22
+ view
23
+ returns (address, bytes32)
24
+ {
25
+ address hookAddress;
26
+ bytes memory creationCodeWithArgs = abi.encodePacked(creationCode, constructorArgs);
27
+
28
+ uint256 salt;
29
+ for (salt; salt < MAX_LOOP; salt++) {
30
+ hookAddress = computeAddress(deployer, salt, creationCodeWithArgs);
31
+ if (uint160(hookAddress) & FLAG_MASK == flags && hookAddress.code.length == 0) {
32
+ return (hookAddress, bytes32(salt));
33
+ }
34
+ }
35
+ revert("HookMiner: could not find salt");
36
+ }
37
+
38
+ /// @notice Precompute a contract address deployed via CREATE2
39
+ /// @param deployer The address that will deploy the hook. In `forge test`, this will be the test contract `address(this)` or the pranking address
40
+ /// In `forge script`, this should be `0x4e59b44847b379578588920cA78FbF26c0B4956C` (CREATE2 Deployer Proxy)
41
+ /// @param salt The salt used to deploy the hook
42
+ /// @param creationCode The creation code of a hook contract
43
+ function computeAddress(address deployer, uint256 salt, bytes memory creationCode)
44
+ internal
45
+ pure
46
+ returns (address hookAddress)
47
+ {
48
+ return address(
49
+ uint160(uint256(keccak256(abi.encodePacked(bytes1(0xFF), deployer, salt, keccak256(creationCode)))))
50
+ );
51
+ }
52
+ }
@@ -0,0 +1,486 @@
1
+ // SPDX-License-Identifier: UNLICENSED
2
+ pragma solidity ^0.8.19;
3
+
4
+ import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
5
+ import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol";
6
+ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
7
+ import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol";
8
+ import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol";
9
+ import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol";
10
+ import {FullMath} from "@uniswap/v4-core/src/libraries/FullMath.sol";
11
+ import {SafeCast} from "@uniswap/v4-core/src/libraries/SafeCast.sol";
12
+ // import {IERC20Minimal} from "@uniswap/v4-core/src/interfaces/external/IERC20Minimal.sol";
13
+ import {IUnlockCallback} from '@uniswap/v4-core/src/interfaces/callback/IUnlockCallback.sol';
14
+ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
15
+ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
16
+ import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
17
+ import {Currency, CurrencyDelta} from "@uniswap/v4-core/src/libraries/CurrencyDelta.sol";
18
+ import {BalanceDelta, toBalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol";
19
+ import {BeforeSwapDelta, toBeforeSwapDelta} from "@uniswap/v4-core/src/types/BeforeSwapDelta.sol";
20
+ import {ModifyLiquidityParams, SwapParams} from "@uniswap/v4-core/src/types/PoolOperation.sol";
21
+ import "hardhat/console.sol";
22
+
23
+ type Epoch is uint232;
24
+
25
+ library EpochLibrary {
26
+ function equals(Epoch a, Epoch b) internal pure returns (bool) {
27
+ return Epoch.unwrap(a) == Epoch.unwrap(b);
28
+ }
29
+
30
+ function unsafeIncrement(Epoch a) internal pure returns (Epoch) {
31
+ unchecked {
32
+ return Epoch.wrap(Epoch.unwrap(a) + 1);
33
+ }
34
+ }
35
+ }
36
+
37
+ contract LimitOrder is IUnlockCallback{
38
+ using EpochLibrary for Epoch;
39
+ using PoolIdLibrary for PoolKey;
40
+ using CurrencyDelta for Currency;
41
+ using StateLibrary for IPoolManager;
42
+ using SafeERC20 for IERC20;
43
+
44
+ error ZeroLiquidity();
45
+ error InRange();
46
+ error CrossedRange();
47
+ error Filled();
48
+ error NotFilled();
49
+ error NotPoolManagerToken();
50
+ error InvalidFunc(bytes4 funcsig);
51
+ error InvalidPool();
52
+ error NotPoolManager();
53
+
54
+ event Place(
55
+ address indexed owner,
56
+ Epoch indexed epoch,
57
+ PoolKey key,
58
+ int24 tickLower,
59
+ bool zeroForOne,
60
+ uint128 liquidity
61
+ );
62
+
63
+ event Fill(Epoch indexed epoch, PoolKey key, int24 tickLower, bool zeroForOne);
64
+
65
+ event Kill(
66
+ address indexed owner,
67
+ Epoch indexed epoch,
68
+ PoolKey key,
69
+ int24 tickLower,
70
+ bool zeroForOne,
71
+ uint128 liquidity
72
+ );
73
+
74
+ event Withdraw(address indexed owner, Epoch indexed epoch, uint128 liquidity);
75
+
76
+ Epoch private constant EPOCH_DEFAULT = Epoch.wrap(0);
77
+
78
+ mapping(PoolId => int24) public tickLowerLasts;
79
+ Epoch public epochNext = Epoch.wrap(1);
80
+
81
+ struct EpochInfo {
82
+ bool filled;
83
+ Currency currency0;
84
+ Currency currency1;
85
+ uint256 token0Total;
86
+ uint256 token1Total;
87
+ uint128 liquidityTotal;
88
+ mapping(address => uint128) liquidity;
89
+ }
90
+
91
+ mapping(bytes32 => Epoch) public epochs;
92
+ mapping(Epoch => EpochInfo) public epochInfos;
93
+
94
+ IPoolManager public immutable poolManager;
95
+ constructor(IPoolManager _poolManager) {
96
+ poolManager = _poolManager;
97
+ }
98
+
99
+ // modifer 修饰的函数,会在_的位置执行
100
+ modifier onlyByPoolManager() {
101
+ if (msg.sender != address(poolManager)) revert NotPoolManager();
102
+ _; // 被修饰函数执行逻辑
103
+ }
104
+ modifier onlyValidPools(IHooks hooks) {
105
+ if (address(hooks) != address(this)) revert InvalidPool();
106
+ _;
107
+ }
108
+
109
+ function getHookPermissions() public pure returns (Hooks.Permissions memory) {
110
+ return Hooks.Permissions({
111
+ beforeInitialize: false,
112
+ afterInitialize: true,
113
+ beforeAddLiquidity: false,
114
+ afterAddLiquidity: false,
115
+ beforeRemoveLiquidity: false,
116
+ afterRemoveLiquidity: false,
117
+ beforeSwap: false,
118
+ afterSwap: true,
119
+ beforeDonate: false,
120
+ afterDonate: false,
121
+ beforeSwapReturnDelta: false,
122
+ afterSwapReturnDelta: false,
123
+ afterAddLiquidityReturnDelta: false,
124
+ afterRemoveLiquidityReturnDelta: false
125
+ });
126
+ }
127
+
128
+ function getTickLowerLast(PoolId poolId) public view returns (int24) {
129
+ return tickLowerLasts[poolId];
130
+ }
131
+
132
+ function setTickLowerLast(PoolId poolId, int24 tickLower) private {
133
+ tickLowerLasts[poolId] = tickLower;
134
+ }
135
+
136
+ function getEpoch(PoolKey memory key, int24 tickLower, bool zeroForOne) public view returns (Epoch) {
137
+ return epochs[keccak256(abi.encode(key, tickLower, zeroForOne))];
138
+ }
139
+
140
+ function setEpoch(PoolKey memory key, int24 tickLower, bool zeroForOne, Epoch epoch) private {
141
+ epochs[keccak256(abi.encode(key, tickLower, zeroForOne))] = epoch;
142
+ }
143
+
144
+ function getEpochLiquidity(Epoch epoch, address owner) external view returns (uint256) {
145
+ return epochInfos[epoch].liquidity[owner];
146
+ }
147
+
148
+ function getTick(PoolId poolId) private view returns (int24 tick) {
149
+ (,tick,,) = poolManager.getSlot0(poolId);
150
+ }
151
+
152
+ function getTickLower(int24 tick, int24 tickSpacing) private pure returns (int24) {
153
+ int24 compressed = tick / tickSpacing;
154
+ if (tick < 0 && tick % tickSpacing != 0) compressed--; // round towards negative infinity
155
+ return compressed * tickSpacing;
156
+ }
157
+
158
+ // see src/base/SafeCallback.sol
159
+ function unlockCallback(bytes calldata data) external returns (bytes memory) {
160
+ bytes4 funcSig = bytes4(data[:4]);
161
+ bytes memory args = bytes(data[4:]);
162
+
163
+ if (funcSig == this.lockAcquiredPlace.selector) {
164
+ (PoolKey memory key, int24 tickLower, bool zeroForOne, int256 liquidityDelta, address owner, bytes32 salt) = abi.decode(args, (PoolKey, int24, bool, int256, address, bytes32));
165
+ lockAcquiredPlace(key, tickLower, zeroForOne, liquidityDelta, owner, salt);
166
+ } else if (funcSig == this.lockAcquiredKill.selector) {
167
+ (PoolKey memory key, int24 tickLower, int256 liquidityDelta, address to, bool removingAllLiquidity, bytes32 salt) = abi.decode(args, (PoolKey, int24, int256, address, bool, bytes32));
168
+ (uint256 amount0, uint256 amount1, uint128 amount0Fee, uint128 amount1Fee) = lockAcquiredKill(key, tickLower, liquidityDelta, to, removingAllLiquidity, salt);
169
+ return abi.encode(amount0, amount1, amount0Fee, amount1Fee);
170
+ } else if (funcSig == this.lockAcquiredWithdraw.selector) {
171
+ (Currency currency0, Currency currency1, uint256 token0Amount, uint256 token1Amount, address to) = abi.decode(args, (Currency, Currency, uint256, uint256, address));
172
+ lockAcquiredWithdraw(currency0, currency1, token0Amount, token1Amount, to);
173
+ } else {
174
+ // todo: reverted with an unrecognized custom error (return data: 0x29c3b7ee)
175
+ revert InvalidFunc(funcSig);
176
+ }
177
+
178
+ return bytes("");
179
+ }
180
+
181
+ function afterInitialize(address, PoolKey calldata key, uint160, int24 tick)
182
+ external
183
+ returns (bytes4)
184
+ {
185
+ setTickLowerLast(key.toId(), getTickLower(tick, key.tickSpacing));
186
+ return LimitOrder.afterInitialize.selector;
187
+ }
188
+
189
+ function afterSwap(
190
+ address,
191
+ PoolKey calldata key,
192
+ SwapParams calldata params,
193
+ BalanceDelta,
194
+ bytes calldata hookData
195
+ ) external returns (bytes4, int128) {
196
+ (int24 tickLower, int24 lower, int24 upper) = _getCrossedTicks(key.toId(), key.tickSpacing);
197
+ if (lower > upper) return (LimitOrder.afterSwap.selector, 0);
198
+
199
+ bytes32 salt = abi.decode(hookData, (bytes32));
200
+ salt=bytes32(0); // 临时修改为0,方便测试""
201
+
202
+ // note that a zeroForOne swap means that the pool is actually gaining token0, so limit
203
+ // order fills are the opposite of swap fills, hence the inversion below
204
+ bool zeroForOne = !params.zeroForOne;
205
+ for (; lower <= upper; lower += key.tickSpacing) {
206
+ Epoch epoch = getEpoch(key, lower, zeroForOne);
207
+
208
+ if (!epoch.equals(EPOCH_DEFAULT)) {
209
+ EpochInfo storage epochInfo = epochInfos[epoch];
210
+ epochInfo.filled = true;
211
+ console.log("epoch(set true):", uint256(Epoch.unwrap(epoch)));
212
+ (uint256 amount0, uint256 amount1) = lockAcquiredFill(key, lower, -int256(uint256(epochInfo.liquidityTotal)), salt);
213
+ unchecked {
214
+ epochInfo.token0Total += amount0;
215
+ epochInfo.token1Total += amount1;
216
+ }
217
+ // 惰性删除,等到withdraw再删除
218
+ setEpoch(key, lower, zeroForOne, EPOCH_DEFAULT);
219
+ emit Fill(epoch, key, lower, zeroForOne);
220
+ }
221
+ }
222
+ setTickLowerLast(key.toId(), tickLower);
223
+ return (LimitOrder.afterSwap.selector, 0);
224
+ }
225
+
226
+ function _getCrossedTicks(PoolId poolId, int24 tickSpacing)
227
+ internal
228
+ view
229
+ returns (int24 tickLower, int24 lower, int24 upper)
230
+ {
231
+ tickLower = getTickLower(getTick(poolId), tickSpacing);
232
+ int24 tickLowerLast = getTickLowerLast(poolId);
233
+
234
+ if (tickLower < tickLowerLast) {
235
+ lower = tickLower + tickSpacing;
236
+ upper = tickLowerLast;
237
+ } else {
238
+ lower = tickLowerLast;
239
+ upper = tickLower - tickSpacing;
240
+ }
241
+ }
242
+
243
+ function lockAcquiredFill(PoolKey memory key, int24 tickLower, int256 liquidityDelta, bytes32 salt)
244
+ internal
245
+ onlyByPoolManager
246
+ returns (uint128 amount0, uint128 amount1)
247
+ {
248
+ console.log("lockAcquiredFill called with tickLower:", uint256(uint24(tickLower)));
249
+ console.log("liquidityDelta");
250
+ console.logInt(liquidityDelta);
251
+ console.log("salt:");
252
+ console.logBytes32(salt);
253
+ // 整形溢出问题。
254
+ (BalanceDelta delta, ) = poolManager.modifyLiquidity(
255
+ key,
256
+ ModifyLiquidityParams({
257
+ tickLower: tickLower,
258
+ tickUpper: tickLower + key.tickSpacing,
259
+ liquidityDelta: liquidityDelta,
260
+ salt: salt
261
+ }),
262
+ hex'00' // used for beforeModifyLiquidity/afterModifyLiquidity
263
+ );
264
+ console.log("lockAcquiredFill liquidity modified.");
265
+ if (delta.amount0() > 0) poolManager.mint(address(this), key.currency0.toId(), amount0 = uint128(delta.amount0()));
266
+ if (delta.amount1() > 0) poolManager.mint(address(this), key.currency1.toId(), amount1 = uint128(delta.amount1()));
267
+ }
268
+
269
+ function place(PoolKey calldata key, int24 tickLower, bool zeroForOne, uint128 liquidity)
270
+ external
271
+ onlyValidPools(key.hooks)
272
+ {
273
+ if (liquidity == 0) revert ZeroLiquidity();
274
+
275
+ // Used to identify liquidity modifications from this.tx.sender, not limitorder contract.
276
+ // bytes32 salt = bytes32(uint256(uint160(msg.sender)));
277
+ bytes32 salt=bytes32(0); // 临时修改为0,方便测试
278
+
279
+ poolManager.unlock(
280
+ abi.encodeCall(this.lockAcquiredPlace, (key, tickLower, zeroForOne, int256(uint256(liquidity)), msg.sender, salt))
281
+ );
282
+
283
+ EpochInfo storage epochInfo;
284
+ Epoch epoch = getEpoch(key, tickLower, zeroForOne);
285
+ if (epoch.equals(EPOCH_DEFAULT)) {
286
+ unchecked {
287
+ setEpoch(key, tickLower, zeroForOne, epoch = epochNext);
288
+ epochNext = epoch.unsafeIncrement();
289
+ }
290
+ epochInfo = epochInfos[epoch];
291
+ epochInfo.currency0 = key.currency0;
292
+ epochInfo.currency1 = key.currency1;
293
+ } else {
294
+ epochInfo = epochInfos[epoch];
295
+ }
296
+ unchecked {
297
+ epochInfo.liquidityTotal += liquidity;
298
+ epochInfo.liquidity[msg.sender] += liquidity;
299
+ }
300
+
301
+ emit Place(msg.sender, epoch, key, tickLower, zeroForOne, liquidity);
302
+ }
303
+
304
+ function lockAcquiredPlace(
305
+ PoolKey memory key,
306
+ int24 tickLower,
307
+ bool zeroForOne,
308
+ int256 liquidityDelta,
309
+ address owner,
310
+ bytes32 salt
311
+ ) public onlyByPoolManager {
312
+ (BalanceDelta delta, ) = poolManager.modifyLiquidity(
313
+ key,
314
+ ModifyLiquidityParams({
315
+ tickLower: tickLower,
316
+ tickUpper: tickLower + key.tickSpacing,
317
+ liquidityDelta: liquidityDelta,
318
+ salt: salt
319
+ }),
320
+ hex'00'
321
+ );
322
+
323
+ // [Mark] based on lib/v4-core/src/libraries/StateLibrary.sol
324
+ // the amount0 is computed negative when liquidity is positive
325
+ // same to LiquidityPool: function _settleCurrencyBalance
326
+ if (delta.amount0() < 0) {
327
+ if (delta.amount1() != 0) revert InRange();
328
+ if (!zeroForOne) revert CrossedRange();
329
+ poolManager.sync(key.currency0);
330
+ IERC20(Currency.unwrap(key.currency0)).safeTransferFrom(
331
+ owner, address(poolManager), uint256(uint128(-delta.amount0()))
332
+ );
333
+ poolManager.settle();
334
+ } else {
335
+ if (delta.amount0() != 0) revert InRange();
336
+ if (zeroForOne) revert CrossedRange();
337
+ poolManager.sync(key.currency1);
338
+ IERC20(Currency.unwrap(key.currency1)).safeTransferFrom(
339
+ owner, address(poolManager), uint256(uint128(-delta.amount1()))
340
+ );
341
+ poolManager.settle();
342
+ }
343
+ }
344
+
345
+ function kill(PoolKey calldata key, int24 tickLower, bool zeroForOne, address to)
346
+ external
347
+ returns (uint256 amount0, uint256 amount1)
348
+ {
349
+ Epoch epoch = getEpoch(key, tickLower, zeroForOne);
350
+ console.log("[kill] epoch", uint256(Epoch.unwrap(epoch)));
351
+
352
+ EpochInfo storage epochInfo = epochInfos[epoch];
353
+
354
+ if (epochInfo.filled) revert Filled();
355
+
356
+ uint128 liquidity = epochInfo.liquidity[msg.sender];
357
+ if (liquidity == 0) revert ZeroLiquidity();
358
+ delete epochInfo.liquidity[msg.sender];
359
+
360
+ uint128 liquidityTotal = epochInfo.liquidityTotal;
361
+ epochInfo.liquidityTotal = liquidityTotal - liquidity;
362
+
363
+ // [Custom]
364
+ bytes32 salt = bytes32(0);
365
+ uint256 amount0Fee;
366
+ uint256 amount1Fee;
367
+ (amount0, amount1, amount0Fee, amount1Fee) = abi.decode(
368
+ poolManager.unlock(
369
+ abi.encodeCall(
370
+ this.lockAcquiredKill,
371
+ (key, tickLower, -int256(uint256(liquidity)), to, liquidity == liquidityTotal, salt)
372
+ )
373
+ ),
374
+ (uint256, uint256, uint256, uint256)
375
+ );
376
+
377
+ unchecked {
378
+ epochInfo.token0Total += amount0Fee;
379
+ epochInfo.token1Total += amount1Fee;
380
+ }
381
+
382
+ emit Kill(msg.sender, epoch, key, tickLower, zeroForOne, liquidity);
383
+ }
384
+
385
+ function lockAcquiredKill(
386
+ PoolKey memory key,
387
+ int24 tickLower,
388
+ int256 liquidityDelta,
389
+ address to,
390
+ bool removingAllLiquidity,
391
+ bytes32 salt
392
+ ) public onlyByPoolManager returns (uint256 amount0, uint256 amount1, uint128 amount0Fee, uint128 amount1Fee) {
393
+ int24 tickUpper = tickLower + key.tickSpacing;
394
+
395
+ // because `modifyPosition` includes not just principal value but also fees, we cannot allocate
396
+ // the proceeds pro-rata. if we were to do so, users who have been in a limit order that's partially filled
397
+ // could be unfairly diluted by a user sychronously placing then killing a limit order to skim off fees.
398
+ // to prevent this, we allocate all fee revenue to remaining limit order placers, unless this is the last order.
399
+ if (!removingAllLiquidity) {
400
+ (BalanceDelta deltaFee, ) = poolManager.modifyLiquidity(
401
+ key, ModifyLiquidityParams({tickLower: tickLower, tickUpper: tickUpper, liquidityDelta: 0, salt: salt}), hex'00'
402
+ );
403
+
404
+ if (deltaFee.amount0() > 0) {
405
+ poolManager.mint(address(this), key.currency0.toId(), amount0Fee = uint128(deltaFee.amount0()));
406
+ }
407
+ if (deltaFee.amount1() > 0) {
408
+ poolManager.mint(address(this), key.currency1.toId(), amount1Fee = uint128(deltaFee.amount1()));
409
+ }
410
+ }
411
+
412
+ (BalanceDelta delta, ) = poolManager.modifyLiquidity(
413
+ key,
414
+ ModifyLiquidityParams({
415
+ tickLower: tickLower,
416
+ tickUpper: tickUpper,
417
+ liquidityDelta: liquidityDelta,
418
+ salt: salt
419
+ }),
420
+ hex'00'
421
+ );
422
+
423
+ // [Mark]
424
+ if (delta.amount0() > 0) poolManager.take(key.currency0, to, amount0 = uint128(delta.amount0()));
425
+ if (delta.amount1() > 0) poolManager.take(key.currency1, to, amount1 = uint128(delta.amount1()));
426
+ }
427
+
428
+ function withdraw(Epoch epoch, address to) external returns (uint256 amount0, uint256 amount1) {
429
+ EpochInfo storage epochInfo = epochInfos[epoch];
430
+
431
+ // 检查限价单是否已经完成
432
+ if (!epochInfo.filled) revert NotFilled();
433
+
434
+ uint128 liquidity = epochInfo.liquidity[msg.sender];
435
+ if (liquidity == 0) revert ZeroLiquidity();
436
+ delete epochInfo.liquidity[msg.sender];
437
+
438
+ uint256 token0Total = epochInfo.token0Total;
439
+ uint256 token1Total = epochInfo.token1Total;
440
+ uint128 liquidityTotal = epochInfo.liquidityTotal;
441
+
442
+ amount0 = FullMath.mulDiv(token0Total, liquidity, liquidityTotal);
443
+ amount1 = FullMath.mulDiv(token1Total, liquidity, liquidityTotal);
444
+
445
+ epochInfo.token0Total = token0Total - amount0;
446
+ epochInfo.token1Total = token1Total - amount1;
447
+ epochInfo.liquidityTotal = liquidityTotal - liquidity;
448
+
449
+ poolManager.unlock(
450
+ abi.encodeCall(this.lockAcquiredWithdraw, (epochInfo.currency0, epochInfo.currency1, amount0, amount1, to))
451
+ );
452
+
453
+ emit Withdraw(msg.sender, epoch, liquidity);
454
+ }
455
+
456
+ function lockAcquiredWithdraw(
457
+ Currency currency0,
458
+ Currency currency1,
459
+ uint256 token0Amount,
460
+ uint256 token1Amount,
461
+ address to
462
+ ) public onlyByPoolManager {
463
+ if (token0Amount > 0) {
464
+ poolManager.burn(address(this), currency0.toId(), token0Amount);
465
+ poolManager.take(currency0, to, token0Amount);
466
+ }
467
+ if (token1Amount > 0) {
468
+ // Note: instead of poolManager.safeTransferFrom, we use poolManager.burn
469
+ // poolManager need to call currency.transfer, thus poolManager.take is needed
470
+ // inorder to balance the NonZeroDeltaCount of poolManager.take, we use poolManager.burn
471
+ // poolManager.safeTransferFrom only modify the state of ERC6909, but not applicable for poolManager.sync() and settle()
472
+ /*
473
+ poolManager.safeTransferFrom(
474
+ address(this), address(poolManager), uint256(uint160(Currency.unwrap(currency1))), token1Amount, ""
475
+ );*/
476
+ poolManager.burn(address(this), currency1.toId(), token1Amount);
477
+ poolManager.take(currency1, to, token1Amount);
478
+ }
479
+ }
480
+
481
+ function onERC1155Received(address, address, uint256, uint256, bytes calldata) external view returns (bytes4) {
482
+ if (msg.sender != address(poolManager)) revert NotPoolManagerToken();
483
+ return IERC1155Receiver.onERC1155Received.selector;
484
+ }
485
+
486
+ }