@zoralabs/limit-orders 0.2.1 → 0.2.4
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/.abi-stability +70 -0
- package/.turbo/turbo-build$colon$js.log +50 -51
- package/CHANGELOG.md +35 -0
- package/abis/ISetLimitOrderConfig.json +27 -0
- package/abis/{SimpleAccessManaged.json → Ownable.json} +29 -10
- package/abis/Ownable2Step.json +115 -0
- package/abis/PermittedCallers.json +181 -0
- package/abis/SwapWithLimitOrders.json +116 -3
- package/abis/ZoraLimitOrderBook.json +159 -35
- package/cache/solidity-files-cache.json +1 -1
- package/dist/index.cjs +191 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +191 -27
- package/dist/index.js.map +1 -1
- package/dist/wagmiGenerated.d.ts +217 -32
- package/dist/wagmiGenerated.d.ts.map +1 -1
- package/out/BalanceDelta.sol/BalanceDeltaLibrary.json +1 -1
- package/out/BeforeSwapDelta.sol/BeforeSwapDeltaLibrary.json +1 -1
- package/out/BitMath.sol/BitMath.json +1 -1
- package/out/BytesLib.sol/BytesLib.json +1 -1
- package/out/CoinCommon.sol/CoinCommon.json +1 -1
- package/out/CoinConfigurationVersions.sol/CoinConfigurationVersions.json +1 -1
- package/out/CoinConstants.sol/CoinConstants.json +1 -1
- package/out/Context.sol/Context.json +1 -1
- package/out/Currency.sol/CurrencyLibrary.json +1 -1
- package/out/CurrencyReserves.sol/CurrencyReserves.json +1 -1
- package/out/CustomRevert.sol/CustomRevert.json +1 -1
- package/out/DopplerMath.sol/DopplerMath.json +1 -1
- package/out/FixedPoint128.sol/FixedPoint128.json +1 -1
- package/out/FixedPoint96.sol/FixedPoint96.json +1 -1
- package/out/FullMath.sol/FullMath.json +1 -1
- package/out/IAllowanceTransfer.sol/IAllowanceTransfer.json +1 -1
- package/out/ICoin.sol/ICoin.json +1 -1
- package/out/ICoin.sol/IHasCoinType.json +1 -1
- package/out/ICoin.sol/IHasPoolKey.json +1 -1
- package/out/ICoin.sol/IHasSwapPath.json +1 -1
- package/out/ICoin.sol/IHasTotalSupplyForPositions.json +1 -1
- package/out/IDeployedCoinVersionLookup.sol/IDeployedCoinVersionLookup.json +1 -1
- package/out/IDopplerErrors.sol/IDopplerErrors.json +1 -1
- package/out/IEIP712.sol/IEIP712.json +1 -1
- package/out/IERC1363.sol/IERC1363.json +1 -1
- package/out/IERC165.sol/IERC165.json +1 -1
- package/out/IERC20.sol/IERC20.json +1 -1
- package/out/IERC20Minimal.sol/IERC20Minimal.json +1 -1
- package/out/IERC6909Claims.sol/IERC6909Claims.json +1 -1
- package/out/IERC7572.sol/IERC7572.json +1 -1
- package/out/IExtsload.sol/IExtsload.json +1 -1
- package/out/IExttload.sol/IExttload.json +1 -1
- package/out/IHasRewardsRecipients.sol/IHasRewardsRecipients.json +1 -1
- package/out/IHooks.sol/IHooks.json +1 -1
- package/out/IMsgSender.sol/IMsgSender.json +1 -1
- package/out/IPoolManager.sol/IPoolManager.json +1 -1
- package/out/IProtocolFees.sol/IProtocolFees.json +1 -1
- package/out/ISetLimitOrderConfig.sol/ISetLimitOrderConfig.json +1 -0
- package/out/ISupportsLimitOrderFill.sol/ISupportsLimitOrderFill.json +1 -1
- package/out/ISwapPathRouter.sol/ISwapPathRouter.json +1 -1
- package/out/ISwapRouter.sol/ISwapRouter.json +1 -1
- package/out/IUniswapV3SwapCallback.sol/IUniswapV3SwapCallback.json +1 -1
- package/out/IUpgradeableV4Hook.sol/IUpgradeableDestinationV4Hook.json +1 -1
- package/out/IUpgradeableV4Hook.sol/IUpgradeableDestinationV4HookWithUpdateableFee.json +1 -1
- package/out/IUpgradeableV4Hook.sol/IUpgradeableV4Hook.json +1 -1
- package/out/IWETH.sol/IWETH.json +1 -1
- package/out/IZoraHookRegistry.sol/IZoraHookRegistry.json +1 -1
- package/out/IZoraLimitOrderBook.sol/IZoraLimitOrderBook.json +1 -1
- package/out/IZoraLimitOrderBookCoinsInterface.sol/IZoraLimitOrderBookCoinsInterface.json +1 -1
- package/out/IZoraV4CoinHook.sol/IZoraV4CoinHook.json +1 -1
- package/out/LimitOrderBitmap.sol/LimitOrderBitmap.json +1 -1
- package/out/LimitOrderCommon.sol/LimitOrderCommon.json +1 -1
- package/out/LimitOrderCreate.sol/LimitOrderCreate.json +1 -1
- package/out/LimitOrderFill.sol/LimitOrderFill.json +1 -1
- package/out/LimitOrderLiquidity.sol/LimitOrderLiquidity.json +1 -1
- package/out/LimitOrderViews.sol/LimitOrderViews.json +1 -1
- package/out/LimitOrderWithdraw.sol/LimitOrderWithdraw.json +1 -1
- package/out/LiquidityAmounts.sol/LiquidityAmounts.json +1 -1
- package/out/LiquidityMath.sol/LiquidityMath.json +1 -1
- package/out/Lock.sol/Lock.json +1 -1
- package/out/NonzeroDeltaCount.sol/NonzeroDeltaCount.json +1 -1
- package/out/Ownable.sol/Ownable.json +1 -0
- package/out/Ownable2Step.sol/Ownable2Step.json +1 -0
- package/out/Path.sol/Path.json +1 -1
- package/out/PathKey.sol/PathKeyLibrary.json +1 -1
- package/out/PermittedCallers.sol/PermittedCallers.json +1 -0
- package/out/PoolId.sol/PoolIdLibrary.json +1 -1
- package/out/Position.sol/Position.json +1 -1
- package/out/SafeCast.sol/SafeCast.json +1 -1
- package/out/SafeCast160.sol/SafeCast160.json +1 -1
- package/out/SafeERC20.sol/SafeERC20.json +1 -1
- package/out/SqrtPriceMath.sol/SqrtPriceMath.json +1 -1
- package/out/StateLibrary.sol/StateLibrary.json +1 -1
- package/out/SwapLimitOrders.sol/SwapLimitOrders.json +1 -1
- package/out/SwapWithLimitOrders.sol/SwapWithLimitOrders.json +1 -1
- package/out/TickBitmap.sol/TickBitmap.json +1 -1
- package/out/TickMath.sol/TickMath.json +1 -1
- package/out/TransientSlot.sol/TransientSlot.json +1 -1
- package/out/TransientStateLibrary.sol/TransientStateLibrary.json +1 -1
- package/out/UniV4SwapToCurrency.sol/UniV4SwapToCurrency.json +1 -1
- package/out/UnsafeMath.sol/UnsafeMath.json +1 -1
- package/out/V3ToV4SwapLib.sol/V3ToV4SwapLib.json +1 -1
- package/out/ZoraLimitOrderBook.sol/ZoraLimitOrderBook.json +1 -1
- package/out/build-info/68b2e124c4a02a45.json +1 -0
- package/out/uniswap/BitMath.sol/BitMath.json +1 -1
- package/out/uniswap/CustomRevert.sol/CustomRevert.json +1 -1
- package/out/uniswap/FullMath.sol/FullMath.json +1 -1
- package/out/uniswap/SafeCast.sol/SafeCast.json +1 -1
- package/out/uniswap/TickMath.sol/TickMath.json +1 -1
- package/package/wagmiGenerated.ts +190 -26
- package/package.json +4 -2
- package/src/IZoraLimitOrderBook.sol +3 -5
- package/src/ZoraLimitOrderBook.sol +14 -45
- package/src/access/PermittedCallers.sol +45 -0
- package/src/libs/LimitOrderBitmap.sol +0 -1
- package/src/libs/LimitOrderCommon.sol +14 -0
- package/src/libs/LimitOrderCreate.sol +6 -0
- package/src/libs/LimitOrderFill.sol +3 -20
- package/src/libs/LimitOrderLiquidity.sol +61 -38
- package/src/libs/LimitOrderWithdraw.sol +2 -6
- package/src/libs/SwapLimitOrders.sol +13 -7
- package/src/router/ISetLimitOrderConfig.sol +12 -0
- package/src/router/SwapWithLimitOrders.sol +9 -9
- package/test/LimitOrderAccessControl.t.sol +173 -156
- package/test/LimitOrderFill.t.sol +0 -5
- package/test/LimitOrderLiquidityPayouts.t.sol +14 -14
- package/test/SwapWithLimitOrders.t.sol +0 -2
- package/test/SwapWithLimitOrdersRouter.t.sol +4 -2
- package/test/gas/LimitOrderFillGas.t.sol +0 -7
- package/test/gas/LimitOrderSwapGas.t.sol +0 -6
- package/test/unit/LimitOrderCreateUnit.t.sol +17 -17
- package/test/unit/SwapLimitOrdersUnit.t.sol +49 -88
- package/test/unit/SwapLimitOrdersValidation.t.sol +11 -19
- package/test/utils/BaseTest.sol +8 -17
- package/test/utils/TestableZoraLimitOrderBook.sol +2 -2
- package/abis/IAuthority.json +0 -31
- package/abis/SimpleAccessManager.json +0 -351
- package/out/IAuthority.sol/IAuthority.json +0 -1
- package/out/SimpleAccessManaged.sol/SimpleAccessManaged.json +0 -1
- package/out/SimpleAccessManager.sol/SimpleAccessManager.json +0 -1
- package/out/build-info/876cc09bc44cc8a7.json +0 -1
- package/src/access/SimpleAccessManaged.sol +0 -76
- package/src/access/SimpleAccessManager.sol +0 -268
- package/test/SimpleAccessManager.t.sol +0 -420
|
@@ -10,7 +10,6 @@ pragma solidity ^0.8.28;
|
|
|
10
10
|
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
|
|
11
11
|
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
|
|
12
12
|
import {TransientStateLibrary} from "@uniswap/v4-core/src/libraries/TransientStateLibrary.sol";
|
|
13
|
-
import {SimpleAccessManaged} from "./access/SimpleAccessManaged.sol";
|
|
14
13
|
|
|
15
14
|
import {IDeployedCoinVersionLookup} from "@zoralabs/coins/src/interfaces/IDeployedCoinVersionLookup.sol";
|
|
16
15
|
import {IZoraHookRegistry} from "@zoralabs/coins/src/interfaces/IZoraHookRegistry.sol";
|
|
@@ -21,50 +20,35 @@ import {LimitOrderFill} from "./libs/LimitOrderFill.sol";
|
|
|
21
20
|
import {LimitOrderWithdraw} from "./libs/LimitOrderWithdraw.sol";
|
|
22
21
|
import {LimitOrderViews} from "./libs/LimitOrderViews.sol";
|
|
23
22
|
import {LimitOrderTypes} from "./libs/LimitOrderTypes.sol";
|
|
23
|
+
import {PermittedCallers} from "./access/PermittedCallers.sol";
|
|
24
24
|
|
|
25
|
-
contract ZoraLimitOrderBook is IZoraLimitOrderBook,
|
|
25
|
+
contract ZoraLimitOrderBook is IZoraLimitOrderBook, PermittedCallers {
|
|
26
26
|
IPoolManager public immutable poolManager;
|
|
27
27
|
IDeployedCoinVersionLookup public immutable zoraCoinVersionLookup;
|
|
28
28
|
IZoraHookRegistry public immutable zoraHookRegistry;
|
|
29
29
|
address public immutable weth;
|
|
30
30
|
|
|
31
|
-
constructor(
|
|
32
|
-
address
|
|
33
|
-
address
|
|
34
|
-
address
|
|
35
|
-
address
|
|
36
|
-
|
|
37
|
-
) SimpleAccessManaged(authority_) {
|
|
31
|
+
constructor(address poolManager_, address zoraCoinVersionLookup_, address zoraHookRegistry_, address owner_, address weth_) PermittedCallers(owner_) {
|
|
32
|
+
require(poolManager_ != address(0), AddressZero());
|
|
33
|
+
require(zoraCoinVersionLookup_ != address(0), AddressZero());
|
|
34
|
+
require(zoraHookRegistry_ != address(0), AddressZero());
|
|
35
|
+
require(weth_ != address(0), AddressZero());
|
|
36
|
+
|
|
38
37
|
poolManager = IPoolManager(poolManager_);
|
|
39
38
|
zoraCoinVersionLookup = IDeployedCoinVersionLookup(zoraCoinVersionLookup_);
|
|
40
39
|
zoraHookRegistry = IZoraHookRegistry(zoraHookRegistry_);
|
|
41
|
-
if (weth_ == address(0)) {
|
|
42
|
-
revert AddressZero();
|
|
43
|
-
}
|
|
44
40
|
weth = weth_;
|
|
45
41
|
}
|
|
46
42
|
|
|
47
|
-
function tickQueueBalance(bytes32 poolKeyHash, address coin, int24 tick) internal view returns (uint256) {
|
|
48
|
-
return LimitOrderStorage.layout().tickQueues[poolKeyHash][coin][tick].balance;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
43
|
/// @inheritdoc IZoraLimitOrderBook
|
|
52
44
|
function balanceOf(address maker, address coin) external view override returns (uint256) {
|
|
53
45
|
return LimitOrderStorage.layout().makerBalances[maker][coin];
|
|
54
46
|
}
|
|
55
47
|
|
|
56
|
-
function getOrder(bytes32 id) internal view returns (LimitOrderTypes.LimitOrder memory) {
|
|
57
|
-
return LimitOrderStorage.layout().limitOrders[id];
|
|
58
|
-
}
|
|
59
|
-
|
|
60
48
|
function getTickQueue(bytes32 poolKeyHash, address coin, int24 tick) internal view returns (LimitOrderTypes.Queue memory) {
|
|
61
49
|
return LimitOrderStorage.layout().tickQueues[poolKeyHash][coin][tick];
|
|
62
50
|
}
|
|
63
51
|
|
|
64
|
-
function getPoolKey(bytes32 poolKeyHash) internal view returns (PoolKey memory) {
|
|
65
|
-
return LimitOrderStorage.layout().poolKeys[poolKeyHash];
|
|
66
|
-
}
|
|
67
|
-
|
|
68
52
|
function getPoolEpoch(bytes32 poolKeyHash) internal view returns (uint256) {
|
|
69
53
|
return LimitOrderStorage.layout().poolEpochs[poolKeyHash];
|
|
70
54
|
}
|
|
@@ -73,10 +57,6 @@ contract ZoraLimitOrderBook is IZoraLimitOrderBook, SimpleAccessManaged {
|
|
|
73
57
|
return LimitOrderStorage.layout().makerNonces[maker];
|
|
74
58
|
}
|
|
75
59
|
|
|
76
|
-
function getOrderId(bytes32 poolKeyHash, address coin, int24 tick, address maker, uint256 nonce) internal pure returns (bytes32) {
|
|
77
|
-
return keccak256(abi.encode(poolKeyHash, coin, tick, maker, nonce));
|
|
78
|
-
}
|
|
79
|
-
|
|
80
60
|
/// @inheritdoc IZoraLimitOrderBook
|
|
81
61
|
function create(
|
|
82
62
|
PoolKey memory key,
|
|
@@ -84,8 +64,7 @@ contract ZoraLimitOrderBook is IZoraLimitOrderBook, SimpleAccessManaged {
|
|
|
84
64
|
uint256[] memory orderSizes,
|
|
85
65
|
int24[] memory orderTicks,
|
|
86
66
|
address maker
|
|
87
|
-
) external payable override returns (bytes32[] memory) {
|
|
88
|
-
_checkCanCall(this.create.selector);
|
|
67
|
+
) external payable override onlyPermitted returns (bytes32[] memory) {
|
|
89
68
|
return LimitOrderCreate.create(LimitOrderStorage.layout(), poolManager, key, isCurrency0, orderSizes, orderTicks, maker);
|
|
90
69
|
}
|
|
91
70
|
|
|
@@ -152,8 +131,10 @@ contract ZoraLimitOrderBook is IZoraLimitOrderBook, SimpleAccessManaged {
|
|
|
152
131
|
|
|
153
132
|
/// @inheritdoc IZoraLimitOrderBook
|
|
154
133
|
function withdraw(bytes32[] calldata orderIds, address coin, uint256 minAmountOut, address recipient) external override {
|
|
155
|
-
|
|
156
|
-
|
|
134
|
+
_unlock(
|
|
135
|
+
CallbackId.WITHDRAW_ORDERS,
|
|
136
|
+
abi.encode(WithdrawOrdersCallbackData({maker: msg.sender, orderIds: orderIds, coin: coin, minAmountOut: minAmountOut, recipient: recipient}))
|
|
137
|
+
);
|
|
157
138
|
}
|
|
158
139
|
|
|
159
140
|
/// @notice Processes pool-manager unlock callbacks and routes them to the correct handler.
|
|
@@ -184,9 +165,7 @@ contract ZoraLimitOrderBook is IZoraLimitOrderBook, SimpleAccessManaged {
|
|
|
184
165
|
return LimitOrderStorage.layout().maxFillCount;
|
|
185
166
|
}
|
|
186
167
|
|
|
187
|
-
function setMaxFillCount(uint256 maxFillCount) external {
|
|
188
|
-
_checkCanCall(this.setMaxFillCount.selector);
|
|
189
|
-
|
|
168
|
+
function setMaxFillCount(uint256 maxFillCount) external onlyOwner {
|
|
190
169
|
LimitOrderStorage.layout().maxFillCount = maxFillCount;
|
|
191
170
|
}
|
|
192
171
|
|
|
@@ -218,16 +197,6 @@ contract ZoraLimitOrderBook is IZoraLimitOrderBook, SimpleAccessManaged {
|
|
|
218
197
|
data.orderIds = orderIds;
|
|
219
198
|
}
|
|
220
199
|
|
|
221
|
-
function _encodeWithdrawOrders(
|
|
222
|
-
address maker,
|
|
223
|
-
bytes32[] memory orderIds,
|
|
224
|
-
address coin,
|
|
225
|
-
uint256 minAmountOut,
|
|
226
|
-
address recipient
|
|
227
|
-
) private pure returns (bytes memory) {
|
|
228
|
-
return abi.encode(WithdrawOrdersCallbackData({maker: maker, orderIds: orderIds, coin: coin, minAmountOut: minAmountOut, recipient: recipient}));
|
|
229
|
-
}
|
|
230
|
-
|
|
231
200
|
function _unlock(CallbackId callbackId, bytes memory payload) private {
|
|
232
201
|
poolManager.unlock(abi.encode(callbackId, payload));
|
|
233
202
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
|
|
5
|
+
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
|
|
6
|
+
|
|
7
|
+
abstract contract PermittedCallers is Ownable2Step {
|
|
8
|
+
address private constant PUBLIC_ACCESS = address(0);
|
|
9
|
+
|
|
10
|
+
mapping(address caller => bool permitted) private _permittedCallers;
|
|
11
|
+
|
|
12
|
+
event PermittedCallerUpdated(address indexed caller, bool permitted);
|
|
13
|
+
|
|
14
|
+
error CallerNotPermitted();
|
|
15
|
+
error PermittedCallersLengthMismatch();
|
|
16
|
+
|
|
17
|
+
constructor(address owner_) Ownable(owner_) {
|
|
18
|
+
_permittedCallers[PUBLIC_ACCESS] = true;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
modifier onlyPermitted() {
|
|
22
|
+
_onlyPermitted();
|
|
23
|
+
_;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function _onlyPermitted() internal view {
|
|
27
|
+
require(_isPermitted(msg.sender), CallerNotPermitted());
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function isPermittedCaller(address caller) public view returns (bool) {
|
|
31
|
+
return _isPermitted(caller);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function setPermittedCallers(address[] calldata callers, bool[] calldata permitted) external onlyOwner {
|
|
35
|
+
require(callers.length == permitted.length, PermittedCallersLengthMismatch());
|
|
36
|
+
for (uint256 i = 0; i < callers.length; i++) {
|
|
37
|
+
_permittedCallers[callers[i]] = permitted[i];
|
|
38
|
+
emit PermittedCallerUpdated(callers[i], permitted[i]);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function _isPermitted(address caller) internal view returns (bool) {
|
|
43
|
+
return _permittedCallers[PUBLIC_ACCESS] || _permittedCallers[caller];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
pragma solidity ^0.8.23;
|
|
9
9
|
|
|
10
10
|
import {TickBitmap} from "@uniswap/v4-core/src/libraries/TickBitmap.sol";
|
|
11
|
-
import {LimitOrderTypes} from "./LimitOrderTypes.sol";
|
|
12
11
|
|
|
13
12
|
library LimitOrderBitmap {
|
|
14
13
|
function setIfFirst(mapping(int16 => uint256) storage bm, int24 tick, int24 spacing, uint256 sizeBefore) internal {
|
|
@@ -9,6 +9,9 @@ pragma solidity ^0.8.28;
|
|
|
9
9
|
|
|
10
10
|
import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
|
|
11
11
|
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
|
|
12
|
+
import {PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol";
|
|
13
|
+
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
|
|
14
|
+
import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol";
|
|
12
15
|
|
|
13
16
|
import {IZoraLimitOrderBook} from "../IZoraLimitOrderBook.sol";
|
|
14
17
|
import {LimitOrderStorage} from "./LimitOrderStorage.sol";
|
|
@@ -18,6 +21,8 @@ import {LimitOrderBitmap} from "./LimitOrderBitmap.sol";
|
|
|
18
21
|
import {LimitOrderCreate} from "./LimitOrderCreate.sol";
|
|
19
22
|
|
|
20
23
|
library LimitOrderCommon {
|
|
24
|
+
using PoolIdLibrary for PoolKey;
|
|
25
|
+
|
|
21
26
|
/// @dev Currency0 orders are executed when the price rises to the upper tick.
|
|
22
27
|
/// Currency1 orders are executed when the price falls to the lower tick.
|
|
23
28
|
function getOrderTick(LimitOrderTypes.LimitOrder storage order) internal view returns (int24) {
|
|
@@ -28,6 +33,15 @@ library LimitOrderCommon {
|
|
|
28
33
|
return Currency.unwrap(isCurrency0 ? key.currency0 : key.currency1);
|
|
29
34
|
}
|
|
30
35
|
|
|
36
|
+
/// @dev Returns true if the pool tick has fully crossed the order's range.
|
|
37
|
+
function hasCrossed(LimitOrderTypes.LimitOrder storage order, int24 currentTick) internal view returns (bool) {
|
|
38
|
+
return order.isCurrency0 ? currentTick >= order.tickUpper : currentTick < order.tickLower;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function currentPoolTick(IPoolManager poolManager, PoolKey memory key) internal view returns (int24 tick) {
|
|
42
|
+
(, tick, , ) = StateLibrary.getSlot0(poolManager, key.toId());
|
|
43
|
+
}
|
|
44
|
+
|
|
31
45
|
function recordCreation(
|
|
32
46
|
LimitOrderStorage.Layout storage state,
|
|
33
47
|
LimitOrderCreate.CreateContext memory ctx,
|
|
@@ -223,6 +223,8 @@ library LimitOrderCreate {
|
|
|
223
223
|
ModifyLiquidityParams({
|
|
224
224
|
tickLower: params.tickLower,
|
|
225
225
|
tickUpper: params.tickUpper,
|
|
226
|
+
// This is safe because liquidity is always positive
|
|
227
|
+
//forge-lint: disable-next-line(unsafe-typecast)
|
|
226
228
|
liquidityDelta: int256(uint256(params.liquidity)),
|
|
227
229
|
salt: params.orderId
|
|
228
230
|
}),
|
|
@@ -233,8 +235,12 @@ library LimitOrderCreate {
|
|
|
233
235
|
int128 amount1 = delta.amount1();
|
|
234
236
|
|
|
235
237
|
if (isCurrency0) {
|
|
238
|
+
// This is safe because amount0 is always negative
|
|
239
|
+
//forge-lint: disable-next-line(unsafe-typecast)
|
|
236
240
|
realizedSize = amount0 < 0 ? uint128(uint256(int256(-amount0))) : 0;
|
|
237
241
|
} else {
|
|
242
|
+
// This is safe because amount1 is always negative
|
|
243
|
+
//forge-lint: disable-next-line(unsafe-typecast)
|
|
238
244
|
realizedSize = amount1 < 0 ? uint128(uint256(int256(-amount1))) : 0;
|
|
239
245
|
}
|
|
240
246
|
|
|
@@ -10,8 +10,6 @@ pragma solidity ^0.8.28;
|
|
|
10
10
|
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
|
|
11
11
|
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
|
|
12
12
|
import {TickBitmap} from "@uniswap/v4-core/src/libraries/TickBitmap.sol";
|
|
13
|
-
import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol";
|
|
14
|
-
import {PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol";
|
|
15
13
|
import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
|
|
16
14
|
|
|
17
15
|
import {LimitOrderStorage} from "./LimitOrderStorage.sol";
|
|
@@ -19,13 +17,10 @@ import {IZoraLimitOrderBook} from "../IZoraLimitOrderBook.sol";
|
|
|
19
17
|
import {LimitOrderTypes} from "./LimitOrderTypes.sol";
|
|
20
18
|
import {LimitOrderLiquidity} from "./LimitOrderLiquidity.sol";
|
|
21
19
|
import {LimitOrderCommon} from "./LimitOrderCommon.sol";
|
|
22
|
-
import {LimitOrderViews} from "./LimitOrderViews.sol";
|
|
23
20
|
import {CoinCommon} from "@zoralabs/coins/src/libs/CoinCommon.sol";
|
|
24
21
|
import {IDeployedCoinVersionLookup} from "@zoralabs/coins/src/interfaces/IDeployedCoinVersionLookup.sol";
|
|
25
22
|
|
|
26
23
|
library LimitOrderFill {
|
|
27
|
-
using PoolIdLibrary for PoolKey;
|
|
28
|
-
|
|
29
24
|
struct Context {
|
|
30
25
|
IPoolManager poolManager;
|
|
31
26
|
IDeployedCoinVersionLookup versionLookup;
|
|
@@ -67,12 +62,9 @@ library LimitOrderFill {
|
|
|
67
62
|
order.status == LimitOrderTypes.OrderStatus.OPEN &&
|
|
68
63
|
order.poolKeyHash == poolKeyHash &&
|
|
69
64
|
order.isCurrency0 == isCurrency0 &&
|
|
70
|
-
order.createdEpoch < currentEpoch
|
|
65
|
+
order.createdEpoch < currentEpoch &&
|
|
66
|
+
LimitOrderCommon.hasCrossed(order, LimitOrderCommon.currentPoolTick(ctx.poolManager, key))
|
|
71
67
|
) {
|
|
72
|
-
if (!_hasCrossed(order, _currentPoolTick(ctx.poolManager, key))) {
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
68
|
int24 orderTick = LimitOrderCommon.getOrderTick(order);
|
|
77
69
|
LimitOrderTypes.Queue storage tickQueue = tickQueues[orderTick];
|
|
78
70
|
|
|
@@ -160,7 +152,7 @@ library LimitOrderFill {
|
|
|
160
152
|
if (order.createdEpoch == currentEpoch) {
|
|
161
153
|
break;
|
|
162
154
|
}
|
|
163
|
-
if (!
|
|
155
|
+
if (!LimitOrderCommon.hasCrossed(order, LimitOrderCommon.currentPoolTick(ctx.poolManager, data.poolKey))) {
|
|
164
156
|
return;
|
|
165
157
|
}
|
|
166
158
|
|
|
@@ -221,13 +213,4 @@ library LimitOrderFill {
|
|
|
221
213
|
orderId
|
|
222
214
|
);
|
|
223
215
|
}
|
|
224
|
-
|
|
225
|
-
function _currentPoolTick(IPoolManager poolManager, PoolKey memory key) private view returns (int24 tick) {
|
|
226
|
-
(, tick, , ) = StateLibrary.getSlot0(poolManager, key.toId());
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/// @dev Returns true if the pool tick has fully crossed the order's range.
|
|
230
|
-
function _hasCrossed(LimitOrderTypes.LimitOrder storage order, int24 currentTick) private view returns (bool) {
|
|
231
|
-
return order.isCurrency0 ? currentTick >= order.tickUpper : currentTick <= order.tickLower;
|
|
232
|
-
}
|
|
233
216
|
}
|
|
@@ -45,6 +45,21 @@ library LimitOrderLiquidity {
|
|
|
45
45
|
settleDeltas(poolManager, key, delta0, delta1, payout0, payout1);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
/// @notice Burns a filled limit order and pays out proceeds to maker and fee recipient
|
|
49
|
+
/// @dev Consolidates payouts into the target currency (opposite of the deposit currency).
|
|
50
|
+
/// Handles fee distribution and ensures payouts are in a single currency by swapping
|
|
51
|
+
/// when necessary. Makers receive proceeds in the target currency they were expecting.
|
|
52
|
+
/// @param poolManager The Uniswap V4 pool manager
|
|
53
|
+
/// @param key The pool key for the limit order
|
|
54
|
+
/// @param order The limit order storage struct containing order details
|
|
55
|
+
/// @param orderId The unique identifier for the limit order
|
|
56
|
+
/// @param feeRecipient The address to receive referral fees (address(0) if no fees)
|
|
57
|
+
/// @param coinIn The coin contract address for multi-hop swap paths (if applicable)
|
|
58
|
+
/// @param coinLookup The deployed coin version lookup contract
|
|
59
|
+
/// @param weth The WETH contract address for ETH handling
|
|
60
|
+
/// @return makerCoinOut The currency paid to the maker
|
|
61
|
+
/// @return makerAmountOut The amount paid to the maker
|
|
62
|
+
/// @return referralAmountOut The amount paid to the fee recipient
|
|
48
63
|
function burnAndPayout(
|
|
49
64
|
IPoolManager poolManager,
|
|
50
65
|
PoolKey memory key,
|
|
@@ -82,6 +97,20 @@ library LimitOrderLiquidity {
|
|
|
82
97
|
}
|
|
83
98
|
}
|
|
84
99
|
|
|
100
|
+
/// @notice Burns limit order liquidity and refunds the proceeds to a recipient
|
|
101
|
+
/// @dev Consolidates payouts into the original deposit currency. If both currencies
|
|
102
|
+
/// have positive amounts after burning, the counter-asset is swapped into the original
|
|
103
|
+
/// deposit currency, ensuring the recipient receives a single consolidated payout.
|
|
104
|
+
/// @param poolManager The Uniswap V4 pool manager
|
|
105
|
+
/// @param key The pool key for the limit order
|
|
106
|
+
/// @param tickLower The lower tick of the limit order position
|
|
107
|
+
/// @param tickUpper The upper tick of the limit order position
|
|
108
|
+
/// @param liquidity The amount of liquidity to burn
|
|
109
|
+
/// @param salt The salt used for position identification
|
|
110
|
+
/// @param recipient The address to receive the refund
|
|
111
|
+
/// @param isCurrency0 True if the original deposit was in currency0, false if currency1
|
|
112
|
+
/// @param weth The WETH contract address for ETH handling
|
|
113
|
+
/// @return amountOut The total amount refunded in the original deposit currency
|
|
85
114
|
function burnAndRefund(
|
|
86
115
|
IPoolManager poolManager,
|
|
87
116
|
PoolKey memory key,
|
|
@@ -95,20 +124,9 @@ library LimitOrderLiquidity {
|
|
|
95
124
|
) internal returns (uint128 amountOut) {
|
|
96
125
|
(int128 amount0, int128 amount1) = _burnLiquidity(poolManager, key, tickLower, tickUpper, liquidity, salt);
|
|
97
126
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if (isCurrency0) {
|
|
102
|
-
amountOut = amount0Out;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
if (amount1 > 0) {
|
|
106
|
-
uint128 amount1Out = uint128(amount1);
|
|
107
|
-
_takeCurrency(poolManager, key.currency1, recipient, amount1Out, weth);
|
|
108
|
-
if (!isCurrency0) {
|
|
109
|
-
amountOut = amount1Out;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
127
|
+
// Refund to original deposit currency (swaps any accumulated counter-asset)
|
|
128
|
+
Currency payoutCurrency = isCurrency0 ? key.currency0 : key.currency1;
|
|
129
|
+
(, amountOut) = _payoutRecipient(poolManager, recipient, amount0, amount1, _buildSingleHopPath(key, payoutCurrency), weth);
|
|
112
130
|
}
|
|
113
131
|
|
|
114
132
|
function settleDeltas(IPoolManager poolManager, PoolKey memory key, int256 d0, int256 d1, address payout0, address payout1) internal {
|
|
@@ -116,8 +134,13 @@ library LimitOrderLiquidity {
|
|
|
116
134
|
poolManager.take(key.currency0, payout0, uint256(d0));
|
|
117
135
|
}
|
|
118
136
|
if (d0 < 0) {
|
|
119
|
-
|
|
137
|
+
// This is safe because d0 is always negative
|
|
138
|
+
//forge-lint: disable-next-line(unsafe-typecast)
|
|
139
|
+
uint256 amount = uint256(-d0);
|
|
120
140
|
poolManager.sync(key.currency0);
|
|
141
|
+
|
|
142
|
+
// For native ETH, settle with value
|
|
143
|
+
// Ensured to be currency0 by token ordering
|
|
121
144
|
if (key.currency0.isAddressZero()) {
|
|
122
145
|
poolManager.settle{value: amount}();
|
|
123
146
|
} else {
|
|
@@ -127,17 +150,17 @@ library LimitOrderLiquidity {
|
|
|
127
150
|
}
|
|
128
151
|
|
|
129
152
|
if (d1 > 0 && payout1 != address(0)) {
|
|
153
|
+
// This is safe because d1 is always positive
|
|
154
|
+
//forge-lint: disable-next-line(unsafe-typecast)
|
|
130
155
|
poolManager.take(key.currency1, payout1, uint256(d1));
|
|
131
156
|
}
|
|
132
157
|
if (d1 < 0) {
|
|
133
|
-
|
|
158
|
+
// This is safe because d1 is always negative
|
|
159
|
+
//forge-lint: disable-next-line(unsafe-typecast)
|
|
160
|
+
uint256 amount = uint256(-d1);
|
|
134
161
|
poolManager.sync(key.currency1);
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
} else {
|
|
138
|
-
key.currency1.transfer(address(poolManager), amount);
|
|
139
|
-
poolManager.settle();
|
|
140
|
-
}
|
|
162
|
+
key.currency1.transfer(address(poolManager), amount);
|
|
163
|
+
poolManager.settle();
|
|
141
164
|
}
|
|
142
165
|
}
|
|
143
166
|
|
|
@@ -178,8 +201,12 @@ library LimitOrderLiquidity {
|
|
|
178
201
|
}
|
|
179
202
|
|
|
180
203
|
// Fallback: construct simple single-hop path
|
|
181
|
-
|
|
182
|
-
|
|
204
|
+
return _buildSingleHopPath(key, payoutCurrency);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function _buildSingleHopPath(PoolKey memory key, Currency payoutCurrency) private pure returns (IHasSwapPath.PayoutSwapPath memory payoutPath) {
|
|
208
|
+
Currency inputCurrency = payoutCurrency == key.currency0 ? key.currency1 : key.currency0;
|
|
209
|
+
payoutPath.currencyIn = inputCurrency;
|
|
183
210
|
payoutPath.path = new PathKey[](1);
|
|
184
211
|
payoutPath.path[0] = PathKey({intermediateCurrency: payoutCurrency, fee: key.fee, tickSpacing: key.tickSpacing, hooks: key.hooks, hookData: bytes("")});
|
|
185
212
|
}
|
|
@@ -192,15 +219,6 @@ library LimitOrderLiquidity {
|
|
|
192
219
|
}
|
|
193
220
|
}
|
|
194
221
|
|
|
195
|
-
function _settleNegativeDeltas(IPoolManager poolManager, PoolKey memory key, int128 amount0, int128 amount1) private {
|
|
196
|
-
int256 repay0 = amount0 < 0 ? int256(amount0) : int256(0);
|
|
197
|
-
int256 repay1 = amount1 < 0 ? int256(amount1) : int256(0);
|
|
198
|
-
|
|
199
|
-
if (repay0 != 0 || repay1 != 0) {
|
|
200
|
-
settleDeltas(poolManager, key, repay0, repay1, address(0), address(0));
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
222
|
function _takeCurrency(IPoolManager poolManager, Currency currency, address recipient, uint128 amount, address weth) private {
|
|
205
223
|
if (!currency.isAddressZero()) {
|
|
206
224
|
poolManager.take(currency, recipient, amount);
|
|
@@ -222,15 +240,20 @@ library LimitOrderLiquidity {
|
|
|
222
240
|
IHasSwapPath.PayoutSwapPath memory payoutPath,
|
|
223
241
|
address weth
|
|
224
242
|
) private returns (Currency coinOut, uint128 amountOut) {
|
|
225
|
-
// Convert to uint128, treating negative/zero as zero
|
|
226
|
-
uint128 amt0 = amount0 > 0 ? uint128(amount0) : 0;
|
|
227
|
-
uint128 amt1 = amount1 > 0 ? uint128(amount1) : 0;
|
|
228
|
-
|
|
229
243
|
// Use swapToPath which handles all cases:
|
|
230
244
|
// - Single positive delta: returns that currency
|
|
231
245
|
// - Dual positive deltas: swaps one to the other and returns combined amount
|
|
232
246
|
// - Multi-hop paths: handles coin -> backingCoin -> backingCoin's currency
|
|
233
|
-
(coinOut, amountOut) = UniV4SwapToCurrency.swapToPath(
|
|
247
|
+
(coinOut, amountOut) = UniV4SwapToCurrency.swapToPath(
|
|
248
|
+
poolManager,
|
|
249
|
+
// This is safe because amount0 and amount1 are only needed if positive in this function.
|
|
250
|
+
//forge-lint: disable-next-line(unsafe-typecast)
|
|
251
|
+
amount0 > 0 ? uint128(amount0) : 0,
|
|
252
|
+
//forge-lint: disable-next-line(unsafe-typecast)
|
|
253
|
+
amount1 > 0 ? uint128(amount1) : 0,
|
|
254
|
+
payoutPath.currencyIn,
|
|
255
|
+
payoutPath.path
|
|
256
|
+
);
|
|
234
257
|
|
|
235
258
|
if (amountOut > 0) {
|
|
236
259
|
Currency payoutCurrency = coinOut;
|
|
@@ -9,8 +9,6 @@ pragma solidity ^0.8.28;
|
|
|
9
9
|
|
|
10
10
|
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
|
|
11
11
|
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
|
|
12
|
-
import {PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol";
|
|
13
|
-
import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol";
|
|
14
12
|
|
|
15
13
|
import {LimitOrderStorage} from "./LimitOrderStorage.sol";
|
|
16
14
|
import {IZoraLimitOrderBook} from "../IZoraLimitOrderBook.sol";
|
|
@@ -81,10 +79,8 @@ library LimitOrderWithdraw {
|
|
|
81
79
|
PoolKey memory key = state.poolKeys[order.poolKeyHash];
|
|
82
80
|
require(key.tickSpacing != 0, IZoraLimitOrderBook.InvalidOrder());
|
|
83
81
|
|
|
84
|
-
// Prevent withdrawal of
|
|
85
|
-
(,
|
|
86
|
-
bool fillable = order.isCurrency0 ? currentTick >= order.tickUpper : currentTick <= order.tickLower;
|
|
87
|
-
require(!fillable, IZoraLimitOrderBook.OrderFillable());
|
|
82
|
+
// Prevent withdrawal of crossed orders - they must be filled instead
|
|
83
|
+
require(!LimitOrderCommon.hasCrossed(order, LimitOrderCommon.currentPoolTick(poolManager, key)), IZoraLimitOrderBook.OrderFillable());
|
|
88
84
|
|
|
89
85
|
int24 orderTick = LimitOrderCommon.getOrderTick(order);
|
|
90
86
|
coin = LimitOrderCommon.getOrderCoin(key, order.isCurrency0);
|
|
@@ -104,13 +104,12 @@ library SwapLimitOrders {
|
|
|
104
104
|
return (o, allocated, unallocated);
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
// Skip order creation when at
|
|
108
|
-
// For currency0 (buy orders): cannot place if
|
|
109
|
-
// For currency1 (sell orders): cannot place if
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (isCurrency0 ? alignedBaseTick >= maxTick : alignedBaseTick <= -maxTick) {
|
|
107
|
+
// Skip order creation when at hard price boundaries
|
|
108
|
+
// For currency0 (buy orders): cannot place if price is at max
|
|
109
|
+
// For currency1 (sell orders): cannot place if price is at min
|
|
110
|
+
uint160 maxPrice = TickMath.MAX_SQRT_PRICE - 1;
|
|
111
|
+
uint160 minPrice = TickMath.MIN_SQRT_PRICE + 1;
|
|
112
|
+
if (isCurrency0 ? sqrtPriceX96 >= maxPrice : sqrtPriceX96 <= minPrice) {
|
|
114
113
|
unallocated = totalSize;
|
|
115
114
|
return (o, allocated, unallocated);
|
|
116
115
|
}
|
|
@@ -129,7 +128,11 @@ library SwapLimitOrders {
|
|
|
129
128
|
uint256 orderSize = FullMath.mulDiv(uint256(remaining), config.percentages[i], PERCENT_SCALE);
|
|
130
129
|
if (orderSize == 0) continue;
|
|
131
130
|
|
|
131
|
+
// This is safe because orderSize is bounded by remaining which is uint128
|
|
132
|
+
//forge-lint: disable-next-line(unsafe-typecast)
|
|
132
133
|
allocated += uint128(orderSize);
|
|
134
|
+
// This is safe because orderSize is bounded by remaining which is uint128
|
|
135
|
+
//forge-lint: disable-next-line(unsafe-typecast)
|
|
133
136
|
remaining -= uint128(orderSize);
|
|
134
137
|
|
|
135
138
|
int24 targetTick = _tickForMultiple(key, isCurrency0, baseTick, sqrtPriceX96, config.multiples[i]);
|
|
@@ -139,6 +142,7 @@ library SwapLimitOrders {
|
|
|
139
142
|
o.multiples[count] = config.multiples[i];
|
|
140
143
|
o.percentages[count] = config.percentages[i];
|
|
141
144
|
|
|
145
|
+
// This is safe because orderCount is validated to be an array length which cannot overflow by definition
|
|
142
146
|
unchecked {
|
|
143
147
|
++count;
|
|
144
148
|
}
|
|
@@ -179,6 +183,8 @@ library SwapLimitOrders {
|
|
|
179
183
|
uint256 scaled = FullMath.mulDiv(uint256(sqrtPriceX96), sqrtMultiplier, SQRT_MULTIPLE_SCALE);
|
|
180
184
|
if (scaled > type(uint160).max) scaled = type(uint160).max;
|
|
181
185
|
|
|
186
|
+
// This is safe because scaled is always less than type(uint160).max
|
|
187
|
+
//forge-lint: disable-next-line(unsafe-typecast)
|
|
182
188
|
int24 rawTick = TickMath.getTickAtSqrtPrice(uint160(scaled));
|
|
183
189
|
aligned = DopplerMath.alignTickToTickSpacing(isCurrency0, rawTick, key.tickSpacing);
|
|
184
190
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
import {LimitOrderConfig} from "../libs/SwapLimitOrders.sol";
|
|
5
|
+
|
|
6
|
+
/// @title ISetLimitOrderConfig
|
|
7
|
+
/// @notice Interface for setting limit order configuration
|
|
8
|
+
interface ISetLimitOrderConfig {
|
|
9
|
+
/// @notice Sets the canonical limit order configuration
|
|
10
|
+
/// @param config The new limit order configuration
|
|
11
|
+
function setLimitOrderConfig(LimitOrderConfig memory config) external;
|
|
12
|
+
}
|
|
@@ -18,6 +18,7 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
|
18
18
|
|
|
19
19
|
import {IZoraLimitOrderBook} from "../IZoraLimitOrderBook.sol";
|
|
20
20
|
import {SwapLimitOrders, LimitOrderConfig, Orders} from "../libs/SwapLimitOrders.sol";
|
|
21
|
+
import {ISetLimitOrderConfig} from "./ISetLimitOrderConfig.sol";
|
|
21
22
|
import {ISwapRouter} from "@zoralabs/shared-contracts/interfaces/uniswap/ISwapRouter.sol";
|
|
22
23
|
import {ISupportsLimitOrderFill} from "@zoralabs/coins/src/interfaces/ISupportsLimitOrderFill.sol";
|
|
23
24
|
import {IMsgSender} from "@zoralabs/coins/src/interfaces/IMsgSender.sol";
|
|
@@ -25,7 +26,8 @@ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
|
25
26
|
import {TransientSlot} from "@openzeppelin/contracts/utils/TransientSlot.sol";
|
|
26
27
|
import {Path} from "@zoralabs/shared-contracts/libs/UniswapV3/Path.sol";
|
|
27
28
|
import {V3ToV4SwapLib} from "@zoralabs/coins/src/libs/V3ToV4SwapLib.sol";
|
|
28
|
-
import {
|
|
29
|
+
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
|
|
30
|
+
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
|
|
29
31
|
import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol";
|
|
30
32
|
|
|
31
33
|
/// @title SwapWithLimitOrders
|
|
@@ -35,7 +37,7 @@ import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol"
|
|
|
35
37
|
/// Users call swapWithLimitOrders() directly, which triggers the unlock callback flow.
|
|
36
38
|
/// Uses Permit2 for token approvals, matching the universal-router pattern.
|
|
37
39
|
/// @author oveddan
|
|
38
|
-
contract SwapWithLimitOrders is IMsgSender {
|
|
40
|
+
contract SwapWithLimitOrders is ISetLimitOrderConfig, Ownable2Step, IMsgSender {
|
|
39
41
|
using SafeERC20 for IERC20;
|
|
40
42
|
using BalanceDeltaLibrary for BalanceDelta;
|
|
41
43
|
using CurrencyLibrary for Currency;
|
|
@@ -43,6 +45,7 @@ contract SwapWithLimitOrders is IMsgSender {
|
|
|
43
45
|
using Path for bytes;
|
|
44
46
|
|
|
45
47
|
/// @notice The Uniswap V4 pool manager
|
|
48
|
+
// forge-lint-ignore screaming-snake-case-immutable
|
|
46
49
|
IPoolManager public immutable poolManager;
|
|
47
50
|
|
|
48
51
|
/// @notice The limit order book contract
|
|
@@ -121,9 +124,6 @@ contract SwapWithLimitOrders is IMsgSender {
|
|
|
121
124
|
/// @notice Error thrown when caller is not the pool manager
|
|
122
125
|
error OnlyPoolManager();
|
|
123
126
|
|
|
124
|
-
/// @notice Error thrown when caller is not the authority
|
|
125
|
-
error OnlyAuthority();
|
|
126
|
-
|
|
127
127
|
/// @notice Error thrown when config does not match canonical config
|
|
128
128
|
error InvalidLimitOrderConfig();
|
|
129
129
|
|
|
@@ -141,7 +141,8 @@ contract SwapWithLimitOrders is IMsgSender {
|
|
|
141
141
|
/// @param zoraLimitOrderBook_ The limit order book contract
|
|
142
142
|
/// @param swapRouter_ The Uniswap V3 swap router
|
|
143
143
|
/// @param permit2_ The Permit2 contract address (0x000000000022D473030F116dDEE9F6B43aC78BA3)
|
|
144
|
-
|
|
144
|
+
/// @param owner_ The owner address
|
|
145
|
+
constructor(IPoolManager poolManager_, IZoraLimitOrderBook zoraLimitOrderBook_, ISwapRouter swapRouter_, address permit2_, address owner_) Ownable(owner_) {
|
|
145
146
|
require(address(poolManager_) != address(0), "PoolManager cannot be zero");
|
|
146
147
|
require(address(zoraLimitOrderBook_) != address(0), "ZoraLimitOrderBook cannot be zero");
|
|
147
148
|
require(address(swapRouter_) != address(0), "SwapRouter cannot be zero");
|
|
@@ -159,10 +160,9 @@ contract SwapWithLimitOrders is IMsgSender {
|
|
|
159
160
|
}
|
|
160
161
|
|
|
161
162
|
/// @notice Sets the canonical limit order configuration
|
|
162
|
-
/// @dev Only callable by
|
|
163
|
+
/// @dev Only callable by the owner
|
|
163
164
|
/// @param config The new limit order configuration
|
|
164
|
-
function setLimitOrderConfig(LimitOrderConfig memory config) external {
|
|
165
|
-
require(msg.sender == SimpleAccessManaged(address(zoraLimitOrderBook)).authority(), OnlyAuthority());
|
|
165
|
+
function setLimitOrderConfig(LimitOrderConfig memory config) external onlyOwner {
|
|
166
166
|
SwapLimitOrders.validate(config);
|
|
167
167
|
_limitOrderConfig = config;
|
|
168
168
|
emit LimitOrderConfigUpdated(config.multiples, config.percentages);
|