@zoralabs/limit-orders 0.2.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.
- package/.turbo/turbo-build$colon$js.log +85 -0
- package/AUDIT_NOTES.md +33 -0
- package/AUDIT_RFP.md +408 -0
- package/CHANGELOG.md +25 -0
- package/GAS_COMPARISON_RESULTS.md +194 -0
- package/LICENSE +21 -0
- package/README.md +650 -0
- package/SPEC.md +291 -0
- package/abis/BalanceDeltaLibrary.json +15 -0
- package/abis/BeforeSwapDeltaLibrary.json +15 -0
- package/abis/CurrencyLibrary.json +25 -0
- package/abis/CustomRevert.json +28 -0
- package/abis/IAllowanceTransfer.json +486 -0
- package/abis/IAuthority.json +31 -0
- package/abis/ICoin.json +1074 -0
- package/abis/IDeployedCoinVersionLookup.json +21 -0
- package/abis/IDopplerErrors.json +44 -0
- package/abis/IEIP712.json +15 -0
- package/abis/IERC1363.json +373 -0
- package/abis/IERC165.json +21 -0
- package/abis/IERC20.json +185 -0
- package/abis/IERC20Minimal.json +172 -0
- package/abis/IERC6909Claims.json +288 -0
- package/abis/IERC7572.json +21 -0
- package/abis/IExtsload.json +64 -0
- package/abis/IExttload.json +40 -0
- package/abis/IHasCoinType.json +15 -0
- package/abis/IHasPoolKey.json +42 -0
- package/abis/IHasRewardsRecipients.json +54 -0
- package/abis/IHasSwapPath.json +60 -0
- package/abis/IHasTotalSupplyForPositions.json +15 -0
- package/abis/IHooks.json +789 -0
- package/abis/IMsgSender.json +15 -0
- package/abis/IPoolManager.json +1286 -0
- package/abis/IProtocolFees.json +174 -0
- package/abis/ISupportsLimitOrderFill.json +15 -0
- package/abis/ISwapPathRouter.json +92 -0
- package/abis/ISwapRouter.json +219 -0
- package/abis/IUniswapV3SwapCallback.json +25 -0
- package/abis/IUpgradeableDestinationV4Hook.json +84 -0
- package/abis/IUpgradeableDestinationV4HookWithUpdateableFee.json +95 -0
- package/abis/IUpgradeableV4Hook.json +112 -0
- package/abis/IZoraHookRegistry.json +188 -0
- package/abis/IZoraLimitOrderBook.json +623 -0
- package/abis/IZoraLimitOrderBookCoinsInterface.json +67 -0
- package/abis/IZoraV4CoinHook.json +610 -0
- package/abis/Permit2Payments.json +7 -0
- package/abis/Position.json +7 -0
- package/abis/SafeCast.json +7 -0
- package/abis/SafeCast160.json +7 -0
- package/abis/SafeERC20.json +34 -0
- package/abis/SimpleAccessManaged.json +57 -0
- package/abis/SimpleAccessManager.json +351 -0
- package/abis/SqrtPriceMath.json +22 -0
- package/abis/StateLibrary.json +80 -0
- package/abis/SwapLimitOrders.json +22 -0
- package/abis/SwapWithLimitOrders.json +457 -0
- package/abis/TickBitmap.json +18 -0
- package/abis/TickMath.json +24 -0
- package/abis/V3ToV4SwapLib.json +28 -0
- package/abis/ZoraLimitOrderBook.json +771 -0
- package/cache/solidity-files-cache.json +1 -0
- package/dist/index.cjs +760 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +731 -0
- package/dist/index.js.map +1 -0
- package/dist/wagmiGenerated.d.ts +1012 -0
- package/dist/wagmiGenerated.d.ts.map +1 -0
- package/foundry.toml +29 -0
- package/gas_comparison.py +49 -0
- package/out/BalanceDelta.sol/BalanceDeltaLibrary.json +1 -0
- package/out/BeforeSwapDelta.sol/BeforeSwapDeltaLibrary.json +1 -0
- package/out/BitMath.sol/BitMath.json +1 -0
- package/out/BytesLib.sol/BytesLib.json +1 -0
- package/out/CoinCommon.sol/CoinCommon.json +1 -0
- package/out/CoinConfigurationVersions.sol/CoinConfigurationVersions.json +1 -0
- package/out/CoinConstants.sol/CoinConstants.json +1 -0
- package/out/Context.sol/Context.json +1 -0
- package/out/Currency.sol/CurrencyLibrary.json +1 -0
- package/out/CurrencyReserves.sol/CurrencyReserves.json +1 -0
- package/out/CustomRevert.sol/CustomRevert.json +1 -0
- package/out/DopplerMath.sol/DopplerMath.json +1 -0
- package/out/FixedPoint128.sol/FixedPoint128.json +1 -0
- package/out/FixedPoint96.sol/FixedPoint96.json +1 -0
- package/out/FullMath.sol/FullMath.json +1 -0
- package/out/IAllowanceTransfer.sol/IAllowanceTransfer.json +1 -0
- package/out/IAuthority.sol/IAuthority.json +1 -0
- package/out/ICoin.sol/ICoin.json +1 -0
- package/out/ICoin.sol/IHasCoinType.json +1 -0
- package/out/ICoin.sol/IHasPoolKey.json +1 -0
- package/out/ICoin.sol/IHasSwapPath.json +1 -0
- package/out/ICoin.sol/IHasTotalSupplyForPositions.json +1 -0
- package/out/IDeployedCoinVersionLookup.sol/IDeployedCoinVersionLookup.json +1 -0
- package/out/IDopplerErrors.sol/IDopplerErrors.json +1 -0
- package/out/IEIP712.sol/IEIP712.json +1 -0
- package/out/IERC1363.sol/IERC1363.json +1 -0
- package/out/IERC165.sol/IERC165.json +1 -0
- package/out/IERC20.sol/IERC20.json +1 -0
- package/out/IERC20Minimal.sol/IERC20Minimal.json +1 -0
- package/out/IERC6909Claims.sol/IERC6909Claims.json +1 -0
- package/out/IERC7572.sol/IERC7572.json +1 -0
- package/out/IExtsload.sol/IExtsload.json +1 -0
- package/out/IExttload.sol/IExttload.json +1 -0
- package/out/IHasRewardsRecipients.sol/IHasRewardsRecipients.json +1 -0
- package/out/IHooks.sol/IHooks.json +1 -0
- package/out/IMsgSender.sol/IMsgSender.json +1 -0
- package/out/IPoolManager.sol/IPoolManager.json +1 -0
- package/out/IProtocolFees.sol/IProtocolFees.json +1 -0
- package/out/ISupportsLimitOrderFill.sol/ISupportsLimitOrderFill.json +1 -0
- package/out/ISwapPathRouter.sol/ISwapPathRouter.json +1 -0
- package/out/ISwapRouter.sol/ISwapRouter.json +1 -0
- package/out/IUniswapV3SwapCallback.sol/IUniswapV3SwapCallback.json +1 -0
- package/out/IUpgradeableV4Hook.sol/IUpgradeableDestinationV4Hook.json +1 -0
- package/out/IUpgradeableV4Hook.sol/IUpgradeableDestinationV4HookWithUpdateableFee.json +1 -0
- package/out/IUpgradeableV4Hook.sol/IUpgradeableV4Hook.json +1 -0
- package/out/IZoraHookRegistry.sol/IZoraHookRegistry.json +1 -0
- package/out/IZoraLimitOrderBook.sol/IZoraLimitOrderBook.json +1 -0
- package/out/IZoraLimitOrderBookCoinsInterface.sol/IZoraLimitOrderBookCoinsInterface.json +1 -0
- package/out/IZoraV4CoinHook.sol/IZoraV4CoinHook.json +1 -0
- package/out/LimitOrderBitmap.sol/LimitOrderBitmap.json +1 -0
- package/out/LimitOrderCommon.sol/LimitOrderCommon.json +1 -0
- package/out/LimitOrderCreate.sol/LimitOrderCreate.json +1 -0
- package/out/LimitOrderFill.sol/LimitOrderFill.json +1 -0
- package/out/LimitOrderLiquidity.sol/LimitOrderLiquidity.json +1 -0
- package/out/LimitOrderQueues.sol/LimitOrderQueues.json +1 -0
- package/out/LimitOrderStorage.sol/LimitOrderStorage.json +1 -0
- package/out/LimitOrderTypes.sol/LimitOrderTypes.json +1 -0
- package/out/LimitOrderWithdraw.sol/LimitOrderWithdraw.json +1 -0
- package/out/LiquidityAmounts.sol/LiquidityAmounts.json +1 -0
- package/out/LiquidityMath.sol/LiquidityMath.json +1 -0
- package/out/Lock.sol/Lock.json +1 -0
- package/out/NonzeroDeltaCount.sol/NonzeroDeltaCount.json +1 -0
- package/out/Path.sol/Path.json +1 -0
- package/out/PathKey.sol/PathKeyLibrary.json +1 -0
- package/out/Permit2Payments.sol/Permit2Payments.json +1 -0
- package/out/PoolId.sol/PoolIdLibrary.json +1 -0
- package/out/Position.sol/Position.json +1 -0
- package/out/SafeCast.sol/SafeCast.json +1 -0
- package/out/SafeCast160.sol/SafeCast160.json +1 -0
- package/out/SafeERC20.sol/SafeERC20.json +1 -0
- package/out/SimpleAccessManaged.sol/SimpleAccessManaged.json +1 -0
- package/out/SimpleAccessManager.sol/SimpleAccessManager.json +1 -0
- package/out/SqrtPriceMath.sol/SqrtPriceMath.json +1 -0
- package/out/StateLibrary.sol/StateLibrary.json +1 -0
- package/out/SwapLimitOrders.sol/SwapLimitOrders.json +1 -0
- package/out/SwapWithLimitOrders.sol/SwapWithLimitOrders.json +1 -0
- package/out/TickBitmap.sol/TickBitmap.json +1 -0
- package/out/TickMath.sol/TickMath.json +1 -0
- package/out/TransientSlot.sol/TransientSlot.json +1 -0
- package/out/TransientStateLibrary.sol/TransientStateLibrary.json +1 -0
- package/out/UniV4SwapToCurrency.sol/UniV4SwapToCurrency.json +1 -0
- package/out/UnsafeMath.sol/UnsafeMath.json +1 -0
- package/out/V3ToV4SwapLib.sol/V3ToV4SwapLib.json +1 -0
- package/out/ZoraLimitOrderBook.sol/ZoraLimitOrderBook.json +1 -0
- package/out/build-info/69718f10d1dc37f0.json +1 -0
- package/out/uniswap/BitMath.sol/BitMath.json +1 -0
- package/out/uniswap/CustomRevert.sol/CustomRevert.json +1 -0
- package/out/uniswap/FullMath.sol/FullMath.json +1 -0
- package/out/uniswap/SafeCast.sol/SafeCast.json +1 -0
- package/out/uniswap/TickMath.sol/TickMath.json +1 -0
- package/package/index.ts +1 -0
- package/package/wagmiGenerated.ts +738 -0
- package/package.json +57 -0
- package/remappings.txt +11 -0
- package/src/IZoraLimitOrderBook.sol +195 -0
- package/src/ZoraLimitOrderBook.sol +220 -0
- package/src/access/SimpleAccessManaged.sol +76 -0
- package/src/access/SimpleAccessManager.sol +268 -0
- package/src/libs/LimitOrderBitmap.sol +84 -0
- package/src/libs/LimitOrderCommon.sol +91 -0
- package/src/libs/LimitOrderCreate.sol +277 -0
- package/src/libs/LimitOrderFill.sol +362 -0
- package/src/libs/LimitOrderLiquidity.sol +222 -0
- package/src/libs/LimitOrderQueues.sol +101 -0
- package/src/libs/LimitOrderStorage.sol +34 -0
- package/src/libs/LimitOrderTypes.sol +41 -0
- package/src/libs/LimitOrderWithdraw.sol +100 -0
- package/src/libs/Permit2Payments.sol +41 -0
- package/src/libs/SwapLimitOrders.sol +209 -0
- package/src/router/SwapWithLimitOrders.sol +454 -0
- package/test/LimitOrderAccessControl.t.sol +461 -0
- package/test/LimitOrderBitmap.t.sol +194 -0
- package/test/LimitOrderCreate.t.sol +348 -0
- package/test/LimitOrderFill.t.sol +1005 -0
- package/test/LimitOrderLibraries.t.sol +354 -0
- package/test/LimitOrderLiquidityPayouts.t.sol +333 -0
- package/test/LimitOrderV4Pools.t.sol +157 -0
- package/test/LimitOrderWithdraw.t.sol +653 -0
- package/test/SimpleAccessManager.t.sol +420 -0
- package/test/SwapWithLimitOrders.t.sol +107 -0
- package/test/SwapWithLimitOrdersRouter.t.sol +1073 -0
- package/test/gas/LimitOrderFillGas.t.sol +1008 -0
- package/test/gas/LimitOrderSwapGas.t.sol +403 -0
- package/test/gas/logs/gas_benchmarks_fill_20251201.log +30 -0
- package/test/gas/logs/gas_benchmarks_swap_20251201.log +27 -0
- package/test/unit/LimitOrderBitmapUnit.t.sol +276 -0
- package/test/unit/LimitOrderCreateUnit.t.sol +358 -0
- package/test/unit/SwapLimitOrdersUnit.t.sol +672 -0
- package/test/unit/SwapLimitOrdersValidation.t.sol +423 -0
- package/test/unit/SwapWithLimitOrdersUnit.t.sol +321 -0
- package/test/utils/BaseTest.sol +793 -0
- package/test/utils/TestableZoraLimitOrderBook.sol +54 -0
- package/tsconfig.build.json +10 -0
- package/tsconfig.json +9 -0
- package/tsup.config.ts +11 -0
- package/wagmi.config.ts +18 -0
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.13;
|
|
3
|
+
|
|
4
|
+
import {Test} from "forge-std/Test.sol";
|
|
5
|
+
import {SwapLimitOrders, LimitOrderConfig, Orders} from "../../src/libs/SwapLimitOrders.sol";
|
|
6
|
+
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
|
|
7
|
+
import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
|
|
8
|
+
import {IHooks} from "@uniswap/v4-core/src/interfaces/IHooks.sol";
|
|
9
|
+
import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol";
|
|
10
|
+
import {SwapLimitOrdersWrapper} from "./SwapLimitOrdersUnit.t.sol";
|
|
11
|
+
|
|
12
|
+
contract SwapLimitOrdersValidationTest is Test {
|
|
13
|
+
using SwapLimitOrders for *;
|
|
14
|
+
|
|
15
|
+
PoolKey internal testKey;
|
|
16
|
+
int24 constant TICK_SPACING = 10;
|
|
17
|
+
SwapLimitOrdersWrapper internal wrapper;
|
|
18
|
+
|
|
19
|
+
function setUp() public {
|
|
20
|
+
// Create a minimal valid PoolKey for testing
|
|
21
|
+
testKey = PoolKey({
|
|
22
|
+
currency0: Currency.wrap(address(0x1)),
|
|
23
|
+
currency1: Currency.wrap(address(0x2)),
|
|
24
|
+
fee: 3000,
|
|
25
|
+
tickSpacing: TICK_SPACING,
|
|
26
|
+
hooks: IHooks(address(0))
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Deploy wrapper for testing library reverts
|
|
30
|
+
wrapper = new SwapLimitOrdersWrapper();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function test_isLimitOrder_NotCoinBuy() public {
|
|
34
|
+
LimitOrderConfig memory params = _createValidParams(makeAddr("maker"), 1);
|
|
35
|
+
|
|
36
|
+
bool result = wrapper.isLimitOrder(
|
|
37
|
+
false, // not a coin buy
|
|
38
|
+
makeAddr("testSwapper"),
|
|
39
|
+
int128(uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE)),
|
|
40
|
+
params
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
assertFalse(result, "should return false for non-buy swaps");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function test_isLimitOrder_NoOrders() public {
|
|
47
|
+
LimitOrderConfig memory params = _createEmptyParams(makeAddr("maker"));
|
|
48
|
+
|
|
49
|
+
bool result = wrapper.isLimitOrder(true, makeAddr("testSwapper"), int128(uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE)), params);
|
|
50
|
+
|
|
51
|
+
assertFalse(result, "should return false when no orders configured");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function test_isLimitOrder_ZeroSwapper() public {
|
|
55
|
+
LimitOrderConfig memory params = _createValidParams(makeAddr("maker"), 1);
|
|
56
|
+
|
|
57
|
+
bool result = wrapper.isLimitOrder(
|
|
58
|
+
true,
|
|
59
|
+
address(0), // zero swapper
|
|
60
|
+
int128(uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE)),
|
|
61
|
+
params
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
assertFalse(result, "should return false for zero swapper");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function test_isLimitOrder_NegativeCoinDelta() public {
|
|
68
|
+
LimitOrderConfig memory params = _createValidParams(makeAddr("maker"), 1);
|
|
69
|
+
|
|
70
|
+
bool result = wrapper.isLimitOrder(
|
|
71
|
+
true,
|
|
72
|
+
makeAddr("testSwapper"),
|
|
73
|
+
-1, // negative delta
|
|
74
|
+
params
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
assertFalse(result, "should return false for negative coinDelta");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function test_isLimitOrder_ZeroCoinDelta() public {
|
|
81
|
+
LimitOrderConfig memory params = _createValidParams(makeAddr("maker"), 1);
|
|
82
|
+
|
|
83
|
+
bool result = wrapper.isLimitOrder(
|
|
84
|
+
true,
|
|
85
|
+
makeAddr("testSwapper"),
|
|
86
|
+
0, // zero delta
|
|
87
|
+
params
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
assertFalse(result, "should return false for zero coinDelta");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function test_isLimitOrder_BelowMinimumSize() public {
|
|
94
|
+
LimitOrderConfig memory params = _createValidParams(makeAddr("maker"), 1);
|
|
95
|
+
|
|
96
|
+
bool result = wrapper.isLimitOrder(
|
|
97
|
+
true,
|
|
98
|
+
makeAddr("testSwapper"),
|
|
99
|
+
int128(uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE - 1)), // dust amount
|
|
100
|
+
params
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
assertFalse(result, "should return false for dust amounts below minimum");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function test_isLimitOrder_ValidCase() public {
|
|
107
|
+
LimitOrderConfig memory params = _createValidParams(makeAddr("maker"), 1);
|
|
108
|
+
|
|
109
|
+
bool result = wrapper.isLimitOrder(true, makeAddr("testSwapper"), int128(uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE)), params);
|
|
110
|
+
|
|
111
|
+
assertTrue(result, "should return true when all conditions met");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function test_validate_LengthMismatch() public {
|
|
115
|
+
LimitOrderConfig memory params;
|
|
116
|
+
params.multiples = new uint256[](2);
|
|
117
|
+
params.percentages = new uint256[](1); // mismatched length
|
|
118
|
+
|
|
119
|
+
params.multiples[0] = 2e18;
|
|
120
|
+
params.multiples[1] = 4e18;
|
|
121
|
+
params.percentages[0] = 10000;
|
|
122
|
+
|
|
123
|
+
vm.expectRevert(SwapLimitOrders.LengthMismatch.selector);
|
|
124
|
+
wrapper.validate(params);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function test_validate_ZeroPercent() public {
|
|
128
|
+
LimitOrderConfig memory params;
|
|
129
|
+
params.multiples = new uint256[](2);
|
|
130
|
+
params.percentages = new uint256[](2);
|
|
131
|
+
|
|
132
|
+
params.multiples[0] = 2e18;
|
|
133
|
+
params.multiples[1] = 4e18;
|
|
134
|
+
params.percentages[0] = 5000;
|
|
135
|
+
params.percentages[1] = 0; // zero percent - invalid
|
|
136
|
+
|
|
137
|
+
vm.expectRevert(SwapLimitOrders.InvalidPercent.selector);
|
|
138
|
+
wrapper.validate(params);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function test_validate_PercentOverflow() public {
|
|
142
|
+
LimitOrderConfig memory params;
|
|
143
|
+
params.multiples = new uint256[](2);
|
|
144
|
+
params.percentages = new uint256[](2);
|
|
145
|
+
|
|
146
|
+
params.multiples[0] = 2e18;
|
|
147
|
+
params.multiples[1] = 4e18;
|
|
148
|
+
params.percentages[0] = 6000;
|
|
149
|
+
params.percentages[1] = 5000; // total = 11000 > 10000
|
|
150
|
+
|
|
151
|
+
vm.expectRevert(SwapLimitOrders.PercentOverflow.selector);
|
|
152
|
+
wrapper.validate(params);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function test_validate_InvalidMultiple() public {
|
|
156
|
+
LimitOrderConfig memory params;
|
|
157
|
+
params.multiples = new uint256[](2);
|
|
158
|
+
params.percentages = new uint256[](2);
|
|
159
|
+
|
|
160
|
+
params.multiples[0] = 2e18;
|
|
161
|
+
params.multiples[1] = 1e18; // 1.0x - not strictly greater
|
|
162
|
+
params.percentages[0] = 5000;
|
|
163
|
+
params.percentages[1] = 5000;
|
|
164
|
+
|
|
165
|
+
vm.expectRevert(SwapLimitOrders.InvalidMultiple.selector);
|
|
166
|
+
wrapper.validate(params);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function test_validate_UnderOneHundredPercent() public {
|
|
170
|
+
LimitOrderConfig memory params;
|
|
171
|
+
params.multiples = new uint256[](2);
|
|
172
|
+
params.percentages = new uint256[](2);
|
|
173
|
+
|
|
174
|
+
params.multiples[0] = 2e18;
|
|
175
|
+
params.multiples[1] = 4e18;
|
|
176
|
+
params.percentages[0] = 3000; // 30%
|
|
177
|
+
params.percentages[1] = 5000; // 50% - total 80%
|
|
178
|
+
|
|
179
|
+
uint256 totalPercent = SwapLimitOrders.validate(params);
|
|
180
|
+
assertEq(totalPercent, 8000, "should allow undershoot");
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function test_computeOrders_BelowMinimum() public {
|
|
184
|
+
LimitOrderConfig memory params = _createValidParams(makeAddr("maker"), 2);
|
|
185
|
+
uint128 dustSize = uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE - 1);
|
|
186
|
+
|
|
187
|
+
(Orders memory orders, uint128 allocated, uint128 unallocated) = SwapLimitOrders.computeOrders(
|
|
188
|
+
testKey,
|
|
189
|
+
true, // isCurrency0
|
|
190
|
+
uint128(dustSize),
|
|
191
|
+
0, // baseTick
|
|
192
|
+
TickMath.getSqrtPriceAtTick(0),
|
|
193
|
+
params
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
assertEq(orders.sizes.length, 0, "should return empty orders");
|
|
197
|
+
assertEq(orders.ticks.length, 0, "should return empty ticks");
|
|
198
|
+
assertEq(allocated, 0, "should have zero allocated");
|
|
199
|
+
assertEq(unallocated, uint128(dustSize), "all should be unallocated");
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function test_computeOrders_SkipsZeroSizeOrders() public {
|
|
203
|
+
LimitOrderConfig memory params;
|
|
204
|
+
params.multiples = new uint256[](4);
|
|
205
|
+
params.percentages = new uint256[](4);
|
|
206
|
+
|
|
207
|
+
// Use a small total size so that tiny percentages round to zero
|
|
208
|
+
// With 10000 wei total: 1 bp = 1 wei, so we need very small percentages
|
|
209
|
+
params.multiples[0] = 2e18;
|
|
210
|
+
params.multiples[1] = 4e18;
|
|
211
|
+
params.multiples[2] = 8e18;
|
|
212
|
+
params.multiples[3] = 16e18;
|
|
213
|
+
|
|
214
|
+
// First two get most of allocation, last two get tiny amounts that might round to zero
|
|
215
|
+
params.percentages[0] = 4999; // ~50%
|
|
216
|
+
params.percentages[1] = 4999; // ~50%
|
|
217
|
+
params.percentages[2] = 1; // 0.01% - may round to zero with small size
|
|
218
|
+
params.percentages[3] = 1; // 0.01% - may round to zero with small size
|
|
219
|
+
|
|
220
|
+
// Use exactly MIN size which is 1e18
|
|
221
|
+
uint128 totalSize = uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE);
|
|
222
|
+
|
|
223
|
+
(Orders memory orders, uint128 allocated, uint128 unallocated) = SwapLimitOrders.computeOrders(
|
|
224
|
+
testKey,
|
|
225
|
+
true,
|
|
226
|
+
uint128(totalSize),
|
|
227
|
+
0,
|
|
228
|
+
TickMath.getSqrtPriceAtTick(0),
|
|
229
|
+
params
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
// With MIN_LIMIT_ORDER_SIZE (1e18) and 1bp = 1e14, orders should not be skipped
|
|
233
|
+
// Let's just verify we get at least one order
|
|
234
|
+
assertGt(orders.sizes.length, 0, "should create at least one order");
|
|
235
|
+
assertEq(orders.sizes.length, orders.ticks.length, "sizes and ticks should match");
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function test_computeOrders_ClampsToMaxTick() public {
|
|
239
|
+
LimitOrderConfig memory params;
|
|
240
|
+
params.multiples = new uint256[](1);
|
|
241
|
+
params.percentages = new uint256[](1);
|
|
242
|
+
|
|
243
|
+
// Extremely high multiple to exceed MAX_TICK
|
|
244
|
+
params.multiples[0] = 1000000e18; // 1,000,000x
|
|
245
|
+
params.percentages[0] = 10000;
|
|
246
|
+
|
|
247
|
+
int24 maxTick = TickMath.maxUsableTick(TICK_SPACING);
|
|
248
|
+
|
|
249
|
+
(Orders memory orders, , ) = SwapLimitOrders.computeOrders(
|
|
250
|
+
testKey,
|
|
251
|
+
true,
|
|
252
|
+
uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE),
|
|
253
|
+
0,
|
|
254
|
+
TickMath.getSqrtPriceAtTick(0),
|
|
255
|
+
params
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
assertEq(orders.ticks.length, 1, "should create one order");
|
|
259
|
+
// Should clamp to max usable tick - the function applies clamping logic
|
|
260
|
+
// including minimum separation from base tick
|
|
261
|
+
assertLe(orders.ticks[0], maxTick, "should not exceed maxTick");
|
|
262
|
+
// The function enforces at least tickSpacing separation from base tick
|
|
263
|
+
// With such a high multiple, we expect to be at or near max tick
|
|
264
|
+
assertGt(orders.ticks[0], 0, "should be positive tick for buy orders");
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function test_computeOrders_ClampsToMinTick() public {
|
|
268
|
+
LimitOrderConfig memory params;
|
|
269
|
+
params.multiples = new uint256[](1);
|
|
270
|
+
params.percentages = new uint256[](1);
|
|
271
|
+
|
|
272
|
+
// High multiple for token1 (inverted) to approach MIN_TICK
|
|
273
|
+
params.multiples[0] = 1000000e18;
|
|
274
|
+
params.percentages[0] = 10000;
|
|
275
|
+
|
|
276
|
+
int24 minTick = -TickMath.maxUsableTick(TICK_SPACING);
|
|
277
|
+
int24 startTick = TickMath.MAX_TICK - 1000;
|
|
278
|
+
|
|
279
|
+
(Orders memory orders, , ) = SwapLimitOrders.computeOrders(
|
|
280
|
+
testKey,
|
|
281
|
+
false, // isCurrency0 = false means selling, inverts multiple
|
|
282
|
+
uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE),
|
|
283
|
+
startTick,
|
|
284
|
+
TickMath.getSqrtPriceAtTick(startTick),
|
|
285
|
+
params
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
assertEq(orders.ticks.length, 1, "should create one order");
|
|
289
|
+
assertLe(orders.ticks[0], TickMath.maxUsableTick(TICK_SPACING), "should be within valid range");
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function test_computeOrders_MinimumSeparationCurrency0() public {
|
|
293
|
+
LimitOrderConfig memory params;
|
|
294
|
+
params.multiples = new uint256[](1);
|
|
295
|
+
params.percentages = new uint256[](1);
|
|
296
|
+
|
|
297
|
+
// Very small multiple - barely above 1x
|
|
298
|
+
params.multiples[0] = 1.001e18; // 1.001x
|
|
299
|
+
params.percentages[0] = 10000;
|
|
300
|
+
|
|
301
|
+
int24 baseTick = 0;
|
|
302
|
+
|
|
303
|
+
(Orders memory orders, , ) = SwapLimitOrders.computeOrders(
|
|
304
|
+
testKey,
|
|
305
|
+
true, // isCurrency0
|
|
306
|
+
uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE),
|
|
307
|
+
baseTick,
|
|
308
|
+
TickMath.getSqrtPriceAtTick(baseTick),
|
|
309
|
+
params
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
assertEq(orders.ticks.length, 1, "should create one order");
|
|
313
|
+
// Should be at least tickSpacing away
|
|
314
|
+
assertGe(orders.ticks[0], baseTick + TICK_SPACING, "should maintain minimum separation");
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function test_computeOrders_MinimumSeparationCurrency1() public {
|
|
318
|
+
LimitOrderConfig memory params;
|
|
319
|
+
params.multiples = new uint256[](1);
|
|
320
|
+
params.percentages = new uint256[](1);
|
|
321
|
+
|
|
322
|
+
// Very small multiple
|
|
323
|
+
params.multiples[0] = 1.001e18; // 1.001x
|
|
324
|
+
params.percentages[0] = 10000;
|
|
325
|
+
|
|
326
|
+
int24 baseTick = 0;
|
|
327
|
+
|
|
328
|
+
(Orders memory orders, , ) = SwapLimitOrders.computeOrders(
|
|
329
|
+
testKey,
|
|
330
|
+
false, // not isCurrency0
|
|
331
|
+
uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE),
|
|
332
|
+
baseTick,
|
|
333
|
+
TickMath.getSqrtPriceAtTick(baseTick),
|
|
334
|
+
params
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
assertEq(orders.ticks.length, 1, "should create one order");
|
|
338
|
+
// Should be at least tickSpacing away (negative direction)
|
|
339
|
+
assertLe(orders.ticks[0], baseTick - TICK_SPACING, "should maintain minimum separation");
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function test_computeOrders_MultiplierInversionForCurrency1() public {
|
|
343
|
+
LimitOrderConfig memory params;
|
|
344
|
+
params.multiples = new uint256[](1);
|
|
345
|
+
params.percentages = new uint256[](1);
|
|
346
|
+
|
|
347
|
+
// Use a meaningful multiple
|
|
348
|
+
params.multiples[0] = 2e18; // 2x
|
|
349
|
+
params.percentages[0] = 10000;
|
|
350
|
+
|
|
351
|
+
int24 baseTick = 0;
|
|
352
|
+
|
|
353
|
+
// For isCurrency0 = false, multiplier gets inverted
|
|
354
|
+
(Orders memory orders, , ) = SwapLimitOrders.computeOrders(
|
|
355
|
+
testKey,
|
|
356
|
+
false, // not isCurrency0 - triggers inversion
|
|
357
|
+
uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE),
|
|
358
|
+
baseTick,
|
|
359
|
+
TickMath.getSqrtPriceAtTick(baseTick),
|
|
360
|
+
params
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
assertEq(orders.ticks.length, 1, "should create one order");
|
|
364
|
+
// For !isCurrency0, the tick should be below baseTick
|
|
365
|
+
assertLt(orders.ticks[0], baseTick, "inverted multiplier should place tick below base");
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function test_computeOrders_AllBoundaryClampingBranches() public {
|
|
369
|
+
LimitOrderConfig memory params;
|
|
370
|
+
|
|
371
|
+
// Test 1: aligned > maxTick (line 167)
|
|
372
|
+
params.multiples = new uint256[](1);
|
|
373
|
+
params.percentages = new uint256[](1);
|
|
374
|
+
params.multiples[0] = 1000000e18;
|
|
375
|
+
params.percentages[0] = 10000;
|
|
376
|
+
|
|
377
|
+
(Orders memory orders1, , ) = SwapLimitOrders.computeOrders(
|
|
378
|
+
testKey,
|
|
379
|
+
true,
|
|
380
|
+
uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE),
|
|
381
|
+
0,
|
|
382
|
+
TickMath.getSqrtPriceAtTick(0),
|
|
383
|
+
params
|
|
384
|
+
);
|
|
385
|
+
assertLe(orders1.ticks[0], TickMath.maxUsableTick(TICK_SPACING), "should clamp to maxTick");
|
|
386
|
+
|
|
387
|
+
// Test 2: aligned < minTick (line 168)
|
|
388
|
+
params.multiples[0] = 1000000e18;
|
|
389
|
+
int24 veryHighTick = TickMath.MAX_TICK - 100;
|
|
390
|
+
(Orders memory orders2, , ) = SwapLimitOrders.computeOrders(
|
|
391
|
+
testKey,
|
|
392
|
+
false, // inverted, goes negative
|
|
393
|
+
uint128(SwapLimitOrders.MIN_LIMIT_ORDER_SIZE),
|
|
394
|
+
veryHighTick,
|
|
395
|
+
TickMath.getSqrtPriceAtTick(veryHighTick),
|
|
396
|
+
params
|
|
397
|
+
);
|
|
398
|
+
assertGe(orders2.ticks[0], -TickMath.maxUsableTick(TICK_SPACING), "should clamp to minTick");
|
|
399
|
+
|
|
400
|
+
// Test 3 & 4: minAway enforcement tested in previous minimum separation tests
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function _createValidParams(address maker, uint256 numOrders) internal pure returns (LimitOrderConfig memory) {
|
|
404
|
+
LimitOrderConfig memory params;
|
|
405
|
+
params.multiples = new uint256[](numOrders);
|
|
406
|
+
params.percentages = new uint256[](numOrders);
|
|
407
|
+
|
|
408
|
+
uint256 pctPerOrder = 10000 / numOrders;
|
|
409
|
+
for (uint256 i; i < numOrders; ++i) {
|
|
410
|
+
params.multiples[i] = (2 ** (i + 1)) * 1e18; // 2x, 4x, 8x, etc.
|
|
411
|
+
params.percentages[i] = pctPerOrder;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return params;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function _createEmptyParams(address maker) internal pure returns (LimitOrderConfig memory) {
|
|
418
|
+
LimitOrderConfig memory params;
|
|
419
|
+
params.multiples = new uint256[](0);
|
|
420
|
+
params.percentages = new uint256[](0);
|
|
421
|
+
return params;
|
|
422
|
+
}
|
|
423
|
+
}
|