@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,348 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.13;
|
|
3
|
+
|
|
4
|
+
import {BaseTest} from "./utils/BaseTest.sol";
|
|
5
|
+
|
|
6
|
+
import {IZoraLimitOrderBook} from "../src/IZoraLimitOrderBook.sol";
|
|
7
|
+
import {CoinCommon} from "@zoralabs/coins/src/libs/CoinCommon.sol";
|
|
8
|
+
import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
|
|
9
|
+
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
|
|
10
|
+
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
11
|
+
import {Vm} from "forge-std/Vm.sol";
|
|
12
|
+
|
|
13
|
+
contract LimitOrderCreateTest is BaseTest {
|
|
14
|
+
function test_create_prefundedViaAutosellCreatesOrders() public {
|
|
15
|
+
PoolKey memory key = creatorCoin.getPoolKey();
|
|
16
|
+
bool isCurrency0 = Currency.unwrap(key.currency0) == address(creatorCoin);
|
|
17
|
+
address orderCoin = _orderCoin(key, isCurrency0);
|
|
18
|
+
|
|
19
|
+
vm.recordLogs();
|
|
20
|
+
_executeSingleHopSwapWithLimitOrders(users.buyer, key, DEFAULT_LIMIT_ORDER_AMOUNT, _defaultMultiples(), _defaultPercentages());
|
|
21
|
+
CreatedOrderLog[] memory created = _decodeCreatedLogs(vm.getRecordedLogs());
|
|
22
|
+
|
|
23
|
+
assertGt(created.length, 0, "expected limit orders orders");
|
|
24
|
+
for (uint256 i; i < created.length; ++i) {
|
|
25
|
+
assertEq(created[i].maker, users.buyer, "maker mismatch");
|
|
26
|
+
assertEq(created[i].coin, orderCoin, "coin mismatch");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
uint256 realized = _sumOrderSizes(created);
|
|
30
|
+
assertEq(_makerBalance(users.buyer, orderCoin), realized, "maker balance mismatch");
|
|
31
|
+
_assertOpenOrderState(users.buyer, orderCoin, created[0].poolKeyHash, created, key.tickSpacing);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function test_create_pullsErc20FundsForExternalMaker() public {
|
|
35
|
+
PoolKey memory key = creatorCoin.getPoolKey();
|
|
36
|
+
bool isCurrency0 = Currency.unwrap(key.currency0) == address(creatorCoin);
|
|
37
|
+
address orderCoin = _orderCoin(key, isCurrency0);
|
|
38
|
+
|
|
39
|
+
uint256[] memory orderSizes = new uint256[](2);
|
|
40
|
+
orderSizes[0] = 40e18;
|
|
41
|
+
orderSizes[1] = 25e18;
|
|
42
|
+
uint256 totalSize = orderSizes[0] + orderSizes[1];
|
|
43
|
+
|
|
44
|
+
int24 baseTick = _alignedTick(_currentTick(key), key.tickSpacing);
|
|
45
|
+
int24[] memory orderTicks = new int24[](2);
|
|
46
|
+
orderTicks[0] = isCurrency0 ? baseTick + key.tickSpacing : baseTick - key.tickSpacing;
|
|
47
|
+
orderTicks[1] = isCurrency0 ? orderTicks[0] + key.tickSpacing : orderTicks[0] - key.tickSpacing;
|
|
48
|
+
|
|
49
|
+
if (orderCoin == address(0)) {
|
|
50
|
+
vm.deal(users.seller, totalSize);
|
|
51
|
+
} else {
|
|
52
|
+
deal(orderCoin, users.seller, totalSize);
|
|
53
|
+
vm.startPrank(users.seller);
|
|
54
|
+
IERC20(orderCoin).approve(address(limitOrderBook), totalSize);
|
|
55
|
+
vm.stopPrank();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
vm.recordLogs();
|
|
59
|
+
vm.prank(users.seller);
|
|
60
|
+
bytes32[] memory orderIds = limitOrderBook.create{value: orderCoin == address(0) ? totalSize : 0}(
|
|
61
|
+
key,
|
|
62
|
+
isCurrency0,
|
|
63
|
+
orderSizes,
|
|
64
|
+
orderTicks,
|
|
65
|
+
users.seller
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
CreatedOrderLog[] memory created = _decodeCreatedLogs(vm.getRecordedLogs());
|
|
69
|
+
assertEq(created.length, orderIds.length, "mismatch between emitted and returned orders");
|
|
70
|
+
|
|
71
|
+
uint256 realized = _sumOrderSizes(created);
|
|
72
|
+
assertApproxEqAbs(realized, totalSize, 1, "realized size drift");
|
|
73
|
+
assertEq(_makerBalance(users.seller, orderCoin), realized, "maker balance mismatch");
|
|
74
|
+
_assertOpenOrderState(users.seller, orderCoin, CoinCommon.hashPoolKey(key), created, key.tickSpacing);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function test_create_refundsResidualAndIncrementsNonce() public {
|
|
78
|
+
PoolKey memory key = creatorCoin.getPoolKey();
|
|
79
|
+
bool isCurrency0 = Currency.unwrap(key.currency0) == address(creatorCoin);
|
|
80
|
+
address orderCoin = _orderCoin(key, isCurrency0);
|
|
81
|
+
|
|
82
|
+
uint256[] memory orderSizes = new uint256[](3);
|
|
83
|
+
orderSizes[0] = 123456789123456789;
|
|
84
|
+
orderSizes[1] = 987654321987654321;
|
|
85
|
+
orderSizes[2] = 222222222222222222;
|
|
86
|
+
|
|
87
|
+
int24[] memory orderTicks = new int24[](3);
|
|
88
|
+
int24 baseTick = _alignedTick(_currentTick(key), key.tickSpacing);
|
|
89
|
+
for (uint256 i; i < orderTicks.length; ++i) {
|
|
90
|
+
orderTicks[i] = _alignedTickForOrder(isCurrency0, baseTick, key.tickSpacing, i);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
uint256 totalSize = orderSizes[0] + orderSizes[1] + orderSizes[2];
|
|
94
|
+
deal(orderCoin, users.seller, totalSize);
|
|
95
|
+
|
|
96
|
+
vm.startPrank(users.seller);
|
|
97
|
+
IERC20(orderCoin).approve(address(limitOrderBook), totalSize);
|
|
98
|
+
vm.stopPrank();
|
|
99
|
+
|
|
100
|
+
uint256 nonceBefore = _makerNonce(users.seller);
|
|
101
|
+
uint256 tokenBalanceBefore = IERC20(orderCoin).balanceOf(users.seller);
|
|
102
|
+
|
|
103
|
+
vm.recordLogs();
|
|
104
|
+
vm.prank(users.seller);
|
|
105
|
+
limitOrderBook.create(key, isCurrency0, orderSizes, orderTicks, users.seller);
|
|
106
|
+
CreatedOrderLog[] memory created = _decodeCreatedLogs(vm.getRecordedLogs());
|
|
107
|
+
assertEq(created.length, orderSizes.length, "unexpected created order count");
|
|
108
|
+
|
|
109
|
+
uint256 realized = _sumOrderSizes(created);
|
|
110
|
+
uint256 residual = totalSize - realized;
|
|
111
|
+
assertGt(residual, 0, "expected residual refund");
|
|
112
|
+
|
|
113
|
+
assertEq(_makerBalance(users.seller, orderCoin), realized, "maker balance mismatch");
|
|
114
|
+
assertEq(IERC20(orderCoin).balanceOf(users.seller), tokenBalanceBefore - totalSize + residual, "maker residual mismatch");
|
|
115
|
+
assertEq(_makerNonce(users.seller), nonceBefore + orderSizes.length, "maker nonce mismatch");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function test_create_revertsWhenRealizedZero() public {
|
|
119
|
+
PoolKey memory key = creatorCoin.getPoolKey();
|
|
120
|
+
bool isCurrency0 = Currency.unwrap(key.currency0) == address(creatorCoin);
|
|
121
|
+
address orderCoin = _orderCoin(key, isCurrency0);
|
|
122
|
+
|
|
123
|
+
uint256[] memory orderSizes = new uint256[](1);
|
|
124
|
+
orderSizes[0] = 1;
|
|
125
|
+
|
|
126
|
+
int24[] memory orderTicks = new int24[](1);
|
|
127
|
+
orderTicks[0] = _alignedTick(_currentTick(key), key.tickSpacing);
|
|
128
|
+
|
|
129
|
+
deal(orderCoin, users.seller, orderSizes[0]);
|
|
130
|
+
vm.startPrank(users.seller);
|
|
131
|
+
IERC20(orderCoin).approve(address(limitOrderBook), orderSizes[0]);
|
|
132
|
+
vm.stopPrank();
|
|
133
|
+
|
|
134
|
+
vm.expectRevert(IZoraLimitOrderBook.ZeroRealizedOrder.selector);
|
|
135
|
+
vm.prank(users.seller);
|
|
136
|
+
limitOrderBook.create(key, isCurrency0, orderSizes, orderTicks, users.seller);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function test_create_revertsOnZeroOrderSize() public {
|
|
140
|
+
PoolKey memory key = creatorCoin.getPoolKey();
|
|
141
|
+
bool isCurrency0 = Currency.unwrap(key.currency0) == address(creatorCoin);
|
|
142
|
+
|
|
143
|
+
uint256[] memory orderSizes = new uint256[](1);
|
|
144
|
+
orderSizes[0] = 0;
|
|
145
|
+
|
|
146
|
+
int24[] memory orderTicks = new int24[](1);
|
|
147
|
+
orderTicks[0] = _alignedTick(_currentTick(key), key.tickSpacing);
|
|
148
|
+
|
|
149
|
+
vm.expectRevert(IZoraLimitOrderBook.ZeroOrderSize.selector);
|
|
150
|
+
limitOrderBook.create(key, isCurrency0, orderSizes, orderTicks, users.seller);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function test_create_revertsOnArrayLengthMismatch() public {
|
|
154
|
+
PoolKey memory key = creatorCoin.getPoolKey();
|
|
155
|
+
bool isCurrency0 = Currency.unwrap(key.currency0) == address(creatorCoin);
|
|
156
|
+
|
|
157
|
+
uint256[] memory orderSizes = new uint256[](2);
|
|
158
|
+
orderSizes[0] = 1 ether;
|
|
159
|
+
orderSizes[1] = 1 ether;
|
|
160
|
+
|
|
161
|
+
int24[] memory orderTicks = new int24[](1);
|
|
162
|
+
orderTicks[0] = _alignedTick(_currentTick(key), key.tickSpacing);
|
|
163
|
+
|
|
164
|
+
vm.expectRevert(IZoraLimitOrderBook.ArrayLengthMismatch.selector);
|
|
165
|
+
limitOrderBook.create(key, isCurrency0, orderSizes, orderTicks, users.seller);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function test_create_revertsOnZeroMaker() public {
|
|
169
|
+
PoolKey memory key = creatorCoin.getPoolKey();
|
|
170
|
+
bool isCurrency0 = Currency.unwrap(key.currency0) == address(creatorCoin);
|
|
171
|
+
|
|
172
|
+
uint256[] memory orderSizes = new uint256[](1);
|
|
173
|
+
orderSizes[0] = 1 ether;
|
|
174
|
+
|
|
175
|
+
int24[] memory orderTicks = new int24[](1);
|
|
176
|
+
orderTicks[0] = _alignedTick(_currentTick(key), key.tickSpacing);
|
|
177
|
+
|
|
178
|
+
vm.expectRevert(IZoraLimitOrderBook.ZeroMaker.selector);
|
|
179
|
+
limitOrderBook.create(key, isCurrency0, orderSizes, orderTicks, address(0));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function test_create_revertsOnZeroOrderSizeInBatch() public {
|
|
183
|
+
PoolKey memory key = creatorCoin.getPoolKey();
|
|
184
|
+
bool isCurrency0 = Currency.unwrap(key.currency0) == address(creatorCoin);
|
|
185
|
+
|
|
186
|
+
uint256[] memory orderSizes = new uint256[](2);
|
|
187
|
+
orderSizes[0] = 1 ether;
|
|
188
|
+
orderSizes[1] = 0; // Zero size - invalid
|
|
189
|
+
|
|
190
|
+
int24[] memory orderTicks = new int24[](2);
|
|
191
|
+
orderTicks[0] = _alignedTick(_currentTick(key), key.tickSpacing);
|
|
192
|
+
orderTicks[1] = orderTicks[0] + key.tickSpacing;
|
|
193
|
+
|
|
194
|
+
vm.expectRevert(IZoraLimitOrderBook.ZeroOrderSize.selector);
|
|
195
|
+
limitOrderBook.create(key, isCurrency0, orderSizes, orderTicks, users.seller);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function test_create_revertsOnExcessNativeValue() public {
|
|
199
|
+
PoolKey memory key = creatorCoin.getPoolKey();
|
|
200
|
+
bool isCurrency0 = Currency.unwrap(key.currency0) == address(creatorCoin);
|
|
201
|
+
address orderCoin = _orderCoin(key, isCurrency0);
|
|
202
|
+
|
|
203
|
+
// Only test if order coin is native currency
|
|
204
|
+
if (orderCoin != address(0)) {
|
|
205
|
+
return; // Skip if not native
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
uint256[] memory orderSizes = new uint256[](1);
|
|
209
|
+
orderSizes[0] = 1 ether;
|
|
210
|
+
|
|
211
|
+
int24[] memory orderTicks = new int24[](1);
|
|
212
|
+
int24 baseTick = _alignedTick(_currentTick(key), key.tickSpacing);
|
|
213
|
+
orderTicks[0] = isCurrency0 ? baseTick + key.tickSpacing : baseTick - key.tickSpacing;
|
|
214
|
+
|
|
215
|
+
vm.deal(users.seller, 2 ether);
|
|
216
|
+
|
|
217
|
+
vm.prank(users.seller);
|
|
218
|
+
vm.expectRevert(IZoraLimitOrderBook.NativeValueMismatch.selector);
|
|
219
|
+
limitOrderBook.create{value: 2 ether}(key, isCurrency0, orderSizes, orderTicks, users.seller); // Too much value
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function test_create_revertsOnInsufficientNativeValue() public {
|
|
223
|
+
PoolKey memory key = creatorCoin.getPoolKey();
|
|
224
|
+
bool isCurrency0 = Currency.unwrap(key.currency0) == address(creatorCoin);
|
|
225
|
+
address orderCoin = _orderCoin(key, isCurrency0);
|
|
226
|
+
|
|
227
|
+
// Only test if order coin is native currency
|
|
228
|
+
if (orderCoin != address(0)) {
|
|
229
|
+
return; // Skip if not native
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
uint256[] memory orderSizes = new uint256[](1);
|
|
233
|
+
orderSizes[0] = 1 ether;
|
|
234
|
+
|
|
235
|
+
int24[] memory orderTicks = new int24[](1);
|
|
236
|
+
int24 baseTick = _alignedTick(_currentTick(key), key.tickSpacing);
|
|
237
|
+
orderTicks[0] = isCurrency0 ? baseTick + key.tickSpacing : baseTick - key.tickSpacing;
|
|
238
|
+
|
|
239
|
+
vm.deal(users.seller, 0.5 ether);
|
|
240
|
+
|
|
241
|
+
vm.prank(users.seller);
|
|
242
|
+
vm.expectRevert(IZoraLimitOrderBook.NativeValueMismatch.selector);
|
|
243
|
+
limitOrderBook.create{value: 0.5 ether}(key, isCurrency0, orderSizes, orderTicks, users.seller); // Too little value
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function test_create_revertsOnInsufficientERC20Approval() public {
|
|
247
|
+
PoolKey memory key = creatorCoin.getPoolKey();
|
|
248
|
+
bool isCurrency0 = Currency.unwrap(key.currency0) == address(creatorCoin);
|
|
249
|
+
address orderCoin = _orderCoin(key, isCurrency0);
|
|
250
|
+
|
|
251
|
+
// Only test if order coin is ERC20
|
|
252
|
+
if (orderCoin == address(0)) {
|
|
253
|
+
return; // Skip if native
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
uint256[] memory orderSizes = new uint256[](1);
|
|
257
|
+
orderSizes[0] = 1 ether;
|
|
258
|
+
|
|
259
|
+
int24[] memory orderTicks = new int24[](1);
|
|
260
|
+
int24 baseTick = _alignedTick(_currentTick(key), key.tickSpacing);
|
|
261
|
+
orderTicks[0] = isCurrency0 ? baseTick + key.tickSpacing : baseTick - key.tickSpacing;
|
|
262
|
+
|
|
263
|
+
deal(orderCoin, users.seller, 1 ether);
|
|
264
|
+
// Don't approve - should fail with ERC20 error
|
|
265
|
+
|
|
266
|
+
vm.prank(users.seller);
|
|
267
|
+
vm.expectRevert(); // ERC20 will throw standard error
|
|
268
|
+
limitOrderBook.create(key, isCurrency0, orderSizes, orderTicks, users.seller);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function test_create_revertsOnInsufficientERC20Balance() public {
|
|
272
|
+
PoolKey memory key = creatorCoin.getPoolKey();
|
|
273
|
+
bool isCurrency0 = Currency.unwrap(key.currency0) == address(creatorCoin);
|
|
274
|
+
address orderCoin = _orderCoin(key, isCurrency0);
|
|
275
|
+
|
|
276
|
+
// Only test if order coin is ERC20
|
|
277
|
+
if (orderCoin == address(0)) {
|
|
278
|
+
return; // Skip if native
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
uint256[] memory orderSizes = new uint256[](1);
|
|
282
|
+
orderSizes[0] = 1 ether;
|
|
283
|
+
|
|
284
|
+
int24[] memory orderTicks = new int24[](1);
|
|
285
|
+
int24 baseTick = _alignedTick(_currentTick(key), key.tickSpacing);
|
|
286
|
+
orderTicks[0] = isCurrency0 ? baseTick + key.tickSpacing : baseTick - key.tickSpacing;
|
|
287
|
+
|
|
288
|
+
// Give only half the needed balance
|
|
289
|
+
deal(orderCoin, users.seller, 0.5 ether);
|
|
290
|
+
vm.startPrank(users.seller);
|
|
291
|
+
IERC20(orderCoin).approve(address(limitOrderBook), 1 ether);
|
|
292
|
+
|
|
293
|
+
vm.expectRevert(); // ERC20 will throw standard error
|
|
294
|
+
limitOrderBook.create(key, isCurrency0, orderSizes, orderTicks, users.seller);
|
|
295
|
+
vm.stopPrank();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function test_makerBalanceUpdated_emittedOnCreate() public {
|
|
299
|
+
PoolKey memory key = creatorCoin.getPoolKey();
|
|
300
|
+
bool isCurrency0 = Currency.unwrap(key.currency0) == address(creatorCoin);
|
|
301
|
+
address orderCoin = _orderCoin(key, isCurrency0);
|
|
302
|
+
|
|
303
|
+
uint256[] memory orderSizes = new uint256[](3);
|
|
304
|
+
orderSizes[0] = 100 ether;
|
|
305
|
+
orderSizes[1] = 200 ether;
|
|
306
|
+
orderSizes[2] = 150 ether;
|
|
307
|
+
(, int24[] memory orderTicks) = _buildDeterministicOrders(key, isCurrency0, orderSizes.length, 1);
|
|
308
|
+
|
|
309
|
+
uint256 totalSize;
|
|
310
|
+
for (uint256 i; i < orderSizes.length; ++i) {
|
|
311
|
+
totalSize += orderSizes[i];
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (orderCoin == address(0)) {
|
|
315
|
+
vm.deal(users.seller, totalSize);
|
|
316
|
+
} else {
|
|
317
|
+
deal(orderCoin, users.seller, totalSize);
|
|
318
|
+
vm.prank(users.seller);
|
|
319
|
+
IERC20(orderCoin).approve(address(limitOrderBook), totalSize);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
vm.recordLogs();
|
|
323
|
+
vm.prank(users.seller);
|
|
324
|
+
limitOrderBook.create{value: orderCoin == address(0) ? totalSize : 0}(key, isCurrency0, orderSizes, orderTicks, users.seller);
|
|
325
|
+
|
|
326
|
+
Vm.Log[] memory logs = vm.getRecordedLogs();
|
|
327
|
+
|
|
328
|
+
// Find MakerBalanceUpdated events
|
|
329
|
+
uint256 eventCount;
|
|
330
|
+
uint256 finalBalance;
|
|
331
|
+
|
|
332
|
+
for (uint256 i; i < logs.length; ++i) {
|
|
333
|
+
if (logs[i].topics[0] == IZoraLimitOrderBook.MakerBalanceUpdated.selector) {
|
|
334
|
+
address eventMaker = address(uint160(uint256(logs[i].topics[1])));
|
|
335
|
+
address eventCoin = address(uint160(uint256(logs[i].topics[2])));
|
|
336
|
+
if (eventMaker == users.seller && eventCoin == orderCoin) {
|
|
337
|
+
finalBalance = abi.decode(logs[i].data, (uint256));
|
|
338
|
+
++eventCount;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Should have 3 events (one per order created)
|
|
344
|
+
assertEq(eventCount, 3, "should emit 3 MakerBalanceUpdated events");
|
|
345
|
+
assertEq(finalBalance, _makerBalance(users.seller, orderCoin), "final balance from event should match actual");
|
|
346
|
+
assertGt(finalBalance, 0, "final balance should be positive after creation");
|
|
347
|
+
}
|
|
348
|
+
}
|