From 233f1877ac2b29c1f87649dcc1a079dde10a0269 Mon Sep 17 00:00:00 2001 From: X Date: Mon, 27 Jan 2025 15:44:12 +0800 Subject: [PATCH 001/205] update yarn --- yarn.lock | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/yarn.lock b/yarn.lock index 164217e7f..1e45a0ee7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1098,6 +1098,13 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" +"@gelatonetwork/relay-context@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@gelatonetwork/relay-context/-/relay-context-4.0.0.tgz#ef531fa1cb9038be384c657b64a8142d3483c271" + integrity sha512-tFKJECzYIwJo+Ey4ZRyjRsyYu6JBbMzVCbEFDh/wpnz8sOr8dxX59QyXrP68KvzTegM9VypLc+nHFfy7gPR8zw== + dependencies: + "@openzeppelin/contracts" "4.9.3" + "@graphql-typed-document-node/core@^3.1.1": version "3.2.0" resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" From 48ee48c0a6707b745452b092d6a079e13afffe98 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 28 Jan 2025 00:01:04 +0200 Subject: [PATCH 002/205] WIP multichain --- contracts/data/Keys.sol | 8 +- contracts/multichain/LayerZeroProvider.sol | 5 +- .../LayerZeroProviderEventUtils.sol | 18 +- contracts/multichain/MultichainEventUtils.sol | 18 +- contracts/multichain/MultichainHandler.sol | 37 ++-- contracts/multichain/MultichainRouter.sol | 166 ++++++++++++++++++ contracts/multichain/MultichainUtils.sol | 6 +- contracts/position/Position.sol | 12 +- test/multichain/LayerZeroProvider.ts | 5 +- utils/keys.ts | 4 +- utils/multichain.ts | 8 - 11 files changed, 237 insertions(+), 50 deletions(-) create mode 100644 contracts/multichain/MultichainRouter.sol diff --git a/contracts/data/Keys.sol b/contracts/data/Keys.sol index f67d37c98..5f1f2359e 100644 --- a/contracts/data/Keys.sol +++ b/contracts/data/Keys.sol @@ -2098,13 +2098,15 @@ library Keys { } // @dev key for user's balance for a source chain, recorded under the user's virtual account - // @param virtualAccount the virtual account for which to retreive the user balance key + // @param chianId the chain id for the source chain + // @param account the account for which to retreive the user balance key // @param token the token for which to retreive the user balance key // @return key for a source chain balance for a given user and token - function sourceChainBalanceKey(address virtualAccount, address token) internal pure returns (bytes32) { + function sourceChainBalanceKey(uint256 chainId, address account, address token) internal pure returns (bytes32) { return keccak256(abi.encode( SOURCE_CHAIN_BALANCE, - virtualAccount, + chainId, + account, token )); } diff --git a/contracts/multichain/LayerZeroProvider.sol b/contracts/multichain/LayerZeroProvider.sol index 6b0d58c29..c9bd5980c 100644 --- a/contracts/multichain/LayerZeroProvider.sol +++ b/contracts/multichain/LayerZeroProvider.sol @@ -64,11 +64,12 @@ contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer { _transferToVault(token, address(multichainVault)); - address virtualAccount = multichainHandler.recordDeposit(account, token, sourceChainId); + multichainHandler.recordDeposit(account, token, sourceChainId); LayerZeroProviderEventUtils.emitComposedMessageReceived( eventEmitter, - virtualAccount, + sourceChainId, + account, from, guid, message, diff --git a/contracts/multichain/LayerZeroProviderEventUtils.sol b/contracts/multichain/LayerZeroProviderEventUtils.sol index 441f7a073..405c19ec4 100644 --- a/contracts/multichain/LayerZeroProviderEventUtils.sol +++ b/contracts/multichain/LayerZeroProviderEventUtils.sol @@ -17,7 +17,8 @@ library LayerZeroProviderEventUtils { function emitComposedMessageReceived( EventEmitter eventEmitter, - address virtualAccount, + uint256 sourceChainId, + address account, address from, bytes32 guid, bytes calldata message, @@ -27,9 +28,12 @@ library LayerZeroProviderEventUtils { EventUtils.EventLogData memory eventData; eventData.addressItems.initItems(3); - eventData.addressItems.setItem(0, "virtualAccount", virtualAccount); + eventData.addressItems.setItem(0, "account", account); eventData.addressItems.setItem(1, "from", from); eventData.addressItems.setItem(2, "executor", executor); + + eventData.uintItems.initItems(1); + eventData.uintItems.setItem(0, "sourceChainId", sourceChainId); eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "guid", guid); @@ -38,12 +42,13 @@ library LayerZeroProviderEventUtils { eventData.bytesItems.setItem(0, "message", message); eventData.bytesItems.setItem(1, "extraData", extraData); - eventEmitter.emitEventLog1("MessageComposedReceived", Cast.toBytes32(virtualAccount), eventData); + eventEmitter.emitEventLog2("MessageComposedReceived", bytes32(sourceChainId), Cast.toBytes32(account), eventData); } function emitWithdrawalReceipt( EventEmitter eventEmitter, - address virtualAccount, + uint256 sourceChainId, + address account, bytes32 guid, uint64 nonce, uint256 nativeFee, @@ -53,16 +58,17 @@ library LayerZeroProviderEventUtils { ) internal { EventUtils.EventLogData memory eventData; - eventData.uintItems.initItems(5); + eventData.uintItems.initItems(6); eventData.uintItems.setItem(0, "nonce", uint256(nonce)); eventData.uintItems.setItem(1, "nativeFee", nativeFee); eventData.uintItems.setItem(2, "lzTokenFee", lzTokenFee); eventData.uintItems.setItem(3, "amountSentLD", amountSentLD); eventData.uintItems.setItem(4, "amountReceivedLD", amountReceivedLD); + eventData.uintItems.setItem(5, "sourceChainId", sourceChainId); eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "guid", guid); - eventEmitter.emitEventLog1("WithdrawalReceipt", Cast.toBytes32(virtualAccount), eventData); + eventEmitter.emitEventLog2("WithdrawalReceipt", bytes32(sourceChainId), Cast.toBytes32(account), eventData); } } diff --git a/contracts/multichain/MultichainEventUtils.sol b/contracts/multichain/MultichainEventUtils.sol index b971d03a7..231fb65ec 100644 --- a/contracts/multichain/MultichainEventUtils.sol +++ b/contracts/multichain/MultichainEventUtils.sol @@ -16,7 +16,7 @@ library MultichainEventUtils { function emitMultichainDeposit( EventEmitter eventEmitter, address token, - address virtualAccount, + address account, uint256 amount, uint256 sourceChainId ) internal { @@ -24,35 +24,35 @@ library MultichainEventUtils { eventData.addressItems.initItems(2); eventData.addressItems.setItem(0, "token", token); - eventData.addressItems.setItem(1, "virtualAccount", virtualAccount); + eventData.addressItems.setItem(1, "account", account); eventData.uintItems.initItems(2); eventData.uintItems.setItem(0, "amount", amount); eventData.uintItems.setItem(1, "sourceChainId", sourceChainId); - eventEmitter.emitEventLog1("MultichainDeposit", Cast.toBytes32(virtualAccount), eventData); + eventEmitter.emitEventLog2("MultichainDeposit", bytes32(sourceChainId), Cast.toBytes32(account), eventData); } function emitMultichainMessage( EventEmitter eventEmitter, - address virtualAccount, + address account, uint256 sourceChainId ) internal { EventUtils.EventLogData memory eventData; eventData.addressItems.initItems(1); - eventData.addressItems.setItem(0, "virtualAccount", virtualAccount); + eventData.addressItems.setItem(0, "account", account); eventData.uintItems.initItems(1); eventData.uintItems.setItem(0, "sourceChainId", sourceChainId); - eventEmitter.emitEventLog1("MultichainMessage", Cast.toBytes32(virtualAccount), eventData); + eventEmitter.emitEventLog1("MultichainMessage", Cast.toBytes32(account), eventData); } function emitMultichainWithdrawal( EventEmitter eventEmitter, address token, - address virtualAccount, + address account, uint256 amount, uint256 sourceChainId ) internal { @@ -60,12 +60,12 @@ library MultichainEventUtils { eventData.addressItems.initItems(2); eventData.addressItems.setItem(0, "token", token); - eventData.addressItems.setItem(1, "virtualAccount", virtualAccount); + eventData.addressItems.setItem(1, "account", account); eventData.uintItems.initItems(2); eventData.uintItems.setItem(0, "amount", amount); eventData.uintItems.setItem(1, "sourceChainId", sourceChainId); - eventEmitter.emitEventLog1("MultichainWithdrawal", Cast.toBytes32(virtualAccount), eventData); + eventEmitter.emitEventLog2("MultichainWithdrawal", bytes32(sourceChainId), Cast.toBytes32(account), eventData); } } diff --git a/contracts/multichain/MultichainHandler.sol b/contracts/multichain/MultichainHandler.sol index 9469e8a27..876f47a56 100644 --- a/contracts/multichain/MultichainHandler.sol +++ b/contracts/multichain/MultichainHandler.sol @@ -2,6 +2,9 @@ pragma solidity ^0.8.0; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + import { RoleStore } from "../role/RoleStore.sol"; import { RoleModule } from "../role/RoleModule.sol"; import { DataStore } from "../data/DataStore.sol"; @@ -21,6 +24,8 @@ import { MultichainEventUtils } from "./MultichainEventUtils.sol"; * @title MultichainHandler */ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { + using SafeERC20 for IERC20; + MultichainVault public multichainVault; EventEmitter public eventEmitter; ExchangeRouter public exchangeRouter; @@ -39,7 +44,7 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { } /** - * Records a deposit from another chain. IMultichainProvider has MULTICHAIN_CONTROLLER role + * Records a deposit from another chain. IMultichainProvider has CONTROLLER role * @param account user address on the source chain * @param token address of the token being deposited * @param sourceChainId chain id of the source chain @@ -48,18 +53,30 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { address account, address token, uint256 sourceChainId - ) external onlyController returns (address virtualAccount) { + ) external onlyController { // token should have been transferred to multichainVault by IMultichainProvider uint256 amount = multichainVault.recordTransferIn(token); if (amount == 0) { revert Errors.EmptyMultichainDepositAmount(); } - virtualAccount = MultichainUtils.getVirtualAccount(account, sourceChainId); + dataStore.incrementUint(Keys.sourceChainBalanceKey(sourceChainId, account, token), amount); - dataStore.incrementUint(Keys.sourceChainBalanceKey(virtualAccount, token), amount); + MultichainEventUtils.emitMultichainDeposit(eventEmitter, token, account, amount, sourceChainId); + } + + /** + * @dev transfer the specified amount of tokens from account to receiver + * @param account the account for which the tokens are subtracted + * @param token the token to transfer + * @param receiver the account to transfer to + * @param amount the amount of tokens to transfer + */ + function pluginTransfer(address token, address account, address receiver, uint256 amount) external onlyRouterPlugin { // TODO: confirm access control + // TODO: tokens should come from MultichainVault and the user's multichain balance should be decreased + // dataStore.decrementUint(Keys.sourceChainBalanceKey(sourceChainId, account, token), amount); - MultichainEventUtils.emitMultichainDeposit(eventEmitter, token, virtualAccount, amount, sourceChainId); + IERC20(token).safeTransferFrom(account, receiver, amount); } /** @@ -77,12 +94,11 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { // execute multicall exchangeRouter.multicall(multicallArgs); - address virtualAccount = MultichainUtils.getVirtualAccount(account, sourceChainId); - MultichainEventUtils.emitMultichainMessage(eventEmitter, virtualAccount, sourceChainId); + MultichainEventUtils.emitMultichainMessage(eventEmitter, account, sourceChainId); } /** - * Record a withdrawal to another chain. IMultichainProvider has MULTICHAIN_CONTROLLER role + * Record a withdrawal to another chain. IMultichainProvider has CONTROLLER role * @param account user address on the source chain * @param token address of the token being withdrawn * @param amount amount of token being withdrawn @@ -98,8 +114,7 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { revert Errors.EmptyMultichainWithdrawalAmount(); } - address virtualAccount = MultichainUtils.getVirtualAccount(account, sourceChainId); - bytes32 balanceKey = Keys.sourceChainBalanceKey(virtualAccount, token); + bytes32 balanceKey = Keys.sourceChainBalanceKey(sourceChainId, account, token); uint256 balance = dataStore.getUint(balanceKey); if (balance < amount) { @@ -112,6 +127,6 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { // transfer tokens to IMultichainProvider multichainVault.transferOut(token, msg.sender, amount); - MultichainEventUtils.emitMultichainWithdrawal(eventEmitter, token, virtualAccount, amount, sourceChainId); + MultichainEventUtils.emitMultichainWithdrawal(eventEmitter, token, account, amount, sourceChainId); } } diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol new file mode 100644 index 000000000..4016e2484 --- /dev/null +++ b/contracts/multichain/MultichainRouter.sol @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; + +import "../router/relay/GelatoRelayRouter.sol"; +import "../deposit/DepositUtils.sol"; +import "../deposit/DepositVault.sol"; +import "../exchange/IDepositHandler.sol"; + +import "./MultichainHandler.sol"; +import "./MultichainUtils.sol"; + +contract MultichainRouter is GelatoRelayRouter { + bytes32 public constant CREATE_DEPOSIT_TYPEHASH = + keccak256( + bytes( + "CreateDeposit(CreateDepositParams params,uint256 userNonce,uint256 deadline,bytes32 relayParams)CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit)" + ) + ); + bytes32 public constant CREATE_DEPOSIT_PARAMS_TYPEHASH = + keccak256( + bytes( + "CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath, uint256 minMarketTokens, bool shouldUnwrapNativeToken, uint256 executionFee, uint256 callbackGasLimit)" + ) + ); + + DepositVault depositVault; + IDepositHandler depositHandler; + MultichainVault multichainVault; + MultichainHandler multichainHandler; + + constructor( + Router _router, + DataStore _dataStore, + EventEmitter _eventEmitter, + Oracle _oracle, + IOrderHandler _orderHandler, + OrderVault _orderVault, + IDepositHandler _depositHandler, + DepositVault _depositVault, + MultichainVault _multichainVault, + MultichainHandler _multichainHandler + ) GelatoRelayRouter(_router, _dataStore, _eventEmitter, _oracle, _orderHandler, _orderVault) { + depositVault = _depositVault; + depositHandler = _depositHandler; + multichainVault = _multichainVault; + multichainHandler = _multichainHandler; + } + + // user funds are bridged into MultichainVault + // inside depositHandler.createDeposit funds are recorded --> recordTransferIn + // it is assumed that funds have already been transfered when recordTransferIn is reached + // TODO: what is the amount and when are tokens transferred to DepositVault (from MultichainVault)? + + function createDeposit( + RelayParams calldata relayParams, + address account, + uint256 chainId, + DepositUtils.CreateDepositParams memory params, // can't use calldata because need to modify params.numbers.executionFee + bytes calldata signature, + uint256 userNonce, + uint256 deadline + ) external nonReentrant onlyGelatoRelay returns (bytes32) { + bytes32 structHash = _getCreateDepositStructHash(relayParams, params, userNonce, deadline); + _validateCall(userNonce, deadline, account, structHash, signature); + + return _createDeposit(relayParams.tokenPermits, relayParams.fee, params, account, chainId); + } + + function _createDeposit( + TokenPermit[] calldata tokenPermits, + RelayFeeParams calldata fee, + DepositUtils.CreateDepositParams memory params, // can't use calldata because need to modify params.numbers.executionFee + address account, + uint256 chainId + ) internal returns (bytes32) { + Contracts memory contracts = Contracts({ + dataStore: dataStore, + eventEmitter: eventEmitter, + // TODO: confirm Contracts struct can be modified --> replace `OrderVault orderVault;` field with `StrictBank vault;` + // otherwise, should probably overridde _handleRelay + orderVault: OrderVault(payable(depositVault)) + }); + + // calculate next key without incrementing (createDeposit will recalculate and increment) + bytes32 nextKey = NonceUtils.getKey(contracts.dataStore, NonceUtils.getCurrentNonce(dataStore) + 1); + + // funds have already been transferred/bridged to multichain vault + // pay relay fee from the multicahin vault and decrease user's multichain balance + params.executionFee = _handleRelay( + contracts, + tokenPermits, + fee, + address(multichainVault), // account + nextKey, // deposit key + address(depositVault) // residualFeeReceiver + ); + + return depositHandler.createDeposit(account, params); + } + + // TODO: confirm BaseGelatoRelayRouter._sendTokens override + function _sendTokens(address account, address token, address receiver, uint256 amount) internal override { + AccountUtils.validateReceiver(receiver); + multichainHandler.pluginTransfer(token, account, receiver, amount); + + // relay fee ise sent from MultichainVault, from the user's multichain balance + + // to access user's multichain balance --> chainId, account, token are needed + // dataStore.decrementUint(Keys.sourceChainBalanceKey(chainId, account, token), amount); + + // TODO: means adding the chainId param but then can't override _sendTokens + // should BaseGelatoRelayRouter._sendTokens also have the chainId param? + } + + function _getCreateDepositStructHash( + RelayParams calldata relayParams, + DepositUtils.CreateDepositParams memory params, + uint256 userNonce, + uint256 deadline + ) internal pure returns (bytes32) { + bytes32 relayParamsHash = keccak256(abi.encode(relayParams)); + + return + keccak256( + abi.encode( + CREATE_DEPOSIT_TYPEHASH, + _getCreateDepositParamsStructHash(params), + userNonce, + deadline, + relayParamsHash + ) + ); + } + + function _getCreateDepositParamsStructHash( + DepositUtils.CreateDepositParams memory params + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + CREATE_DEPOSIT_PARAMS_TYPEHASH, + params.receiver, + params.callbackContract, + params.uiFeeReceiver, + params.market, + params.initialLongToken, + params.initialShortToken, + keccak256(abi.encodePacked(params.longTokenSwapPath)), + keccak256(abi.encodePacked(params.shortTokenSwapPath)), + params.minMarketTokens, + params.shouldUnwrapNativeToken, + params.executionFee, + params.callbackGasLimit + ) + ); + } + + function createWithdrawal() external nonReentrant onlyGelatoRelay {} + + function createGlvDeposit() external nonReentrant onlyGelatoRelay {} + + function createGlvWithdrawal() external nonReentrant onlyGelatoRelay {} + + function createShift() external nonReentrant onlyGelatoRelay {} +} diff --git a/contracts/multichain/MultichainUtils.sol b/contracts/multichain/MultichainUtils.sol index 6df8a27b3..2c848d8cf 100644 --- a/contracts/multichain/MultichainUtils.sol +++ b/contracts/multichain/MultichainUtils.sol @@ -5,8 +5,4 @@ pragma solidity ^0.8.0; /** * @title MultichainUtils */ -library MultichainUtils { - function getVirtualAccount(address account, uint256 sourceChainId) internal pure returns (address) { - return address(uint160(uint256(keccak256(abi.encode("GMX Multichain", account, sourceChainId))))); - } -} +library MultichainUtils {} diff --git a/contracts/position/Position.sol b/contracts/position/Position.sol index f3e7645a6..94caef0de 100644 --- a/contracts/position/Position.sol +++ b/contracts/position/Position.sol @@ -197,9 +197,19 @@ library Position { // @param market the position's market // @param collateralToken the position's collateralToken // @param isLong whether the position is long or short + // @param chainId the source chain id // @return the position key - function getPositionKey(address _account, address _market, address _collateralToken, bool _isLong) internal pure returns (bytes32) { + function getPositionKey(address _account, address _market, address _collateralToken, bool _isLong/*, uint256 _chainId*/) internal pure returns (bytes32) { + // TODO: confirm orders should have a chainId as well + // bytes32 _key; + // if (_chainId == 0) { + // _key = keccak256(abi.encode(_account, _market, _collateralToken, _isLong)); + // } else { + // _key = keccak256(abi.encode(_account, _chainId, _market, _collateralToken, _isLong)); + // } + bytes32 _key = keccak256(abi.encode(_account, _market, _collateralToken, _isLong)); + return _key; } } diff --git a/test/multichain/LayerZeroProvider.ts b/test/multichain/LayerZeroProvider.ts index 4919bc41f..fe36d6d8f 100644 --- a/test/multichain/LayerZeroProvider.ts +++ b/test/multichain/LayerZeroProvider.ts @@ -34,10 +34,9 @@ describe("LayerZeroProvider", () => { const lzUsdcBalance = await usdc.balanceOf(layerZeroProvider.address); const multichainVaultBalance = await usdc.balanceOf(multichainVault.address); - const virtualAccount = multichain.getVirtualAccount(user0.address, sourceChainId); - const userBalance = await dataStore.getUint(keys.sourceChainBalanceKey(virtualAccount, usdc.address)); + const userBalance = await dataStore.getUint(keys.sourceChainBalanceKey(sourceChainId, user0.address, usdc.address)); - // usdc has been transterred from LayerZeroProvider to MultichainVault and recorded under the user's virtual account + // usdc has been transterred from LayerZeroProvider to MultichainVault and recorded under the user's chainId + account expect(lzUsdcBalance).eq(0); expect(multichainVaultBalance).eq(amountUsdc); expect(userBalance).eq(amountUsdc); diff --git a/utils/keys.ts b/utils/keys.ts index c2ec59d12..0c582074f 100644 --- a/utils/keys.ts +++ b/utils/keys.ts @@ -792,6 +792,6 @@ export function withdrawableBuybackTokenAmountKey(buybackToken: string) { return hashData(["bytes32", "address"], [WITHDRAWABLE_BUYBACK_TOKEN_AMOUNT, buybackToken]); } -export function sourceChainBalanceKey(virtualAccount: string, token: string) { - return hashData(["bytes32", "address", "address"], [SOURCE_CHAIN_BALANCE, virtualAccount, token]); +export function sourceChainBalanceKey(sourceChainId: number, account: string, token: string) { + return hashData(["bytes32", "uint256", "address", "address"], [SOURCE_CHAIN_BALANCE, sourceChainId, account, token]); } diff --git a/utils/multichain.ts b/utils/multichain.ts index 24a03459d..e69de29bb 100644 --- a/utils/multichain.ts +++ b/utils/multichain.ts @@ -1,8 +0,0 @@ -import { hashData, getAddressFromHash } from "./hash"; - -export const GMX_MULTICHAIN = "GMX Multichain"; - -export function getVirtualAccount(account: string, sourceChainId: number): string { - const hash = hashData(["string", "address", "uint256"], [GMX_MULTICHAIN, account, sourceChainId]); - return getAddressFromHash(hash); -} From 00dd7d15b2b44016556f5b7d154f4e2f04e22ab6 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 1 Feb 2025 09:06:19 +0200 Subject: [PATCH 003/205] Make BaseGelatoRelayRouter._sendTokens virtual to be able to override it in MultichainRouter --- contracts/router/relay/BaseGelatoRelayRouter.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/router/relay/BaseGelatoRelayRouter.sol b/contracts/router/relay/BaseGelatoRelayRouter.sol index 7d0f4dd1e..fcc06f3d5 100644 --- a/contracts/router/relay/BaseGelatoRelayRouter.sol +++ b/contracts/router/relay/BaseGelatoRelayRouter.sol @@ -313,7 +313,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, return residualFee; } - function _sendTokens(address account, address token, address receiver, uint256 amount) internal { + function _sendTokens(address account, address token, address receiver, uint256 amount) internal virtual { AccountUtils.validateReceiver(receiver); router.pluginTransfer(token, account, receiver, amount); } From 1200a1a29101c5390fd41d625c94b6d8f2eba3a1 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 1 Feb 2025 09:06:43 +0200 Subject: [PATCH 004/205] fix compile errors --- contracts/multichain/MultichainRouter.sol | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 4016e2484..22c05a984 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -56,13 +56,10 @@ contract MultichainRouter is GelatoRelayRouter { RelayParams calldata relayParams, address account, uint256 chainId, - DepositUtils.CreateDepositParams memory params, // can't use calldata because need to modify params.numbers.executionFee - bytes calldata signature, - uint256 userNonce, - uint256 deadline + DepositUtils.CreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { - bytes32 structHash = _getCreateDepositStructHash(relayParams, params, userNonce, deadline); - _validateCall(userNonce, deadline, account, structHash, signature); + bytes32 structHash = _getCreateDepositStructHash(relayParams, params); + _validateCall(relayParams, account, structHash); return _createDeposit(relayParams.tokenPermits, relayParams.fee, params, account, chainId); } @@ -115,9 +112,7 @@ contract MultichainRouter is GelatoRelayRouter { function _getCreateDepositStructHash( RelayParams calldata relayParams, - DepositUtils.CreateDepositParams memory params, - uint256 userNonce, - uint256 deadline + DepositUtils.CreateDepositParams memory params ) internal pure returns (bytes32) { bytes32 relayParamsHash = keccak256(abi.encode(relayParams)); @@ -126,8 +121,6 @@ contract MultichainRouter is GelatoRelayRouter { abi.encode( CREATE_DEPOSIT_TYPEHASH, _getCreateDepositParamsStructHash(params), - userNonce, - deadline, relayParamsHash ) ); From 6cb97f8f4dad93744ac489b6fb1ef9ab8648cb59 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 1 Feb 2025 09:08:20 +0200 Subject: [PATCH 005/205] Rename MultichainHandler in MultichainVaultHandler --- contracts/multichain/LayerZeroProvider.sol | 16 ++++++++-------- contracts/multichain/MultichainRouter.sol | 10 +++++----- ...ainHandler.sol => MultichainVaultHandler.sol} | 4 ++-- deploy/deployLayerZeroProvider.ts | 2 +- deploy/deployMultichainHandler.ts | 2 +- utils/fixture.ts | 4 ++-- 6 files changed, 19 insertions(+), 19 deletions(-) rename contracts/multichain/{MultichainHandler.sol => MultichainVaultHandler.sol} (97%) diff --git a/contracts/multichain/LayerZeroProvider.sol b/contracts/multichain/LayerZeroProvider.sol index c9bd5980c..2abf0649f 100644 --- a/contracts/multichain/LayerZeroProvider.sol +++ b/contracts/multichain/LayerZeroProvider.sol @@ -10,7 +10,7 @@ import { EventEmitter } from "../event/EventEmitter.sol"; import { IMultichainProvider } from "./IMultichainProvider.sol"; import { MultichainVault } from "./MultichainVault.sol"; -import { MultichainHandler } from "./MultichainHandler.sol"; +import { MultichainVaultHandler } from "./MultichainVaultHandler.sol"; import { MultichainProviderUtils } from "./MultichainProviderUtils.sol"; import { LayerZeroProviderEventUtils } from "./LayerZeroProviderEventUtils.sol"; @@ -19,22 +19,22 @@ import { LayerZeroProviderEventUtils } from "./LayerZeroProviderEventUtils.sol"; * Receives tokens and messages from source chains. * Defines lzCompose function which: * - is called by the Stargate executor after tokens are delivered to this contract - * - forwards the received tokens to MultichainVault and records the deposit in MultichainHandler + * - forwards the received tokens to MultichainVault and records the deposit in MultichainVaultHandler */ contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer { EventEmitter public eventEmitter; MultichainVault public multichainVault; - MultichainHandler public multichainHandler; + MultichainVaultHandler public multichainVaultHandler; /** - * @param _multichainVault MultichainHandler address - * @param _multichainHandler MultichainVault address + * @param _multichainVault MultichainVaultHandler address + * @param _multichainVaultHandler MultichainVault address */ - constructor(address _eventEmitter, address _multichainVault, address _multichainHandler) { + constructor(address _eventEmitter, address _multichainVault, address _multichainVaultHandler) { eventEmitter = EventEmitter(_eventEmitter); multichainVault = MultichainVault(payable(_multichainVault)); - multichainHandler = MultichainHandler(_multichainHandler); + multichainVaultHandler = MultichainVaultHandler(_multichainVaultHandler); } ///////////////////// Stargate ////////////////////// @@ -64,7 +64,7 @@ contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer { _transferToVault(token, address(multichainVault)); - multichainHandler.recordDeposit(account, token, sourceChainId); + multichainVaultHandler.recordDeposit(account, token, sourceChainId); LayerZeroProviderEventUtils.emitComposedMessageReceived( eventEmitter, diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 22c05a984..2bed234b5 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -7,7 +7,7 @@ import "../deposit/DepositUtils.sol"; import "../deposit/DepositVault.sol"; import "../exchange/IDepositHandler.sol"; -import "./MultichainHandler.sol"; +import "./MultichainVaultHandler.sol"; import "./MultichainUtils.sol"; contract MultichainRouter is GelatoRelayRouter { @@ -27,7 +27,7 @@ contract MultichainRouter is GelatoRelayRouter { DepositVault depositVault; IDepositHandler depositHandler; MultichainVault multichainVault; - MultichainHandler multichainHandler; + MultichainVaultHandler multichainVaultHandler; constructor( Router _router, @@ -39,12 +39,12 @@ contract MultichainRouter is GelatoRelayRouter { IDepositHandler _depositHandler, DepositVault _depositVault, MultichainVault _multichainVault, - MultichainHandler _multichainHandler + MultichainVaultHandler _multichainVaultHandler ) GelatoRelayRouter(_router, _dataStore, _eventEmitter, _oracle, _orderHandler, _orderVault) { depositVault = _depositVault; depositHandler = _depositHandler; multichainVault = _multichainVault; - multichainHandler = _multichainHandler; + multichainVaultHandler = _multichainVaultHandler; } // user funds are bridged into MultichainVault @@ -99,7 +99,7 @@ contract MultichainRouter is GelatoRelayRouter { // TODO: confirm BaseGelatoRelayRouter._sendTokens override function _sendTokens(address account, address token, address receiver, uint256 amount) internal override { AccountUtils.validateReceiver(receiver); - multichainHandler.pluginTransfer(token, account, receiver, amount); + multichainVaultHandler.pluginTransfer(token, account, receiver, amount); // relay fee ise sent from MultichainVault, from the user's multichain balance diff --git a/contracts/multichain/MultichainHandler.sol b/contracts/multichain/MultichainVaultHandler.sol similarity index 97% rename from contracts/multichain/MultichainHandler.sol rename to contracts/multichain/MultichainVaultHandler.sol index 876f47a56..e13812e39 100644 --- a/contracts/multichain/MultichainHandler.sol +++ b/contracts/multichain/MultichainVaultHandler.sol @@ -21,9 +21,9 @@ import { MultichainUtils } from "./MultichainUtils.sol"; import { MultichainEventUtils } from "./MultichainEventUtils.sol"; /** - * @title MultichainHandler + * @title MultichainVaultHandler */ -contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { +contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModule { using SafeERC20 for IERC20; MultichainVault public multichainVault; diff --git a/deploy/deployLayerZeroProvider.ts b/deploy/deployLayerZeroProvider.ts index dda7143f3..3f8e7ff4f 100644 --- a/deploy/deployLayerZeroProvider.ts +++ b/deploy/deployLayerZeroProvider.ts @@ -1,7 +1,7 @@ import { grantRoleIfNotGranted } from "../utils/role"; import { createDeployFunction } from "../utils/deploy"; -const constructorContracts = ["EventEmitter", "MultichainVault", "MultichainHandler"]; +const constructorContracts = ["EventEmitter", "MultichainVault", "MultichainVaultHandler"]; const func = createDeployFunction({ contractName: "LayerZeroProvider", diff --git a/deploy/deployMultichainHandler.ts b/deploy/deployMultichainHandler.ts index cf8559ed3..3f28c9645 100644 --- a/deploy/deployMultichainHandler.ts +++ b/deploy/deployMultichainHandler.ts @@ -4,7 +4,7 @@ import { createDeployFunction } from "../utils/deploy"; const constructorContracts = ["RoleStore", "DataStore", "EventEmitter", "Oracle", "MultichainVault", "ExchangeRouter"]; const func = createDeployFunction({ - contractName: "MultichainHandler", + contractName: "MultichainVaultHandler", dependencyNames: constructorContracts, getDeployArgs: async ({ dependencyContracts }) => { return constructorContracts.map((dependencyName) => dependencyContracts[dependencyName].address); diff --git a/utils/fixture.ts b/utils/fixture.ts index 228f85f83..f8d0a123b 100644 --- a/utils/fixture.ts +++ b/utils/fixture.ts @@ -118,7 +118,7 @@ export async function deployFixture() { const feeHandler = await hre.ethers.getContract("FeeHandler"); const mockVaultV1 = await hre.ethers.getContract("MockVaultV1"); const multichainVault = await hre.ethers.getContract("MultichainVault"); - const multichainHandler = await hre.ethers.getContract("MultichainHandler"); + const multichainVaultHandler = await hre.ethers.getContract("MultichainVaultHandler"); const layerZeroProvider = await hre.ethers.getContract("LayerZeroProvider"); const mockStargatePool = await hre.ethers.getContract("MockStargatePool"); @@ -328,7 +328,7 @@ export async function deployFixture() { glvReader, mockVaultV1, multichainVault, - multichainHandler, + multichainVaultHandler, layerZeroProvider, mockStargatePool, }, From 21e9c1496c647d52eda28c2a7c03fb5291d1f618 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 1 Feb 2025 09:33:13 +0200 Subject: [PATCH 006/205] Add GaslessCreateDepositParams --- contracts/multichain/MultichainRouter.sol | 44 +++++++++++++++++------ 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 2bed234b5..07bf2db51 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -11,16 +11,28 @@ import "./MultichainVaultHandler.sol"; import "./MultichainUtils.sol"; contract MultichainRouter is GelatoRelayRouter { + struct GaslessCreateDepositParams { + uint256 longTokenAmount; + uint256 shortTokenAmount; + DepositUtils.CreateDepositParams createDepositParams; + } + bytes32 public constant CREATE_DEPOSIT_TYPEHASH = keccak256( bytes( - "CreateDeposit(CreateDepositParams params,uint256 userNonce,uint256 deadline,bytes32 relayParams)CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit)" + "CreateDeposit(uint256 chainId,CreateDepositParams params,bytes32 relayParams)CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" ) ); bytes32 public constant CREATE_DEPOSIT_PARAMS_TYPEHASH = keccak256( bytes( - "CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath, uint256 minMarketTokens, bool shouldUnwrapNativeToken, uint256 executionFee, uint256 callbackGasLimit)" + "CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" + ) + ); + bytes32 public constant GASLESS_CREATE_DEPOSIT_PARAMS_TYPEHASH = + keccak256( + bytes( + "GaslessCreateDepositParams(uint256 longTokenAmount,uint256 shortTokenAmount,CreateDepositParams params)CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" ) ); @@ -56,9 +68,9 @@ contract MultichainRouter is GelatoRelayRouter { RelayParams calldata relayParams, address account, uint256 chainId, - DepositUtils.CreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee + GaslessCreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { - bytes32 structHash = _getCreateDepositStructHash(relayParams, params); + bytes32 structHash = _getGaslessCreateDepositStructHash(relayParams, params); _validateCall(relayParams, account, structHash); return _createDeposit(relayParams.tokenPermits, relayParams.fee, params, account, chainId); @@ -67,7 +79,7 @@ contract MultichainRouter is GelatoRelayRouter { function _createDeposit( TokenPermit[] calldata tokenPermits, RelayFeeParams calldata fee, - DepositUtils.CreateDepositParams memory params, // can't use calldata because need to modify params.numbers.executionFee + GaslessCreateDepositParams memory params, // can't use calldata because need to modify params.numbers.executionFee address account, uint256 chainId ) internal returns (bytes32) { @@ -84,7 +96,7 @@ contract MultichainRouter is GelatoRelayRouter { // funds have already been transferred/bridged to multichain vault // pay relay fee from the multicahin vault and decrease user's multichain balance - params.executionFee = _handleRelay( + params.createDepositParams.executionFee = _handleRelay( contracts, tokenPermits, fee, @@ -93,7 +105,7 @@ contract MultichainRouter is GelatoRelayRouter { address(depositVault) // residualFeeReceiver ); - return depositHandler.createDeposit(account, params); + return depositHandler.createDeposit(account, params.createDepositParams); } // TODO: confirm BaseGelatoRelayRouter._sendTokens override @@ -110,9 +122,9 @@ contract MultichainRouter is GelatoRelayRouter { // should BaseGelatoRelayRouter._sendTokens also have the chainId param? } - function _getCreateDepositStructHash( + function _getGaslessCreateDepositStructHash( RelayParams calldata relayParams, - DepositUtils.CreateDepositParams memory params + GaslessCreateDepositParams memory params ) internal pure returns (bytes32) { bytes32 relayParamsHash = keccak256(abi.encode(relayParams)); @@ -120,12 +132,24 @@ contract MultichainRouter is GelatoRelayRouter { keccak256( abi.encode( CREATE_DEPOSIT_TYPEHASH, - _getCreateDepositParamsStructHash(params), + _getGaslessCreateDepositParamsStructHash(params), relayParamsHash ) ); } + function _getGaslessCreateDepositParamsStructHash( + GaslessCreateDepositParams memory params + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + GASLESS_CREATE_DEPOSIT_PARAMS_TYPEHASH, + _getCreateDepositParamsStructHash(params.createDepositParams) + ) + ); + } + function _getCreateDepositParamsStructHash( DepositUtils.CreateDepositParams memory params ) internal pure returns (bytes32) { From 11298530ecd68bfa0f5f4fd021f541c87ab8ad9e Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 1 Feb 2025 13:49:50 +0200 Subject: [PATCH 007/205] Add chainId to Deposit.Props --- contracts/deposit/Deposit.sol | 9 +++++++++ contracts/deposit/DepositEventUtils.sol | 4 +++- contracts/deposit/DepositStoreUtils.sol | 14 ++++++++++++++ contracts/deposit/DepositUtils.sol | 4 +++- contracts/glv/glvDeposit/GlvDepositUtils.sol | 3 ++- contracts/migration/GlpMigrator.sol | 1 + contracts/shift/ShiftUtils.sol | 3 ++- test/router/ExchangeRouter.ts | 3 +++ utils/deposit.ts | 2 ++ 9 files changed, 39 insertions(+), 4 deletions(-) diff --git a/contracts/deposit/Deposit.sol b/contracts/deposit/Deposit.sol index 35dee7cea..05c219791 100644 --- a/contracts/deposit/Deposit.sol +++ b/contracts/deposit/Deposit.sol @@ -54,6 +54,7 @@ library Deposit { uint256 updatedAtTime; uint256 executionFee; uint256 callbackGasLimit; + uint256 chainId; } // @param shouldUnwrapNativeToken whether to unwrap the native token when @@ -181,6 +182,14 @@ library Deposit { props.numbers.callbackGasLimit = value; } + function chainId(Props memory props) internal pure returns (uint256) { + return props.numbers.chainId; + } + + function setChainId(Props memory props, uint256 value) internal pure { + props.numbers.chainId = value; + } + function shouldUnwrapNativeToken(Props memory props) internal pure returns (bool) { return props.flags.shouldUnwrapNativeToken; } diff --git a/contracts/deposit/DepositEventUtils.sol b/contracts/deposit/DepositEventUtils.sol index 6695448d6..33aa3e1e0 100644 --- a/contracts/deposit/DepositEventUtils.sol +++ b/contracts/deposit/DepositEventUtils.sol @@ -41,7 +41,7 @@ library DepositEventUtils { eventData.addressItems.setItem(0, "longTokenSwapPath", deposit.longTokenSwapPath()); eventData.addressItems.setItem(1, "shortTokenSwapPath", deposit.shortTokenSwapPath()); - eventData.uintItems.initItems(7); + eventData.uintItems.initItems(8); eventData.uintItems.setItem(0, "initialLongTokenAmount", deposit.initialLongTokenAmount()); eventData.uintItems.setItem(1, "initialShortTokenAmount", deposit.initialShortTokenAmount()); eventData.uintItems.setItem(2, "minMarketTokens", deposit.minMarketTokens()); @@ -49,6 +49,7 @@ library DepositEventUtils { eventData.uintItems.setItem(4, "executionFee", deposit.executionFee()); eventData.uintItems.setItem(5, "callbackGasLimit", deposit.callbackGasLimit()); eventData.uintItems.setItem(6, "depositType", uint256(depositType)); + eventData.uintItems.setItem(7, "chainId", deposit.chainId()); eventData.boolItems.initItems(1); eventData.boolItems.setItem(0, "shouldUnwrapNativeToken", deposit.shouldUnwrapNativeToken()); @@ -67,6 +68,7 @@ library DepositEventUtils { function emitDepositExecuted( EventEmitter eventEmitter, bytes32 key, + // uint256 chainId, TODO: should chainId be added as param? Is it ok to change the event signature (for e.g. analytics) address account, uint256 longTokenAmount, uint256 shortTokenAmount, diff --git a/contracts/deposit/DepositStoreUtils.sol b/contracts/deposit/DepositStoreUtils.sol index 667bbb0ac..aeba013f1 100644 --- a/contracts/deposit/DepositStoreUtils.sol +++ b/contracts/deposit/DepositStoreUtils.sol @@ -30,6 +30,7 @@ library DepositStoreUtils { bytes32 public constant UPDATED_AT_TIME = keccak256(abi.encode("UPDATED_AT_TIME")); bytes32 public constant EXECUTION_FEE = keccak256(abi.encode("EXECUTION_FEE")); bytes32 public constant CALLBACK_GAS_LIMIT = keccak256(abi.encode("CALLBACK_GAS_LIMIT")); + bytes32 public constant CHAIN_ID = keccak256(abi.encode("CHAIN_ID")); bytes32 public constant SHOULD_UNWRAP_NATIVE_TOKEN = keccak256(abi.encode("SHOULD_UNWRAP_NATIVE_TOKEN")); @@ -101,6 +102,10 @@ library DepositStoreUtils { keccak256(abi.encode(key, CALLBACK_GAS_LIMIT)) )); + deposit.setChainId(dataStore.getUint( + keccak256(abi.encode(key, CHAIN_ID)) + )); + deposit.setShouldUnwrapNativeToken(dataStore.getBool( keccak256(abi.encode(key, SHOULD_UNWRAP_NATIVE_TOKEN)) )); @@ -198,6 +203,11 @@ library DepositStoreUtils { deposit.callbackGasLimit() ); + dataStore.setUint( + keccak256(abi.encode(key, CHAIN_ID)), + deposit.chainId() + ); + dataStore.setBool( keccak256(abi.encode(key, SHOULD_UNWRAP_NATIVE_TOKEN)), deposit.shouldUnwrapNativeToken() @@ -284,6 +294,10 @@ library DepositStoreUtils { keccak256(abi.encode(key, CALLBACK_GAS_LIMIT)) ); + dataStore.removeUint( + keccak256(abi.encode(key, CHAIN_ID)) + ); + dataStore.removeBool( keccak256(abi.encode(key, SHOULD_UNWRAP_NATIVE_TOKEN)) ); diff --git a/contracts/deposit/DepositUtils.sol b/contracts/deposit/DepositUtils.sol index 63e057ed9..a19e1ec80 100644 --- a/contracts/deposit/DepositUtils.sol +++ b/contracts/deposit/DepositUtils.sol @@ -50,6 +50,7 @@ library DepositUtils { bool shouldUnwrapNativeToken; uint256 executionFee; uint256 callbackGasLimit; + uint256 chainId; bytes32[] dataList; } @@ -117,7 +118,8 @@ library DepositUtils { params.minMarketTokens, Chain.currentTimestamp(), // updatedAtTime params.executionFee, - params.callbackGasLimit + params.callbackGasLimit, + params.chainId ), Deposit.Flags( params.shouldUnwrapNativeToken diff --git a/contracts/glv/glvDeposit/GlvDepositUtils.sol b/contracts/glv/glvDeposit/GlvDepositUtils.sol index b3dd29622..eda07cfec 100644 --- a/contracts/glv/glvDeposit/GlvDepositUtils.sol +++ b/contracts/glv/glvDeposit/GlvDepositUtils.sol @@ -354,7 +354,8 @@ library GlvDepositUtils { minMarketTokens: 0, updatedAtTime: glvDeposit.updatedAtTime(), executionFee: 0, - callbackGasLimit: 0 + callbackGasLimit: 0, + chainId: 0 }), Deposit.Flags({shouldUnwrapNativeToken: false}), new bytes32[](0) // dataList diff --git a/contracts/migration/GlpMigrator.sol b/contracts/migration/GlpMigrator.sol index cac6edb1c..abf6b99ec 100644 --- a/contracts/migration/GlpMigrator.sol +++ b/contracts/migration/GlpMigrator.sol @@ -208,6 +208,7 @@ contract GlpMigrator is ReentrancyGuard, RoleModule { false, // shouldUnwrapNativeToken; migrationItem.executionFee, // executionFee; 0, // callbackGasLimit; + 0, // chainId new bytes32[](0) // dataList; ); diff --git a/contracts/shift/ShiftUtils.sol b/contracts/shift/ShiftUtils.sol index 95c512024..1e91a8a35 100644 --- a/contracts/shift/ShiftUtils.sol +++ b/contracts/shift/ShiftUtils.sol @@ -259,7 +259,8 @@ library ShiftUtils { shift.minMarketTokens(), shift.updatedAtTime(), 0, // executionFee - 0 // callbackGasLimit + 0, // callbackGasLimit + 0 // chainId // TODO: change to shift.chainId ), Deposit.Flags( false // shouldUnwrapNativeToken diff --git a/test/router/ExchangeRouter.ts b/test/router/ExchangeRouter.ts index 2003c54a2..a539c6d99 100644 --- a/test/router/ExchangeRouter.ts +++ b/test/router/ExchangeRouter.ts @@ -74,6 +74,7 @@ describe("ExchangeRouter", () => { shouldUnwrapNativeToken: true, executionFee, callbackGasLimit: "200000", + chainId: 0, dataList, }, ]), @@ -97,6 +98,7 @@ describe("ExchangeRouter", () => { expect(deposit.numbers.minMarketTokens).eq(100); expect(deposit.numbers.executionFee).eq(expandDecimals(1, 18)); expect(deposit.numbers.callbackGasLimit).eq("200000"); + expect(deposit.numbers.chainId).eq(0); expect(deposit.flags.shouldUnwrapNativeToken).eq(true); expect(deposit._dataList).deep.eq(dataList); @@ -277,6 +279,7 @@ describe("ExchangeRouter", () => { shouldUnwrapNativeToken: true, executionFee, callbackGasLimit: "200000", + chainId: 0, dataList: [], }, ]), diff --git a/utils/deposit.ts b/utils/deposit.ts index cc79f98b3..0c1ca9020 100644 --- a/utils/deposit.ts +++ b/utils/deposit.ts @@ -45,6 +45,7 @@ export async function createDeposit(fixture, overrides: any = {}) { const callbackGasLimit = overrides.callbackGasLimit || bigNumberify(0); const longTokenAmount = overrides.longTokenAmount || bigNumberify(0); const shortTokenAmount = overrides.shortTokenAmount || bigNumberify(0); + const chainId = overrides.chainId || bigNumberify(0); const dataList = overrides.dataList || []; await wnt.mint(depositVault.address, executionFeeToMint); @@ -72,6 +73,7 @@ export async function createDeposit(fixture, overrides: any = {}) { shouldUnwrapNativeToken, executionFee, callbackGasLimit, + chainId, dataList, }; From 908b2bddf4b748da6b159ef32dd9c81398126765 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 1 Feb 2025 13:55:31 +0200 Subject: [PATCH 008/205] Update multichain createDeposit logic --- contracts/multichain/MultichainRouter.sol | 58 +++++++++---------- .../multichain/MultichainVaultHandler.sol | 12 ++-- .../router/relay/BaseGelatoRelayRouter.sol | 2 +- 3 files changed, 35 insertions(+), 37 deletions(-) diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 07bf2db51..79a6b761e 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -12,6 +12,7 @@ import "./MultichainUtils.sol"; contract MultichainRouter is GelatoRelayRouter { struct GaslessCreateDepositParams { + uint256 chainId; uint256 longTokenAmount; uint256 shortTokenAmount; DepositUtils.CreateDepositParams createDepositParams; @@ -20,19 +21,19 @@ contract MultichainRouter is GelatoRelayRouter { bytes32 public constant CREATE_DEPOSIT_TYPEHASH = keccak256( bytes( - "CreateDeposit(uint256 chainId,CreateDepositParams params,bytes32 relayParams)CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" + "CreateDeposit(CreateDepositParams params,bytes32 relayParams)CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 chainId,bytes32[] dataList)" ) ); bytes32 public constant CREATE_DEPOSIT_PARAMS_TYPEHASH = keccak256( bytes( - "CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" + "CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 chainId,bytes32[] dataList)" ) ); bytes32 public constant GASLESS_CREATE_DEPOSIT_PARAMS_TYPEHASH = keccak256( bytes( - "GaslessCreateDepositParams(uint256 longTokenAmount,uint256 shortTokenAmount,CreateDepositParams params)CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" + "GaslessCreateDepositParams(uint256 chainId,uint256 longTokenAmount,uint256 shortTokenAmount,CreateDepositParams params)CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 chainId,bytes32[] dataList)" ) ); @@ -59,29 +60,22 @@ contract MultichainRouter is GelatoRelayRouter { multichainVaultHandler = _multichainVaultHandler; } - // user funds are bridged into MultichainVault - // inside depositHandler.createDeposit funds are recorded --> recordTransferIn - // it is assumed that funds have already been transfered when recordTransferIn is reached - // TODO: what is the amount and when are tokens transferred to DepositVault (from MultichainVault)? - function createDeposit( RelayParams calldata relayParams, address account, - uint256 chainId, GaslessCreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { bytes32 structHash = _getGaslessCreateDepositStructHash(relayParams, params); _validateCall(relayParams, account, structHash); - return _createDeposit(relayParams.tokenPermits, relayParams.fee, params, account, chainId); + return _createDeposit(relayParams.tokenPermits, relayParams.fee, account, params); } function _createDeposit( TokenPermit[] calldata tokenPermits, RelayFeeParams calldata fee, - GaslessCreateDepositParams memory params, // can't use calldata because need to modify params.numbers.executionFee address account, - uint256 chainId + GaslessCreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee ) internal returns (bytes32) { Contracts memory contracts = Contracts({ dataStore: dataStore, @@ -91,35 +85,38 @@ contract MultichainRouter is GelatoRelayRouter { orderVault: OrderVault(payable(depositVault)) }); - // calculate next key without incrementing (createDeposit will recalculate and increment) - bytes32 nextKey = NonceUtils.getKey(contracts.dataStore, NonceUtils.getCurrentNonce(dataStore) + 1); + // transfer long & short tokens from MultichainVault to DepositVault and decrement user's multichain balance + _sendTokens( + params.createDepositParams.chainId, + params.createDepositParams.initialLongToken, + account, + address(depositVault), // receiver + params.longTokenAmount + ); + _sendTokens( + params.createDepositParams.chainId, + params.createDepositParams.initialShortToken, + account, + address(depositVault), // receiver + params.shortTokenAmount + ); - // funds have already been transferred/bridged to multichain vault - // pay relay fee from the multicahin vault and decrease user's multichain balance + // pay relay fee tokens from MultichainVault to DepositVault and decrement user's multichain balance params.createDepositParams.executionFee = _handleRelay( contracts, tokenPermits, fee, - address(multichainVault), // account - nextKey, // deposit key + account, + NonceUtils.getKey(contracts.dataStore, NonceUtils.getCurrentNonce(dataStore) + 1), // calculate next key without incrementing address(depositVault) // residualFeeReceiver ); return depositHandler.createDeposit(account, params.createDepositParams); } - // TODO: confirm BaseGelatoRelayRouter._sendTokens override - function _sendTokens(address account, address token, address receiver, uint256 amount) internal override { + function _sendTokens(uint256 chainId, address account, address token, address receiver, uint256 amount) internal { // TODO: confirm _sendTokens override and make BaseGelatoRelayRouter._sendTokens virtual AccountUtils.validateReceiver(receiver); - multichainVaultHandler.pluginTransfer(token, account, receiver, amount); - - // relay fee ise sent from MultichainVault, from the user's multichain balance - - // to access user's multichain balance --> chainId, account, token are needed - // dataStore.decrementUint(Keys.sourceChainBalanceKey(chainId, account, token), amount); - - // TODO: means adding the chainId param but then can't override _sendTokens - // should BaseGelatoRelayRouter._sendTokens also have the chainId param? + multichainVaultHandler.pluginTransfer(chainId, token, account, receiver, amount); } function _getGaslessCreateDepositStructHash( @@ -145,6 +142,9 @@ contract MultichainRouter is GelatoRelayRouter { keccak256( abi.encode( GASLESS_CREATE_DEPOSIT_PARAMS_TYPEHASH, + params.chainId, + params.longTokenAmount, + params.shortTokenAmount, _getCreateDepositParamsStructHash(params.createDepositParams) ) ); diff --git a/contracts/multichain/MultichainVaultHandler.sol b/contracts/multichain/MultichainVaultHandler.sol index e13812e39..cf4ce016d 100644 --- a/contracts/multichain/MultichainVaultHandler.sol +++ b/contracts/multichain/MultichainVaultHandler.sol @@ -66,17 +66,15 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu } /** - * @dev transfer the specified amount of tokens from account to receiver - * @param account the account for which the tokens are subtracted + * @dev transfer the specified amount of tokens from MultichainVault to receiver and decrement multichain account balance + * @param account the account for which the tokens are transferred and multichain balance decremented * @param token the token to transfer * @param receiver the account to transfer to * @param amount the amount of tokens to transfer */ - function pluginTransfer(address token, address account, address receiver, uint256 amount) external onlyRouterPlugin { // TODO: confirm access control - // TODO: tokens should come from MultichainVault and the user's multichain balance should be decreased - // dataStore.decrementUint(Keys.sourceChainBalanceKey(sourceChainId, account, token), amount); - - IERC20(token).safeTransferFrom(account, receiver, amount); + function pluginTransfer(uint256 chainId, address token, address account, address receiver, uint256 amount) external onlyRouterPlugin { // TODO: confirm access control + dataStore.decrementUint(Keys.sourceChainBalanceKey(chainId, account, token), amount); + IERC20(token).safeTransferFrom(address(multichainVault), receiver, amount); } /** diff --git a/contracts/router/relay/BaseGelatoRelayRouter.sol b/contracts/router/relay/BaseGelatoRelayRouter.sol index fcc06f3d5..7d0f4dd1e 100644 --- a/contracts/router/relay/BaseGelatoRelayRouter.sol +++ b/contracts/router/relay/BaseGelatoRelayRouter.sol @@ -313,7 +313,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, return residualFee; } - function _sendTokens(address account, address token, address receiver, uint256 amount) internal virtual { + function _sendTokens(address account, address token, address receiver, uint256 amount) internal { AccountUtils.validateReceiver(receiver); router.pluginTransfer(token, account, receiver, amount); } From 572d0b78eea977377d3ae52e1aaa71f89115b818 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 2 Feb 2025 14:57:38 +0200 Subject: [PATCH 009/205] Emit dataList for createDeposit, createWithdrawal, createOrder, updateOrder, createShift, createGlvDeposit, createGlvWithdrawal --- contracts/deposit/DepositEventUtils.sol | 3 +++ contracts/glv/glvDeposit/GlvDepositEventUtils.sol | 3 +++ contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol | 3 +++ contracts/order/OrderEventUtils.sol | 6 ++++++ contracts/position/Position.sol | 2 +- contracts/shift/ShiftEventUtils.sol | 3 +++ contracts/withdrawal/WithdrawalEventUtils.sol | 3 +++ scripts/createDepositSynthetic.ts | 1 + scripts/createDepositWethUsdc.ts | 1 + scripts/createDepositWnt.ts | 1 + 10 files changed, 25 insertions(+), 1 deletion(-) diff --git a/contracts/deposit/DepositEventUtils.sol b/contracts/deposit/DepositEventUtils.sol index 33aa3e1e0..60c71ec61 100644 --- a/contracts/deposit/DepositEventUtils.sol +++ b/contracts/deposit/DepositEventUtils.sol @@ -57,6 +57,9 @@ library DepositEventUtils { eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", deposit.dataList()); + eventEmitter.emitEventLog2( "DepositCreated", key, diff --git a/contracts/glv/glvDeposit/GlvDepositEventUtils.sol b/contracts/glv/glvDeposit/GlvDepositEventUtils.sol index b4a6ac5ad..c07467b27 100644 --- a/contracts/glv/glvDeposit/GlvDepositEventUtils.sol +++ b/contracts/glv/glvDeposit/GlvDepositEventUtils.sol @@ -56,6 +56,9 @@ library GlvDepositEventUtils { eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", glvDeposit.dataList()); + eventEmitter.emitEventLog2( "GlvDepositCreated", key, diff --git a/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol b/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol index d7b5ad4ab..231ac79ab 100644 --- a/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol +++ b/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol @@ -52,6 +52,9 @@ library GlvWithdrawalEventUtils { eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", glvWithdrawal.dataList()); + eventEmitter.emitEventLog2("GlvWithdrawalCreated", key, Cast.toBytes32(glvWithdrawal.account()), eventData); } diff --git a/contracts/order/OrderEventUtils.sol b/contracts/order/OrderEventUtils.sol index 080a12e08..c12e21ed8 100644 --- a/contracts/order/OrderEventUtils.sol +++ b/contracts/order/OrderEventUtils.sol @@ -58,6 +58,9 @@ library OrderEventUtils { eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", order.dataList()); + eventEmitter.emitEventLog2( "OrderCreated", key, @@ -115,6 +118,9 @@ library OrderEventUtils { eventData.boolItems.initItems(1); eventData.boolItems.setItem(0, "autoCancel", order.autoCancel()); + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", order.dataList()); + eventEmitter.emitEventLog2( "OrderUpdated", key, diff --git a/contracts/position/Position.sol b/contracts/position/Position.sol index 94caef0de..c2066a705 100644 --- a/contracts/position/Position.sol +++ b/contracts/position/Position.sol @@ -200,7 +200,7 @@ library Position { // @param chainId the source chain id // @return the position key function getPositionKey(address _account, address _market, address _collateralToken, bool _isLong/*, uint256 _chainId*/) internal pure returns (bytes32) { - // TODO: confirm orders should have a chainId as well + // TODO: handle the chainId cases bellow // bytes32 _key; // if (_chainId == 0) { // _key = keccak256(abi.encode(_account, _market, _collateralToken, _isLong)); diff --git a/contracts/shift/ShiftEventUtils.sol b/contracts/shift/ShiftEventUtils.sol index 8034e031a..32c722803 100644 --- a/contracts/shift/ShiftEventUtils.sol +++ b/contracts/shift/ShiftEventUtils.sol @@ -44,6 +44,9 @@ library ShiftEventUtils { eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", shift.dataList()); + eventEmitter.emitEventLog2( "ShiftCreated", key, diff --git a/contracts/withdrawal/WithdrawalEventUtils.sol b/contracts/withdrawal/WithdrawalEventUtils.sol index d8c1e602c..364f146a9 100644 --- a/contracts/withdrawal/WithdrawalEventUtils.sol +++ b/contracts/withdrawal/WithdrawalEventUtils.sol @@ -54,6 +54,9 @@ library WithdrawalEventUtils { eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", withdrawal.dataList()); + eventEmitter.emitEventLog2( "WithdrawalCreated", key, diff --git a/scripts/createDepositSynthetic.ts b/scripts/createDepositSynthetic.ts index df510d8bd..f0fa3e9f3 100644 --- a/scripts/createDepositSynthetic.ts +++ b/scripts/createDepositSynthetic.ts @@ -100,6 +100,7 @@ async function main() { initialShortToken: usdc.address, longTokenSwapPath: [], shortTokenSwapPath: [], + dataList: [], }; console.log("exchange router %s", exchangeRouter.address); console.log("deposit vault %s", depositVault.address); diff --git a/scripts/createDepositWethUsdc.ts b/scripts/createDepositWethUsdc.ts index b82ccc769..71ba63aca 100644 --- a/scripts/createDepositWethUsdc.ts +++ b/scripts/createDepositWethUsdc.ts @@ -108,6 +108,7 @@ async function main() { initialShortToken: usdc.address, shortTokenSwapPath: [], uiFeeReceiver: ethers.constants.AddressZero, + dataList: [], }; console.log("exchange router %s", exchangeRouter.address); console.log("deposit store %s", depositVault.address); diff --git a/scripts/createDepositWnt.ts b/scripts/createDepositWnt.ts index 71b7628b8..6896c26a9 100644 --- a/scripts/createDepositWnt.ts +++ b/scripts/createDepositWnt.ts @@ -92,6 +92,7 @@ async function main() { initialShortToken: usdc.address, shortTokenSwapPath: [], uiFeeReceiver: ethers.constants.AddressZero, + dataList: [], }; console.log("exchange router %s", exchangeRouter.address); console.log("creating deposit %s", JSON.stringify(params)); From 70f486ee34e3e9f1ab97fd4dffd9bc3b0ce47009 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 3 Feb 2025 12:20:01 +0200 Subject: [PATCH 010/205] Rename sourceChain* to multichain* --- contracts/config/Config.sol | 2 +- contracts/data/Keys.sol | 14 +++++------ contracts/multichain/LayerZeroProvider.sol | 8 +++---- .../LayerZeroProviderEventUtils.sol | 12 +++++----- contracts/multichain/MultichainEventUtils.sol | 16 ++++++------- .../MultichainProviderSignature.sol | 10 ++++---- .../multichain/MultichainProviderUtils.sol | 4 ++-- .../multichain/MultichainVaultHandler.sol | 24 +++++++++---------- contracts/position/Position.sol | 2 +- test/multichain/LayerZeroProvider.ts | 6 ++--- utils/keys.ts | 6 ++--- 11 files changed, 52 insertions(+), 52 deletions(-) diff --git a/contracts/config/Config.sol b/contracts/config/Config.sol index c4f41b5f7..8046a08c9 100644 --- a/contracts/config/Config.sol +++ b/contracts/config/Config.sol @@ -515,7 +515,7 @@ contract Config is ReentrancyGuard, RoleModule, BasicMulticall { allowedBaseKeys[Keys.DATA_STREAM_SPREAD_REDUCTION_FACTOR] = true; - allowedBaseKeys[Keys.SOURCE_CHAIN_BALANCE] = true; + allowedBaseKeys[Keys.MULTICHAIN_BALANCE] = true; allowedBaseKeys[Keys.MAX_DATA_LENGTH] = true; diff --git a/contracts/data/Keys.sol b/contracts/data/Keys.sol index 605d7bc0d..412b34665 100644 --- a/contracts/data/Keys.sol +++ b/contracts/data/Keys.sol @@ -471,8 +471,8 @@ library Keys { // @dev key for the buyback withdrawable fees bytes32 public constant WITHDRAWABLE_BUYBACK_TOKEN_AMOUNT = keccak256(abi.encode("WITHDRAWABLE_BUYBACK_TOKEN_AMOUNT")); - // @dev key for user's balance for a source chain, recorded under the user's virtual account - bytes32 public constant SOURCE_CHAIN_BALANCE = keccak256(abi.encode("SOURCE_CHAIN_BALANCE")); + // @dev key for user's multichain balance + bytes32 public constant MULTICHAIN_BALANCE = keccak256(abi.encode("MULTICHAIN_BALANCE")); // @dev key for the maximum length for data list array of bytes32 bytes32 public constant MAX_DATA_LENGTH = keccak256(abi.encode("MAX_DATA_LENGTH")); @@ -2120,14 +2120,14 @@ library Keys { )); } - // @dev key for user's balance for a source chain, recorded under the user's virtual account - // @param chianId the chain id for the source chain + // @dev key for user's multichain balance + // @param chainId the chain id for the destination chain // @param account the account for which to retreive the user balance key // @param token the token for which to retreive the user balance key - // @return key for a source chain balance for a given user and token - function sourceChainBalanceKey(uint256 chainId, address account, address token) internal pure returns (bytes32) { + // @return key for multichain balance for a given user and token + function multichainBalanceKey(uint256 chainId, address account, address token) internal pure returns (bytes32) { return keccak256(abi.encode( - SOURCE_CHAIN_BALANCE, + MULTICHAIN_BALANCE, chainId, account, token diff --git a/contracts/multichain/LayerZeroProvider.sol b/contracts/multichain/LayerZeroProvider.sol index 2abf0649f..9ba04ca0d 100644 --- a/contracts/multichain/LayerZeroProvider.sol +++ b/contracts/multichain/LayerZeroProvider.sol @@ -49,7 +49,7 @@ contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer { * TBD if this will change * @param from The address of the sender (i.e. Stargate address, not user's address). * @param guid A global unique identifier for tracking the packet. - * @param message Encoded message. Contains the params needed to record the deposit (account, token, sourceChainId) + * @param message Encoded message. Contains the params needed to record the deposit (account, token, multichainId) * @param executor The address of the Executor. * @param extraData Any extra data or options to trigger on receipt. */ @@ -60,15 +60,15 @@ contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer { address executor, bytes calldata extraData ) external payable { - (address account, address token, uint256 sourceChainId) = MultichainProviderUtils.decodeDeposit(message); + (address account, address token, uint256 multichainId) = MultichainProviderUtils.decodeDeposit(message); _transferToVault(token, address(multichainVault)); - multichainVaultHandler.recordDeposit(account, token, sourceChainId); + multichainVaultHandler.recordDeposit(account, token, multichainId); LayerZeroProviderEventUtils.emitComposedMessageReceived( eventEmitter, - sourceChainId, + multichainId, account, from, guid, diff --git a/contracts/multichain/LayerZeroProviderEventUtils.sol b/contracts/multichain/LayerZeroProviderEventUtils.sol index 405c19ec4..dbbea1f9c 100644 --- a/contracts/multichain/LayerZeroProviderEventUtils.sol +++ b/contracts/multichain/LayerZeroProviderEventUtils.sol @@ -17,7 +17,7 @@ library LayerZeroProviderEventUtils { function emitComposedMessageReceived( EventEmitter eventEmitter, - uint256 sourceChainId, + uint256 multichainId, address account, address from, bytes32 guid, @@ -33,7 +33,7 @@ library LayerZeroProviderEventUtils { eventData.addressItems.setItem(2, "executor", executor); eventData.uintItems.initItems(1); - eventData.uintItems.setItem(0, "sourceChainId", sourceChainId); + eventData.uintItems.setItem(0, "multichainId", multichainId); eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "guid", guid); @@ -42,12 +42,12 @@ library LayerZeroProviderEventUtils { eventData.bytesItems.setItem(0, "message", message); eventData.bytesItems.setItem(1, "extraData", extraData); - eventEmitter.emitEventLog2("MessageComposedReceived", bytes32(sourceChainId), Cast.toBytes32(account), eventData); + eventEmitter.emitEventLog2("MessageComposedReceived", bytes32(multichainId), Cast.toBytes32(account), eventData); } function emitWithdrawalReceipt( EventEmitter eventEmitter, - uint256 sourceChainId, + uint256 multichainId, address account, bytes32 guid, uint64 nonce, @@ -64,11 +64,11 @@ library LayerZeroProviderEventUtils { eventData.uintItems.setItem(2, "lzTokenFee", lzTokenFee); eventData.uintItems.setItem(3, "amountSentLD", amountSentLD); eventData.uintItems.setItem(4, "amountReceivedLD", amountReceivedLD); - eventData.uintItems.setItem(5, "sourceChainId", sourceChainId); + eventData.uintItems.setItem(5, "multichainId", multichainId); eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "guid", guid); - eventEmitter.emitEventLog2("WithdrawalReceipt", bytes32(sourceChainId), Cast.toBytes32(account), eventData); + eventEmitter.emitEventLog2("WithdrawalReceipt", bytes32(multichainId), Cast.toBytes32(account), eventData); } } diff --git a/contracts/multichain/MultichainEventUtils.sol b/contracts/multichain/MultichainEventUtils.sol index 231fb65ec..909448e6a 100644 --- a/contracts/multichain/MultichainEventUtils.sol +++ b/contracts/multichain/MultichainEventUtils.sol @@ -18,7 +18,7 @@ library MultichainEventUtils { address token, address account, uint256 amount, - uint256 sourceChainId + uint256 multichainId ) internal { EventUtils.EventLogData memory eventData; @@ -28,15 +28,15 @@ library MultichainEventUtils { eventData.uintItems.initItems(2); eventData.uintItems.setItem(0, "amount", amount); - eventData.uintItems.setItem(1, "sourceChainId", sourceChainId); + eventData.uintItems.setItem(1, "multichainId", multichainId); - eventEmitter.emitEventLog2("MultichainDeposit", bytes32(sourceChainId), Cast.toBytes32(account), eventData); + eventEmitter.emitEventLog2("MultichainDeposit", bytes32(multichainId), Cast.toBytes32(account), eventData); } function emitMultichainMessage( EventEmitter eventEmitter, address account, - uint256 sourceChainId + uint256 multichainId ) internal { EventUtils.EventLogData memory eventData; @@ -44,7 +44,7 @@ library MultichainEventUtils { eventData.addressItems.setItem(0, "account", account); eventData.uintItems.initItems(1); - eventData.uintItems.setItem(0, "sourceChainId", sourceChainId); + eventData.uintItems.setItem(0, "multichainId", multichainId); eventEmitter.emitEventLog1("MultichainMessage", Cast.toBytes32(account), eventData); } @@ -54,7 +54,7 @@ library MultichainEventUtils { address token, address account, uint256 amount, - uint256 sourceChainId + uint256 multichainId ) internal { EventUtils.EventLogData memory eventData; @@ -64,8 +64,8 @@ library MultichainEventUtils { eventData.uintItems.initItems(2); eventData.uintItems.setItem(0, "amount", amount); - eventData.uintItems.setItem(1, "sourceChainId", sourceChainId); + eventData.uintItems.setItem(1, "multichainId", multichainId); - eventEmitter.emitEventLog2("MultichainWithdrawal", bytes32(sourceChainId), Cast.toBytes32(account), eventData); + eventEmitter.emitEventLog2("MultichainWithdrawal", bytes32(multichainId), Cast.toBytes32(account), eventData); } } diff --git a/contracts/multichain/MultichainProviderSignature.sol b/contracts/multichain/MultichainProviderSignature.sol index e2e159503..1903c9262 100644 --- a/contracts/multichain/MultichainProviderSignature.sol +++ b/contracts/multichain/MultichainProviderSignature.sol @@ -14,24 +14,24 @@ contract MultichainProviderSignature is EIP712 { string private constant SIGNATURE_VERSION = "1"; // Define the EIP-712 struct type: - // Message(address token,uint256 amount,address account,uint256 sourceChainId,uint32 srcEid) + // Message(address token,uint256 amount,address account,uint256 multichainId,uint32 srcEid) bytes32 private constant _MESSAGE_TYPEHASH = - keccak256("Message(address token,uint256 amount,address account,uint256 sourceChainId,uint32 srcEid)"); + keccak256("Message(address token,uint256 amount,address account,uint256 multichainId,uint32 srcEid)"); constructor() EIP712(SIGNING_DOMAIN, SIGNATURE_VERSION) {} /** * Check the signature for a given message - * @param message The ABI encoded parameters (token, amount, account, sourceChainId, srcEid). + * @param message The ABI encoded parameters (token, amount, account, multichainId, srcEid). * @param signature The EIP-712 signature. */ function isSigner(bytes calldata message, bytes calldata signature) external view returns (bool) { // Decode the message - (address token, uint256 amount, address account, uint256 sourceChainId, uint32 srcEid) = MultichainProviderUtils + (address token, uint256 amount, address account, uint256 multichainId, uint32 srcEid) = MultichainProviderUtils .decodeWithdrawal(message); // Build the struct hash - bytes32 structHash = keccak256(abi.encode(_MESSAGE_TYPEHASH, token, amount, account, sourceChainId, srcEid)); + bytes32 structHash = keccak256(abi.encode(_MESSAGE_TYPEHASH, token, amount, account, multichainId, srcEid)); // Get the typed data hash for EIP-712 bytes32 hash = _hashTypedDataV4(structHash); diff --git a/contracts/multichain/MultichainProviderUtils.sol b/contracts/multichain/MultichainProviderUtils.sol index da54dcb64..98d95a9eb 100644 --- a/contracts/multichain/MultichainProviderUtils.sol +++ b/contracts/multichain/MultichainProviderUtils.sol @@ -8,13 +8,13 @@ pragma solidity ^0.8.0; library MultichainProviderUtils { function decodeDeposit( bytes calldata message - ) internal pure returns (address account, address token, uint256 sourceChainId) { + ) internal pure returns (address account, address token, uint256 multichainId) { return abi.decode(message, (address, address, uint256)); } function decodeWithdrawal( bytes calldata message - ) internal pure returns (address token, uint256 amount, address account, uint256 sourceChainId, uint32 srcEid) { + ) internal pure returns (address token, uint256 amount, address account, uint256 multichainId, uint32 srcEid) { return abi.decode(message, (address, uint256, address, uint256, uint32)); } diff --git a/contracts/multichain/MultichainVaultHandler.sol b/contracts/multichain/MultichainVaultHandler.sol index cf4ce016d..290790112 100644 --- a/contracts/multichain/MultichainVaultHandler.sol +++ b/contracts/multichain/MultichainVaultHandler.sol @@ -47,12 +47,12 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu * Records a deposit from another chain. IMultichainProvider has CONTROLLER role * @param account user address on the source chain * @param token address of the token being deposited - * @param sourceChainId chain id of the source chain + * @param multichainId chain id of the destination chain */ function recordDeposit( address account, address token, - uint256 sourceChainId + uint256 multichainId ) external onlyController { // token should have been transferred to multichainVault by IMultichainProvider uint256 amount = multichainVault.recordTransferIn(token); @@ -60,9 +60,9 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu revert Errors.EmptyMultichainDepositAmount(); } - dataStore.incrementUint(Keys.sourceChainBalanceKey(sourceChainId, account, token), amount); + dataStore.incrementUint(Keys.multichainBalanceKey(multichainId, account, token), amount); - MultichainEventUtils.emitMultichainDeposit(eventEmitter, token, account, amount, sourceChainId); + MultichainEventUtils.emitMultichainDeposit(eventEmitter, token, account, amount, multichainId); } /** @@ -73,7 +73,7 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu * @param amount the amount of tokens to transfer */ function pluginTransfer(uint256 chainId, address token, address account, address receiver, uint256 amount) external onlyRouterPlugin { // TODO: confirm access control - dataStore.decrementUint(Keys.sourceChainBalanceKey(chainId, account, token), amount); + dataStore.decrementUint(Keys.multichainBalanceKey(chainId, account, token), amount); IERC20(token).safeTransferFrom(address(multichainVault), receiver, amount); } @@ -81,18 +81,18 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu * Executes the multicall for the given args * The multicall arguments contains the function calls to be executed (e.g. createDeposit, createOrder, createWithdrawal, etc) * @param account user address on the source chain - * @param sourceChainId chain id of the source chain + * @param multichainId chain id of the destination chain * @param multicallArgs array of bytes containing the multicall arguments */ function executeMulticall( address account, - uint256 sourceChainId, + uint256 multichainId, bytes[] calldata multicallArgs ) external onlyController { // execute multicall exchangeRouter.multicall(multicallArgs); - MultichainEventUtils.emitMultichainMessage(eventEmitter, account, sourceChainId); + MultichainEventUtils.emitMultichainMessage(eventEmitter, account, multichainId); } /** @@ -100,19 +100,19 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu * @param account user address on the source chain * @param token address of the token being withdrawn * @param amount amount of token being withdrawn - * @param sourceChainId chain id of the source chain + * @param multichainId chain id of the destination chain */ function recordWithdrawal( address account, address token, uint256 amount, - uint256 sourceChainId + uint256 multichainId ) external onlyController { if (amount == 0) { revert Errors.EmptyMultichainWithdrawalAmount(); } - bytes32 balanceKey = Keys.sourceChainBalanceKey(sourceChainId, account, token); + bytes32 balanceKey = Keys.multichainBalanceKey(multichainId, account, token); uint256 balance = dataStore.getUint(balanceKey); if (balance < amount) { @@ -125,6 +125,6 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu // transfer tokens to IMultichainProvider multichainVault.transferOut(token, msg.sender, amount); - MultichainEventUtils.emitMultichainWithdrawal(eventEmitter, token, account, amount, sourceChainId); + MultichainEventUtils.emitMultichainWithdrawal(eventEmitter, token, account, amount, multichainId); } } diff --git a/contracts/position/Position.sol b/contracts/position/Position.sol index c2066a705..713f86b9a 100644 --- a/contracts/position/Position.sol +++ b/contracts/position/Position.sol @@ -197,7 +197,7 @@ library Position { // @param market the position's market // @param collateralToken the position's collateralToken // @param isLong whether the position is long or short - // @param chainId the source chain id + // @param chainId the destination chain id // @return the position key function getPositionKey(address _account, address _market, address _collateralToken, bool _isLong/*, uint256 _chainId*/) internal pure returns (bytes32) { // TODO: handle the chainId cases bellow diff --git a/test/multichain/LayerZeroProvider.ts b/test/multichain/LayerZeroProvider.ts index fe36d6d8f..dfd5fd8c8 100644 --- a/test/multichain/LayerZeroProvider.ts +++ b/test/multichain/LayerZeroProvider.ts @@ -19,7 +19,7 @@ describe("LayerZeroProvider", () => { }); it("lzCompose", async () => { - const sourceChainId = 1; + const multichainId = 1; const amountUsdc = expandDecimals(50, 6); // mint usdc to users and approve StargatePool to spend it @@ -27,14 +27,14 @@ describe("LayerZeroProvider", () => { await usdc.connect(user0).approve(mockStargatePool.address, amountUsdc); // encoded message must match the decoded message in MultichainProviderUtils.decodeDeposit(message) - const message0 = encodeData(["address", "address", "uint256"], [user0.address, usdc.address, sourceChainId]); + const message0 = encodeData(["address", "address", "uint256"], [user0.address, usdc.address, multichainId]); // StargatePool would deliver usdc to LayerZeroProvider contract and call LayerZeroProvider.lzCompose await mockStargatePool.connect(user0).sendToken(usdc.address, layerZeroProvider.address, amountUsdc, message0); const lzUsdcBalance = await usdc.balanceOf(layerZeroProvider.address); const multichainVaultBalance = await usdc.balanceOf(multichainVault.address); - const userBalance = await dataStore.getUint(keys.sourceChainBalanceKey(sourceChainId, user0.address, usdc.address)); + const userBalance = await dataStore.getUint(keys.multichainBalanceKey(multichainId, user0.address, usdc.address)); // usdc has been transterred from LayerZeroProvider to MultichainVault and recorded under the user's chainId + account expect(lzUsdcBalance).eq(0); diff --git a/utils/keys.ts b/utils/keys.ts index 343f81c19..4daf4bb32 100644 --- a/utils/keys.ts +++ b/utils/keys.ts @@ -257,7 +257,7 @@ export const BUYBACK_GMX_FACTOR = hashString("BUYBACK_GMX_FACTOR"); export const BUYBACK_MAX_PRICE_IMPACT_FACTOR = hashString("BUYBACK_MAX_PRICE_IMPACT_FACTOR"); export const BUYBACK_MAX_PRICE_AGE = hashString("BUYBACK_MAX_PRICE_AGE"); export const WITHDRAWABLE_BUYBACK_TOKEN_AMOUNT = hashString("WITHDRAWABLE_BUYBACK_TOKEN_AMOUNT"); -export const SOURCE_CHAIN_BALANCE = hashString("SOURCE_CHAIN_BALANCE"); +export const MULTICHAIN_BALANCE = hashString("MULTICHAIN_BALANCE"); export const VALID_FROM_TIME = hashString("VALID_FROM_TIME"); @@ -815,6 +815,6 @@ export function withdrawableBuybackTokenAmountKey(buybackToken: string) { return hashData(["bytes32", "address"], [WITHDRAWABLE_BUYBACK_TOKEN_AMOUNT, buybackToken]); } -export function sourceChainBalanceKey(sourceChainId: number, account: string, token: string) { - return hashData(["bytes32", "uint256", "address", "address"], [SOURCE_CHAIN_BALANCE, sourceChainId, account, token]); +export function multichainBalanceKey(multichainId: number, account: string, token: string) { + return hashData(["bytes32", "uint256", "address", "address"], [MULTICHAIN_BALANCE, multichainId, account, token]); } From 774b598c04b3516361ff9bbe67ecd264b0aace85 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 3 Feb 2025 20:42:52 +0200 Subject: [PATCH 011/205] Add multichain increase and decrease utils methods --- contracts/multichain/MultichainUtils.sol | 13 ++++++++++++- contracts/multichain/MultichainVaultHandler.sol | 9 ++++----- test/multichain/LayerZeroProvider.ts | 2 -- utils/multichain.ts | 0 4 files changed, 16 insertions(+), 8 deletions(-) delete mode 100644 utils/multichain.ts diff --git a/contracts/multichain/MultichainUtils.sol b/contracts/multichain/MultichainUtils.sol index 2c848d8cf..84533722e 100644 --- a/contracts/multichain/MultichainUtils.sol +++ b/contracts/multichain/MultichainUtils.sol @@ -2,7 +2,18 @@ pragma solidity ^0.8.0; +import "../data/DataStore.sol"; +import "../data/Keys.sol"; + /** * @title MultichainUtils */ -library MultichainUtils {} +library MultichainUtils { + function increaseBalance(DataStore dataStore, uint256 chainId, address account, address token, uint256 amount) internal { + dataStore.decrementUint(Keys.multichainBalanceKey(chainId, account, token), amount); + } + + function decreaseBalance(DataStore dataStore, uint256 chainId, address account, address token, uint256 amount) internal { + dataStore.decrementUint(Keys.multichainBalanceKey(chainId, account, token), amount); + } +} diff --git a/contracts/multichain/MultichainVaultHandler.sol b/contracts/multichain/MultichainVaultHandler.sol index 290790112..893912609 100644 --- a/contracts/multichain/MultichainVaultHandler.sol +++ b/contracts/multichain/MultichainVaultHandler.sol @@ -66,15 +66,14 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu } /** - * @dev transfer the specified amount of tokens from MultichainVault to receiver and decrement multichain account balance - * @param account the account for which the tokens are transferred and multichain balance decremented + * @dev transfer the specified amount of tokens from account to receiver * @param token the token to transfer + * @param account the account to transfer from * @param receiver the account to transfer to * @param amount the amount of tokens to transfer */ - function pluginTransfer(uint256 chainId, address token, address account, address receiver, uint256 amount) external onlyRouterPlugin { // TODO: confirm access control - dataStore.decrementUint(Keys.multichainBalanceKey(chainId, account, token), amount); - IERC20(token).safeTransferFrom(address(multichainVault), receiver, amount); + function pluginTransfer(address token, address account, address receiver, uint256 amount) external onlyRouterPlugin { // TODO: confirm access control + IERC20(token).safeTransferFrom(account, receiver, amount); } /** diff --git a/test/multichain/LayerZeroProvider.ts b/test/multichain/LayerZeroProvider.ts index dfd5fd8c8..14bb22c53 100644 --- a/test/multichain/LayerZeroProvider.ts +++ b/test/multichain/LayerZeroProvider.ts @@ -1,8 +1,6 @@ import { expect } from "chai"; import * as keys from "../../utils/keys"; -import * as multichain from "../../utils/multichain"; - import { deployFixture } from "../../utils/fixture"; import { expandDecimals } from "../../utils/math"; import { encodeData } from "../../utils/hash"; diff --git a/utils/multichain.ts b/utils/multichain.ts deleted file mode 100644 index e69de29bb..000000000 From 9111ef42d26dd71c6e2be0ae1741f73bb0a2bf93 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 3 Feb 2025 22:30:30 +0200 Subject: [PATCH 012/205] Add chainId to Withdrawal, Shift, GlvDeposit, GlvWithdrawal --- contracts/exchange/DepositHandler.sol | 3 ++- contracts/glv/glvDeposit/GlvDeposit.sol | 9 +++++++++ contracts/glv/glvDeposit/GlvDepositStoreUtils.sol | 15 +++++++++++++++ contracts/glv/glvDeposit/GlvDepositUtils.sol | 7 +++++-- contracts/glv/glvShift/GlvShiftUtils.sol | 3 ++- contracts/glv/glvWithdrawal/GlvWithdrawal.sol | 9 +++++++++ .../glv/glvWithdrawal/GlvWithdrawalStoreUtils.sol | 14 ++++++++++++++ .../glv/glvWithdrawal/GlvWithdrawalUtils.sol | 7 +++++-- contracts/shift/Shift.sol | 9 +++++++++ contracts/shift/ShiftStoreUtils.sol | 15 +++++++++++++++ contracts/shift/ShiftUtils.sol | 10 +++++++--- contracts/withdrawal/Withdrawal.sol | 9 +++++++++ contracts/withdrawal/WithdrawalStoreUtils.sol | 15 +++++++++++++++ contracts/withdrawal/WithdrawalUtils.sol | 4 +++- test/router/ExchangeRouter.ts | 2 ++ utils/glv/glvDeposit.ts | 2 ++ utils/glv/glvWithdrawal.ts | 3 +++ utils/shift.ts | 2 ++ utils/withdrawal.ts | 4 ++++ 19 files changed, 132 insertions(+), 10 deletions(-) diff --git a/contracts/exchange/DepositHandler.sol b/contracts/exchange/DepositHandler.sol index 65a4ca619..cb0b8ac08 100644 --- a/contracts/exchange/DepositHandler.sol +++ b/contracts/exchange/DepositHandler.sol @@ -152,7 +152,8 @@ contract DepositHandler is IDepositHandler, BaseHandler { keeper, startingGas, ISwapPricingUtils.SwapPricingType.Deposit, - true // includeVirtualInventoryImpact + true, // includeVirtualInventoryImpact + deposit.chainId() ); ExecuteDepositUtils.executeDeposit(params, deposit); diff --git a/contracts/glv/glvDeposit/GlvDeposit.sol b/contracts/glv/glvDeposit/GlvDeposit.sol index 6d43c90a2..2615657a6 100644 --- a/contracts/glv/glvDeposit/GlvDeposit.sol +++ b/contracts/glv/glvDeposit/GlvDeposit.sol @@ -52,6 +52,7 @@ library GlvDeposit { uint256 updatedAtTime; uint256 executionFee; uint256 callbackGasLimit; + uint256 chainId; } // @param shouldUnwrapNativeToken whether to unwrap the native token when @@ -198,6 +199,14 @@ library GlvDeposit { props.numbers.callbackGasLimit = value; } + function chainId(Props memory props) internal pure returns (uint256) { + return props.numbers.chainId; + } + + function setChainId(Props memory props, uint256 value) internal pure { + props.numbers.chainId = value; + } + function shouldUnwrapNativeToken(Props memory props) internal pure returns (bool) { return props.flags.shouldUnwrapNativeToken; } diff --git a/contracts/glv/glvDeposit/GlvDepositStoreUtils.sol b/contracts/glv/glvDeposit/GlvDepositStoreUtils.sol index 44b308e58..8fe6c1ead 100644 --- a/contracts/glv/glvDeposit/GlvDepositStoreUtils.sol +++ b/contracts/glv/glvDeposit/GlvDepositStoreUtils.sol @@ -38,6 +38,8 @@ library GlvDepositStoreUtils { bytes32 public constant DATA_LIST = keccak256(abi.encode("DATA_LIST")); + bytes32 public constant CHAIN_ID = keccak256(abi.encode("CHAIN_ID")); + function get(DataStore dataStore, bytes32 key) external view returns (GlvDeposit.Props memory) { GlvDeposit.Props memory glvDeposit; if (!dataStore.containsBytes32(Keys.GLV_DEPOSIT_LIST, key)) { @@ -112,6 +114,10 @@ library GlvDepositStoreUtils { keccak256(abi.encode(key, CALLBACK_GAS_LIMIT)) )); + glvDeposit.setChainId(dataStore.getUint( + keccak256(abi.encode(key, CHAIN_ID)) + )); + glvDeposit.setShouldUnwrapNativeToken(dataStore.getBool( keccak256(abi.encode(key, SHOULD_UNWRAP_NATIVE_TOKEN)) )); @@ -223,6 +229,11 @@ library GlvDepositStoreUtils { glvDeposit.callbackGasLimit() ); + dataStore.setUint( + keccak256(abi.encode(key, CHAIN_ID)), + glvDeposit.chainId() + ); + dataStore.setBool( keccak256(abi.encode(key, SHOULD_UNWRAP_NATIVE_TOKEN)), glvDeposit.shouldUnwrapNativeToken() @@ -322,6 +333,10 @@ library GlvDepositStoreUtils { keccak256(abi.encode(key, CALLBACK_GAS_LIMIT)) ); + dataStore.removeUint( + keccak256(abi.encode(key, CHAIN_ID)) + ); + dataStore.removeBool( keccak256(abi.encode(key, SHOULD_UNWRAP_NATIVE_TOKEN)) ); diff --git a/contracts/glv/glvDeposit/GlvDepositUtils.sol b/contracts/glv/glvDeposit/GlvDepositUtils.sol index eda07cfec..806ff94bf 100644 --- a/contracts/glv/glvDeposit/GlvDepositUtils.sol +++ b/contracts/glv/glvDeposit/GlvDepositUtils.sol @@ -32,6 +32,7 @@ library GlvDepositUtils { uint256 minGlvTokens; uint256 executionFee; uint256 callbackGasLimit; + uint256 chainId; bool shouldUnwrapNativeToken; bool isMarketTokenDeposit; bytes32[] dataList; @@ -177,7 +178,8 @@ library GlvDepositUtils { minGlvTokens: params.minGlvTokens, updatedAtTime: Chain.currentTimestamp(), executionFee: params.executionFee, - callbackGasLimit: params.callbackGasLimit + callbackGasLimit: params.callbackGasLimit, + chainId: params.chainId }), GlvDeposit.Flags({ shouldUnwrapNativeToken: params.shouldUnwrapNativeToken, @@ -374,7 +376,8 @@ library GlvDepositUtils { params.keeper, params.startingGas, ISwapPricingUtils.SwapPricingType.Deposit, - true // includeVirtualInventoryImpact + true, // includeVirtualInventoryImpact + glvDeposit.chainId() ); uint256 receivedMarketTokens = ExecuteDepositUtils.executeDeposit(executeDepositParams, deposit); diff --git a/contracts/glv/glvShift/GlvShiftUtils.sol b/contracts/glv/glvShift/GlvShiftUtils.sol index f042f378a..5e16d7214 100644 --- a/contracts/glv/glvShift/GlvShiftUtils.sol +++ b/contracts/glv/glvShift/GlvShiftUtils.sol @@ -138,7 +138,8 @@ library GlvShiftUtils { marketTokenAmount: glvShift.marketTokenAmount(), updatedAtTime: glvShift.updatedAtTime(), executionFee: 0, - callbackGasLimit: 0 + callbackGasLimit: 0, + chainId: 0 // TODO: should glvShift have the chainId as well? }), new bytes32[](0) ); diff --git a/contracts/glv/glvWithdrawal/GlvWithdrawal.sol b/contracts/glv/glvWithdrawal/GlvWithdrawal.sol index cd5e702da..a45052a76 100644 --- a/contracts/glv/glvWithdrawal/GlvWithdrawal.sol +++ b/contracts/glv/glvWithdrawal/GlvWithdrawal.sol @@ -49,6 +49,7 @@ library GlvWithdrawal { uint256 updatedAtTime; uint256 executionFee; uint256 callbackGasLimit; + uint256 chainId; } // @param shouldUnwrapNativeToken whether to unwrap the native token when @@ -168,6 +169,14 @@ library GlvWithdrawal { props.numbers.callbackGasLimit = value; } + function chainId(Props memory props) internal pure returns (uint256) { + return props.numbers.chainId; + } + + function setChainId(Props memory props, uint256 value) internal pure { + props.numbers.chainId = value; + } + function shouldUnwrapNativeToken(Props memory props) internal pure returns (bool) { return props.flags.shouldUnwrapNativeToken; } diff --git a/contracts/glv/glvWithdrawal/GlvWithdrawalStoreUtils.sol b/contracts/glv/glvWithdrawal/GlvWithdrawalStoreUtils.sol index d09bdcbdd..b569668fb 100644 --- a/contracts/glv/glvWithdrawal/GlvWithdrawalStoreUtils.sol +++ b/contracts/glv/glvWithdrawal/GlvWithdrawalStoreUtils.sol @@ -29,6 +29,7 @@ library GlvWithdrawalStoreUtils { bytes32 public constant UPDATED_AT_TIME = keccak256(abi.encode("UPDATED_AT_TIME")); bytes32 public constant EXECUTION_FEE = keccak256(abi.encode("EXECUTION_FEE")); bytes32 public constant CALLBACK_GAS_LIMIT = keccak256(abi.encode("CALLBACK_GAS_LIMIT")); + bytes32 public constant CHAIN_ID = keccak256(abi.encode("CHAIN_ID")); bytes32 public constant SHOULD_UNWRAP_NATIVE_TOKEN = keccak256(abi.encode("SHOULD_UNWRAP_NATIVE_TOKEN")); @@ -96,6 +97,10 @@ library GlvWithdrawalStoreUtils { keccak256(abi.encode(key, CALLBACK_GAS_LIMIT)) )); + withdrawal.setChainId(dataStore.getUint( + keccak256(abi.encode(key, CHAIN_ID)) + )); + withdrawal.setShouldUnwrapNativeToken(dataStore.getBool( keccak256(abi.encode(key, SHOULD_UNWRAP_NATIVE_TOKEN)) )); @@ -188,6 +193,11 @@ library GlvWithdrawalStoreUtils { withdrawal.callbackGasLimit() ); + dataStore.setUint( + keccak256(abi.encode(key, CHAIN_ID)), + withdrawal.chainId() + ); + dataStore.setBool( keccak256(abi.encode(key, SHOULD_UNWRAP_NATIVE_TOKEN)), withdrawal.shouldUnwrapNativeToken() @@ -270,6 +280,10 @@ library GlvWithdrawalStoreUtils { keccak256(abi.encode(key, CALLBACK_GAS_LIMIT)) ); + dataStore.removeUint( + keccak256(abi.encode(key, CHAIN_ID)) + ); + dataStore.removeBool( keccak256(abi.encode(key, SHOULD_UNWRAP_NATIVE_TOKEN)) ); diff --git a/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol b/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol index 2e1b20cca..48719a2d8 100644 --- a/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol +++ b/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol @@ -31,6 +31,7 @@ library GlvWithdrawalUtils { bool shouldUnwrapNativeToken; uint256 executionFee; uint256 callbackGasLimit; + uint256 chainId; bytes32[] dataList; } @@ -109,7 +110,8 @@ library GlvWithdrawalUtils { minShortTokenAmount: params.minShortTokenAmount, updatedAtTime: Chain.currentTimestamp(), executionFee: params.executionFee, - callbackGasLimit: params.callbackGasLimit + callbackGasLimit: params.callbackGasLimit, + chainId: params.chainId }), GlvWithdrawal.Flags({shouldUnwrapNativeToken: params.shouldUnwrapNativeToken}), params.dataList @@ -228,7 +230,8 @@ library GlvWithdrawalUtils { marketTokenAmount: marketTokenAmount, updatedAtTime: glvWithdrawal.updatedAtTime(), executionFee: 0, - callbackGasLimit: 0 + callbackGasLimit: 0, + chainId: glvWithdrawal.chainId() }), Withdrawal.Flags({shouldUnwrapNativeToken: glvWithdrawal.shouldUnwrapNativeToken()}), glvWithdrawal.dataList() diff --git a/contracts/shift/Shift.sol b/contracts/shift/Shift.sol index 33215359b..d30b5e07d 100644 --- a/contracts/shift/Shift.sol +++ b/contracts/shift/Shift.sol @@ -24,6 +24,7 @@ library Shift { uint256 updatedAtTime; uint256 executionFee; uint256 callbackGasLimit; + uint256 chainId; } function account(Props memory props) internal pure returns (address) { @@ -114,6 +115,14 @@ library Shift { props.numbers.callbackGasLimit = value; } + function chainId(Props memory props) internal pure returns (uint256) { + return props.numbers.chainId; + } + + function setChainId(Props memory props, uint256 value) internal pure { + props.numbers.chainId = value; + } + function dataList(Props memory props) internal pure returns (bytes32[] memory) { return props._dataList; } diff --git a/contracts/shift/ShiftStoreUtils.sol b/contracts/shift/ShiftStoreUtils.sol index 4a8c43655..9e0e28257 100644 --- a/contracts/shift/ShiftStoreUtils.sol +++ b/contracts/shift/ShiftStoreUtils.sol @@ -25,6 +25,8 @@ library ShiftStoreUtils { bytes32 public constant DATA_LIST = keccak256(abi.encode("DATA_LIST")); + bytes32 public constant CHAIN_ID = keccak256(abi.encode("CHAIN_ID")); + function get(DataStore dataStore, bytes32 key) external view returns (Shift.Props memory) { Shift.Props memory shift; if (!dataStore.containsBytes32(Keys.SHIFT_LIST, key)) { @@ -75,6 +77,10 @@ library ShiftStoreUtils { keccak256(abi.encode(key, CALLBACK_GAS_LIMIT)) )); + shift.setChainId(dataStore.getUint( + keccak256(abi.encode(key, CHAIN_ID)) + )); + shift.setDataList(dataStore.getBytes32Array( keccak256(abi.encode(key, DATA_LIST)) )); @@ -148,6 +154,11 @@ library ShiftStoreUtils { shift.callbackGasLimit() ); + dataStore.setUint( + keccak256(abi.encode(key, CHAIN_ID)), + shift.chainId() + ); + dataStore.setBytes32Array( keccak256(abi.encode(key, DATA_LIST)), shift.dataList() @@ -213,6 +224,10 @@ library ShiftStoreUtils { keccak256(abi.encode(key, CALLBACK_GAS_LIMIT)) ); + dataStore.removeUint( + keccak256(abi.encode(key, CHAIN_ID)) + ); + dataStore.removeBytes32Array( keccak256(abi.encode(key, DATA_LIST)) ); diff --git a/contracts/shift/ShiftUtils.sol b/contracts/shift/ShiftUtils.sol index 1e91a8a35..c53a42fcd 100644 --- a/contracts/shift/ShiftUtils.sol +++ b/contracts/shift/ShiftUtils.sol @@ -40,6 +40,7 @@ library ShiftUtils { uint256 minMarketTokens; uint256 executionFee; uint256 callbackGasLimit; + uint256 chainId; bytes32[] dataList; } @@ -127,7 +128,8 @@ library ShiftUtils { params.minMarketTokens, Chain.currentTimestamp(), params.executionFee, - params.callbackGasLimit + params.callbackGasLimit, + params.chainId ), params.dataList ); @@ -198,7 +200,8 @@ library ShiftUtils { 0, // minShortTokenAmount shift.updatedAtTime(), 0, // executionFee - 0 // callbackGasLimit + 0, // callbackGasLimit + shift.chainId() ), Withdrawal.Flags( false @@ -287,7 +290,8 @@ library ShiftUtils { params.keeper, params.startingGas, ISwapPricingUtils.SwapPricingType.Shift, - false // includeVirtualInventoryImpact + false, // includeVirtualInventoryImpact + shift.chainId() ); cache.receivedMarketTokens = ExecuteDepositUtils.executeDeposit( diff --git a/contracts/withdrawal/Withdrawal.sol b/contracts/withdrawal/Withdrawal.sol index 7a2db038e..642546fc8 100644 --- a/contracts/withdrawal/Withdrawal.sol +++ b/contracts/withdrawal/Withdrawal.sol @@ -53,6 +53,7 @@ library Withdrawal { uint256 updatedAtTime; uint256 executionFee; uint256 callbackGasLimit; + uint256 chainId; } // @param shouldUnwrapNativeToken whether to unwrap the native token when @@ -164,6 +165,14 @@ library Withdrawal { props.numbers.callbackGasLimit = value; } + function chainId(Props memory props) internal pure returns (uint256) { + return props.numbers.chainId; + } + + function setChainId(Props memory props, uint256 value) internal pure { + props.numbers.chainId = value; + } + function shouldUnwrapNativeToken(Props memory props) internal pure returns (bool) { return props.flags.shouldUnwrapNativeToken; } diff --git a/contracts/withdrawal/WithdrawalStoreUtils.sol b/contracts/withdrawal/WithdrawalStoreUtils.sol index 18c511752..a2e718400 100644 --- a/contracts/withdrawal/WithdrawalStoreUtils.sol +++ b/contracts/withdrawal/WithdrawalStoreUtils.sol @@ -33,6 +33,8 @@ library WithdrawalStoreUtils { bytes32 public constant DATA_LIST = keccak256(abi.encode("DATA_LIST")); + bytes32 public constant CHAIN_ID = keccak256(abi.encode("CHAIN_ID")); + function get(DataStore dataStore, bytes32 key) external view returns (Withdrawal.Props memory) { Withdrawal.Props memory withdrawal; if (!dataStore.containsBytes32(Keys.WITHDRAWAL_LIST, key)) { @@ -91,6 +93,10 @@ library WithdrawalStoreUtils { keccak256(abi.encode(key, CALLBACK_GAS_LIMIT)) )); + withdrawal.setChainId(dataStore.getUint( + keccak256(abi.encode(key, CHAIN_ID)) + )); + withdrawal.setShouldUnwrapNativeToken(dataStore.getBool( keccak256(abi.encode(key, SHOULD_UNWRAP_NATIVE_TOKEN)) )); @@ -178,6 +184,11 @@ library WithdrawalStoreUtils { withdrawal.callbackGasLimit() ); + dataStore.setUint( + keccak256(abi.encode(key, CHAIN_ID)), + withdrawal.chainId() + ); + dataStore.setBool( keccak256(abi.encode(key, SHOULD_UNWRAP_NATIVE_TOKEN)), withdrawal.shouldUnwrapNativeToken() @@ -252,6 +263,10 @@ library WithdrawalStoreUtils { keccak256(abi.encode(key, EXECUTION_FEE)) ); + dataStore.removeUint( + keccak256(abi.encode(key, CHAIN_ID)) + ); + dataStore.removeUint( keccak256(abi.encode(key, CALLBACK_GAS_LIMIT)) ); diff --git a/contracts/withdrawal/WithdrawalUtils.sol b/contracts/withdrawal/WithdrawalUtils.sol index c4924dba1..9cd3160bf 100644 --- a/contracts/withdrawal/WithdrawalUtils.sol +++ b/contracts/withdrawal/WithdrawalUtils.sol @@ -58,6 +58,7 @@ library WithdrawalUtils { bool shouldUnwrapNativeToken; uint256 executionFee; uint256 callbackGasLimit; + uint256 chainId; bytes32[] dataList; } @@ -117,7 +118,8 @@ library WithdrawalUtils { params.minShortTokenAmount, Chain.currentTimestamp(), // updatedAtTime params.executionFee, - params.callbackGasLimit + params.callbackGasLimit, + params.chainId ), Withdrawal.Flags( params.shouldUnwrapNativeToken diff --git a/test/router/ExchangeRouter.ts b/test/router/ExchangeRouter.ts index a539c6d99..dee7b6a2d 100644 --- a/test/router/ExchangeRouter.ts +++ b/test/router/ExchangeRouter.ts @@ -216,6 +216,7 @@ describe("ExchangeRouter", () => { shouldUnwrapNativeToken: true, executionFee, callbackGasLimit: "200000", + chainId: 1, dataList, }, ]), @@ -238,6 +239,7 @@ describe("ExchangeRouter", () => { expect(withdrawal.numbers.minShortTokenAmount).eq(900); expect(withdrawal.numbers.executionFee).eq(expandDecimals(1, 18)); expect(withdrawal.numbers.callbackGasLimit).eq("200000"); + expect(withdrawal.numbers.chainId).eq(1); expect(withdrawal.flags.shouldUnwrapNativeToken).eq(true); expect(withdrawal._dataList).deep.eq(dataList); diff --git a/utils/glv/glvDeposit.ts b/utils/glv/glvDeposit.ts index d3fdb626e..f4d0723ea 100644 --- a/utils/glv/glvDeposit.ts +++ b/utils/glv/glvDeposit.ts @@ -48,6 +48,7 @@ export async function createGlvDeposit(fixture, overrides: any = {}) { const shouldUnwrapNativeToken = overrides.shouldUnwrapNativeToken || false; const executionFee = bigNumberify(overrides.executionFee ?? "1000000000000000"); const callbackGasLimit = bigNumberify(overrides.callbackGasLimit ?? 0); + const chainId = bigNumberify(overrides.chainId ?? 0); const marketTokenAmount = bigNumberify(overrides.marketTokenAmount ?? 0); const longTokenAmount = bigNumberify(overrides.longTokenAmount ?? 0); const shortTokenAmount = bigNumberify(overrides.shortTokenAmount ?? 0); @@ -95,6 +96,7 @@ export async function createGlvDeposit(fixture, overrides: any = {}) { shouldUnwrapNativeToken, executionFee, callbackGasLimit, + chainId, isMarketTokenDeposit, gasUsageLabel, dataList, diff --git a/utils/glv/glvWithdrawal.ts b/utils/glv/glvWithdrawal.ts index 93398592e..0870f2f25 100644 --- a/utils/glv/glvWithdrawal.ts +++ b/utils/glv/glvWithdrawal.ts @@ -48,6 +48,7 @@ export async function createGlvWithdrawal(fixture, overrides: any = {}) { const shouldUnwrapNativeToken = overrides.shouldUnwrapNativeToken || false; const executionFee = bigNumberify(overrides.executionFee ?? "1000000000000000"); const callbackGasLimit = bigNumberify(overrides.callbackGasLimit ?? 0); + const chainId = bigNumberify(overrides.chainId ?? 0); const useGlvHandler = Boolean(overrides.useGlvHandler) || false; const dataList = overrides.dataList || []; @@ -79,6 +80,7 @@ export async function createGlvWithdrawal(fixture, overrides: any = {}) { shouldUnwrapNativeToken, executionFee, callbackGasLimit, + chainId, dataList, }; @@ -190,6 +192,7 @@ export function expectEmptyGlvWithdrawal(glvWithdrawal: any) { expect(glvWithdrawal.numbers.updatedAtTime).eq(0); expect(glvWithdrawal.numbers.executionFee).eq(0); expect(glvWithdrawal.numbers.callbackGasLimit).eq(0); + expect(glvWithdrawal.numbers.chainId).eq(0); expect(glvWithdrawal.flags.shouldUnwrapNativeToken).eq(false); } diff --git a/utils/shift.ts b/utils/shift.ts index 2ee5390fc..22e9e9211 100644 --- a/utils/shift.ts +++ b/utils/shift.ts @@ -39,6 +39,7 @@ export async function createShift(fixture, overrides: any = {}) { const minMarketTokens = overrides.minMarketTokens || bigNumberify(0); const executionFee = overrides.executionFee || "1000000000000000"; const callbackGasLimit = overrides.callbackGasLimit || bigNumberify(0); + const chainId = overrides.chainId || bigNumberify(0); const dataList = overrides.dataList || []; await wnt.mint(shiftVault.address, executionFee); @@ -55,6 +56,7 @@ export async function createShift(fixture, overrides: any = {}) { minMarketTokens, executionFee, callbackGasLimit, + chainId, dataList, }; diff --git a/utils/withdrawal.ts b/utils/withdrawal.ts index 72619c6ab..514a60de0 100644 --- a/utils/withdrawal.ts +++ b/utils/withdrawal.ts @@ -41,6 +41,7 @@ export async function createWithdrawal(fixture, overrides: any = {}) { const shouldUnwrapNativeToken = overrides.shouldUnwrapNativeToken || false; const executionFee = overrides.executionFee || "1000000000000000"; const callbackGasLimit = overrides.callbackGasLimit || bigNumberify(0); + const chainId = overrides.chainId || bigNumberify(0); const dataList = overrides.dataList || []; await wnt.mint(withdrawalVault.address, executionFee); @@ -61,6 +62,7 @@ export async function createWithdrawal(fixture, overrides: any = {}) { shouldUnwrapNativeToken, executionFee, callbackGasLimit, + chainId, dataList, }; @@ -139,6 +141,7 @@ export async function executeAtomicWithdrawal(fixture, overrides: any = {}) { const shouldUnwrapNativeToken = overrides.shouldUnwrapNativeToken || false; const executionFee = overrides.executionFee || "1000000000000000"; const callbackGasLimit = overrides.callbackGasLimit || bigNumberify(0); + const chainId = overrides.chainId || bigNumberify(0); const dataList = overrides.dataList || []; await wnt.mint(withdrawalVault.address, executionFee); @@ -159,6 +162,7 @@ export async function executeAtomicWithdrawal(fixture, overrides: any = {}) { shouldUnwrapNativeToken, executionFee, callbackGasLimit, + chainId, dataList, }; From 6c490c19e4290aadabaa9614c8d01f5353948d88 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 4 Feb 2025 21:06:32 +0200 Subject: [PATCH 013/205] Fix increaseBalance: incrementUint instead of decrementUint --- contracts/multichain/MultichainUtils.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/multichain/MultichainUtils.sol b/contracts/multichain/MultichainUtils.sol index 84533722e..fa0740ae9 100644 --- a/contracts/multichain/MultichainUtils.sol +++ b/contracts/multichain/MultichainUtils.sol @@ -10,7 +10,7 @@ import "../data/Keys.sol"; */ library MultichainUtils { function increaseBalance(DataStore dataStore, uint256 chainId, address account, address token, uint256 amount) internal { - dataStore.decrementUint(Keys.multichainBalanceKey(chainId, account, token), amount); + dataStore.incrementUint(Keys.multichainBalanceKey(chainId, account, token), amount); } function decreaseBalance(DataStore dataStore, uint256 chainId, address account, address token, uint256 amount) internal { From 59940017a682d56ac721889b6c4a4cefbd1fac3e Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 4 Feb 2025 22:34:18 +0200 Subject: [PATCH 014/205] Set chainId for Deposit on executeShift, remove position chainId related comments --- contracts/position/Position.sol | 9 --------- contracts/shift/ShiftUtils.sol | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/contracts/position/Position.sol b/contracts/position/Position.sol index 713f86b9a..0679fa515 100644 --- a/contracts/position/Position.sol +++ b/contracts/position/Position.sol @@ -197,17 +197,8 @@ library Position { // @param market the position's market // @param collateralToken the position's collateralToken // @param isLong whether the position is long or short - // @param chainId the destination chain id // @return the position key function getPositionKey(address _account, address _market, address _collateralToken, bool _isLong/*, uint256 _chainId*/) internal pure returns (bytes32) { - // TODO: handle the chainId cases bellow - // bytes32 _key; - // if (_chainId == 0) { - // _key = keccak256(abi.encode(_account, _market, _collateralToken, _isLong)); - // } else { - // _key = keccak256(abi.encode(_account, _chainId, _market, _collateralToken, _isLong)); - // } - bytes32 _key = keccak256(abi.encode(_account, _market, _collateralToken, _isLong)); return _key; diff --git a/contracts/shift/ShiftUtils.sol b/contracts/shift/ShiftUtils.sol index c53a42fcd..c840ac3bc 100644 --- a/contracts/shift/ShiftUtils.sol +++ b/contracts/shift/ShiftUtils.sol @@ -263,7 +263,7 @@ library ShiftUtils { shift.updatedAtTime(), 0, // executionFee 0, // callbackGasLimit - 0 // chainId // TODO: change to shift.chainId + shift.chainId() ), Deposit.Flags( false // shouldUnwrapNativeToken From 6bd246bc38d0f20fa0d64a4451f3a03392e0adbd Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 4 Feb 2025 22:39:55 +0200 Subject: [PATCH 015/205] WIP multichain deposits and gm logic --- contracts/deposit/ExecuteDepositUtils.sol | 15 +++++++- contracts/multichain/MultichainRouter.sol | 37 +++++++++++++++++-- .../router/relay/BaseGelatoRelayRouter.sol | 23 ++++++++++-- contracts/token/TokenUtils.sol | 18 +++++++++ 4 files changed, 86 insertions(+), 7 deletions(-) diff --git a/contracts/deposit/ExecuteDepositUtils.sol b/contracts/deposit/ExecuteDepositUtils.sol index e77b75836..718ddd79b 100644 --- a/contracts/deposit/ExecuteDepositUtils.sol +++ b/contracts/deposit/ExecuteDepositUtils.sol @@ -13,6 +13,9 @@ import "../pricing/SwapPricingUtils.sol"; import "../oracle/Oracle.sol"; import "../position/PositionUtils.sol"; +import "../multichain/MultichainVault.sol"; +import "../multichain/MultichainUtils.sol"; + import "../gas/GasUtils.sol"; import "../callback/CallbackUtils.sol"; @@ -43,12 +46,14 @@ library ExecuteDepositUtils { DataStore dataStore; EventEmitter eventEmitter; DepositVault depositVault; + MultichainVault multichainVault; Oracle oracle; bytes32 key; address keeper; uint256 startingGas; ISwapPricingUtils.SwapPricingType swapPricingType; bool includeVirtualInventoryImpact; + uint256 chainId; } // @dev _ExecuteDepositParams struct used in executeDeposit to avoid stack @@ -507,7 +512,15 @@ library ExecuteDepositUtils { _params.tokenIn ); - MarketToken(payable(_params.market.marketToken)).mint(_params.receiver, mintAmount); + // TODO: added multichainVault address to ExecuteDepositParams, but think if there is a better way + if (params.chainId == 0) { + // mint GM tokens to receiver + MarketToken(payable(_params.market.marketToken)).mint(_params.receiver, mintAmount); + } else { + // mint GM tokens to MultichainVault and increase account's multichain GM balance + MarketToken(payable(_params.market.marketToken)).mint(address(params.multichainVault), mintAmount); + MultichainUtils.increaseBalance(params.dataStore, params.chainId, _params.account, _params.market.marketToken, mintAmount); + } return mintAmount; } diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 79a6b761e..25e90d0d4 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -18,6 +18,13 @@ contract MultichainRouter is GelatoRelayRouter { DepositUtils.CreateDepositParams createDepositParams; } + // TODO: change struct + // struct MultichainCreateDepositParams { + // uint256 chainId; + // uint256 longTokenAmount; + // uint256 shortTokenAmount; + // } + bytes32 public constant CREATE_DEPOSIT_TYPEHASH = keccak256( bytes( @@ -101,24 +108,48 @@ contract MultichainRouter is GelatoRelayRouter { params.shortTokenAmount ); - // pay relay fee tokens from MultichainVault to DepositVault and decrement user's multichain balance + // On create step: can deduct relay fee fully or partially depending on user’s MultichainVault balance, save any excess pending relay fees and validate that the user has sufficient position collateral to pay for the remaining relay fee + // On execute step: Deduct pending relay fees from user’s position collateral + // TODO: confirm partial fee deduction logic + + // pay relay fee tokens from MultichainVault to DepositVault and decrease user's multichain balance params.createDepositParams.executionFee = _handleRelay( contracts, tokenPermits, - fee, + fee, // feeAmount is relayFee + executionFee account, NonceUtils.getKey(contracts.dataStore, NonceUtils.getCurrentNonce(dataStore) + 1), // calculate next key without incrementing + // if initialLongTokenAmount or initialShortTokenAmount is wnt then executionFee will be subracted (in DepositUtils.createDeposit) from one of them + // otherwise executionFee amount of wnt must be sent to DepositVault => it means the residualFeeReceiver should be the DepositVault address(depositVault) // residualFeeReceiver ); + // TODO: revisit this logic and think if a different approach is better + address wnt = TokenUtils.wnt(contracts.dataStore); + if (params.createDepositParams.initialLongToken == wnt || params.createDepositParams.initialShortToken == wnt) { + // executionFee will be paid from long or short token + // but _handleRelay has also transferred the executionFee to depositVault + // send back executionFee to MultichainVault and increase user's multichain balance + MultichainUtils.increaseBalance(dataStore, params.createDepositParams.chainId, account, wnt, params.createDepositParams.executionFee); + multichainVaultHandler.pluginTransfer(wnt, address(depositVault), address(multichainVault), params.createDepositParams.executionFee); + } + return depositHandler.createDeposit(account, params.createDepositParams); } function _sendTokens(uint256 chainId, address account, address token, address receiver, uint256 amount) internal { // TODO: confirm _sendTokens override and make BaseGelatoRelayRouter._sendTokens virtual AccountUtils.validateReceiver(receiver); - multichainVaultHandler.pluginTransfer(chainId, token, account, receiver, amount); + MultichainUtils.decreaseBalance(dataStore, chainId, account, token, amount); + multichainVaultHandler.pluginTransfer(token, address(multichainVault), receiver, amount); } + // function _transferResidualFee(address wnt, address residualFeeReceiver, uint256 residualFee, address account, uint256 chainId) internal override { + // TokenUtils.transfer(dataStore, wnt, residualFeeReceiver, residualFee); + // if (chainId != 0) { + // MultichainUtils.increaseBalance(dataStore, chainId, account, wnt, residualFee); + // } + // } + function _getGaslessCreateDepositStructHash( RelayParams calldata relayParams, GaslessCreateDepositParams memory params diff --git a/contracts/router/relay/BaseGelatoRelayRouter.sol b/contracts/router/relay/BaseGelatoRelayRouter.sol index 7d0f4dd1e..123b7abf8 100644 --- a/contracts/router/relay/BaseGelatoRelayRouter.sol +++ b/contracts/router/relay/BaseGelatoRelayRouter.sol @@ -196,7 +196,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, ); } - return orderHandler.createOrder(account, params); + return orderHandler.createOrder(account, params); // TODO: key is incremented here, but also when passed as param to _handleRelay. Seems the relayFee is paid for prev key? } function _swapFeeTokens( @@ -306,10 +306,14 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, _transferRelayFee(); + // TODO: should it be named remainingFee as it's intended to always include RelayFee + executionFee? + // For createOrder it's the executionFee and goes into depositVault. For update/cancelOrder is goes back to account uint256 residualFee = outputAmount - requiredRelayFee; // for orders the residual fee is sent to the order vault // for other actions the residual fee is sent back to the user - TokenUtils.transfer(contracts.dataStore, wnt, residualFeeReceiver, residualFee); + // for multichain actions, the residual fee is send back to MultichainVault and user's multichain balance is increased + _transferResidualFee(wnt, residualFeeReceiver, residualFee, account, 0); // TODO: solve chainId + return residualFee; } @@ -318,7 +322,20 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, router.pluginTransfer(token, account, receiver, amount); } - function _getDomainSeparator(uint256 sourceChainId) internal view returns (bytes32) { + // for multichain actions, residualFeeReceiver should be the MultichainVault contract + function _transferResidualFee(address wnt, address residualFeeReceiver, uint256 residualFee, address account, uint256 chainId) internal virtual { + // account and chainId not used here, but necessary when overriding _transferResidualFee in MultichainRouter + TokenUtils.transfer(dataStore, wnt, residualFeeReceiver, residualFee); + + // TODO: confirm order actions (e.g. createOrder, updateOrder, cancelOrder) will have a chainId, otherwise how would we increase user's multichain balance with the residualFee? + // if orders have the chainId, then increasing user's multchain balance could be done here instead of overriding this method in MultichainRouter + // e.g. + // if (chainId != 0) { + // MultichainUtils.increaseBalance(dataStore, chainId, account, wnt, residualFee); + // } + } + + function _getDomainSeparator(uint256 sourceChainId) internal view returns (bytes32) { // TODO: why is this named sourceChainId if it's the block.chainid? It makes you think it refers to a source chain e.g. Base return keccak256( abi.encode( diff --git a/contracts/token/TokenUtils.sol b/contracts/token/TokenUtils.sol index 9a7518f18..996b5dfd3 100644 --- a/contracts/token/TokenUtils.sol +++ b/contracts/token/TokenUtils.sol @@ -10,6 +10,7 @@ import "../data/DataStore.sol"; import "../data/Keys.sol"; import "../error/ErrorUtils.sol"; import "../utils/AccountUtils.sol"; +// import "../multichain/MultichainUtils.sol"; import "./IWNT.sol"; @@ -95,6 +96,23 @@ library TokenUtils { revert Errors.TokenTransferError(token, receiver, amount); } + // function multichainTransfer( + // DataStore dataStore, + // address token, + // address receiver, + // uint256 amount, + // uint256 chainId, + // address multichainVault, + // address account + // ) internal { + // if (chainId == 0) { + // transfer(dataStore, token, receiver, amount); + // } else { + // transfer(dataStore, token, multichainVault, amount); + // MultichainUtils.increaseBalance(dataStore, chainId, account, token, amount); + // } + // } + function sendNativeToken( DataStore dataStore, address receiver, From 68c06ce154621a4e62a499ec7d1ea9faa563dd6d Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 5 Feb 2025 10:44:41 +0200 Subject: [PATCH 016/205] Update multichain logic for fee payment --- contracts/deposit/ExecuteDepositUtils.sol | 6 ++-- contracts/multichain/MultichainRouter.sol | 31 +++++++++++------- .../router/relay/BaseGelatoRelayRouter.sol | 32 +++++++++---------- contracts/router/relay/GelatoRelayRouter.sol | 9 ++++-- .../relay/SubaccountGelatoRelayRouter.sol | 9 ++++-- contracts/token/TokenUtils.sol | 29 +++++++---------- 6 files changed, 60 insertions(+), 56 deletions(-) diff --git a/contracts/deposit/ExecuteDepositUtils.sol b/contracts/deposit/ExecuteDepositUtils.sol index 718ddd79b..6d72c7822 100644 --- a/contracts/deposit/ExecuteDepositUtils.sol +++ b/contracts/deposit/ExecuteDepositUtils.sol @@ -13,7 +13,6 @@ import "../pricing/SwapPricingUtils.sol"; import "../oracle/Oracle.sol"; import "../position/PositionUtils.sol"; -import "../multichain/MultichainVault.sol"; import "../multichain/MultichainUtils.sol"; import "../gas/GasUtils.sol"; @@ -46,7 +45,6 @@ library ExecuteDepositUtils { DataStore dataStore; EventEmitter eventEmitter; DepositVault depositVault; - MultichainVault multichainVault; Oracle oracle; bytes32 key; address keeper; @@ -54,6 +52,7 @@ library ExecuteDepositUtils { ISwapPricingUtils.SwapPricingType swapPricingType; bool includeVirtualInventoryImpact; uint256 chainId; + // address multichainVault; } // @dev _ExecuteDepositParams struct used in executeDeposit to avoid stack @@ -512,13 +511,12 @@ library ExecuteDepositUtils { _params.tokenIn ); - // TODO: added multichainVault address to ExecuteDepositParams, but think if there is a better way if (params.chainId == 0) { // mint GM tokens to receiver MarketToken(payable(_params.market.marketToken)).mint(_params.receiver, mintAmount); } else { // mint GM tokens to MultichainVault and increase account's multichain GM balance - MarketToken(payable(_params.market.marketToken)).mint(address(params.multichainVault), mintAmount); + // MarketToken(payable(_params.market.marketToken)).mint(params.multichainVault, mintAmount); // TODO: add multichainVault address to ExecuteDepositParams, or is there a better approach? MultichainUtils.increaseBalance(params.dataStore, params.chainId, _params.account, _params.market.marketToken, mintAmount); } diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 25e90d0d4..09a5088c8 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -18,7 +18,7 @@ contract MultichainRouter is GelatoRelayRouter { DepositUtils.CreateDepositParams createDepositParams; } - // TODO: change struct + // TODO: extract multichain part from GaslessCreateDepositParams struct // struct MultichainCreateDepositParams { // uint256 chainId; // uint256 longTokenAmount; @@ -89,6 +89,7 @@ contract MultichainRouter is GelatoRelayRouter { eventEmitter: eventEmitter, // TODO: confirm Contracts struct can be modified --> replace `OrderVault orderVault;` field with `StrictBank vault;` // otherwise, should probably overridde _handleRelay + // A: yes, it can be modified orderVault: OrderVault(payable(depositVault)) }); @@ -117,6 +118,7 @@ contract MultichainRouter is GelatoRelayRouter { contracts, tokenPermits, fee, // feeAmount is relayFee + executionFee + params.createDepositParams.chainId, account, NonceUtils.getKey(contracts.dataStore, NonceUtils.getCurrentNonce(dataStore) + 1), // calculate next key without incrementing // if initialLongTokenAmount or initialShortTokenAmount is wnt then executionFee will be subracted (in DepositUtils.createDeposit) from one of them @@ -124,12 +126,14 @@ contract MultichainRouter is GelatoRelayRouter { address(depositVault) // residualFeeReceiver ); - // TODO: revisit this logic and think if a different approach is better + // TODO: revisit and confirm this logic + // executionFee will be paid (in DepositUtils.createDeposit) from long or short token + // but _handleRelay has also transferred the executionFee to depositVault + // send back executionFee to MultichainVault and re-increase user's multichain balance by the executionFee amount + // by not doing this check, I think execution fee could get paid twice when initial long or short tokens are the wnt + // the alternative would be to have MultichainVault as the residualFeeReceiver, but then if none of the initial tokens are wnt, DepositUtils.createDeposit expects the fee to have already been transferred to depositVault and reverts otherwise address wnt = TokenUtils.wnt(contracts.dataStore); if (params.createDepositParams.initialLongToken == wnt || params.createDepositParams.initialShortToken == wnt) { - // executionFee will be paid from long or short token - // but _handleRelay has also transferred the executionFee to depositVault - // send back executionFee to MultichainVault and increase user's multichain balance MultichainUtils.increaseBalance(dataStore, params.createDepositParams.chainId, account, wnt, params.createDepositParams.executionFee); multichainVaultHandler.pluginTransfer(wnt, address(depositVault), address(multichainVault), params.createDepositParams.executionFee); } @@ -137,18 +141,21 @@ contract MultichainRouter is GelatoRelayRouter { return depositHandler.createDeposit(account, params.createDepositParams); } - function _sendTokens(uint256 chainId, address account, address token, address receiver, uint256 amount) internal { // TODO: confirm _sendTokens override and make BaseGelatoRelayRouter._sendTokens virtual + function _sendTokens(uint256 chainId, address account, address token, address receiver, uint256 amount) internal override { AccountUtils.validateReceiver(receiver); MultichainUtils.decreaseBalance(dataStore, chainId, account, token, amount); multichainVaultHandler.pluginTransfer(token, address(multichainVault), receiver, amount); } - // function _transferResidualFee(address wnt, address residualFeeReceiver, uint256 residualFee, address account, uint256 chainId) internal override { - // TokenUtils.transfer(dataStore, wnt, residualFeeReceiver, residualFee); - // if (chainId != 0) { - // MultichainUtils.increaseBalance(dataStore, chainId, account, wnt, residualFee); - // } - // } + function _transferResidualFee(address wnt, address residualFeeReceiver, uint256 residualFee, uint256 chainId, address account) internal override { + if (chainId == 0) { + // sent residualFee to residualFeeReceiver + TokenUtils.transfer(dataStore, wnt, residualFeeReceiver, residualFee); + } else { + // sent residualFee to MultichainVault and increase user's multichain balance + TokenUtils.multichainTransfer(dataStore, wnt, address(multichainVault), residualFee, chainId, account); + } + } function _getGaslessCreateDepositStructHash( RelayParams calldata relayParams, diff --git a/contracts/router/relay/BaseGelatoRelayRouter.sol b/contracts/router/relay/BaseGelatoRelayRouter.sol index 123b7abf8..e4aad659f 100644 --- a/contracts/router/relay/BaseGelatoRelayRouter.sol +++ b/contracts/router/relay/BaseGelatoRelayRouter.sol @@ -105,6 +105,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, function _updateOrder( RelayParams calldata relayParams, + uint256 chainId, address account, bytes32 key, UpdateOrderParams calldata params @@ -125,7 +126,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, revert Errors.Unauthorized(account, "account for updateOrder"); } - _handleRelay(contracts, relayParams.tokenPermits, relayParams.fee, account, key, account); + _handleRelay(contracts, relayParams.tokenPermits, relayParams.fee, chainId, account, key, account); orderHandler.updateOrder( key, @@ -139,7 +140,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, ); } - function _cancelOrder(RelayParams calldata relayParams, address account, bytes32 key) internal { + function _cancelOrder(RelayParams calldata relayParams, uint256 chainId, address account, bytes32 key) internal { Contracts memory contracts = Contracts({ dataStore: dataStore, eventEmitter: eventEmitter, @@ -155,13 +156,14 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, revert Errors.Unauthorized(account, "account for cancelOrder"); } - _handleRelay(contracts, relayParams.tokenPermits, relayParams.fee, account, key, account); + _handleRelay(contracts, relayParams.tokenPermits, relayParams.fee, chainId, account, key, account); orderHandler.cancelOrder(key); } function _createOrder( RelayParams calldata relayParams, + uint256 chainId, address account, uint256 collateralDeltaAmount, IBaseOrderUtils.CreateOrderParams memory params // can't use calldata because need to modify params.numbers.executionFee @@ -176,6 +178,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, contracts, relayParams.tokenPermits, relayParams.fee, + chainId, account, NonceUtils.getNextKey(contracts.dataStore), // order key address(contracts.orderVault) @@ -189,6 +192,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, params.orderType == Order.OrderType.StopIncrease ) { _sendTokens( + chainId, account, params.addresses.initialCollateralToken, address(contracts.orderVault), @@ -241,12 +245,13 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, Contracts memory contracts, TokenPermit[] calldata tokenPermits, RelayFeeParams calldata fee, + uint256 chainId, address account, bytes32 orderKey, address residualFeeReceiver ) internal returns (uint256) { _handleTokenPermits(tokenPermits); - return _handleRelayFee(contracts, fee, account, orderKey, residualFeeReceiver); + return _handleRelayFee(contracts, fee, chainId, account, orderKey, residualFeeReceiver); } function _handleTokenPermits(TokenPermit[] calldata tokenPermits) internal { @@ -286,6 +291,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, function _handleRelayFee( Contracts memory contracts, RelayFeeParams calldata fee, + uint256 chainId, address account, bytes32 orderKey, address residualFeeReceiver @@ -296,7 +302,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, revert Errors.UnsupportedRelayFeeToken(_getFeeToken(), wnt); } - _sendTokens(account, fee.feeToken, address(contracts.orderVault), fee.feeAmount); + _sendTokens(chainId, account, fee.feeToken, address(contracts.orderVault), fee.feeAmount); uint256 outputAmount = _swapFeeTokens(contracts, wnt, fee, orderKey); uint256 requiredRelayFee = _getFee(); @@ -311,28 +317,20 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, uint256 residualFee = outputAmount - requiredRelayFee; // for orders the residual fee is sent to the order vault // for other actions the residual fee is sent back to the user - // for multichain actions, the residual fee is send back to MultichainVault and user's multichain balance is increased - _transferResidualFee(wnt, residualFeeReceiver, residualFee, account, 0); // TODO: solve chainId + _transferResidualFee(wnt, residualFeeReceiver, residualFee, chainId, account); return residualFee; } - function _sendTokens(address account, address token, address receiver, uint256 amount) internal { + function _sendTokens(uint256 /*chainId*/, address account, address token, address receiver, uint256 amount) internal virtual { AccountUtils.validateReceiver(receiver); router.pluginTransfer(token, account, receiver, amount); } - // for multichain actions, residualFeeReceiver should be the MultichainVault contract - function _transferResidualFee(address wnt, address residualFeeReceiver, uint256 residualFee, address account, uint256 chainId) internal virtual { + // for multichain actions, the residual fee is send back to MultichainVault and user's multichain balance is increased + function _transferResidualFee(address wnt, address residualFeeReceiver, uint256 residualFee, uint256 /*chainId*/, address /*account*/) internal virtual { // account and chainId not used here, but necessary when overriding _transferResidualFee in MultichainRouter TokenUtils.transfer(dataStore, wnt, residualFeeReceiver, residualFee); - - // TODO: confirm order actions (e.g. createOrder, updateOrder, cancelOrder) will have a chainId, otherwise how would we increase user's multichain balance with the residualFee? - // if orders have the chainId, then increasing user's multchain balance could be done here instead of overriding this method in MultichainRouter - // e.g. - // if (chainId != 0) { - // MultichainUtils.increaseBalance(dataStore, chainId, account, wnt, residualFee); - // } } function _getDomainSeparator(uint256 sourceChainId) internal view returns (bytes32) { // TODO: why is this named sourceChainId if it's the block.chainid? It makes you think it refers to a source chain e.g. Base diff --git a/contracts/router/relay/GelatoRelayRouter.sol b/contracts/router/relay/GelatoRelayRouter.sol index aea6844dd..ebbd795a2 100644 --- a/contracts/router/relay/GelatoRelayRouter.sol +++ b/contracts/router/relay/GelatoRelayRouter.sol @@ -57,6 +57,7 @@ contract GelatoRelayRouter is BaseGelatoRelayRouter { function createOrder( RelayParams calldata relayParams, + uint256 chainId, // TODO: should this be part of CreateOrderParams instead? means adding it to Order.Props as well. address account, uint256 collateralDeltaAmount, IBaseOrderUtils.CreateOrderParams memory params // can't use calldata because need to modify params.numbers.executionFee @@ -70,11 +71,12 @@ contract GelatoRelayRouter is BaseGelatoRelayRouter { bytes32 structHash = _getCreateOrderStructHash(relayParams, collateralDeltaAmount, params); _validateCall(relayParams, account, structHash); - return _createOrder(relayParams, account, collateralDeltaAmount, params); + return _createOrder(relayParams, chainId, account, collateralDeltaAmount, params); } function updateOrder( RelayParams calldata relayParams, + uint256 chainId, address account, bytes32 key, UpdateOrderParams calldata params @@ -82,18 +84,19 @@ contract GelatoRelayRouter is BaseGelatoRelayRouter { bytes32 structHash = _getUpdateOrderStructHash(relayParams, key, params); _validateCall(relayParams, account, structHash); - _updateOrder(relayParams, account, key, params); + _updateOrder(relayParams, chainId, account, key, params); } function cancelOrder( RelayParams calldata relayParams, + uint256 chainId, address account, bytes32 key ) external nonReentrant withOraclePricesForAtomicAction(relayParams.oracleParams) onlyGelatoRelay { bytes32 structHash = _getCancelOrderStructHash(relayParams, key); _validateCall(relayParams, account, structHash); - _cancelOrder(relayParams, account, key); + _cancelOrder(relayParams, chainId, account, key); } function _getUpdateOrderStructHash( diff --git a/contracts/router/relay/SubaccountGelatoRelayRouter.sol b/contracts/router/relay/SubaccountGelatoRelayRouter.sol index 9aa61026b..12af78eda 100644 --- a/contracts/router/relay/SubaccountGelatoRelayRouter.sol +++ b/contracts/router/relay/SubaccountGelatoRelayRouter.sol @@ -79,6 +79,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { function createOrder( RelayParams calldata relayParams, SubaccountApproval calldata subaccountApproval, + uint256 chainId, address account, // main account address subaccount, uint256 collateralDeltaAmount, @@ -108,12 +109,13 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { revert Errors.InvalidCancellationReceiverForSubaccountOrder(params.addresses.cancellationReceiver, account); } - return _createOrder(relayParams, account, collateralDeltaAmount, params); + return _createOrder(relayParams, chainId, account, collateralDeltaAmount, params); } function updateOrder( RelayParams calldata relayParams, SubaccountApproval calldata subaccountApproval, + uint256 chainId, address account, // main account address subaccount, bytes32 key, @@ -122,12 +124,13 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { bytes32 structHash = _getUpdateOrderStructHash(relayParams, subaccountApproval, account, key, params); _validateCall(relayParams, subaccount, structHash); _handleSubaccountAction(account, subaccount, Keys.SUBACCOUNT_ORDER_ACTION, subaccountApproval); - _updateOrder(relayParams, account, key, params); + _updateOrder(relayParams, chainId, account, key, params); } function cancelOrder( RelayParams calldata relayParams, SubaccountApproval calldata subaccountApproval, + uint256 chainId, address account, // main account address subaccount, bytes32 key @@ -135,7 +138,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { bytes32 structHash = _getCancelOrderStructHash(relayParams, subaccountApproval, account, key); _validateCall(relayParams, subaccount, structHash); _handleSubaccountAction(account, subaccount, Keys.SUBACCOUNT_ORDER_ACTION, subaccountApproval); - _cancelOrder(relayParams, account, key); + _cancelOrder(relayParams, chainId, account, key); } function removeSubaccount( diff --git a/contracts/token/TokenUtils.sol b/contracts/token/TokenUtils.sol index 996b5dfd3..3f8dfa264 100644 --- a/contracts/token/TokenUtils.sol +++ b/contracts/token/TokenUtils.sol @@ -10,7 +10,7 @@ import "../data/DataStore.sol"; import "../data/Keys.sol"; import "../error/ErrorUtils.sol"; import "../utils/AccountUtils.sol"; -// import "../multichain/MultichainUtils.sol"; +import "../multichain/MultichainUtils.sol"; import "./IWNT.sol"; @@ -96,22 +96,17 @@ library TokenUtils { revert Errors.TokenTransferError(token, receiver, amount); } - // function multichainTransfer( - // DataStore dataStore, - // address token, - // address receiver, - // uint256 amount, - // uint256 chainId, - // address multichainVault, - // address account - // ) internal { - // if (chainId == 0) { - // transfer(dataStore, token, receiver, amount); - // } else { - // transfer(dataStore, token, multichainVault, amount); - // MultichainUtils.increaseBalance(dataStore, chainId, account, token, amount); - // } - // } + function multichainTransfer( + DataStore dataStore, + address token, + address receiver, + uint256 amount, + uint256 chainId, + address account + ) internal { + transfer(dataStore, token, receiver, amount); + MultichainUtils.decreaseBalance(dataStore, chainId, account, token, amount); + } function sendNativeToken( DataStore dataStore, From 48c9a918323148b1576bf4a39a6c71c489777915 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 5 Feb 2025 15:42:23 +0200 Subject: [PATCH 017/205] Remove chainId from multichain balance --- contracts/data/Keys.sol | 4 +--- contracts/deposit/ExecuteDepositUtils.sol | 2 +- contracts/multichain/MultichainRouter.sol | 10 ++++------ contracts/multichain/MultichainUtils.sol | 8 ++++---- contracts/multichain/MultichainVaultHandler.sol | 5 ++--- contracts/router/relay/BaseGelatoRelayRouter.sol | 5 ++--- contracts/token/TokenUtils.sol | 3 +-- test/multichain/LayerZeroProvider.ts | 2 +- utils/keys.ts | 4 ++-- 9 files changed, 18 insertions(+), 25 deletions(-) diff --git a/contracts/data/Keys.sol b/contracts/data/Keys.sol index 412b34665..275de75f6 100644 --- a/contracts/data/Keys.sol +++ b/contracts/data/Keys.sol @@ -2121,14 +2121,12 @@ library Keys { } // @dev key for user's multichain balance - // @param chainId the chain id for the destination chain // @param account the account for which to retreive the user balance key // @param token the token for which to retreive the user balance key // @return key for multichain balance for a given user and token - function multichainBalanceKey(uint256 chainId, address account, address token) internal pure returns (bytes32) { + function multichainBalanceKey(address account, address token) internal pure returns (bytes32) { return keccak256(abi.encode( MULTICHAIN_BALANCE, - chainId, account, token )); diff --git a/contracts/deposit/ExecuteDepositUtils.sol b/contracts/deposit/ExecuteDepositUtils.sol index 6d72c7822..41b08cd2a 100644 --- a/contracts/deposit/ExecuteDepositUtils.sol +++ b/contracts/deposit/ExecuteDepositUtils.sol @@ -517,7 +517,7 @@ library ExecuteDepositUtils { } else { // mint GM tokens to MultichainVault and increase account's multichain GM balance // MarketToken(payable(_params.market.marketToken)).mint(params.multichainVault, mintAmount); // TODO: add multichainVault address to ExecuteDepositParams, or is there a better approach? - MultichainUtils.increaseBalance(params.dataStore, params.chainId, _params.account, _params.market.marketToken, mintAmount); + MultichainUtils.increaseBalance(params.dataStore, _params.account, _params.market.marketToken, mintAmount); } return mintAmount; diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 09a5088c8..80c32b0f9 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -95,14 +95,12 @@ contract MultichainRouter is GelatoRelayRouter { // transfer long & short tokens from MultichainVault to DepositVault and decrement user's multichain balance _sendTokens( - params.createDepositParams.chainId, params.createDepositParams.initialLongToken, account, address(depositVault), // receiver params.longTokenAmount ); _sendTokens( - params.createDepositParams.chainId, params.createDepositParams.initialShortToken, account, address(depositVault), // receiver @@ -134,16 +132,16 @@ contract MultichainRouter is GelatoRelayRouter { // the alternative would be to have MultichainVault as the residualFeeReceiver, but then if none of the initial tokens are wnt, DepositUtils.createDeposit expects the fee to have already been transferred to depositVault and reverts otherwise address wnt = TokenUtils.wnt(contracts.dataStore); if (params.createDepositParams.initialLongToken == wnt || params.createDepositParams.initialShortToken == wnt) { - MultichainUtils.increaseBalance(dataStore, params.createDepositParams.chainId, account, wnt, params.createDepositParams.executionFee); + MultichainUtils.increaseBalance(dataStore, account, wnt, params.createDepositParams.executionFee); multichainVaultHandler.pluginTransfer(wnt, address(depositVault), address(multichainVault), params.createDepositParams.executionFee); } return depositHandler.createDeposit(account, params.createDepositParams); } - function _sendTokens(uint256 chainId, address account, address token, address receiver, uint256 amount) internal override { + function _sendTokens(address account, address token, address receiver, uint256 amount) internal override { AccountUtils.validateReceiver(receiver); - MultichainUtils.decreaseBalance(dataStore, chainId, account, token, amount); + MultichainUtils.decreaseBalance(dataStore, account, token, amount); multichainVaultHandler.pluginTransfer(token, address(multichainVault), receiver, amount); } @@ -153,7 +151,7 @@ contract MultichainRouter is GelatoRelayRouter { TokenUtils.transfer(dataStore, wnt, residualFeeReceiver, residualFee); } else { // sent residualFee to MultichainVault and increase user's multichain balance - TokenUtils.multichainTransfer(dataStore, wnt, address(multichainVault), residualFee, chainId, account); + TokenUtils.multichainTransfer(dataStore, wnt, address(multichainVault), residualFee, account); } } diff --git a/contracts/multichain/MultichainUtils.sol b/contracts/multichain/MultichainUtils.sol index fa0740ae9..475eb01ea 100644 --- a/contracts/multichain/MultichainUtils.sol +++ b/contracts/multichain/MultichainUtils.sol @@ -9,11 +9,11 @@ import "../data/Keys.sol"; * @title MultichainUtils */ library MultichainUtils { - function increaseBalance(DataStore dataStore, uint256 chainId, address account, address token, uint256 amount) internal { - dataStore.incrementUint(Keys.multichainBalanceKey(chainId, account, token), amount); + function increaseBalance(DataStore dataStore, address account, address token, uint256 amount) internal { + dataStore.incrementUint(Keys.multichainBalanceKey(account, token), amount); } - function decreaseBalance(DataStore dataStore, uint256 chainId, address account, address token, uint256 amount) internal { - dataStore.decrementUint(Keys.multichainBalanceKey(chainId, account, token), amount); + function decreaseBalance(DataStore dataStore, address account, address token, uint256 amount) internal { + dataStore.decrementUint(Keys.multichainBalanceKey(account, token), amount); } } diff --git a/contracts/multichain/MultichainVaultHandler.sol b/contracts/multichain/MultichainVaultHandler.sol index 893912609..c1dde6990 100644 --- a/contracts/multichain/MultichainVaultHandler.sol +++ b/contracts/multichain/MultichainVaultHandler.sol @@ -60,7 +60,7 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu revert Errors.EmptyMultichainDepositAmount(); } - dataStore.incrementUint(Keys.multichainBalanceKey(multichainId, account, token), amount); + dataStore.incrementUint(Keys.multichainBalanceKey(account, token), amount); MultichainEventUtils.emitMultichainDeposit(eventEmitter, token, account, amount, multichainId); } @@ -99,7 +99,6 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu * @param account user address on the source chain * @param token address of the token being withdrawn * @param amount amount of token being withdrawn - * @param multichainId chain id of the destination chain */ function recordWithdrawal( address account, @@ -111,7 +110,7 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu revert Errors.EmptyMultichainWithdrawalAmount(); } - bytes32 balanceKey = Keys.multichainBalanceKey(multichainId, account, token); + bytes32 balanceKey = Keys.multichainBalanceKey(account, token); uint256 balance = dataStore.getUint(balanceKey); if (balance < amount) { diff --git a/contracts/router/relay/BaseGelatoRelayRouter.sol b/contracts/router/relay/BaseGelatoRelayRouter.sol index e4aad659f..2b6e233b3 100644 --- a/contracts/router/relay/BaseGelatoRelayRouter.sol +++ b/contracts/router/relay/BaseGelatoRelayRouter.sol @@ -192,7 +192,6 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, params.orderType == Order.OrderType.StopIncrease ) { _sendTokens( - chainId, account, params.addresses.initialCollateralToken, address(contracts.orderVault), @@ -302,7 +301,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, revert Errors.UnsupportedRelayFeeToken(_getFeeToken(), wnt); } - _sendTokens(chainId, account, fee.feeToken, address(contracts.orderVault), fee.feeAmount); + _sendTokens(account, fee.feeToken, address(contracts.orderVault), fee.feeAmount); uint256 outputAmount = _swapFeeTokens(contracts, wnt, fee, orderKey); uint256 requiredRelayFee = _getFee(); @@ -322,7 +321,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, return residualFee; } - function _sendTokens(uint256 /*chainId*/, address account, address token, address receiver, uint256 amount) internal virtual { + function _sendTokens(address account, address token, address receiver, uint256 amount) internal virtual { AccountUtils.validateReceiver(receiver); router.pluginTransfer(token, account, receiver, amount); } diff --git a/contracts/token/TokenUtils.sol b/contracts/token/TokenUtils.sol index 3f8dfa264..91db7138b 100644 --- a/contracts/token/TokenUtils.sol +++ b/contracts/token/TokenUtils.sol @@ -101,11 +101,10 @@ library TokenUtils { address token, address receiver, uint256 amount, - uint256 chainId, address account ) internal { transfer(dataStore, token, receiver, amount); - MultichainUtils.decreaseBalance(dataStore, chainId, account, token, amount); + MultichainUtils.decreaseBalance(dataStore, account, token, amount); } function sendNativeToken( diff --git a/test/multichain/LayerZeroProvider.ts b/test/multichain/LayerZeroProvider.ts index 14bb22c53..934a4dd20 100644 --- a/test/multichain/LayerZeroProvider.ts +++ b/test/multichain/LayerZeroProvider.ts @@ -32,7 +32,7 @@ describe("LayerZeroProvider", () => { const lzUsdcBalance = await usdc.balanceOf(layerZeroProvider.address); const multichainVaultBalance = await usdc.balanceOf(multichainVault.address); - const userBalance = await dataStore.getUint(keys.multichainBalanceKey(multichainId, user0.address, usdc.address)); + const userBalance = await dataStore.getUint(keys.multichainBalanceKey(user0.address, usdc.address)); // usdc has been transterred from LayerZeroProvider to MultichainVault and recorded under the user's chainId + account expect(lzUsdcBalance).eq(0); diff --git a/utils/keys.ts b/utils/keys.ts index 4daf4bb32..01137169a 100644 --- a/utils/keys.ts +++ b/utils/keys.ts @@ -815,6 +815,6 @@ export function withdrawableBuybackTokenAmountKey(buybackToken: string) { return hashData(["bytes32", "address"], [WITHDRAWABLE_BUYBACK_TOKEN_AMOUNT, buybackToken]); } -export function multichainBalanceKey(multichainId: number, account: string, token: string) { - return hashData(["bytes32", "uint256", "address", "address"], [MULTICHAIN_BALANCE, multichainId, account, token]); +export function multichainBalanceKey(account: string, token: string) { + return hashData(["bytes32", "address", "address"], [MULTICHAIN_BALANCE, account, token]); } From 370d42a284341aca85f5603d3a2b5e6afef250e9 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 5 Feb 2025 20:32:02 +0200 Subject: [PATCH 018/205] Rename chainId to srcChainId --- contracts/deposit/Deposit.sol | 10 ++++---- contracts/deposit/DepositEventUtils.sol | 3 +-- contracts/deposit/DepositStoreUtils.sol | 12 ++++----- contracts/deposit/DepositUtils.sol | 4 +-- contracts/deposit/ExecuteDepositUtils.sol | 4 +-- contracts/exchange/DepositHandler.sol | 2 +- contracts/glv/glvDeposit/GlvDeposit.sol | 10 ++++---- .../glv/glvDeposit/GlvDepositStoreUtils.sol | 12 ++++----- contracts/glv/glvDeposit/GlvDepositUtils.sol | 8 +++--- contracts/glv/glvShift/GlvShiftUtils.sol | 2 +- contracts/glv/glvWithdrawal/GlvWithdrawal.sol | 10 ++++---- .../glvWithdrawal/GlvWithdrawalStoreUtils.sol | 12 ++++----- .../glv/glvWithdrawal/GlvWithdrawalUtils.sol | 6 ++--- contracts/multichain/MultichainRouter.sol | 12 +++++---- .../router/relay/BaseGelatoRelayRouter.sol | 25 ++++++++++--------- contracts/shift/Shift.sol | 10 ++++---- contracts/shift/ShiftStoreUtils.sol | 13 +++++----- contracts/shift/ShiftUtils.sol | 10 ++++---- contracts/withdrawal/Withdrawal.sol | 10 ++++---- contracts/withdrawal/WithdrawalStoreUtils.sol | 13 +++++----- contracts/withdrawal/WithdrawalUtils.sol | 4 +-- 21 files changed, 96 insertions(+), 96 deletions(-) diff --git a/contracts/deposit/Deposit.sol b/contracts/deposit/Deposit.sol index 05c219791..b3e552fd9 100644 --- a/contracts/deposit/Deposit.sol +++ b/contracts/deposit/Deposit.sol @@ -54,7 +54,7 @@ library Deposit { uint256 updatedAtTime; uint256 executionFee; uint256 callbackGasLimit; - uint256 chainId; + uint256 srcChainId; } // @param shouldUnwrapNativeToken whether to unwrap the native token when @@ -182,12 +182,12 @@ library Deposit { props.numbers.callbackGasLimit = value; } - function chainId(Props memory props) internal pure returns (uint256) { - return props.numbers.chainId; + function srcChainId(Props memory props) internal pure returns (uint256) { + return props.numbers.srcChainId; } - function setChainId(Props memory props, uint256 value) internal pure { - props.numbers.chainId = value; + function setSrcChainId(Props memory props, uint256 value) internal pure { + props.numbers.srcChainId = value; } function shouldUnwrapNativeToken(Props memory props) internal pure returns (bool) { diff --git a/contracts/deposit/DepositEventUtils.sol b/contracts/deposit/DepositEventUtils.sol index 60c71ec61..0d202af05 100644 --- a/contracts/deposit/DepositEventUtils.sol +++ b/contracts/deposit/DepositEventUtils.sol @@ -49,7 +49,7 @@ library DepositEventUtils { eventData.uintItems.setItem(4, "executionFee", deposit.executionFee()); eventData.uintItems.setItem(5, "callbackGasLimit", deposit.callbackGasLimit()); eventData.uintItems.setItem(6, "depositType", uint256(depositType)); - eventData.uintItems.setItem(7, "chainId", deposit.chainId()); + eventData.uintItems.setItem(7, "srcChainId", deposit.srcChainId()); eventData.boolItems.initItems(1); eventData.boolItems.setItem(0, "shouldUnwrapNativeToken", deposit.shouldUnwrapNativeToken()); @@ -71,7 +71,6 @@ library DepositEventUtils { function emitDepositExecuted( EventEmitter eventEmitter, bytes32 key, - // uint256 chainId, TODO: should chainId be added as param? Is it ok to change the event signature (for e.g. analytics) address account, uint256 longTokenAmount, uint256 shortTokenAmount, diff --git a/contracts/deposit/DepositStoreUtils.sol b/contracts/deposit/DepositStoreUtils.sol index aeba013f1..da49fe76d 100644 --- a/contracts/deposit/DepositStoreUtils.sol +++ b/contracts/deposit/DepositStoreUtils.sol @@ -30,7 +30,7 @@ library DepositStoreUtils { bytes32 public constant UPDATED_AT_TIME = keccak256(abi.encode("UPDATED_AT_TIME")); bytes32 public constant EXECUTION_FEE = keccak256(abi.encode("EXECUTION_FEE")); bytes32 public constant CALLBACK_GAS_LIMIT = keccak256(abi.encode("CALLBACK_GAS_LIMIT")); - bytes32 public constant CHAIN_ID = keccak256(abi.encode("CHAIN_ID")); + bytes32 public constant SRC_CHAIN_ID = keccak256(abi.encode("SRC_CHAIN_ID")); bytes32 public constant SHOULD_UNWRAP_NATIVE_TOKEN = keccak256(abi.encode("SHOULD_UNWRAP_NATIVE_TOKEN")); @@ -102,8 +102,8 @@ library DepositStoreUtils { keccak256(abi.encode(key, CALLBACK_GAS_LIMIT)) )); - deposit.setChainId(dataStore.getUint( - keccak256(abi.encode(key, CHAIN_ID)) + deposit.setSrcChainId(dataStore.getUint( + keccak256(abi.encode(key, SRC_CHAIN_ID)) )); deposit.setShouldUnwrapNativeToken(dataStore.getBool( @@ -204,8 +204,8 @@ library DepositStoreUtils { ); dataStore.setUint( - keccak256(abi.encode(key, CHAIN_ID)), - deposit.chainId() + keccak256(abi.encode(key, SRC_CHAIN_ID)), + deposit.srcChainId() ); dataStore.setBool( @@ -295,7 +295,7 @@ library DepositStoreUtils { ); dataStore.removeUint( - keccak256(abi.encode(key, CHAIN_ID)) + keccak256(abi.encode(key, SRC_CHAIN_ID)) ); dataStore.removeBool( diff --git a/contracts/deposit/DepositUtils.sol b/contracts/deposit/DepositUtils.sol index a19e1ec80..6c2ac2e5f 100644 --- a/contracts/deposit/DepositUtils.sol +++ b/contracts/deposit/DepositUtils.sol @@ -50,7 +50,7 @@ library DepositUtils { bool shouldUnwrapNativeToken; uint256 executionFee; uint256 callbackGasLimit; - uint256 chainId; + uint256 srcChainId; bytes32[] dataList; } @@ -119,7 +119,7 @@ library DepositUtils { Chain.currentTimestamp(), // updatedAtTime params.executionFee, params.callbackGasLimit, - params.chainId + params.srcChainId ), Deposit.Flags( params.shouldUnwrapNativeToken diff --git a/contracts/deposit/ExecuteDepositUtils.sol b/contracts/deposit/ExecuteDepositUtils.sol index 41b08cd2a..bbd064e51 100644 --- a/contracts/deposit/ExecuteDepositUtils.sol +++ b/contracts/deposit/ExecuteDepositUtils.sol @@ -51,7 +51,7 @@ library ExecuteDepositUtils { uint256 startingGas; ISwapPricingUtils.SwapPricingType swapPricingType; bool includeVirtualInventoryImpact; - uint256 chainId; + uint256 srcChainId; // address multichainVault; } @@ -511,7 +511,7 @@ library ExecuteDepositUtils { _params.tokenIn ); - if (params.chainId == 0) { + if (params.srcChainId == 0) { // mint GM tokens to receiver MarketToken(payable(_params.market.marketToken)).mint(_params.receiver, mintAmount); } else { diff --git a/contracts/exchange/DepositHandler.sol b/contracts/exchange/DepositHandler.sol index cb0b8ac08..9d250852b 100644 --- a/contracts/exchange/DepositHandler.sol +++ b/contracts/exchange/DepositHandler.sol @@ -153,7 +153,7 @@ contract DepositHandler is IDepositHandler, BaseHandler { startingGas, ISwapPricingUtils.SwapPricingType.Deposit, true, // includeVirtualInventoryImpact - deposit.chainId() + deposit.srcChainId() ); ExecuteDepositUtils.executeDeposit(params, deposit); diff --git a/contracts/glv/glvDeposit/GlvDeposit.sol b/contracts/glv/glvDeposit/GlvDeposit.sol index 2615657a6..01ea6a915 100644 --- a/contracts/glv/glvDeposit/GlvDeposit.sol +++ b/contracts/glv/glvDeposit/GlvDeposit.sol @@ -52,7 +52,7 @@ library GlvDeposit { uint256 updatedAtTime; uint256 executionFee; uint256 callbackGasLimit; - uint256 chainId; + uint256 srcChainId; } // @param shouldUnwrapNativeToken whether to unwrap the native token when @@ -199,12 +199,12 @@ library GlvDeposit { props.numbers.callbackGasLimit = value; } - function chainId(Props memory props) internal pure returns (uint256) { - return props.numbers.chainId; + function srcChainId(Props memory props) internal pure returns (uint256) { + return props.numbers.srcChainId; } - function setChainId(Props memory props, uint256 value) internal pure { - props.numbers.chainId = value; + function setSrcChainId(Props memory props, uint256 value) internal pure { + props.numbers.srcChainId = value; } function shouldUnwrapNativeToken(Props memory props) internal pure returns (bool) { diff --git a/contracts/glv/glvDeposit/GlvDepositStoreUtils.sol b/contracts/glv/glvDeposit/GlvDepositStoreUtils.sol index 8fe6c1ead..8b275abdd 100644 --- a/contracts/glv/glvDeposit/GlvDepositStoreUtils.sol +++ b/contracts/glv/glvDeposit/GlvDepositStoreUtils.sol @@ -38,7 +38,7 @@ library GlvDepositStoreUtils { bytes32 public constant DATA_LIST = keccak256(abi.encode("DATA_LIST")); - bytes32 public constant CHAIN_ID = keccak256(abi.encode("CHAIN_ID")); + bytes32 public constant SRC_CHAIN_ID = keccak256(abi.encode("SRC_CHAIN_ID")); function get(DataStore dataStore, bytes32 key) external view returns (GlvDeposit.Props memory) { GlvDeposit.Props memory glvDeposit; @@ -114,8 +114,8 @@ library GlvDepositStoreUtils { keccak256(abi.encode(key, CALLBACK_GAS_LIMIT)) )); - glvDeposit.setChainId(dataStore.getUint( - keccak256(abi.encode(key, CHAIN_ID)) + glvDeposit.setSrcChainId(dataStore.getUint( + keccak256(abi.encode(key, SRC_CHAIN_ID)) )); glvDeposit.setShouldUnwrapNativeToken(dataStore.getBool( @@ -230,8 +230,8 @@ library GlvDepositStoreUtils { ); dataStore.setUint( - keccak256(abi.encode(key, CHAIN_ID)), - glvDeposit.chainId() + keccak256(abi.encode(key, SRC_CHAIN_ID)), + glvDeposit.srcChainId() ); dataStore.setBool( @@ -334,7 +334,7 @@ library GlvDepositStoreUtils { ); dataStore.removeUint( - keccak256(abi.encode(key, CHAIN_ID)) + keccak256(abi.encode(key, SRC_CHAIN_ID)) ); dataStore.removeBool( diff --git a/contracts/glv/glvDeposit/GlvDepositUtils.sol b/contracts/glv/glvDeposit/GlvDepositUtils.sol index 806ff94bf..34c29bd55 100644 --- a/contracts/glv/glvDeposit/GlvDepositUtils.sol +++ b/contracts/glv/glvDeposit/GlvDepositUtils.sol @@ -32,7 +32,7 @@ library GlvDepositUtils { uint256 minGlvTokens; uint256 executionFee; uint256 callbackGasLimit; - uint256 chainId; + uint256 srcChainId; bool shouldUnwrapNativeToken; bool isMarketTokenDeposit; bytes32[] dataList; @@ -179,7 +179,7 @@ library GlvDepositUtils { updatedAtTime: Chain.currentTimestamp(), executionFee: params.executionFee, callbackGasLimit: params.callbackGasLimit, - chainId: params.chainId + srcChainId: params.srcChainId }), GlvDeposit.Flags({ shouldUnwrapNativeToken: params.shouldUnwrapNativeToken, @@ -357,7 +357,7 @@ library GlvDepositUtils { updatedAtTime: glvDeposit.updatedAtTime(), executionFee: 0, callbackGasLimit: 0, - chainId: 0 + srcChainId: 0 }), Deposit.Flags({shouldUnwrapNativeToken: false}), new bytes32[](0) // dataList @@ -377,7 +377,7 @@ library GlvDepositUtils { params.startingGas, ISwapPricingUtils.SwapPricingType.Deposit, true, // includeVirtualInventoryImpact - glvDeposit.chainId() + glvDeposit.srcChainId() ); uint256 receivedMarketTokens = ExecuteDepositUtils.executeDeposit(executeDepositParams, deposit); diff --git a/contracts/glv/glvShift/GlvShiftUtils.sol b/contracts/glv/glvShift/GlvShiftUtils.sol index 5e16d7214..9a69316a3 100644 --- a/contracts/glv/glvShift/GlvShiftUtils.sol +++ b/contracts/glv/glvShift/GlvShiftUtils.sol @@ -139,7 +139,7 @@ library GlvShiftUtils { updatedAtTime: glvShift.updatedAtTime(), executionFee: 0, callbackGasLimit: 0, - chainId: 0 // TODO: should glvShift have the chainId as well? + srcChainId: 0 // TODO: should glvShift have the srcChainId as well? }), new bytes32[](0) ); diff --git a/contracts/glv/glvWithdrawal/GlvWithdrawal.sol b/contracts/glv/glvWithdrawal/GlvWithdrawal.sol index a45052a76..11aa7c629 100644 --- a/contracts/glv/glvWithdrawal/GlvWithdrawal.sol +++ b/contracts/glv/glvWithdrawal/GlvWithdrawal.sol @@ -49,7 +49,7 @@ library GlvWithdrawal { uint256 updatedAtTime; uint256 executionFee; uint256 callbackGasLimit; - uint256 chainId; + uint256 srcChainId; } // @param shouldUnwrapNativeToken whether to unwrap the native token when @@ -169,12 +169,12 @@ library GlvWithdrawal { props.numbers.callbackGasLimit = value; } - function chainId(Props memory props) internal pure returns (uint256) { - return props.numbers.chainId; + function srcChainId(Props memory props) internal pure returns (uint256) { + return props.numbers.srcChainId; } - function setChainId(Props memory props, uint256 value) internal pure { - props.numbers.chainId = value; + function setSrcChainId(Props memory props, uint256 value) internal pure { + props.numbers.srcChainId = value; } function shouldUnwrapNativeToken(Props memory props) internal pure returns (bool) { diff --git a/contracts/glv/glvWithdrawal/GlvWithdrawalStoreUtils.sol b/contracts/glv/glvWithdrawal/GlvWithdrawalStoreUtils.sol index b569668fb..924575a47 100644 --- a/contracts/glv/glvWithdrawal/GlvWithdrawalStoreUtils.sol +++ b/contracts/glv/glvWithdrawal/GlvWithdrawalStoreUtils.sol @@ -29,7 +29,7 @@ library GlvWithdrawalStoreUtils { bytes32 public constant UPDATED_AT_TIME = keccak256(abi.encode("UPDATED_AT_TIME")); bytes32 public constant EXECUTION_FEE = keccak256(abi.encode("EXECUTION_FEE")); bytes32 public constant CALLBACK_GAS_LIMIT = keccak256(abi.encode("CALLBACK_GAS_LIMIT")); - bytes32 public constant CHAIN_ID = keccak256(abi.encode("CHAIN_ID")); + bytes32 public constant SRC_CHAIN_ID = keccak256(abi.encode("SRC_CHAIN_ID")); bytes32 public constant SHOULD_UNWRAP_NATIVE_TOKEN = keccak256(abi.encode("SHOULD_UNWRAP_NATIVE_TOKEN")); @@ -97,8 +97,8 @@ library GlvWithdrawalStoreUtils { keccak256(abi.encode(key, CALLBACK_GAS_LIMIT)) )); - withdrawal.setChainId(dataStore.getUint( - keccak256(abi.encode(key, CHAIN_ID)) + withdrawal.setSrcChainId(dataStore.getUint( + keccak256(abi.encode(key, SRC_CHAIN_ID)) )); withdrawal.setShouldUnwrapNativeToken(dataStore.getBool( @@ -194,8 +194,8 @@ library GlvWithdrawalStoreUtils { ); dataStore.setUint( - keccak256(abi.encode(key, CHAIN_ID)), - withdrawal.chainId() + keccak256(abi.encode(key, SRC_CHAIN_ID)), + withdrawal.srcChainId() ); dataStore.setBool( @@ -281,7 +281,7 @@ library GlvWithdrawalStoreUtils { ); dataStore.removeUint( - keccak256(abi.encode(key, CHAIN_ID)) + keccak256(abi.encode(key, SRC_CHAIN_ID)) ); dataStore.removeBool( diff --git a/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol b/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol index 48719a2d8..d3f1ea45c 100644 --- a/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol +++ b/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol @@ -31,7 +31,7 @@ library GlvWithdrawalUtils { bool shouldUnwrapNativeToken; uint256 executionFee; uint256 callbackGasLimit; - uint256 chainId; + uint256 srcChainId; bytes32[] dataList; } @@ -111,7 +111,7 @@ library GlvWithdrawalUtils { updatedAtTime: Chain.currentTimestamp(), executionFee: params.executionFee, callbackGasLimit: params.callbackGasLimit, - chainId: params.chainId + srcChainId: params.srcChainId }), GlvWithdrawal.Flags({shouldUnwrapNativeToken: params.shouldUnwrapNativeToken}), params.dataList @@ -231,7 +231,7 @@ library GlvWithdrawalUtils { updatedAtTime: glvWithdrawal.updatedAtTime(), executionFee: 0, callbackGasLimit: 0, - chainId: glvWithdrawal.chainId() + srcChainId: glvWithdrawal.srcChainId() }), Withdrawal.Flags({shouldUnwrapNativeToken: glvWithdrawal.shouldUnwrapNativeToken()}), glvWithdrawal.dataList() diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 80c32b0f9..32731e55b 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -28,19 +28,19 @@ contract MultichainRouter is GelatoRelayRouter { bytes32 public constant CREATE_DEPOSIT_TYPEHASH = keccak256( bytes( - "CreateDeposit(CreateDepositParams params,bytes32 relayParams)CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 chainId,bytes32[] dataList)" + "CreateDeposit(CreateDepositParams params,bytes32 relayParams)CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" ) ); bytes32 public constant CREATE_DEPOSIT_PARAMS_TYPEHASH = keccak256( bytes( - "CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 chainId,bytes32[] dataList)" + "CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" ) ); bytes32 public constant GASLESS_CREATE_DEPOSIT_PARAMS_TYPEHASH = keccak256( bytes( - "GaslessCreateDepositParams(uint256 chainId,uint256 longTokenAmount,uint256 shortTokenAmount,CreateDepositParams params)CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 chainId,bytes32[] dataList)" + "GaslessCreateDepositParams(uint256 destChainId,uint256 longTokenAmount,uint256 shortTokenAmount,CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" ) ); @@ -116,7 +116,7 @@ contract MultichainRouter is GelatoRelayRouter { contracts, tokenPermits, fee, // feeAmount is relayFee + executionFee - params.createDepositParams.chainId, + params.createDepositParams.srcChainId, account, NonceUtils.getKey(contracts.dataStore, NonceUtils.getCurrentNonce(dataStore) + 1), // calculate next key without incrementing // if initialLongTokenAmount or initialShortTokenAmount is wnt then executionFee will be subracted (in DepositUtils.createDeposit) from one of them @@ -204,7 +204,9 @@ contract MultichainRouter is GelatoRelayRouter { params.minMarketTokens, params.shouldUnwrapNativeToken, params.executionFee, - params.callbackGasLimit + params.callbackGasLimit, + // params.srcChainId, // TODO: adding another field throws with slot too deep error + keccak256(abi.encodePacked(params.dataList)) ) ); } diff --git a/contracts/router/relay/BaseGelatoRelayRouter.sol b/contracts/router/relay/BaseGelatoRelayRouter.sol index 2b6e233b3..8fef59700 100644 --- a/contracts/router/relay/BaseGelatoRelayRouter.sol +++ b/contracts/router/relay/BaseGelatoRelayRouter.sol @@ -45,6 +45,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, uint256 userNonce; uint256 deadline; bytes signature; + uint256 srcChainId; } struct UpdateOrderParams { @@ -105,7 +106,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, function _updateOrder( RelayParams calldata relayParams, - uint256 chainId, + uint256 srcChainId, address account, bytes32 key, UpdateOrderParams calldata params @@ -126,7 +127,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, revert Errors.Unauthorized(account, "account for updateOrder"); } - _handleRelay(contracts, relayParams.tokenPermits, relayParams.fee, chainId, account, key, account); + _handleRelay(contracts, relayParams.tokenPermits, relayParams.fee, srcChainId, account, key, account); orderHandler.updateOrder( key, @@ -140,7 +141,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, ); } - function _cancelOrder(RelayParams calldata relayParams, uint256 chainId, address account, bytes32 key) internal { + function _cancelOrder(RelayParams calldata relayParams, uint256 srcChainId, address account, bytes32 key) internal { Contracts memory contracts = Contracts({ dataStore: dataStore, eventEmitter: eventEmitter, @@ -156,14 +157,14 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, revert Errors.Unauthorized(account, "account for cancelOrder"); } - _handleRelay(contracts, relayParams.tokenPermits, relayParams.fee, chainId, account, key, account); + _handleRelay(contracts, relayParams.tokenPermits, relayParams.fee, srcChainId, account, key, account); orderHandler.cancelOrder(key); } function _createOrder( RelayParams calldata relayParams, - uint256 chainId, + uint256 srcChainId, address account, uint256 collateralDeltaAmount, IBaseOrderUtils.CreateOrderParams memory params // can't use calldata because need to modify params.numbers.executionFee @@ -178,7 +179,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, contracts, relayParams.tokenPermits, relayParams.fee, - chainId, + srcChainId, account, NonceUtils.getNextKey(contracts.dataStore), // order key address(contracts.orderVault) @@ -244,13 +245,13 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, Contracts memory contracts, TokenPermit[] calldata tokenPermits, RelayFeeParams calldata fee, - uint256 chainId, + uint256 srcChainId, address account, bytes32 orderKey, address residualFeeReceiver ) internal returns (uint256) { _handleTokenPermits(tokenPermits); - return _handleRelayFee(contracts, fee, chainId, account, orderKey, residualFeeReceiver); + return _handleRelayFee(contracts, fee, srcChainId, account, orderKey, residualFeeReceiver); } function _handleTokenPermits(TokenPermit[] calldata tokenPermits) internal { @@ -290,7 +291,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, function _handleRelayFee( Contracts memory contracts, RelayFeeParams calldata fee, - uint256 chainId, + uint256 srcChainId, address account, bytes32 orderKey, address residualFeeReceiver @@ -316,7 +317,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, uint256 residualFee = outputAmount - requiredRelayFee; // for orders the residual fee is sent to the order vault // for other actions the residual fee is sent back to the user - _transferResidualFee(wnt, residualFeeReceiver, residualFee, chainId, account); + _transferResidualFee(wnt, residualFeeReceiver, residualFee, srcChainId, account); return residualFee; } @@ -327,8 +328,8 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, } // for multichain actions, the residual fee is send back to MultichainVault and user's multichain balance is increased - function _transferResidualFee(address wnt, address residualFeeReceiver, uint256 residualFee, uint256 /*chainId*/, address /*account*/) internal virtual { - // account and chainId not used here, but necessary when overriding _transferResidualFee in MultichainRouter + function _transferResidualFee(address wnt, address residualFeeReceiver, uint256 residualFee, uint256 /*srcChainId*/, address /*account*/) internal virtual { + // account and srcChainId not used here, but necessary when overriding _transferResidualFee in MultichainRouter TokenUtils.transfer(dataStore, wnt, residualFeeReceiver, residualFee); } diff --git a/contracts/shift/Shift.sol b/contracts/shift/Shift.sol index d30b5e07d..6a8f7319c 100644 --- a/contracts/shift/Shift.sol +++ b/contracts/shift/Shift.sol @@ -24,7 +24,7 @@ library Shift { uint256 updatedAtTime; uint256 executionFee; uint256 callbackGasLimit; - uint256 chainId; + uint256 srcChainId; } function account(Props memory props) internal pure returns (address) { @@ -115,12 +115,12 @@ library Shift { props.numbers.callbackGasLimit = value; } - function chainId(Props memory props) internal pure returns (uint256) { - return props.numbers.chainId; + function srcChainId(Props memory props) internal pure returns (uint256) { + return props.numbers.srcChainId; } - function setChainId(Props memory props, uint256 value) internal pure { - props.numbers.chainId = value; + function setSrcChainId(Props memory props, uint256 value) internal pure { + props.numbers.srcChainId = value; } function dataList(Props memory props) internal pure returns (bytes32[] memory) { diff --git a/contracts/shift/ShiftStoreUtils.sol b/contracts/shift/ShiftStoreUtils.sol index 9e0e28257..606bceb95 100644 --- a/contracts/shift/ShiftStoreUtils.sol +++ b/contracts/shift/ShiftStoreUtils.sol @@ -22,11 +22,10 @@ library ShiftStoreUtils { bytes32 public constant UPDATED_AT_TIME = keccak256(abi.encode("UPDATED_AT_TIME")); bytes32 public constant EXECUTION_FEE = keccak256(abi.encode("EXECUTION_FEE")); bytes32 public constant CALLBACK_GAS_LIMIT = keccak256(abi.encode("CALLBACK_GAS_LIMIT")); + bytes32 public constant SRC_CHAIN_ID = keccak256(abi.encode("SRC_CHAIN_ID")); bytes32 public constant DATA_LIST = keccak256(abi.encode("DATA_LIST")); - bytes32 public constant CHAIN_ID = keccak256(abi.encode("CHAIN_ID")); - function get(DataStore dataStore, bytes32 key) external view returns (Shift.Props memory) { Shift.Props memory shift; if (!dataStore.containsBytes32(Keys.SHIFT_LIST, key)) { @@ -77,8 +76,8 @@ library ShiftStoreUtils { keccak256(abi.encode(key, CALLBACK_GAS_LIMIT)) )); - shift.setChainId(dataStore.getUint( - keccak256(abi.encode(key, CHAIN_ID)) + shift.setSrcChainId(dataStore.getUint( + keccak256(abi.encode(key, SRC_CHAIN_ID)) )); shift.setDataList(dataStore.getBytes32Array( @@ -155,8 +154,8 @@ library ShiftStoreUtils { ); dataStore.setUint( - keccak256(abi.encode(key, CHAIN_ID)), - shift.chainId() + keccak256(abi.encode(key, SRC_CHAIN_ID)), + shift.srcChainId() ); dataStore.setBytes32Array( @@ -225,7 +224,7 @@ library ShiftStoreUtils { ); dataStore.removeUint( - keccak256(abi.encode(key, CHAIN_ID)) + keccak256(abi.encode(key, SRC_CHAIN_ID)) ); dataStore.removeBytes32Array( diff --git a/contracts/shift/ShiftUtils.sol b/contracts/shift/ShiftUtils.sol index c840ac3bc..e773c406a 100644 --- a/contracts/shift/ShiftUtils.sol +++ b/contracts/shift/ShiftUtils.sol @@ -40,7 +40,7 @@ library ShiftUtils { uint256 minMarketTokens; uint256 executionFee; uint256 callbackGasLimit; - uint256 chainId; + uint256 srcChainId; bytes32[] dataList; } @@ -129,7 +129,7 @@ library ShiftUtils { Chain.currentTimestamp(), params.executionFee, params.callbackGasLimit, - params.chainId + params.srcChainId ), params.dataList ); @@ -201,7 +201,7 @@ library ShiftUtils { shift.updatedAtTime(), 0, // executionFee 0, // callbackGasLimit - shift.chainId() + shift.srcChainId() ), Withdrawal.Flags( false @@ -263,7 +263,7 @@ library ShiftUtils { shift.updatedAtTime(), 0, // executionFee 0, // callbackGasLimit - shift.chainId() + shift.srcChainId() ), Deposit.Flags( false // shouldUnwrapNativeToken @@ -291,7 +291,7 @@ library ShiftUtils { params.startingGas, ISwapPricingUtils.SwapPricingType.Shift, false, // includeVirtualInventoryImpact - shift.chainId() + shift.srcChainId() ); cache.receivedMarketTokens = ExecuteDepositUtils.executeDeposit( diff --git a/contracts/withdrawal/Withdrawal.sol b/contracts/withdrawal/Withdrawal.sol index 642546fc8..485f331d8 100644 --- a/contracts/withdrawal/Withdrawal.sol +++ b/contracts/withdrawal/Withdrawal.sol @@ -53,7 +53,7 @@ library Withdrawal { uint256 updatedAtTime; uint256 executionFee; uint256 callbackGasLimit; - uint256 chainId; + uint256 srcChainId; } // @param shouldUnwrapNativeToken whether to unwrap the native token when @@ -165,12 +165,12 @@ library Withdrawal { props.numbers.callbackGasLimit = value; } - function chainId(Props memory props) internal pure returns (uint256) { - return props.numbers.chainId; + function srcChainId(Props memory props) internal pure returns (uint256) { + return props.numbers.srcChainId; } - function setChainId(Props memory props, uint256 value) internal pure { - props.numbers.chainId = value; + function setSrcChainId(Props memory props, uint256 value) internal pure { + props.numbers.srcChainId = value; } function shouldUnwrapNativeToken(Props memory props) internal pure returns (bool) { diff --git a/contracts/withdrawal/WithdrawalStoreUtils.sol b/contracts/withdrawal/WithdrawalStoreUtils.sol index a2e718400..07c028d9c 100644 --- a/contracts/withdrawal/WithdrawalStoreUtils.sol +++ b/contracts/withdrawal/WithdrawalStoreUtils.sol @@ -28,13 +28,12 @@ library WithdrawalStoreUtils { bytes32 public constant UPDATED_AT_TIME = keccak256(abi.encode("UPDATED_AT_TIME")); bytes32 public constant EXECUTION_FEE = keccak256(abi.encode("EXECUTION_FEE")); bytes32 public constant CALLBACK_GAS_LIMIT = keccak256(abi.encode("CALLBACK_GAS_LIMIT")); + bytes32 public constant SRC_CHAIN_ID = keccak256(abi.encode("SRC_CHAIN_ID")); bytes32 public constant SHOULD_UNWRAP_NATIVE_TOKEN = keccak256(abi.encode("SHOULD_UNWRAP_NATIVE_TOKEN")); bytes32 public constant DATA_LIST = keccak256(abi.encode("DATA_LIST")); - bytes32 public constant CHAIN_ID = keccak256(abi.encode("CHAIN_ID")); - function get(DataStore dataStore, bytes32 key) external view returns (Withdrawal.Props memory) { Withdrawal.Props memory withdrawal; if (!dataStore.containsBytes32(Keys.WITHDRAWAL_LIST, key)) { @@ -93,8 +92,8 @@ library WithdrawalStoreUtils { keccak256(abi.encode(key, CALLBACK_GAS_LIMIT)) )); - withdrawal.setChainId(dataStore.getUint( - keccak256(abi.encode(key, CHAIN_ID)) + withdrawal.setSrcChainId(dataStore.getUint( + keccak256(abi.encode(key, SRC_CHAIN_ID)) )); withdrawal.setShouldUnwrapNativeToken(dataStore.getBool( @@ -185,8 +184,8 @@ library WithdrawalStoreUtils { ); dataStore.setUint( - keccak256(abi.encode(key, CHAIN_ID)), - withdrawal.chainId() + keccak256(abi.encode(key, SRC_CHAIN_ID)), + withdrawal.srcChainId() ); dataStore.setBool( @@ -264,7 +263,7 @@ library WithdrawalStoreUtils { ); dataStore.removeUint( - keccak256(abi.encode(key, CHAIN_ID)) + keccak256(abi.encode(key, SRC_CHAIN_ID)) ); dataStore.removeUint( diff --git a/contracts/withdrawal/WithdrawalUtils.sol b/contracts/withdrawal/WithdrawalUtils.sol index 9cd3160bf..25094ba42 100644 --- a/contracts/withdrawal/WithdrawalUtils.sol +++ b/contracts/withdrawal/WithdrawalUtils.sol @@ -58,7 +58,7 @@ library WithdrawalUtils { bool shouldUnwrapNativeToken; uint256 executionFee; uint256 callbackGasLimit; - uint256 chainId; + uint256 srcChainId; bytes32[] dataList; } @@ -119,7 +119,7 @@ library WithdrawalUtils { Chain.currentTimestamp(), // updatedAtTime params.executionFee, params.callbackGasLimit, - params.chainId + params.srcChainId ), Withdrawal.Flags( params.shouldUnwrapNativeToken From 0c1ff1aff9cd16ebdb3e327812b32a47e927f68a Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 5 Feb 2025 20:44:28 +0200 Subject: [PATCH 019/205] Move srcChainId into RelayParams --- contracts/multichain/MultichainRouter.sol | 8 ++++---- contracts/router/relay/BaseGelatoRelayRouter.sol | 10 ++++------ contracts/router/relay/GelatoRelayRouter.sol | 9 +++------ contracts/router/relay/SubaccountGelatoRelayRouter.sol | 9 +++------ 4 files changed, 14 insertions(+), 22 deletions(-) diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 32731e55b..1354d826c 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -12,7 +12,6 @@ import "./MultichainUtils.sol"; contract MultichainRouter is GelatoRelayRouter { struct GaslessCreateDepositParams { - uint256 chainId; uint256 longTokenAmount; uint256 shortTokenAmount; DepositUtils.CreateDepositParams createDepositParams; @@ -40,7 +39,7 @@ contract MultichainRouter is GelatoRelayRouter { bytes32 public constant GASLESS_CREATE_DEPOSIT_PARAMS_TYPEHASH = keccak256( bytes( - "GaslessCreateDepositParams(uint256 destChainId,uint256 longTokenAmount,uint256 shortTokenAmount,CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + "GaslessCreateDepositParams(uint256 srcChainId,uint256 longTokenAmount,uint256 shortTokenAmount,CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" ) ); @@ -165,20 +164,21 @@ contract MultichainRouter is GelatoRelayRouter { keccak256( abi.encode( CREATE_DEPOSIT_TYPEHASH, - _getGaslessCreateDepositParamsStructHash(params), + _getGaslessCreateDepositParamsStructHash(relayParams.srcChainId, params), relayParamsHash ) ); } function _getGaslessCreateDepositParamsStructHash( + uint256 srcChainId, GaslessCreateDepositParams memory params ) internal pure returns (bytes32) { return keccak256( abi.encode( GASLESS_CREATE_DEPOSIT_PARAMS_TYPEHASH, - params.chainId, + srcChainId, params.longTokenAmount, params.shortTokenAmount, _getCreateDepositParamsStructHash(params.createDepositParams) diff --git a/contracts/router/relay/BaseGelatoRelayRouter.sol b/contracts/router/relay/BaseGelatoRelayRouter.sol index 8fef59700..c6b03b7a8 100644 --- a/contracts/router/relay/BaseGelatoRelayRouter.sol +++ b/contracts/router/relay/BaseGelatoRelayRouter.sol @@ -106,7 +106,6 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, function _updateOrder( RelayParams calldata relayParams, - uint256 srcChainId, address account, bytes32 key, UpdateOrderParams calldata params @@ -127,7 +126,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, revert Errors.Unauthorized(account, "account for updateOrder"); } - _handleRelay(contracts, relayParams.tokenPermits, relayParams.fee, srcChainId, account, key, account); + _handleRelay(contracts, relayParams.tokenPermits, relayParams.fee, relayParams.srcChainId, account, key, account); orderHandler.updateOrder( key, @@ -141,7 +140,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, ); } - function _cancelOrder(RelayParams calldata relayParams, uint256 srcChainId, address account, bytes32 key) internal { + function _cancelOrder(RelayParams calldata relayParams, address account, bytes32 key) internal { Contracts memory contracts = Contracts({ dataStore: dataStore, eventEmitter: eventEmitter, @@ -157,14 +156,13 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, revert Errors.Unauthorized(account, "account for cancelOrder"); } - _handleRelay(contracts, relayParams.tokenPermits, relayParams.fee, srcChainId, account, key, account); + _handleRelay(contracts, relayParams.tokenPermits, relayParams.fee, relayParams.srcChainId, account, key, account); orderHandler.cancelOrder(key); } function _createOrder( RelayParams calldata relayParams, - uint256 srcChainId, address account, uint256 collateralDeltaAmount, IBaseOrderUtils.CreateOrderParams memory params // can't use calldata because need to modify params.numbers.executionFee @@ -179,7 +177,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, contracts, relayParams.tokenPermits, relayParams.fee, - srcChainId, + relayParams.srcChainId, account, NonceUtils.getNextKey(contracts.dataStore), // order key address(contracts.orderVault) diff --git a/contracts/router/relay/GelatoRelayRouter.sol b/contracts/router/relay/GelatoRelayRouter.sol index ebbd795a2..aea6844dd 100644 --- a/contracts/router/relay/GelatoRelayRouter.sol +++ b/contracts/router/relay/GelatoRelayRouter.sol @@ -57,7 +57,6 @@ contract GelatoRelayRouter is BaseGelatoRelayRouter { function createOrder( RelayParams calldata relayParams, - uint256 chainId, // TODO: should this be part of CreateOrderParams instead? means adding it to Order.Props as well. address account, uint256 collateralDeltaAmount, IBaseOrderUtils.CreateOrderParams memory params // can't use calldata because need to modify params.numbers.executionFee @@ -71,12 +70,11 @@ contract GelatoRelayRouter is BaseGelatoRelayRouter { bytes32 structHash = _getCreateOrderStructHash(relayParams, collateralDeltaAmount, params); _validateCall(relayParams, account, structHash); - return _createOrder(relayParams, chainId, account, collateralDeltaAmount, params); + return _createOrder(relayParams, account, collateralDeltaAmount, params); } function updateOrder( RelayParams calldata relayParams, - uint256 chainId, address account, bytes32 key, UpdateOrderParams calldata params @@ -84,19 +82,18 @@ contract GelatoRelayRouter is BaseGelatoRelayRouter { bytes32 structHash = _getUpdateOrderStructHash(relayParams, key, params); _validateCall(relayParams, account, structHash); - _updateOrder(relayParams, chainId, account, key, params); + _updateOrder(relayParams, account, key, params); } function cancelOrder( RelayParams calldata relayParams, - uint256 chainId, address account, bytes32 key ) external nonReentrant withOraclePricesForAtomicAction(relayParams.oracleParams) onlyGelatoRelay { bytes32 structHash = _getCancelOrderStructHash(relayParams, key); _validateCall(relayParams, account, structHash); - _cancelOrder(relayParams, chainId, account, key); + _cancelOrder(relayParams, account, key); } function _getUpdateOrderStructHash( diff --git a/contracts/router/relay/SubaccountGelatoRelayRouter.sol b/contracts/router/relay/SubaccountGelatoRelayRouter.sol index 12af78eda..9aa61026b 100644 --- a/contracts/router/relay/SubaccountGelatoRelayRouter.sol +++ b/contracts/router/relay/SubaccountGelatoRelayRouter.sol @@ -79,7 +79,6 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { function createOrder( RelayParams calldata relayParams, SubaccountApproval calldata subaccountApproval, - uint256 chainId, address account, // main account address subaccount, uint256 collateralDeltaAmount, @@ -109,13 +108,12 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { revert Errors.InvalidCancellationReceiverForSubaccountOrder(params.addresses.cancellationReceiver, account); } - return _createOrder(relayParams, chainId, account, collateralDeltaAmount, params); + return _createOrder(relayParams, account, collateralDeltaAmount, params); } function updateOrder( RelayParams calldata relayParams, SubaccountApproval calldata subaccountApproval, - uint256 chainId, address account, // main account address subaccount, bytes32 key, @@ -124,13 +122,12 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { bytes32 structHash = _getUpdateOrderStructHash(relayParams, subaccountApproval, account, key, params); _validateCall(relayParams, subaccount, structHash); _handleSubaccountAction(account, subaccount, Keys.SUBACCOUNT_ORDER_ACTION, subaccountApproval); - _updateOrder(relayParams, chainId, account, key, params); + _updateOrder(relayParams, account, key, params); } function cancelOrder( RelayParams calldata relayParams, SubaccountApproval calldata subaccountApproval, - uint256 chainId, address account, // main account address subaccount, bytes32 key @@ -138,7 +135,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { bytes32 structHash = _getCancelOrderStructHash(relayParams, subaccountApproval, account, key); _validateCall(relayParams, subaccount, structHash); _handleSubaccountAction(account, subaccount, Keys.SUBACCOUNT_ORDER_ACTION, subaccountApproval); - _cancelOrder(relayParams, chainId, account, key); + _cancelOrder(relayParams, account, key); } function removeSubaccount( From 76dfaddbf9c980bf19747cab32378e08cc07ffbc Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 6 Feb 2025 09:28:45 +0200 Subject: [PATCH 020/205] Emit srcChainId for createWithdrawal, createShift, createGlvDeposit, createGlvWithdrawal --- contracts/glv/glvDeposit/GlvDepositEventUtils.sol | 3 ++- contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol | 3 ++- contracts/shift/ShiftEventUtils.sol | 3 ++- contracts/withdrawal/WithdrawalEventUtils.sol | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/contracts/glv/glvDeposit/GlvDepositEventUtils.sol b/contracts/glv/glvDeposit/GlvDepositEventUtils.sol index c07467b27..78483fa78 100644 --- a/contracts/glv/glvDeposit/GlvDepositEventUtils.sol +++ b/contracts/glv/glvDeposit/GlvDepositEventUtils.sol @@ -40,7 +40,7 @@ library GlvDepositEventUtils { eventData.addressItems.setItem(0, "longTokenSwapPath", glvDeposit.longTokenSwapPath()); eventData.addressItems.setItem(1, "shortTokenSwapPath", glvDeposit.shortTokenSwapPath()); - eventData.uintItems.initItems(7); + eventData.uintItems.initItems(8); eventData.uintItems.setItem(0, "initialLongTokenAmount", glvDeposit.initialLongTokenAmount()); eventData.uintItems.setItem(1, "initialShortTokenAmount", glvDeposit.initialShortTokenAmount()); eventData.uintItems.setItem(2, "minGlvTokens", glvDeposit.minGlvTokens()); @@ -48,6 +48,7 @@ library GlvDepositEventUtils { eventData.uintItems.setItem(4, "executionFee", glvDeposit.executionFee()); eventData.uintItems.setItem(5, "callbackGasLimit", glvDeposit.callbackGasLimit()); eventData.uintItems.setItem(6, "marketTokenAmount", glvDeposit.marketTokenAmount()); + eventData.uintItems.setItem(7, "srcChainId", glvDeposit.srcChainId()); eventData.boolItems.initItems(2); eventData.boolItems.setItem(0, "shouldUnwrapNativeToken", glvDeposit.shouldUnwrapNativeToken()); diff --git a/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol b/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol index 231ac79ab..dc94afb1b 100644 --- a/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol +++ b/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol @@ -38,13 +38,14 @@ library GlvWithdrawalEventUtils { eventData.addressItems.setItem(0, "longTokenSwapPath", glvWithdrawal.longTokenSwapPath()); eventData.addressItems.setItem(1, "shortTokenSwapPath", glvWithdrawal.shortTokenSwapPath()); - eventData.uintItems.initItems(6); + eventData.uintItems.initItems(7); eventData.uintItems.setItem(0, "glvTokenAmount", glvWithdrawal.glvTokenAmount()); eventData.uintItems.setItem(1, "minLongTokenAmount", glvWithdrawal.minLongTokenAmount()); eventData.uintItems.setItem(2, "minShortTokenAmount", glvWithdrawal.minShortTokenAmount()); eventData.uintItems.setItem(3, "updatedAtTime", glvWithdrawal.updatedAtTime()); eventData.uintItems.setItem(4, "executionFee", glvWithdrawal.executionFee()); eventData.uintItems.setItem(5, "callbackGasLimit", glvWithdrawal.callbackGasLimit()); + eventData.uintItems.setItem(6, "srcChainId", glvWithdrawal.srcChainId()); eventData.boolItems.initItems(1); eventData.boolItems.setItem(0, "shouldUnwrapNativeToken", glvWithdrawal.shouldUnwrapNativeToken()); diff --git a/contracts/shift/ShiftEventUtils.sol b/contracts/shift/ShiftEventUtils.sol index 32c722803..7f99c4294 100644 --- a/contracts/shift/ShiftEventUtils.sol +++ b/contracts/shift/ShiftEventUtils.sol @@ -34,12 +34,13 @@ library ShiftEventUtils { eventData.addressItems.setItem(4, "toMarket", shift.toMarket()); eventData.addressItems.setItem(5, "uiFeeReceiver", shift.uiFeeReceiver()); - eventData.uintItems.initItems(5); + eventData.uintItems.initItems(6); eventData.uintItems.setItem(0, "marketTokenAmount", shift.marketTokenAmount()); eventData.uintItems.setItem(1, "minMarketTokens", shift.minMarketTokens()); eventData.uintItems.setItem(2, "updatedAtTime", shift.updatedAtTime()); eventData.uintItems.setItem(3, "executionFee", shift.executionFee()); eventData.uintItems.setItem(4, "callbackGasLimit", shift.callbackGasLimit()); + eventData.uintItems.setItem(5, "srcChainId", shift.srcChainId()); eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); diff --git a/contracts/withdrawal/WithdrawalEventUtils.sol b/contracts/withdrawal/WithdrawalEventUtils.sol index 364f146a9..99b0eb2d5 100644 --- a/contracts/withdrawal/WithdrawalEventUtils.sol +++ b/contracts/withdrawal/WithdrawalEventUtils.sol @@ -39,7 +39,7 @@ library WithdrawalEventUtils { eventData.addressItems.setItem(0, "longTokenSwapPath", withdrawal.longTokenSwapPath()); eventData.addressItems.setItem(1, "shortTokenSwapPath", withdrawal.shortTokenSwapPath()); - eventData.uintItems.initItems(7); + eventData.uintItems.initItems(8); eventData.uintItems.setItem(0, "marketTokenAmount", withdrawal.marketTokenAmount()); eventData.uintItems.setItem(1, "minLongTokenAmount", withdrawal.minLongTokenAmount()); eventData.uintItems.setItem(2, "minShortTokenAmount", withdrawal.minShortTokenAmount()); @@ -47,6 +47,7 @@ library WithdrawalEventUtils { eventData.uintItems.setItem(4, "executionFee", withdrawal.executionFee()); eventData.uintItems.setItem(5, "callbackGasLimit", withdrawal.callbackGasLimit()); eventData.uintItems.setItem(6, "withdrawalType", uint256(withdrawalType)); + eventData.uintItems.setItem(7, "srcChainId", withdrawal.srcChainId()); eventData.boolItems.initItems(1); eventData.boolItems.setItem(0, "shouldUnwrapNativeToken", withdrawal.shouldUnwrapNativeToken()); From df9ac7085d4eeec38d80f2967a9d31697b8fadad Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 6 Feb 2025 10:36:01 +0200 Subject: [PATCH 021/205] Fix tests after merging gasless branch --- test/router/ExchangeRouter.ts | 10 +++++----- utils/deposit.ts | 4 ++-- utils/glv/glvDeposit.ts | 4 ++-- utils/glv/glvWithdrawal.ts | 6 +++--- utils/shift.ts | 4 ++-- utils/withdrawal.ts | 8 ++++---- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/test/router/ExchangeRouter.ts b/test/router/ExchangeRouter.ts index dee7b6a2d..40672e6f4 100644 --- a/test/router/ExchangeRouter.ts +++ b/test/router/ExchangeRouter.ts @@ -74,7 +74,7 @@ describe("ExchangeRouter", () => { shouldUnwrapNativeToken: true, executionFee, callbackGasLimit: "200000", - chainId: 0, + srcChainId: 0, dataList, }, ]), @@ -98,7 +98,7 @@ describe("ExchangeRouter", () => { expect(deposit.numbers.minMarketTokens).eq(100); expect(deposit.numbers.executionFee).eq(expandDecimals(1, 18)); expect(deposit.numbers.callbackGasLimit).eq("200000"); - expect(deposit.numbers.chainId).eq(0); + expect(deposit.numbers.srcChainId).eq(0); expect(deposit.flags.shouldUnwrapNativeToken).eq(true); expect(deposit._dataList).deep.eq(dataList); @@ -216,7 +216,7 @@ describe("ExchangeRouter", () => { shouldUnwrapNativeToken: true, executionFee, callbackGasLimit: "200000", - chainId: 1, + srcChainId: 1, dataList, }, ]), @@ -239,7 +239,7 @@ describe("ExchangeRouter", () => { expect(withdrawal.numbers.minShortTokenAmount).eq(900); expect(withdrawal.numbers.executionFee).eq(expandDecimals(1, 18)); expect(withdrawal.numbers.callbackGasLimit).eq("200000"); - expect(withdrawal.numbers.chainId).eq(1); + expect(withdrawal.numbers.srcChainId).eq(1); expect(withdrawal.flags.shouldUnwrapNativeToken).eq(true); expect(withdrawal._dataList).deep.eq(dataList); @@ -281,7 +281,7 @@ describe("ExchangeRouter", () => { shouldUnwrapNativeToken: true, executionFee, callbackGasLimit: "200000", - chainId: 0, + srcChainId: 0, dataList: [], }, ]), diff --git a/utils/deposit.ts b/utils/deposit.ts index 0c1ca9020..1e3ebd885 100644 --- a/utils/deposit.ts +++ b/utils/deposit.ts @@ -45,7 +45,7 @@ export async function createDeposit(fixture, overrides: any = {}) { const callbackGasLimit = overrides.callbackGasLimit || bigNumberify(0); const longTokenAmount = overrides.longTokenAmount || bigNumberify(0); const shortTokenAmount = overrides.shortTokenAmount || bigNumberify(0); - const chainId = overrides.chainId || bigNumberify(0); + const srcChainId = overrides.srcChainId || bigNumberify(0); const dataList = overrides.dataList || []; await wnt.mint(depositVault.address, executionFeeToMint); @@ -73,7 +73,7 @@ export async function createDeposit(fixture, overrides: any = {}) { shouldUnwrapNativeToken, executionFee, callbackGasLimit, - chainId, + srcChainId, dataList, }; diff --git a/utils/glv/glvDeposit.ts b/utils/glv/glvDeposit.ts index f4d0723ea..d09862755 100644 --- a/utils/glv/glvDeposit.ts +++ b/utils/glv/glvDeposit.ts @@ -48,7 +48,7 @@ export async function createGlvDeposit(fixture, overrides: any = {}) { const shouldUnwrapNativeToken = overrides.shouldUnwrapNativeToken || false; const executionFee = bigNumberify(overrides.executionFee ?? "1000000000000000"); const callbackGasLimit = bigNumberify(overrides.callbackGasLimit ?? 0); - const chainId = bigNumberify(overrides.chainId ?? 0); + const srcChainId = bigNumberify(overrides.srcChainId ?? 0); const marketTokenAmount = bigNumberify(overrides.marketTokenAmount ?? 0); const longTokenAmount = bigNumberify(overrides.longTokenAmount ?? 0); const shortTokenAmount = bigNumberify(overrides.shortTokenAmount ?? 0); @@ -96,7 +96,7 @@ export async function createGlvDeposit(fixture, overrides: any = {}) { shouldUnwrapNativeToken, executionFee, callbackGasLimit, - chainId, + srcChainId, isMarketTokenDeposit, gasUsageLabel, dataList, diff --git a/utils/glv/glvWithdrawal.ts b/utils/glv/glvWithdrawal.ts index 0870f2f25..728ee8893 100644 --- a/utils/glv/glvWithdrawal.ts +++ b/utils/glv/glvWithdrawal.ts @@ -48,7 +48,7 @@ export async function createGlvWithdrawal(fixture, overrides: any = {}) { const shouldUnwrapNativeToken = overrides.shouldUnwrapNativeToken || false; const executionFee = bigNumberify(overrides.executionFee ?? "1000000000000000"); const callbackGasLimit = bigNumberify(overrides.callbackGasLimit ?? 0); - const chainId = bigNumberify(overrides.chainId ?? 0); + const srcChainId = bigNumberify(overrides.srcChainId ?? 0); const useGlvHandler = Boolean(overrides.useGlvHandler) || false; const dataList = overrides.dataList || []; @@ -80,7 +80,7 @@ export async function createGlvWithdrawal(fixture, overrides: any = {}) { shouldUnwrapNativeToken, executionFee, callbackGasLimit, - chainId, + srcChainId, dataList, }; @@ -192,7 +192,7 @@ export function expectEmptyGlvWithdrawal(glvWithdrawal: any) { expect(glvWithdrawal.numbers.updatedAtTime).eq(0); expect(glvWithdrawal.numbers.executionFee).eq(0); expect(glvWithdrawal.numbers.callbackGasLimit).eq(0); - expect(glvWithdrawal.numbers.chainId).eq(0); + expect(glvWithdrawal.numbers.srcChainId).eq(0); expect(glvWithdrawal.flags.shouldUnwrapNativeToken).eq(false); } diff --git a/utils/shift.ts b/utils/shift.ts index 22e9e9211..7628a7149 100644 --- a/utils/shift.ts +++ b/utils/shift.ts @@ -39,7 +39,7 @@ export async function createShift(fixture, overrides: any = {}) { const minMarketTokens = overrides.minMarketTokens || bigNumberify(0); const executionFee = overrides.executionFee || "1000000000000000"; const callbackGasLimit = overrides.callbackGasLimit || bigNumberify(0); - const chainId = overrides.chainId || bigNumberify(0); + const srcChainId = overrides.srcChainId || bigNumberify(0); const dataList = overrides.dataList || []; await wnt.mint(shiftVault.address, executionFee); @@ -56,7 +56,7 @@ export async function createShift(fixture, overrides: any = {}) { minMarketTokens, executionFee, callbackGasLimit, - chainId, + srcChainId, dataList, }; diff --git a/utils/withdrawal.ts b/utils/withdrawal.ts index 514a60de0..1f0a55229 100644 --- a/utils/withdrawal.ts +++ b/utils/withdrawal.ts @@ -41,7 +41,7 @@ export async function createWithdrawal(fixture, overrides: any = {}) { const shouldUnwrapNativeToken = overrides.shouldUnwrapNativeToken || false; const executionFee = overrides.executionFee || "1000000000000000"; const callbackGasLimit = overrides.callbackGasLimit || bigNumberify(0); - const chainId = overrides.chainId || bigNumberify(0); + const srcChainId = overrides.srcChainId || bigNumberify(0); const dataList = overrides.dataList || []; await wnt.mint(withdrawalVault.address, executionFee); @@ -62,7 +62,7 @@ export async function createWithdrawal(fixture, overrides: any = {}) { shouldUnwrapNativeToken, executionFee, callbackGasLimit, - chainId, + srcChainId, dataList, }; @@ -141,7 +141,7 @@ export async function executeAtomicWithdrawal(fixture, overrides: any = {}) { const shouldUnwrapNativeToken = overrides.shouldUnwrapNativeToken || false; const executionFee = overrides.executionFee || "1000000000000000"; const callbackGasLimit = overrides.callbackGasLimit || bigNumberify(0); - const chainId = overrides.chainId || bigNumberify(0); + const srcChainId = overrides.srcChainId || bigNumberify(0); const dataList = overrides.dataList || []; await wnt.mint(withdrawalVault.address, executionFee); @@ -162,7 +162,7 @@ export async function executeAtomicWithdrawal(fixture, overrides: any = {}) { shouldUnwrapNativeToken, executionFee, callbackGasLimit, - chainId, + srcChainId, dataList, }; From 8064ea1f201ba1c62c9faa8bf218959ff00fa52d Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 6 Feb 2025 12:31:17 +0200 Subject: [PATCH 022/205] Use srcChainId instead of block.chainid to _validateCall --- contracts/router/relay/BaseGelatoRelayRouter.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/contracts/router/relay/BaseGelatoRelayRouter.sol b/contracts/router/relay/BaseGelatoRelayRouter.sol index 37a41abf3..9ac0f50bf 100644 --- a/contracts/router/relay/BaseGelatoRelayRouter.sol +++ b/contracts/router/relay/BaseGelatoRelayRouter.sol @@ -336,7 +336,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, TokenUtils.transfer(dataStore, wnt, residualFeeReceiver, residualFee); } - function _getDomainSeparator(uint256 sourceChainId) internal view returns (bytes32) { // TODO: why is this named sourceChainId if it's the block.chainid? It makes you think it refers to a source chain e.g. Base + function _getDomainSeparator(uint256 sourceChainId) internal view returns (bytes32) { return keccak256( abi.encode( @@ -350,7 +350,8 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, } function _validateCall(RelayParams calldata relayParams, address account, bytes32 structHash) internal { - bytes32 domainSeparator = _getDomainSeparator(block.chainid); + uint256 srcChainId = relayParams.srcChainId == 0 ? block.chain : relayParams.srcChainId; + bytes32 domainSeparator = _getDomainSeparator(relayParams.srcChainId); bytes32 digest = ECDSA.toTypedDataHash(domainSeparator, structHash); _validateSignature(digest, relayParams.signature, account, "call"); From b9cc9bef3a84664462be04b29052845faa5554b8 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 6 Feb 2025 12:54:09 +0200 Subject: [PATCH 023/205] Use block.chainid as the desChainId to genearte gaslessCreateDepositParamsStructHash --- contracts/glv/glvShift/GlvShiftUtils.sol | 2 +- contracts/multichain/MultichainRouter.sol | 12 +++++------- contracts/router/relay/BaseGelatoRelayRouter.sol | 4 ++-- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/contracts/glv/glvShift/GlvShiftUtils.sol b/contracts/glv/glvShift/GlvShiftUtils.sol index 9a69316a3..4f7d6d5c5 100644 --- a/contracts/glv/glvShift/GlvShiftUtils.sol +++ b/contracts/glv/glvShift/GlvShiftUtils.sol @@ -139,7 +139,7 @@ library GlvShiftUtils { updatedAtTime: glvShift.updatedAtTime(), executionFee: 0, callbackGasLimit: 0, - srcChainId: 0 // TODO: should glvShift have the srcChainId as well? + srcChainId: 0 }), new bytes32[](0) ); diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 9b1a20e15..6cb5a68ab 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -19,7 +19,6 @@ contract MultichainRouter is GelatoRelayRouter { // TODO: extract multichain part from GaslessCreateDepositParams struct // struct MultichainCreateDepositParams { - // uint256 chainId; // uint256 longTokenAmount; // uint256 shortTokenAmount; // } @@ -39,7 +38,7 @@ contract MultichainRouter is GelatoRelayRouter { bytes32 public constant GASLESS_CREATE_DEPOSIT_PARAMS_TYPEHASH = keccak256( bytes( - "GaslessCreateDepositParams(uint256 srcChainId,uint256 longTokenAmount,uint256 shortTokenAmount,CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + "GaslessCreateDepositParams(uint256 desChainId,uint256 longTokenAmount,uint256 shortTokenAmount,CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" ) ); @@ -156,28 +155,27 @@ contract MultichainRouter is GelatoRelayRouter { function _getGaslessCreateDepositStructHash( RelayParams calldata relayParams, GaslessCreateDepositParams memory params - ) internal pure returns (bytes32) { + ) internal view returns (bytes32) { bytes32 relayParamsHash = keccak256(abi.encode(relayParams)); return keccak256( abi.encode( CREATE_DEPOSIT_TYPEHASH, - _getGaslessCreateDepositParamsStructHash(relayParams.srcChainId, params), + _getGaslessCreateDepositParamsStructHash(params), relayParamsHash ) ); } function _getGaslessCreateDepositParamsStructHash( - uint256 srcChainId, GaslessCreateDepositParams memory params - ) internal pure returns (bytes32) { + ) internal view returns (bytes32) { return keccak256( abi.encode( GASLESS_CREATE_DEPOSIT_PARAMS_TYPEHASH, - srcChainId, + block.chainid, params.longTokenAmount, params.shortTokenAmount, _getCreateDepositParamsStructHash(params.createDepositParams) diff --git a/contracts/router/relay/BaseGelatoRelayRouter.sol b/contracts/router/relay/BaseGelatoRelayRouter.sol index 9ac0f50bf..a8f7126a4 100644 --- a/contracts/router/relay/BaseGelatoRelayRouter.sol +++ b/contracts/router/relay/BaseGelatoRelayRouter.sol @@ -350,8 +350,8 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, } function _validateCall(RelayParams calldata relayParams, address account, bytes32 structHash) internal { - uint256 srcChainId = relayParams.srcChainId == 0 ? block.chain : relayParams.srcChainId; - bytes32 domainSeparator = _getDomainSeparator(relayParams.srcChainId); + uint256 srcChainId = relayParams.srcChainId == 0 ? block.chainid : relayParams.srcChainId; + bytes32 domainSeparator = _getDomainSeparator(srcChainId); bytes32 digest = ECDSA.toTypedDataHash(domainSeparator, structHash); _validateSignature(digest, relayParams.signature, account, "call"); From 1ba34771625ed904458e0a22bb9ffab8f78b610c Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 7 Feb 2025 13:59:56 +0200 Subject: [PATCH 024/205] Add in and out multichain transfers methods to MultichainUtils lib. Refactor all related contracts. --- contracts/error/Errors.sol | 4 +- contracts/multichain/LayerZeroProvider.sol | 28 ++++---- contracts/multichain/MultichainEventUtils.sol | 20 +++--- .../multichain/MultichainProviderUtils.sol | 4 +- contracts/multichain/MultichainRouter.sol | 29 ++------- contracts/multichain/MultichainUtils.sol | 62 +++++++++++++++++- contracts/multichain/MultichainVault.sol | 4 +- .../multichain/MultichainVaultHandler.sol | 65 ------------------- .../router/relay/BaseGelatoRelayRouter.sol | 4 +- contracts/token/TokenUtils.sol | 12 ---- deploy/deployLayerZeroProvider.ts | 2 +- 11 files changed, 96 insertions(+), 138 deletions(-) diff --git a/contracts/error/Errors.sol b/contracts/error/Errors.sol index 709429352..78c5e03d3 100644 --- a/contracts/error/Errors.sol +++ b/contracts/error/Errors.sol @@ -430,8 +430,8 @@ library Errors { error EmptyMarketPrice(address market); // Multichain errors - error EmptyMultichainDepositAmount(); - error EmptyMultichainWithdrawalAmount(); + error EmptyMultichainTransferInAmount(); + error EmptyMultichainTransferOutAmount(); error InsufficientMultichainBalance(); enum SignatureType { diff --git a/contracts/multichain/LayerZeroProvider.sol b/contracts/multichain/LayerZeroProvider.sol index 9ba04ca0d..296321bc6 100644 --- a/contracts/multichain/LayerZeroProvider.sol +++ b/contracts/multichain/LayerZeroProvider.sol @@ -7,10 +7,11 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ILayerZeroComposer } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroComposer.sol"; import { EventEmitter } from "../event/EventEmitter.sol"; +import { DataStore } from "../data/DataStore.sol"; import { IMultichainProvider } from "./IMultichainProvider.sol"; import { MultichainVault } from "./MultichainVault.sol"; -import { MultichainVaultHandler } from "./MultichainVaultHandler.sol"; +import { MultichainUtils } from "./MultichainUtils.sol"; import { MultichainProviderUtils } from "./MultichainProviderUtils.sol"; import { LayerZeroProviderEventUtils } from "./LayerZeroProviderEventUtils.sol"; @@ -19,22 +20,17 @@ import { LayerZeroProviderEventUtils } from "./LayerZeroProviderEventUtils.sol"; * Receives tokens and messages from source chains. * Defines lzCompose function which: * - is called by the Stargate executor after tokens are delivered to this contract - * - forwards the received tokens to MultichainVault and records the deposit in MultichainVaultHandler + * - forwards the received tokens to MultichainVault and increases user's multichain balance */ contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer { + DataStore public dataStore; EventEmitter public eventEmitter; - MultichainVault public multichainVault; - MultichainVaultHandler public multichainVaultHandler; - /** - * @param _multichainVault MultichainVaultHandler address - * @param _multichainVaultHandler MultichainVault address - */ - constructor(address _eventEmitter, address _multichainVault, address _multichainVaultHandler) { - eventEmitter = EventEmitter(_eventEmitter); - multichainVault = MultichainVault(payable(_multichainVault)); - multichainVaultHandler = MultichainVaultHandler(_multichainVaultHandler); + constructor(DataStore _dataStore, EventEmitter _eventEmitter, MultichainVault _multichainVault) { + dataStore = _dataStore; + eventEmitter = _eventEmitter; + multichainVault = _multichainVault; } ///////////////////// Stargate ////////////////////// @@ -49,7 +45,7 @@ contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer { * TBD if this will change * @param from The address of the sender (i.e. Stargate address, not user's address). * @param guid A global unique identifier for tracking the packet. - * @param message Encoded message. Contains the params needed to record the deposit (account, token, multichainId) + * @param message Encoded message. Contains the params needed to record the deposit (account, token, srcChainId) * @param executor The address of the Executor. * @param extraData Any extra data or options to trigger on receipt. */ @@ -60,15 +56,15 @@ contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer { address executor, bytes calldata extraData ) external payable { - (address account, address token, uint256 multichainId) = MultichainProviderUtils.decodeDeposit(message); + (address account, address token, uint256 srcChainId) = MultichainProviderUtils.decodeDeposit(message); _transferToVault(token, address(multichainVault)); - multichainVaultHandler.recordDeposit(account, token, multichainId); + MultichainUtils.recordTransferIn(dataStore, eventEmitter, multichainVault, token, account, srcChainId); LayerZeroProviderEventUtils.emitComposedMessageReceived( eventEmitter, - multichainId, + srcChainId, account, from, guid, diff --git a/contracts/multichain/MultichainEventUtils.sol b/contracts/multichain/MultichainEventUtils.sol index 909448e6a..a49043aee 100644 --- a/contracts/multichain/MultichainEventUtils.sol +++ b/contracts/multichain/MultichainEventUtils.sol @@ -13,12 +13,12 @@ library MultichainEventUtils { using EventUtils for EventUtils.AddressItems; using EventUtils for EventUtils.UintItems; - function emitMultichainDeposit( + function emitMultichainTransferIn( EventEmitter eventEmitter, address token, address account, uint256 amount, - uint256 multichainId + uint256 srcChainId ) internal { EventUtils.EventLogData memory eventData; @@ -28,15 +28,15 @@ library MultichainEventUtils { eventData.uintItems.initItems(2); eventData.uintItems.setItem(0, "amount", amount); - eventData.uintItems.setItem(1, "multichainId", multichainId); + eventData.uintItems.setItem(1, "srcChainId", srcChainId); - eventEmitter.emitEventLog2("MultichainDeposit", bytes32(multichainId), Cast.toBytes32(account), eventData); + eventEmitter.emitEventLog2("MultichainTransferIn", bytes32(srcChainId), Cast.toBytes32(account), eventData); } function emitMultichainMessage( EventEmitter eventEmitter, address account, - uint256 multichainId + uint256 srcChainId ) internal { EventUtils.EventLogData memory eventData; @@ -44,17 +44,17 @@ library MultichainEventUtils { eventData.addressItems.setItem(0, "account", account); eventData.uintItems.initItems(1); - eventData.uintItems.setItem(0, "multichainId", multichainId); + eventData.uintItems.setItem(0, "srcChainId", srcChainId); eventEmitter.emitEventLog1("MultichainMessage", Cast.toBytes32(account), eventData); } - function emitMultichainWithdrawal( + function emitMultichainTransferOut( EventEmitter eventEmitter, address token, address account, uint256 amount, - uint256 multichainId + uint256 srcChainId ) internal { EventUtils.EventLogData memory eventData; @@ -64,8 +64,8 @@ library MultichainEventUtils { eventData.uintItems.initItems(2); eventData.uintItems.setItem(0, "amount", amount); - eventData.uintItems.setItem(1, "multichainId", multichainId); + eventData.uintItems.setItem(1, "srcChainId", srcChainId); - eventEmitter.emitEventLog2("MultichainWithdrawal", bytes32(multichainId), Cast.toBytes32(account), eventData); + eventEmitter.emitEventLog2("MultichainTransferOut", bytes32(srcChainId), Cast.toBytes32(account), eventData); } } diff --git a/contracts/multichain/MultichainProviderUtils.sol b/contracts/multichain/MultichainProviderUtils.sol index 98d95a9eb..ea214d488 100644 --- a/contracts/multichain/MultichainProviderUtils.sol +++ b/contracts/multichain/MultichainProviderUtils.sol @@ -8,13 +8,13 @@ pragma solidity ^0.8.0; library MultichainProviderUtils { function decodeDeposit( bytes calldata message - ) internal pure returns (address account, address token, uint256 multichainId) { + ) internal pure returns (address account, address token, uint256 srcChainId) { return abi.decode(message, (address, address, uint256)); } function decodeWithdrawal( bytes calldata message - ) internal pure returns (address token, uint256 amount, address account, uint256 multichainId, uint32 srcEid) { + ) internal pure returns (address token, uint256 amount, address account, uint256 srcChainId, uint32 srcEid) { return abi.decode(message, (address, uint256, address, uint256, uint32)); } diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 6cb5a68ab..9f3be84fa 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -7,7 +7,6 @@ import "../deposit/DepositUtils.sol"; import "../deposit/DepositVault.sol"; import "../exchange/IDepositHandler.sol"; -import "./MultichainVaultHandler.sol"; import "./MultichainUtils.sol"; contract MultichainRouter is GelatoRelayRouter { @@ -45,7 +44,6 @@ contract MultichainRouter is GelatoRelayRouter { DepositVault depositVault; IDepositHandler depositHandler; MultichainVault multichainVault; - MultichainVaultHandler multichainVaultHandler; constructor( Router _router, @@ -56,13 +54,11 @@ contract MultichainRouter is GelatoRelayRouter { OrderVault _orderVault, IDepositHandler _depositHandler, DepositVault _depositVault, - MultichainVault _multichainVault, - MultichainVaultHandler _multichainVaultHandler + MultichainVault _multichainVault ) GelatoRelayRouter(_router, _dataStore, _eventEmitter, _oracle, _orderHandler, _orderVault) { depositVault = _depositVault; depositHandler = _depositHandler; multichainVault = _multichainVault; - multichainVaultHandler = _multichainVaultHandler; } function createDeposit( @@ -121,34 +117,21 @@ contract MultichainRouter is GelatoRelayRouter { address(depositVault) // residualFeeReceiver ); - // TODO: revisit and confirm this logic - // executionFee will be paid (in DepositUtils.createDeposit) from long or short token - // but _handleRelay has also transferred the executionFee to depositVault - // send back executionFee to MultichainVault and re-increase user's multichain balance by the executionFee amount - // by not doing this check, I think execution fee could get paid twice when initial long or short tokens are the wnt - // the alternative would be to have MultichainVault as the residualFeeReceiver, but then if none of the initial tokens are wnt, DepositUtils.createDeposit expects the fee to have already been transferred to depositVault and reverts otherwise - address wnt = TokenUtils.wnt(contracts.dataStore); - if (params.createDepositParams.initialLongToken == wnt || params.createDepositParams.initialShortToken == wnt) { - MultichainUtils.increaseBalance(dataStore, account, wnt, params.createDepositParams.executionFee); - multichainVaultHandler.pluginTransfer(wnt, address(depositVault), address(multichainVault), params.createDepositParams.executionFee); - } - return depositHandler.createDeposit(account, params.createDepositParams); } function _sendTokens(address account, address token, address receiver, uint256 amount) internal override { AccountUtils.validateReceiver(receiver); - MultichainUtils.decreaseBalance(dataStore, account, token, amount); - multichainVaultHandler.pluginTransfer(token, address(multichainVault), receiver, amount); + MultichainUtils.transferOut(dataStore, eventEmitter, 0, token, account, receiver, amount); // TODO: add srcChainId } - function _transferResidualFee(address wnt, address residualFeeReceiver, uint256 residualFee, uint256 chainId, address account) internal override { - if (chainId == 0) { - // sent residualFee to residualFeeReceiver + function _transferResidualFee(address wnt, address residualFeeReceiver, uint256 residualFee, address account, uint256 srcChainId) internal override { + if (srcChainId == 0) { + // sent residualFee to residualFeeReceiver (i.e. DepositVault) TokenUtils.transfer(dataStore, wnt, residualFeeReceiver, residualFee); } else { // sent residualFee to MultichainVault and increase user's multichain balance - TokenUtils.multichainTransfer(dataStore, wnt, address(multichainVault), residualFee, account); + MultichainUtils.recordTransferIn(dataStore, eventEmitter, multichainVault, wnt, account, srcChainId); } } diff --git a/contracts/multichain/MultichainUtils.sol b/contracts/multichain/MultichainUtils.sol index 475eb01ea..916829b28 100644 --- a/contracts/multichain/MultichainUtils.sol +++ b/contracts/multichain/MultichainUtils.sol @@ -2,18 +2,76 @@ pragma solidity ^0.8.0; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + import "../data/DataStore.sol"; import "../data/Keys.sol"; +import "../event/EventEmitter.sol"; + +import "./MultichainVault.sol"; +import "./MultichainEventUtils.sol"; /** * @title MultichainUtils */ library MultichainUtils { - function increaseBalance(DataStore dataStore, address account, address token, uint256 amount) internal { + using SafeERC20 for IERC20; + + /** + * Records a deposit from another chain. IMultichainProvider has CONTROLLER role + * @param account user address on the source chain + * @param token address of the token being deposited + * @param srcChainId id of the source chain + */ + function recordTransferIn( + DataStore dataStore, + EventEmitter eventEmitter, + MultichainVault multichainVault, + address token, + address account, + uint256 srcChainId + ) internal { + // token should have been transferred to multichainVault by IMultichainProvider + uint256 amount = multichainVault.recordTransferIn(token); + if (amount == 0) { + revert Errors.EmptyMultichainTransferInAmount(); + } + dataStore.incrementUint(Keys.multichainBalanceKey(account, token), amount); + + MultichainEventUtils.emitMultichainTransferIn(eventEmitter, token, account, amount, srcChainId); } - function decreaseBalance(DataStore dataStore, address account, address token, uint256 amount) internal { + /** + * @dev transfer the specified amount of tokens from account to receiver + * @param token the token to transfer + * @param account the account to transfer from + * @param receiver the account to transfer to + * @param amount the amount of tokens to transfer + */ + function transferOut( + DataStore dataStore, + EventEmitter eventEmitter, + uint256 srcChainId, // TODO: do we need to emit this? + address token, + address account, + address receiver, + uint256 amount + ) internal { + if (amount == 0) { + revert Errors.EmptyMultichainTransferOutAmount(); + } + + bytes32 balanceKey = Keys.multichainBalanceKey(account, token); + + uint256 balance = dataStore.getUint(balanceKey); + if (balance < amount) { + revert Errors.InsufficientMultichainBalance(); + } + + IERC20(token).safeTransferFrom(account, receiver, amount); dataStore.decrementUint(Keys.multichainBalanceKey(account, token), amount); + MultichainEventUtils.emitMultichainTransferOut(eventEmitter, token, account, amount, srcChainId); } } diff --git a/contracts/multichain/MultichainVault.sol b/contracts/multichain/MultichainVault.sol index 467eabfc5..7def09036 100644 --- a/contracts/multichain/MultichainVault.sol +++ b/contracts/multichain/MultichainVault.sol @@ -2,9 +2,7 @@ pragma solidity ^0.8.0; -import { StrictBank } from "../bank/StrictBank.sol"; -import { RoleStore } from "../role/RoleStore.sol"; -import { DataStore } from "../data/DataStore.sol"; +import "../bank/StrictBank.sol"; /** * @title MultichainVault diff --git a/contracts/multichain/MultichainVaultHandler.sol b/contracts/multichain/MultichainVaultHandler.sol index c1dde6990..4cdc8ac29 100644 --- a/contracts/multichain/MultichainVaultHandler.sol +++ b/contracts/multichain/MultichainVaultHandler.sol @@ -43,39 +43,6 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu exchangeRouter = _exchangeRouter; } - /** - * Records a deposit from another chain. IMultichainProvider has CONTROLLER role - * @param account user address on the source chain - * @param token address of the token being deposited - * @param multichainId chain id of the destination chain - */ - function recordDeposit( - address account, - address token, - uint256 multichainId - ) external onlyController { - // token should have been transferred to multichainVault by IMultichainProvider - uint256 amount = multichainVault.recordTransferIn(token); - if (amount == 0) { - revert Errors.EmptyMultichainDepositAmount(); - } - - dataStore.incrementUint(Keys.multichainBalanceKey(account, token), amount); - - MultichainEventUtils.emitMultichainDeposit(eventEmitter, token, account, amount, multichainId); - } - - /** - * @dev transfer the specified amount of tokens from account to receiver - * @param token the token to transfer - * @param account the account to transfer from - * @param receiver the account to transfer to - * @param amount the amount of tokens to transfer - */ - function pluginTransfer(address token, address account, address receiver, uint256 amount) external onlyRouterPlugin { // TODO: confirm access control - IERC20(token).safeTransferFrom(account, receiver, amount); - } - /** * Executes the multicall for the given args * The multicall arguments contains the function calls to be executed (e.g. createDeposit, createOrder, createWithdrawal, etc) @@ -93,36 +60,4 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu MultichainEventUtils.emitMultichainMessage(eventEmitter, account, multichainId); } - - /** - * Record a withdrawal to another chain. IMultichainProvider has CONTROLLER role - * @param account user address on the source chain - * @param token address of the token being withdrawn - * @param amount amount of token being withdrawn - */ - function recordWithdrawal( - address account, - address token, - uint256 amount, - uint256 multichainId - ) external onlyController { - if (amount == 0) { - revert Errors.EmptyMultichainWithdrawalAmount(); - } - - bytes32 balanceKey = Keys.multichainBalanceKey(account, token); - - uint256 balance = dataStore.getUint(balanceKey); - if (balance < amount) { - revert Errors.InsufficientMultichainBalance(); - // TODO: should amount be capped instead of reverting? i.e. amount = balance; - } - - dataStore.decrementUint(balanceKey, amount); - - // transfer tokens to IMultichainProvider - multichainVault.transferOut(token, msg.sender, amount); - - MultichainEventUtils.emitMultichainWithdrawal(eventEmitter, token, account, amount, multichainId); - } } diff --git a/contracts/router/relay/BaseGelatoRelayRouter.sol b/contracts/router/relay/BaseGelatoRelayRouter.sol index ffccdaac7..20b3e2e8f 100644 --- a/contracts/router/relay/BaseGelatoRelayRouter.sol +++ b/contracts/router/relay/BaseGelatoRelayRouter.sol @@ -317,7 +317,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, // for update orders the residual fee could be sent to the order vault if order's execution fee should be increased // otherwise the residual fee is sent back to the user // for other actions the residual fee is sent back to the user - _transferResidualFee(wnt, residualFeeReceiver, residualFee, srcChainId, account); + _transferResidualFee(wnt, residualFeeReceiver, residualFee, account, srcChainId); return residualFee; } @@ -328,7 +328,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, } // for multichain actions, the residual fee is send back to MultichainVault and user's multichain balance is increased - function _transferResidualFee(address wnt, address residualFeeReceiver, uint256 residualFee, uint256 /*srcChainId*/, address /*account*/) internal virtual { + function _transferResidualFee(address wnt, address residualFeeReceiver, uint256 residualFee, address /*account*/, uint256 /*srcChainId*/) internal virtual { // account and srcChainId not used here, but necessary when overriding _transferResidualFee in MultichainRouter TokenUtils.transfer(dataStore, wnt, residualFeeReceiver, residualFee); } diff --git a/contracts/token/TokenUtils.sol b/contracts/token/TokenUtils.sol index 91db7138b..9a7518f18 100644 --- a/contracts/token/TokenUtils.sol +++ b/contracts/token/TokenUtils.sol @@ -10,7 +10,6 @@ import "../data/DataStore.sol"; import "../data/Keys.sol"; import "../error/ErrorUtils.sol"; import "../utils/AccountUtils.sol"; -import "../multichain/MultichainUtils.sol"; import "./IWNT.sol"; @@ -96,17 +95,6 @@ library TokenUtils { revert Errors.TokenTransferError(token, receiver, amount); } - function multichainTransfer( - DataStore dataStore, - address token, - address receiver, - uint256 amount, - address account - ) internal { - transfer(dataStore, token, receiver, amount); - MultichainUtils.decreaseBalance(dataStore, account, token, amount); - } - function sendNativeToken( DataStore dataStore, address receiver, diff --git a/deploy/deployLayerZeroProvider.ts b/deploy/deployLayerZeroProvider.ts index 3f8e7ff4f..d51737311 100644 --- a/deploy/deployLayerZeroProvider.ts +++ b/deploy/deployLayerZeroProvider.ts @@ -1,7 +1,7 @@ import { grantRoleIfNotGranted } from "../utils/role"; import { createDeployFunction } from "../utils/deploy"; -const constructorContracts = ["EventEmitter", "MultichainVault", "MultichainVaultHandler"]; +const constructorContracts = ["DataStore", "EventEmitter", "MultichainVault"]; const func = createDeployFunction({ contractName: "LayerZeroProvider", From 97625ad3a6faf0ac9690b22ed224f2d33afb257b Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 7 Feb 2025 21:21:13 +0200 Subject: [PATCH 025/205] Add multichainVault address to contracts and stucts --- contracts/deposit/ExecuteDepositUtils.sol | 9 +++++---- contracts/exchange/DepositHandler.sol | 6 ++++++ contracts/exchange/GlvHandler.sol | 5 +++++ contracts/exchange/ShiftHandler.sol | 4 ++++ contracts/glv/glvDeposit/GlvDepositUtils.sol | 2 ++ contracts/glv/glvShift/GlvShiftUtils.sol | 3 +++ contracts/shift/ShiftUtils.sol | 2 ++ deploy/deployDepositHandler.ts | 2 +- deploy/deployGlvHandler.ts | 10 +++++++++- deploy/deployShiftHandler.ts | 2 +- 10 files changed, 38 insertions(+), 7 deletions(-) diff --git a/contracts/deposit/ExecuteDepositUtils.sol b/contracts/deposit/ExecuteDepositUtils.sol index 8296ebac6..cf829389d 100644 --- a/contracts/deposit/ExecuteDepositUtils.sol +++ b/contracts/deposit/ExecuteDepositUtils.sol @@ -44,6 +44,7 @@ library ExecuteDepositUtils { struct ExecuteDepositParams { DataStore dataStore; EventEmitter eventEmitter; + MultichainVault multichainVault; DepositVault depositVault; Oracle oracle; bytes32 key; @@ -52,7 +53,6 @@ library ExecuteDepositUtils { ISwapPricingUtils.SwapPricingType swapPricingType; bool includeVirtualInventoryImpact; uint256 srcChainId; - // address multichainVault; } // @dev _ExecuteDepositParams struct used in executeDeposit to avoid stack @@ -515,9 +515,10 @@ library ExecuteDepositUtils { // mint GM tokens to receiver MarketToken(payable(_params.market.marketToken)).mint(_params.receiver, mintAmount); } else { - // mint GM tokens to MultichainVault and increase account's multichain GM balance - // MarketToken(payable(_params.market.marketToken)).mint(params.multichainVault, mintAmount); // TODO: add multichainVault address to ExecuteDepositParams, or is there a better approach? - MultichainUtils.increaseBalance(params.dataStore, _params.account, _params.market.marketToken, mintAmount); + // mint GM tokens to MultichainVault and increase receiver's multichain GM balance + MarketToken(payable(_params.market.marketToken)).mint(address(params.multichainVault), mintAmount); + // TODO: is it possible thet the receiver is a multisig? Can it be an issue if there are different owners on different chains for that address? + MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, _params.receiver, _params.market.marketToken, 0); // srcChainId is the current block.chainId } return mintAmount; diff --git a/contracts/exchange/DepositHandler.sol b/contracts/exchange/DepositHandler.sol index 9d250852b..ef740de9e 100644 --- a/contracts/exchange/DepositHandler.sol +++ b/contracts/exchange/DepositHandler.sol @@ -11,6 +11,8 @@ import "../deposit/DepositVault.sol"; import "../deposit/DepositUtils.sol"; import "../deposit/ExecuteDepositUtils.sol"; +import "../multichain/MultichainVault.sol"; + import "./IDepositHandler.sol"; // @title DepositHandler @@ -19,14 +21,17 @@ contract DepositHandler is IDepositHandler, BaseHandler { using Deposit for Deposit.Props; DepositVault public immutable depositVault; + MultichainVault public immutable multichainVault; constructor( RoleStore _roleStore, DataStore _dataStore, EventEmitter _eventEmitter, Oracle _oracle, + MultichainVault _multichainVault, DepositVault _depositVault ) BaseHandler(_roleStore, _dataStore, _eventEmitter, _oracle) { + multichainVault = _multichainVault; depositVault = _depositVault; } @@ -146,6 +151,7 @@ contract DepositHandler is IDepositHandler, BaseHandler { ExecuteDepositUtils.ExecuteDepositParams memory params = ExecuteDepositUtils.ExecuteDepositParams( dataStore, eventEmitter, + multichainVault, depositVault, oracle, key, diff --git a/contracts/exchange/GlvHandler.sol b/contracts/exchange/GlvHandler.sol index f2b212b7a..9e2d0f447 100644 --- a/contracts/exchange/GlvHandler.sol +++ b/contracts/exchange/GlvHandler.sol @@ -15,6 +15,7 @@ contract GlvHandler is BaseHandler, ReentrancyGuard { using GlvShift for GlvShift.Props; using GlvWithdrawal for GlvWithdrawal.Props; + MultichainVault public immutable multichainVault; GlvVault public immutable glvVault; ShiftVault public immutable shiftVault; @@ -23,9 +24,11 @@ contract GlvHandler is BaseHandler, ReentrancyGuard { DataStore _dataStore, EventEmitter _eventEmitter, Oracle _oracle, + MultichainVault _multichainVault, GlvVault _glvVault, ShiftVault _shiftVault ) BaseHandler(_roleStore, _dataStore, _eventEmitter, _oracle) { + multichainVault = _multichainVault; glvVault = _glvVault; shiftVault = _shiftVault; } @@ -73,6 +76,7 @@ contract GlvHandler is BaseHandler, ReentrancyGuard { key: key, dataStore: dataStore, eventEmitter: eventEmitter, + multichainVault: multichainVault, glvVault: glvVault, oracle: oracle, startingGas: startingGas, @@ -284,6 +288,7 @@ contract GlvHandler is BaseHandler, ReentrancyGuard { key: key, dataStore: dataStore, eventEmitter: eventEmitter, + multichainVault: multichainVault, shiftVault: shiftVault, glvVault: glvVault, oracle: oracle, diff --git a/contracts/exchange/ShiftHandler.sol b/contracts/exchange/ShiftHandler.sol index 16477d573..f19e208f3 100644 --- a/contracts/exchange/ShiftHandler.sol +++ b/contracts/exchange/ShiftHandler.sol @@ -11,6 +11,7 @@ import "./IShiftHandler.sol"; contract ShiftHandler is IShiftHandler, BaseHandler { using Shift for Shift.Props; + MultichainVault public immutable multichainVault; ShiftVault public immutable shiftVault; constructor( @@ -18,8 +19,10 @@ contract ShiftHandler is IShiftHandler, BaseHandler { DataStore _dataStore, EventEmitter _eventEmitter, Oracle _oracle, + MultichainVault _multichainVault, ShiftVault _shiftVault ) BaseHandler(_roleStore, _dataStore, _eventEmitter, _oracle) { + multichainVault = _multichainVault; shiftVault = _shiftVault; } @@ -124,6 +127,7 @@ contract ShiftHandler is IShiftHandler, BaseHandler { ShiftUtils.ExecuteShiftParams memory params = ShiftUtils.ExecuteShiftParams( dataStore, eventEmitter, + multichainVault, shiftVault, oracle, key, diff --git a/contracts/glv/glvDeposit/GlvDepositUtils.sol b/contracts/glv/glvDeposit/GlvDepositUtils.sol index 34c29bd55..d150be1c5 100644 --- a/contracts/glv/glvDeposit/GlvDepositUtils.sol +++ b/contracts/glv/glvDeposit/GlvDepositUtils.sol @@ -47,6 +47,7 @@ library GlvDepositUtils { struct ExecuteGlvDepositParams { DataStore dataStore; EventEmitter eventEmitter; + MultichainVault multichainVault; GlvVault glvVault; Oracle oracle; bytes32 key; @@ -370,6 +371,7 @@ library GlvDepositUtils { ExecuteDepositUtils.ExecuteDepositParams memory executeDepositParams = ExecuteDepositUtils.ExecuteDepositParams( params.dataStore, params.eventEmitter, + params.multichainVault, DepositVault(payable(params.glvVault)), params.oracle, depositKey, diff --git a/contracts/glv/glvShift/GlvShiftUtils.sol b/contracts/glv/glvShift/GlvShiftUtils.sol index 4f7d6d5c5..31e936807 100644 --- a/contracts/glv/glvShift/GlvShiftUtils.sol +++ b/contracts/glv/glvShift/GlvShiftUtils.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.0; import "../../event/EventEmitter.sol"; +import "../../multichain/MultichainVault.sol"; import "../../shift/ShiftUtils.sol"; import "../GlvUtils.sol"; import "../GlvVault.sol"; @@ -27,6 +28,7 @@ library GlvShiftUtils { DataStore dataStore; EventEmitter eventEmitter; Oracle oracle; + MultichainVault multichainVault; ShiftVault shiftVault; GlvVault glvVault; bytes32 key; @@ -151,6 +153,7 @@ library GlvShiftUtils { ShiftUtils.ExecuteShiftParams memory executeShiftParams = ShiftUtils.ExecuteShiftParams({ dataStore: params.dataStore, eventEmitter: params.eventEmitter, + multichainVault: params.multichainVault, shiftVault: params.shiftVault, oracle: params.oracle, key: cache.shiftKey, diff --git a/contracts/shift/ShiftUtils.sol b/contracts/shift/ShiftUtils.sol index e773c406a..de002eaae 100644 --- a/contracts/shift/ShiftUtils.sol +++ b/contracts/shift/ShiftUtils.sol @@ -53,6 +53,7 @@ library ShiftUtils { struct ExecuteShiftParams { DataStore dataStore; EventEmitter eventEmitter; + MultichainVault multichainVault; ShiftVault shiftVault; Oracle oracle; bytes32 key; @@ -284,6 +285,7 @@ library ShiftUtils { cache.executeDepositParams = ExecuteDepositUtils.ExecuteDepositParams( params.dataStore, params.eventEmitter, + params.multichainVault, DepositVault(payable(params.shiftVault)), params.oracle, cache.depositKey, diff --git a/deploy/deployDepositHandler.ts b/deploy/deployDepositHandler.ts index 9bb6e8604..1571e39a7 100644 --- a/deploy/deployDepositHandler.ts +++ b/deploy/deployDepositHandler.ts @@ -1,7 +1,7 @@ import { grantRoleIfNotGranted } from "../utils/role"; import { createDeployFunction } from "../utils/deploy"; -const constructorContracts = ["RoleStore", "DataStore", "EventEmitter", "Oracle", "DepositVault"]; +const constructorContracts = ["RoleStore", "DataStore", "EventEmitter", "Oracle", "MultichainVault", "DepositVault"]; const func = createDeployFunction({ contractName: "DepositHandler", diff --git a/deploy/deployGlvHandler.ts b/deploy/deployGlvHandler.ts index 2e8d55a7a..98eac18ad 100644 --- a/deploy/deployGlvHandler.ts +++ b/deploy/deployGlvHandler.ts @@ -1,7 +1,15 @@ import { grantRoleIfNotGranted } from "../utils/role"; import { createDeployFunction } from "../utils/deploy"; -const constructorContracts = ["RoleStore", "DataStore", "EventEmitter", "Oracle", "GlvVault", "ShiftVault"]; +const constructorContracts = [ + "RoleStore", + "DataStore", + "EventEmitter", + "Oracle", + "MultichainVault", + "GlvVault", + "ShiftVault", +]; const func = createDeployFunction({ contractName: "GlvHandler", diff --git a/deploy/deployShiftHandler.ts b/deploy/deployShiftHandler.ts index 1e7cc276d..d81e05acc 100644 --- a/deploy/deployShiftHandler.ts +++ b/deploy/deployShiftHandler.ts @@ -1,7 +1,7 @@ import { grantRoleIfNotGranted } from "../utils/role"; import { createDeployFunction } from "../utils/deploy"; -const constructorContracts = ["RoleStore", "DataStore", "EventEmitter", "Oracle", "ShiftVault"]; +const constructorContracts = ["RoleStore", "DataStore", "EventEmitter", "Oracle", "MultichainVault", "ShiftVault"]; const func = createDeployFunction({ contractName: "ShiftHandler", From 3d3477dc683dcc5edb5cb962e3252c8d01fe0be6 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 7 Feb 2025 21:46:17 +0200 Subject: [PATCH 026/205] Validate desChainId provided is block.chainid, rename gasless to multichain --- contracts/error/Errors.sol | 1 + contracts/multichain/MultichainRouter.sol | 35 +++++++++++------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/contracts/error/Errors.sol b/contracts/error/Errors.sol index 78c5e03d3..ef38adf87 100644 --- a/contracts/error/Errors.sol +++ b/contracts/error/Errors.sol @@ -433,6 +433,7 @@ library Errors { error EmptyMultichainTransferInAmount(); error EmptyMultichainTransferOutAmount(); error InsufficientMultichainBalance(); + error InvalidDestinationChainId(); enum SignatureType { Call, diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 9f3be84fa..82871b6dc 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -10,18 +10,13 @@ import "../exchange/IDepositHandler.sol"; import "./MultichainUtils.sol"; contract MultichainRouter is GelatoRelayRouter { - struct GaslessCreateDepositParams { + struct MultichainCreateDepositParams { + uint256 desChainId; uint256 longTokenAmount; uint256 shortTokenAmount; DepositUtils.CreateDepositParams createDepositParams; } - // TODO: extract multichain part from GaslessCreateDepositParams struct - // struct MultichainCreateDepositParams { - // uint256 longTokenAmount; - // uint256 shortTokenAmount; - // } - bytes32 public constant CREATE_DEPOSIT_TYPEHASH = keccak256( bytes( @@ -34,10 +29,10 @@ contract MultichainRouter is GelatoRelayRouter { "CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" ) ); - bytes32 public constant GASLESS_CREATE_DEPOSIT_PARAMS_TYPEHASH = + bytes32 public constant MULTICHAIN_CREATE_DEPOSIT_PARAMS_TYPEHASH = keccak256( bytes( - "GaslessCreateDepositParams(uint256 desChainId,uint256 longTokenAmount,uint256 shortTokenAmount,CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + "MultichainCreateDepositParams(uint256 desChainId,uint256 longTokenAmount,uint256 shortTokenAmount,CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" ) ); @@ -64,9 +59,13 @@ contract MultichainRouter is GelatoRelayRouter { function createDeposit( RelayParams calldata relayParams, address account, - GaslessCreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee + MultichainCreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { - bytes32 structHash = _getGaslessCreateDepositStructHash(relayParams, params); + if (params.desChainId != block.chainid) { + revert Errors.InvalidDestinationChainId(); + } + + bytes32 structHash = _getMultichainCreateDepositStructHash(relayParams, params); _validateCall(relayParams, account, structHash); return _createDeposit(relayParams.tokenPermits, relayParams.fee, account, params); @@ -76,7 +75,7 @@ contract MultichainRouter is GelatoRelayRouter { TokenPermit[] calldata tokenPermits, RelayFeeParams calldata fee, address account, - GaslessCreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee + MultichainCreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee ) internal returns (bytes32) { Contracts memory contracts = Contracts({ dataStore: dataStore, @@ -135,9 +134,9 @@ contract MultichainRouter is GelatoRelayRouter { } } - function _getGaslessCreateDepositStructHash( + function _getMultichainCreateDepositStructHash( RelayParams calldata relayParams, - GaslessCreateDepositParams memory params + MultichainCreateDepositParams memory params ) internal view returns (bytes32) { bytes32 relayParamsHash = keccak256(abi.encode(relayParams)); @@ -145,19 +144,19 @@ contract MultichainRouter is GelatoRelayRouter { keccak256( abi.encode( CREATE_DEPOSIT_TYPEHASH, - _getGaslessCreateDepositParamsStructHash(params), + _getMultichainCreateDepositParamsStructHash(params), relayParamsHash ) ); } - function _getGaslessCreateDepositParamsStructHash( - GaslessCreateDepositParams memory params + function _getMultichainCreateDepositParamsStructHash( + MultichainCreateDepositParams memory params ) internal view returns (bytes32) { return keccak256( abi.encode( - GASLESS_CREATE_DEPOSIT_PARAMS_TYPEHASH, + MULTICHAIN_CREATE_DEPOSIT_PARAMS_TYPEHASH, block.chainid, params.longTokenAmount, params.shortTokenAmount, From 3d783dd34ba3f3cd2382b6116108e9283d2b6a02 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 7 Feb 2025 21:59:36 +0200 Subject: [PATCH 027/205] Add srcChainId to _sendTokens --- contracts/multichain/MultichainRouter.sol | 10 ++++++---- contracts/router/relay/BaseGelatoRelayRouter.sol | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 82871b6dc..005404948 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -91,13 +91,15 @@ contract MultichainRouter is GelatoRelayRouter { params.createDepositParams.initialLongToken, account, address(depositVault), // receiver - params.longTokenAmount + params.longTokenAmount, + params.createDepositParams.srcChainId ); _sendTokens( params.createDepositParams.initialShortToken, account, address(depositVault), // receiver - params.shortTokenAmount + params.shortTokenAmount, + params.createDepositParams.srcChainId ); // On create step: can deduct relay fee fully or partially depending on user’s MultichainVault balance, save any excess pending relay fees and validate that the user has sufficient position collateral to pay for the remaining relay fee @@ -119,9 +121,9 @@ contract MultichainRouter is GelatoRelayRouter { return depositHandler.createDeposit(account, params.createDepositParams); } - function _sendTokens(address account, address token, address receiver, uint256 amount) internal override { + function _sendTokens(address account, address token, address receiver, uint256 amount, uint256 srcChainId) internal override { AccountUtils.validateReceiver(receiver); - MultichainUtils.transferOut(dataStore, eventEmitter, 0, token, account, receiver, amount); // TODO: add srcChainId + MultichainUtils.transferOut(dataStore, eventEmitter, srcChainId, token, account, receiver, amount); } function _transferResidualFee(address wnt, address residualFeeReceiver, uint256 residualFee, address account, uint256 srcChainId) internal override { diff --git a/contracts/router/relay/BaseGelatoRelayRouter.sol b/contracts/router/relay/BaseGelatoRelayRouter.sol index 20b3e2e8f..99011d512 100644 --- a/contracts/router/relay/BaseGelatoRelayRouter.sol +++ b/contracts/router/relay/BaseGelatoRelayRouter.sol @@ -138,7 +138,8 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, account, params.addresses.initialCollateralToken, address(contracts.orderVault), - collateralDeltaAmount + collateralDeltaAmount, + 0 // TODO: add srcChainId to orders ); } @@ -301,10 +302,10 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, uint256 outputAmount; if (fee.feeToken == wnt) { - _sendTokens(account, fee.feeToken, address(this), fee.feeAmount); + _sendTokens(account, fee.feeToken, address(this), fee.feeAmount, srcChainId); outputAmount = fee.feeAmount; } else { - _sendTokens(account, fee.feeToken, address(contracts.orderVault), fee.feeAmount); + _sendTokens(account, fee.feeToken, address(contracts.orderVault), fee.feeAmount, srcChainId); outputAmount = _swapFeeTokens(contracts, wnt, fee); } @@ -322,7 +323,8 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, return residualFee; } - function _sendTokens(address account, address token, address receiver, uint256 amount) internal virtual { + function _sendTokens(address account, address token, address receiver, uint256 amount, uint256 /*srcChainId*/) internal virtual { + // srcChainId not used here, but necessary when overriding _sendTokens in MultichainRouter AccountUtils.validateReceiver(receiver); router.pluginTransfer(token, account, receiver, amount); } From d78d5caa9a6ca7f2b4f23bb6124bf768d80f66a9 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 7 Feb 2025 22:07:58 +0200 Subject: [PATCH 028/205] Change Contracts { OrderVault orderVault } into Contracts { StrictBank bank } --- contracts/multichain/MultichainRouter.sol | 5 +---- .../router/relay/BaseGelatoRelayRouter.sol | 18 +++++++++--------- .../relay/SubaccountGelatoRelayRouter.sol | 2 +- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 005404948..7b8d5c40f 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -80,10 +80,7 @@ contract MultichainRouter is GelatoRelayRouter { Contracts memory contracts = Contracts({ dataStore: dataStore, eventEmitter: eventEmitter, - // TODO: confirm Contracts struct can be modified --> replace `OrderVault orderVault;` field with `StrictBank vault;` - // otherwise, should probably overridde _handleRelay - // A: yes, it can be modified - orderVault: OrderVault(payable(depositVault)) + bank: depositVault }); // transfer long & short tokens from MultichainVault to DepositVault and decrement user's multichain balance diff --git a/contracts/router/relay/BaseGelatoRelayRouter.sol b/contracts/router/relay/BaseGelatoRelayRouter.sol index 99011d512..d00285680 100644 --- a/contracts/router/relay/BaseGelatoRelayRouter.sol +++ b/contracts/router/relay/BaseGelatoRelayRouter.sol @@ -61,7 +61,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, struct Contracts { DataStore dataStore; EventEmitter eventEmitter; - OrderVault orderVault; + StrictBank bank; } IOrderHandler public immutable orderHandler; @@ -115,7 +115,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, Contracts memory contracts = Contracts({ dataStore: dataStore, eventEmitter: eventEmitter, - orderVault: orderVault + bank: orderVault }); params.numbers.executionFee = _handleRelay( @@ -124,7 +124,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, relayParams.fee, relayParams.srcChainId, account, - address(contracts.orderVault) + address(contracts.bank) ); if ( @@ -137,7 +137,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, _sendTokens( account, params.addresses.initialCollateralToken, - address(contracts.orderVault), + address(contracts.bank), collateralDeltaAmount, 0 // TODO: add srcChainId to orders ); @@ -157,7 +157,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, Contracts memory contracts = Contracts({ dataStore: dataStore, eventEmitter: eventEmitter, - orderVault: orderVault + bank: orderVault }); Order.Props memory order = OrderStoreUtils.get(contracts.dataStore, key); @@ -170,7 +170,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, revert Errors.Unauthorized(account, "account for updateOrder"); } - address residualFeeReceiver = increaseExecutionFee ? address(contracts.orderVault) : account; + address residualFeeReceiver = increaseExecutionFee ? address(contracts.bank) : account; _handleRelay(contracts, relayParams.tokenPermits, relayParams.fee, relayParams.srcChainId, account, residualFeeReceiver); orderHandler.updateOrder( @@ -190,7 +190,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, Contracts memory contracts = Contracts({ dataStore: dataStore, eventEmitter: eventEmitter, - orderVault: orderVault + bank: orderVault }); Order.Props memory order = OrderStoreUtils.get(contracts.dataStore, key); @@ -224,7 +224,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, dataStore: contracts.dataStore, eventEmitter: contracts.eventEmitter, oracle: oracle, - bank: contracts.orderVault, + bank: contracts.bank, key: bytes32(0), tokenIn: fee.feeToken, amountIn: fee.feeAmount, @@ -305,7 +305,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, _sendTokens(account, fee.feeToken, address(this), fee.feeAmount, srcChainId); outputAmount = fee.feeAmount; } else { - _sendTokens(account, fee.feeToken, address(contracts.orderVault), fee.feeAmount, srcChainId); + _sendTokens(account, fee.feeToken, address(contracts.bank), fee.feeAmount, srcChainId); outputAmount = _swapFeeTokens(contracts, wnt, fee); } diff --git a/contracts/router/relay/SubaccountGelatoRelayRouter.sol b/contracts/router/relay/SubaccountGelatoRelayRouter.sol index 313fc891b..df05d37b9 100644 --- a/contracts/router/relay/SubaccountGelatoRelayRouter.sol +++ b/contracts/router/relay/SubaccountGelatoRelayRouter.sol @@ -154,7 +154,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { Contracts memory contracts = Contracts({ dataStore: dataStore, eventEmitter: eventEmitter, - orderVault: orderVault + bank: orderVault }); _handleRelay(contracts, relayParams.tokenPermits, relayParams.fee, relayParams.srcChainId, account, account); From 65168e671c37cbfe20407228ca8574f4f13f555a Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 8 Feb 2025 13:12:50 +0200 Subject: [PATCH 029/205] Split CreateDepositParams into subgroups --- contracts/deposit/DepositUtils.sol | 46 ++++++++++++----------- contracts/migration/GlpMigrator.sol | 20 +++++----- contracts/multichain/MultichainRouter.sol | 40 ++++++++++++++------ scripts/createDepositSynthetic.ts | 18 +++++---- scripts/createDepositWethUsdc.ts | 19 ++++++---- scripts/createDepositWnt.ts | 19 ++++++---- test/router/ExchangeRouter.ts | 36 ++++++++++-------- utils/deposit.ts | 18 +++++---- 8 files changed, 128 insertions(+), 88 deletions(-) diff --git a/contracts/deposit/DepositUtils.sol b/contracts/deposit/DepositUtils.sol index 6c2ac2e5f..de142bdc7 100644 --- a/contracts/deposit/DepositUtils.sol +++ b/contracts/deposit/DepositUtils.sol @@ -38,6 +38,16 @@ library DepositUtils { // @param executionFee the execution fee for keepers // @param callbackGasLimit the gas limit for the callbackContract struct CreateDepositParams { + CreateDepositParamsAdresses addresses; + uint256 minMarketTokens; + bool shouldUnwrapNativeToken; + uint256 executionFee; + uint256 callbackGasLimit; + uint256 srcChainId; + bytes32[] dataList; + } + + struct CreateDepositParamsAdresses { address receiver; address callbackContract; address uiFeeReceiver; @@ -46,12 +56,6 @@ library DepositUtils { address initialShortToken; address[] longTokenSwapPath; address[] shortTokenSwapPath; - uint256 minMarketTokens; - bool shouldUnwrapNativeToken; - uint256 executionFee; - uint256 callbackGasLimit; - uint256 srcChainId; - bytes32[] dataList; } // @dev creates a deposit @@ -70,20 +74,20 @@ library DepositUtils { ) external returns (bytes32) { AccountUtils.validateAccount(account); - Market.Props memory market = MarketUtils.getEnabledMarket(dataStore, params.market); - MarketUtils.validateSwapPath(dataStore, params.longTokenSwapPath); - MarketUtils.validateSwapPath(dataStore, params.shortTokenSwapPath); + Market.Props memory market = MarketUtils.getEnabledMarket(dataStore, params.addresses.market); + MarketUtils.validateSwapPath(dataStore, params.addresses.longTokenSwapPath); + MarketUtils.validateSwapPath(dataStore, params.addresses.shortTokenSwapPath); // if the initialLongToken and initialShortToken are the same, only the initialLongTokenAmount would // be non-zero, the initialShortTokenAmount would be zero - uint256 initialLongTokenAmount = depositVault.recordTransferIn(params.initialLongToken); - uint256 initialShortTokenAmount = depositVault.recordTransferIn(params.initialShortToken); + uint256 initialLongTokenAmount = depositVault.recordTransferIn(params.addresses.initialLongToken); + uint256 initialShortTokenAmount = depositVault.recordTransferIn(params.addresses.initialShortToken); address wnt = TokenUtils.wnt(dataStore); - if (params.initialLongToken == wnt) { + if (params.addresses.initialLongToken == wnt) { initialLongTokenAmount -= params.executionFee; - } else if (params.initialShortToken == wnt) { + } else if (params.addresses.initialShortToken == wnt) { initialShortTokenAmount -= params.executionFee; } else { uint256 wntAmount = depositVault.recordTransferIn(wnt); @@ -98,19 +102,19 @@ library DepositUtils { revert Errors.EmptyDepositAmounts(); } - AccountUtils.validateReceiver(params.receiver); + AccountUtils.validateReceiver(params.addresses.receiver); Deposit.Props memory deposit = Deposit.Props( Deposit.Addresses( account, - params.receiver, - params.callbackContract, - params.uiFeeReceiver, + params.addresses.receiver, + params.addresses.callbackContract, + params.addresses.uiFeeReceiver, market.marketToken, - params.initialLongToken, - params.initialShortToken, - params.longTokenSwapPath, - params.shortTokenSwapPath + params.addresses.initialLongToken, + params.addresses.initialShortToken, + params.addresses.longTokenSwapPath, + params.addresses.shortTokenSwapPath ), Deposit.Numbers( initialLongTokenAmount, diff --git a/contracts/migration/GlpMigrator.sol b/contracts/migration/GlpMigrator.sol index abf6b99ec..3d2e0ec36 100644 --- a/contracts/migration/GlpMigrator.sol +++ b/contracts/migration/GlpMigrator.sol @@ -196,19 +196,21 @@ contract GlpMigrator is ReentrancyGuard, RoleModule { // any arbitrage / benefit of doing this should be minimal // glp mint fees should also help to discourage this DepositUtils.CreateDepositParams memory depositParams = DepositUtils.CreateDepositParams( - account, // receiver; - address(0), // callbackContract; - address(0), // uiFeeReceiver; - migrationItem.market, // market; - cache.market.longToken, // initialLongToken; - cache.market.shortToken, // initialShortToken; - new address[](0), // longTokenSwapPath; - new address[](0), // shortTokenSwapPath; + DepositUtils.CreateDepositParamsAdresses( + account, // receiver; + address(0), // callbackContract; + address(0), // uiFeeReceiver; + migrationItem.market, // market; + cache.market.longToken, // initialLongToken; + cache.market.shortToken, // initialShortToken; + new address[](0), // longTokenSwapPath; + new address[](0) // shortTokenSwapPath; + ), migrationItem.minMarketTokens, // minMarketTokens; false, // shouldUnwrapNativeToken; migrationItem.executionFee, // executionFee; 0, // callbackGasLimit; - 0, // chainId + 0, // srcChainId new bytes32[](0) // dataList; ); diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 7b8d5c40f..a595924db 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -35,6 +35,12 @@ contract MultichainRouter is GelatoRelayRouter { "MultichainCreateDepositParams(uint256 desChainId,uint256 longTokenAmount,uint256 shortTokenAmount,CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" ) ); + bytes32 public constant CREATE_DEPOSIT_PARAMS_ADDRESSES_TYPEHASH = + keccak256( + bytes( + "CreateDepositParamsAdresses(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath)" + ) + ); DepositVault depositVault; IDepositHandler depositHandler; @@ -85,14 +91,14 @@ contract MultichainRouter is GelatoRelayRouter { // transfer long & short tokens from MultichainVault to DepositVault and decrement user's multichain balance _sendTokens( - params.createDepositParams.initialLongToken, + params.createDepositParams.addresses.initialLongToken, account, address(depositVault), // receiver params.longTokenAmount, params.createDepositParams.srcChainId ); _sendTokens( - params.createDepositParams.initialShortToken, + params.createDepositParams.addresses.initialShortToken, account, address(depositVault), // receiver params.shortTokenAmount, @@ -171,24 +177,36 @@ contract MultichainRouter is GelatoRelayRouter { keccak256( abi.encode( CREATE_DEPOSIT_PARAMS_TYPEHASH, - params.receiver, - params.callbackContract, - params.uiFeeReceiver, - params.market, - params.initialLongToken, - params.initialShortToken, - keccak256(abi.encodePacked(params.longTokenSwapPath)), - keccak256(abi.encodePacked(params.shortTokenSwapPath)), + _getCreateDepositParamsAdressesStructHash(params.addresses), params.minMarketTokens, params.shouldUnwrapNativeToken, params.executionFee, params.callbackGasLimit, - // params.srcChainId, // TODO: adding another field throws with slot too deep error + params.srcChainId, keccak256(abi.encodePacked(params.dataList)) ) ); } + function _getCreateDepositParamsAdressesStructHash( + DepositUtils.CreateDepositParamsAdresses memory addresses + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + CREATE_DEPOSIT_PARAMS_ADDRESSES_TYPEHASH, + addresses.receiver, + addresses.callbackContract, + addresses.uiFeeReceiver, + addresses.market, + addresses.initialLongToken, + addresses.initialShortToken, + keccak256(abi.encodePacked(addresses.longTokenSwapPath)), + keccak256(abi.encodePacked(addresses.shortTokenSwapPath)) + ) + ); + } + function createWithdrawal() external nonReentrant onlyGelatoRelay {} function createGlvDeposit() external nonReentrant onlyGelatoRelay {} diff --git a/scripts/createDepositSynthetic.ts b/scripts/createDepositSynthetic.ts index f0fa3e9f3..446928010 100644 --- a/scripts/createDepositSynthetic.ts +++ b/scripts/createDepositSynthetic.ts @@ -89,17 +89,21 @@ async function main() { console.log("market %s", syntheticMarketAddress); const params: DepositUtils.CreateDepositParamsStruct = { - receiver: wallet.address, - callbackContract: ethers.constants.AddressZero, - market: syntheticMarketAddress, + addresses: { + receiver: wallet.address, + callbackContract: ethers.constants.AddressZero, + uiFeeReceiver: ethers.constants.AddressZero, + market: syntheticMarketAddress, + initialLongToken: weth.address, + initialShortToken: usdc.address, + longTokenSwapPath: [], + shortTokenSwapPath: [], + }, minMarketTokens: 0, shouldUnwrapNativeToken: false, executionFee: executionFee, callbackGasLimit: 0, - initialLongToken: weth.address, - initialShortToken: usdc.address, - longTokenSwapPath: [], - shortTokenSwapPath: [], + srcChainId: 0, dataList: [], }; console.log("exchange router %s", exchangeRouter.address); diff --git a/scripts/createDepositWethUsdc.ts b/scripts/createDepositWethUsdc.ts index 71ba63aca..d6bf0a19a 100644 --- a/scripts/createDepositWethUsdc.ts +++ b/scripts/createDepositWethUsdc.ts @@ -96,18 +96,21 @@ async function main() { console.log("market %s", wethUsdMarketAddress); const params: DepositUtils.CreateDepositParamsStruct = { - receiver: wallet.address, - callbackContract: ethers.constants.AddressZero, - market: wethUsdMarketAddress, + addresses: { + receiver: wallet.address, + callbackContract: ethers.constants.AddressZero, + market: wethUsdMarketAddress, + initialLongToken: weth.address, + longTokenSwapPath: [], + initialShortToken: usdc.address, + shortTokenSwapPath: [], + uiFeeReceiver: ethers.constants.AddressZero, + }, minMarketTokens: 0, shouldUnwrapNativeToken: false, executionFee: executionFee, callbackGasLimit: 0, - initialLongToken: weth.address, - longTokenSwapPath: [], - initialShortToken: usdc.address, - shortTokenSwapPath: [], - uiFeeReceiver: ethers.constants.AddressZero, + srcChainId: 0, dataList: [], }; console.log("exchange router %s", exchangeRouter.address); diff --git a/scripts/createDepositWnt.ts b/scripts/createDepositWnt.ts index 6896c26a9..2ebf58913 100644 --- a/scripts/createDepositWnt.ts +++ b/scripts/createDepositWnt.ts @@ -80,18 +80,21 @@ async function main() { console.log("market %s", wntUsdMarketAddress); const params: DepositUtils.CreateDepositParamsStruct = { - receiver: wallet.address, - callbackContract: ethers.constants.AddressZero, - market: wntUsdMarketAddress, + addresses: { + receiver: wallet.address, + callbackContract: ethers.constants.AddressZero, + market: wntUsdMarketAddress, + initialLongToken: wnt.address, + longTokenSwapPath: [], + initialShortToken: usdc.address, + shortTokenSwapPath: [], + uiFeeReceiver: ethers.constants.AddressZero, + }, minMarketTokens: 0, shouldUnwrapNativeToken: false, executionFee: executionFee, callbackGasLimit: 0, - initialLongToken: wnt.address, - longTokenSwapPath: [], - initialShortToken: usdc.address, - shortTokenSwapPath: [], - uiFeeReceiver: ethers.constants.AddressZero, + srcChainId: 0, dataList: [], }; console.log("exchange router %s", exchangeRouter.address); diff --git a/test/router/ExchangeRouter.ts b/test/router/ExchangeRouter.ts index 40672e6f4..22491e56f 100644 --- a/test/router/ExchangeRouter.ts +++ b/test/router/ExchangeRouter.ts @@ -62,14 +62,16 @@ describe("ExchangeRouter", () => { ]), exchangeRouter.interface.encodeFunctionData("createDeposit", [ { - receiver: user1.address, - callbackContract: user2.address, - uiFeeReceiver: user3.address, - market: ethUsdMarket.marketToken, - initialLongToken: ethUsdMarket.longToken, - initialShortToken: ethUsdMarket.shortToken, - longTokenSwapPath: [ethUsdMarket.marketToken, ethUsdSpotOnlyMarket.marketToken], - shortTokenSwapPath: [ethUsdSpotOnlyMarket.marketToken, ethUsdMarket.marketToken], + addresses: { + receiver: user1.address, + callbackContract: user2.address, + uiFeeReceiver: user3.address, + market: ethUsdMarket.marketToken, + initialLongToken: ethUsdMarket.longToken, + initialShortToken: ethUsdMarket.shortToken, + longTokenSwapPath: [ethUsdMarket.marketToken, ethUsdSpotOnlyMarket.marketToken], + shortTokenSwapPath: [ethUsdSpotOnlyMarket.marketToken, ethUsdMarket.marketToken], + }, minMarketTokens: 100, shouldUnwrapNativeToken: true, executionFee, @@ -269,14 +271,16 @@ describe("ExchangeRouter", () => { ]), exchangeRouter.interface.encodeFunctionData("createDeposit", [ { - receiver: user1.address, - callbackContract: user2.address, - uiFeeReceiver: user3.address, - market: ethUsdMarket.marketToken, - initialLongToken: ethUsdMarket.longToken, - initialShortToken: ethUsdMarket.shortToken, - longTokenSwapPath: [], - shortTokenSwapPath: [], + addresses: { + receiver: user1.address, + callbackContract: user2.address, + uiFeeReceiver: user3.address, + market: ethUsdMarket.marketToken, + initialLongToken: ethUsdMarket.longToken, + initialShortToken: ethUsdMarket.shortToken, + longTokenSwapPath: [], + shortTokenSwapPath: [], + }, minMarketTokens: 100, shouldUnwrapNativeToken: true, executionFee, diff --git a/utils/deposit.ts b/utils/deposit.ts index 1e3ebd885..dd8faf5f5 100644 --- a/utils/deposit.ts +++ b/utils/deposit.ts @@ -61,14 +61,16 @@ export async function createDeposit(fixture, overrides: any = {}) { } const params = { - receiver: receiver.address, - callbackContract: callbackContract.address, - uiFeeReceiver: uiFeeReceiver.address, - market: market.marketToken, - initialLongToken, - initialShortToken, - longTokenSwapPath, - shortTokenSwapPath, + addresses: { + receiver: receiver.address, + callbackContract: callbackContract.address, + uiFeeReceiver: uiFeeReceiver.address, + market: market.marketToken, + initialLongToken, + initialShortToken, + longTokenSwapPath, + shortTokenSwapPath, + }, minMarketTokens, shouldUnwrapNativeToken, executionFee, From c174957bf0f4a606373402c0230a5eeab3cea896 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 9 Feb 2025 19:40:46 +0200 Subject: [PATCH 030/205] Add srcChainId to orders --- contracts/adl/AdlUtils.sol | 3 ++- contracts/liquidation/LiquidationUtils.sol | 3 ++- contracts/order/Order.sol | 8 ++++++++ contracts/order/OrderEventUtils.sol | 6 ++++-- contracts/order/OrderStoreUtils.sol | 14 ++++++++++++++ test/exchange/MarketIncreaseOrder.ts | 2 +- utils/order.ts | 2 ++ 7 files changed, 33 insertions(+), 5 deletions(-) diff --git a/contracts/adl/AdlUtils.sol b/contracts/adl/AdlUtils.sol index e84cacb83..3a9f43415 100644 --- a/contracts/adl/AdlUtils.sol +++ b/contracts/adl/AdlUtils.sol @@ -176,7 +176,8 @@ library AdlUtils { params.dataStore.getUint(Keys.MAX_CALLBACK_GAS_LIMIT), // callbackGasLimit 0, // minOutputAmount params.updatedAtTime, // updatedAtTime - 0 // validFromTime + 0, // validFromTime + 0 // srcChainId ); Order.Flags memory flags = Order.Flags( diff --git a/contracts/liquidation/LiquidationUtils.sol b/contracts/liquidation/LiquidationUtils.sol index 5a9d6432b..c889e0bf1 100644 --- a/contracts/liquidation/LiquidationUtils.sol +++ b/contracts/liquidation/LiquidationUtils.sol @@ -70,7 +70,8 @@ library LiquidationUtils { dataStore.getUint(Keys.MAX_CALLBACK_GAS_LIMIT), // callbackGasLimit 0, // minOutputAmount Chain.currentTimestamp(), // updatedAtTime - 0 // validFromTime + 0, // validFromTime + 0 // srcChainId ); Order.Flags memory flags = Order.Flags( diff --git a/contracts/order/Order.sol b/contracts/order/Order.sol index dc29b7052..ea05c1317 100644 --- a/contracts/order/Order.sol +++ b/contracts/order/Order.sol @@ -115,6 +115,7 @@ library Order { uint256 minOutputAmount; uint256 updatedAtTime; uint256 validFromTime; + uint256 srcChainId; } // @param isLong whether the order is for a long or short @@ -375,6 +376,13 @@ library Order { props.numbers.validFromTime = value; } + function srcChainId(Props memory props) internal pure returns (uint256) { + return props.numbers.srcChainId; + } + function setSrcChainId(Props memory props, uint256 value) internal pure { + props.numbers.srcChainId = value; + } + // @dev whether the order is for a long or short // @param props Props // @return whether the order is for a long or short diff --git a/contracts/order/OrderEventUtils.sol b/contracts/order/OrderEventUtils.sol index c12e21ed8..0e0297262 100644 --- a/contracts/order/OrderEventUtils.sol +++ b/contracts/order/OrderEventUtils.sol @@ -37,7 +37,7 @@ library OrderEventUtils { eventData.addressItems.initArrayItems(1); eventData.addressItems.setItem(0, "swapPath", order.swapPath()); - eventData.uintItems.initItems(11); + eventData.uintItems.initItems(12); eventData.uintItems.setItem(0, "orderType", uint256(order.orderType())); eventData.uintItems.setItem(1, "decreasePositionSwapType", uint256(order.decreasePositionSwapType())); eventData.uintItems.setItem(2, "sizeDeltaUsd", order.sizeDeltaUsd()); @@ -49,6 +49,7 @@ library OrderEventUtils { eventData.uintItems.setItem(8, "minOutputAmount", order.minOutputAmount()); eventData.uintItems.setItem(9, "updatedAtTime", order.updatedAtTime()); eventData.uintItems.setItem(10, "validFromTime", order.validFromTime()); + eventData.uintItems.setItem(11, "srcChainId", order.srcChainId()); eventData.boolItems.initItems(3); eventData.boolItems.setItem(0, "isLong", order.isLong()); @@ -107,13 +108,14 @@ library OrderEventUtils { eventData.addressItems.initItems(1); eventData.addressItems.setItem(0, "account", order.account()); - eventData.uintItems.initItems(6); + eventData.uintItems.initItems(7); eventData.uintItems.setItem(0, "sizeDeltaUsd", order.sizeDeltaUsd()); eventData.uintItems.setItem(1, "acceptablePrice", order.acceptablePrice()); eventData.uintItems.setItem(2, "triggerPrice", order.triggerPrice()); eventData.uintItems.setItem(3, "minOutputAmount", order.minOutputAmount()); eventData.uintItems.setItem(4, "updatedAtTime", order.updatedAtTime()); eventData.uintItems.setItem(5, "validFromTime", order.validFromTime()); + eventData.uintItems.setItem(6, "srcChainId", order.srcChainId()); eventData.boolItems.initItems(1); eventData.boolItems.setItem(0, "autoCancel", order.autoCancel()); diff --git a/contracts/order/OrderStoreUtils.sol b/contracts/order/OrderStoreUtils.sol index db82d9af0..c23195cfd 100644 --- a/contracts/order/OrderStoreUtils.sol +++ b/contracts/order/OrderStoreUtils.sol @@ -34,6 +34,7 @@ library OrderStoreUtils { bytes32 public constant MIN_OUTPUT_AMOUNT = keccak256(abi.encode("MIN_OUTPUT_AMOUNT")); bytes32 public constant VALID_FROM_TIME = keccak256(abi.encode("VALID_FROM_TIME")); bytes32 public constant UPDATED_AT_TIME = keccak256(abi.encode("UPDATED_AT_TIME")); + bytes32 public constant SRC_CHAIN_ID = keccak256(abi.encode("SRC_CHAIN_ID")); bytes32 public constant IS_LONG = keccak256(abi.encode("IS_LONG")); bytes32 public constant SHOULD_UNWRAP_NATIVE_TOKEN = keccak256(abi.encode("SHOULD_UNWRAP_NATIVE_TOKEN")); @@ -124,6 +125,10 @@ library OrderStoreUtils { keccak256(abi.encode(key, UPDATED_AT_TIME)) )); + order.setSrcChainId(dataStore.getUint( + keccak256(abi.encode(key, SRC_CHAIN_ID)) + )); + order.setIsLong(dataStore.getBool( keccak256(abi.encode(key, IS_LONG)) )); @@ -253,6 +258,11 @@ library OrderStoreUtils { order.updatedAtTime() ); + dataStore.setUint( + keccak256(abi.encode(key, SRC_CHAIN_ID)), + order.srcChainId() + ); + dataStore.setBool( keccak256(abi.encode(key, IS_LONG)), order.isLong() @@ -370,6 +380,10 @@ library OrderStoreUtils { keccak256(abi.encode(key, UPDATED_AT_TIME)) ); + dataStore.removeUint( + keccak256(abi.encode(key, SRC_CHAIN_ID)) + ); + dataStore.removeBool( keccak256(abi.encode(key, IS_LONG)) ); diff --git a/test/exchange/MarketIncreaseOrder.ts b/test/exchange/MarketIncreaseOrder.ts index 568b540eb..15e9471cc 100644 --- a/test/exchange/MarketIncreaseOrder.ts +++ b/test/exchange/MarketIncreaseOrder.ts @@ -294,7 +294,7 @@ describe("Exchange.MarketIncreaseOrder", () => { expect((await provider.getBalance(user1.address)).sub(initialBalance)).eq(0); - expect(await provider.getBalance(mockCallbackReceiver.address)).closeTo("109367984874944", "10000000000000"); + expect(await provider.getBalance(mockCallbackReceiver.address)).closeTo("98560984788488", "10000000000000"); }); it("validates reserve", async () => { diff --git a/utils/order.ts b/utils/order.ts index 7ed992031..96b8846e4 100644 --- a/utils/order.ts +++ b/utils/order.ts @@ -95,6 +95,7 @@ export async function createOrder(fixture, overrides) { const autoCancel = overrides.autoCancel || false; const referralCode = overrides.referralCode || ethers.constants.HashZero; const validFromTime = overrides.validFromTime || 0; + const srcChainId = overrides.srcChainId || 0; const dataList = overrides.dataList || []; if ( @@ -130,6 +131,7 @@ export async function createOrder(fixture, overrides) { callbackGasLimit, minOutputAmount, validFromTime, + srcChainId, }, orderType, decreasePositionSwapType, From e6c2dadb85e1187aaf362ce4d51ed9b13320aeeb Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 9 Feb 2025 20:37:41 +0200 Subject: [PATCH 031/205] Add srcChainId to _sendTokens --- contracts/deposit/ExecuteDepositUtils.sol | 1 - contracts/multichain/MultichainRouter.sol | 9 +++++++-- contracts/router/relay/BaseGelatoRelayRouter.sol | 3 +-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/contracts/deposit/ExecuteDepositUtils.sol b/contracts/deposit/ExecuteDepositUtils.sol index cf829389d..2f45bff94 100644 --- a/contracts/deposit/ExecuteDepositUtils.sol +++ b/contracts/deposit/ExecuteDepositUtils.sol @@ -517,7 +517,6 @@ library ExecuteDepositUtils { } else { // mint GM tokens to MultichainVault and increase receiver's multichain GM balance MarketToken(payable(_params.market.marketToken)).mint(address(params.multichainVault), mintAmount); - // TODO: is it possible thet the receiver is a multisig? Can it be an issue if there are different owners on different chains for that address? MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, _params.receiver, _params.market.marketToken, 0); // srcChainId is the current block.chainId } diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index a595924db..26b4027cf 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -108,6 +108,7 @@ contract MultichainRouter is GelatoRelayRouter { // On create step: can deduct relay fee fully or partially depending on user’s MultichainVault balance, save any excess pending relay fees and validate that the user has sufficient position collateral to pay for the remaining relay fee // On execute step: Deduct pending relay fees from user’s position collateral // TODO: confirm partial fee deduction logic + // A: this is in case of increase pos // pay relay fee tokens from MultichainVault to DepositVault and decrease user's multichain balance params.createDepositParams.executionFee = _handleRelay( @@ -126,9 +127,13 @@ contract MultichainRouter is GelatoRelayRouter { function _sendTokens(address account, address token, address receiver, uint256 amount, uint256 srcChainId) internal override { AccountUtils.validateReceiver(receiver); - MultichainUtils.transferOut(dataStore, eventEmitter, srcChainId, token, account, receiver, amount); + if (srcChainId == 0) { + router.pluginTransfer(token, account, receiver, amount); + } else { + MultichainUtils.transferOut(dataStore, eventEmitter, srcChainId, token, account, receiver, amount); + } } - + // TODO: double-check residualFee function _transferResidualFee(address wnt, address residualFeeReceiver, uint256 residualFee, address account, uint256 srcChainId) internal override { if (srcChainId == 0) { // sent residualFee to residualFeeReceiver (i.e. DepositVault) diff --git a/contracts/router/relay/BaseGelatoRelayRouter.sol b/contracts/router/relay/BaseGelatoRelayRouter.sol index d00285680..b2fd716b3 100644 --- a/contracts/router/relay/BaseGelatoRelayRouter.sol +++ b/contracts/router/relay/BaseGelatoRelayRouter.sol @@ -139,7 +139,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, params.addresses.initialCollateralToken, address(contracts.bank), collateralDeltaAmount, - 0 // TODO: add srcChainId to orders + relayParams.srcChainId ); } @@ -312,7 +312,6 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, _transferRelayFeeCapped(outputAmount); // TODO: should it be named remainingFee as it's intended to always include RelayFee + executionFee? - // For createOrder it's the executionFee and goes into depositVault. For update/cancelOrder is goes back to account uint256 residualFee = outputAmount - _getFee(); // for create orders the residual fee is sent to the order vault // for update orders the residual fee could be sent to the order vault if order's execution fee should be increased From 674dddb9320eb9c637b9bbeb0cf0f10c790811ff Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 10 Feb 2025 10:25:57 +0200 Subject: [PATCH 032/205] Rename multichainId to srcChainId, remove second topic log --- contracts/multichain/LayerZeroProviderEventUtils.sol | 12 ++++++------ contracts/multichain/MultichainEventUtils.sol | 4 ++-- contracts/multichain/MultichainProviderSignature.sol | 10 +++++----- contracts/multichain/MultichainVaultHandler.sol | 6 +++--- test/multichain/LayerZeroProvider.ts | 4 ++-- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/contracts/multichain/LayerZeroProviderEventUtils.sol b/contracts/multichain/LayerZeroProviderEventUtils.sol index dbbea1f9c..5f395a7e1 100644 --- a/contracts/multichain/LayerZeroProviderEventUtils.sol +++ b/contracts/multichain/LayerZeroProviderEventUtils.sol @@ -17,7 +17,7 @@ library LayerZeroProviderEventUtils { function emitComposedMessageReceived( EventEmitter eventEmitter, - uint256 multichainId, + uint256 srcChainId, address account, address from, bytes32 guid, @@ -33,7 +33,7 @@ library LayerZeroProviderEventUtils { eventData.addressItems.setItem(2, "executor", executor); eventData.uintItems.initItems(1); - eventData.uintItems.setItem(0, "multichainId", multichainId); + eventData.uintItems.setItem(0, "srcChainId", srcChainId); eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "guid", guid); @@ -42,12 +42,12 @@ library LayerZeroProviderEventUtils { eventData.bytesItems.setItem(0, "message", message); eventData.bytesItems.setItem(1, "extraData", extraData); - eventEmitter.emitEventLog2("MessageComposedReceived", bytes32(multichainId), Cast.toBytes32(account), eventData); + eventEmitter.emitEventLog1("MessageComposedReceived", Cast.toBytes32(account), eventData); } function emitWithdrawalReceipt( EventEmitter eventEmitter, - uint256 multichainId, + uint256 srcChainId, address account, bytes32 guid, uint64 nonce, @@ -64,11 +64,11 @@ library LayerZeroProviderEventUtils { eventData.uintItems.setItem(2, "lzTokenFee", lzTokenFee); eventData.uintItems.setItem(3, "amountSentLD", amountSentLD); eventData.uintItems.setItem(4, "amountReceivedLD", amountReceivedLD); - eventData.uintItems.setItem(5, "multichainId", multichainId); + eventData.uintItems.setItem(5, "srcChainId", srcChainId); eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "guid", guid); - eventEmitter.emitEventLog2("WithdrawalReceipt", bytes32(multichainId), Cast.toBytes32(account), eventData); + eventEmitter.emitEventLog1("WithdrawalReceipt", Cast.toBytes32(account), eventData); } } diff --git a/contracts/multichain/MultichainEventUtils.sol b/contracts/multichain/MultichainEventUtils.sol index a49043aee..6ac917016 100644 --- a/contracts/multichain/MultichainEventUtils.sol +++ b/contracts/multichain/MultichainEventUtils.sol @@ -30,7 +30,7 @@ library MultichainEventUtils { eventData.uintItems.setItem(0, "amount", amount); eventData.uintItems.setItem(1, "srcChainId", srcChainId); - eventEmitter.emitEventLog2("MultichainTransferIn", bytes32(srcChainId), Cast.toBytes32(account), eventData); + eventEmitter.emitEventLog1("MultichainTransferIn", Cast.toBytes32(account), eventData); } function emitMultichainMessage( @@ -66,6 +66,6 @@ library MultichainEventUtils { eventData.uintItems.setItem(0, "amount", amount); eventData.uintItems.setItem(1, "srcChainId", srcChainId); - eventEmitter.emitEventLog2("MultichainTransferOut", bytes32(srcChainId), Cast.toBytes32(account), eventData); + eventEmitter.emitEventLog1("MultichainTransferOut", Cast.toBytes32(account), eventData); } } diff --git a/contracts/multichain/MultichainProviderSignature.sol b/contracts/multichain/MultichainProviderSignature.sol index 1903c9262..e96a7a585 100644 --- a/contracts/multichain/MultichainProviderSignature.sol +++ b/contracts/multichain/MultichainProviderSignature.sol @@ -14,24 +14,24 @@ contract MultichainProviderSignature is EIP712 { string private constant SIGNATURE_VERSION = "1"; // Define the EIP-712 struct type: - // Message(address token,uint256 amount,address account,uint256 multichainId,uint32 srcEid) + // Message(address token,uint256 amount,address account,uint256 srcChainId,uint32 srcEid) bytes32 private constant _MESSAGE_TYPEHASH = - keccak256("Message(address token,uint256 amount,address account,uint256 multichainId,uint32 srcEid)"); + keccak256("Message(address token,uint256 amount,address account,uint256 srcChainId,uint32 srcEid)"); constructor() EIP712(SIGNING_DOMAIN, SIGNATURE_VERSION) {} /** * Check the signature for a given message - * @param message The ABI encoded parameters (token, amount, account, multichainId, srcEid). + * @param message The ABI encoded parameters (token, amount, account, srcChainId, srcEid). * @param signature The EIP-712 signature. */ function isSigner(bytes calldata message, bytes calldata signature) external view returns (bool) { // Decode the message - (address token, uint256 amount, address account, uint256 multichainId, uint32 srcEid) = MultichainProviderUtils + (address token, uint256 amount, address account, uint256 srcChainId, uint32 srcEid) = MultichainProviderUtils .decodeWithdrawal(message); // Build the struct hash - bytes32 structHash = keccak256(abi.encode(_MESSAGE_TYPEHASH, token, amount, account, multichainId, srcEid)); + bytes32 structHash = keccak256(abi.encode(_MESSAGE_TYPEHASH, token, amount, account, srcChainId, srcEid)); // Get the typed data hash for EIP-712 bytes32 hash = _hashTypedDataV4(structHash); diff --git a/contracts/multichain/MultichainVaultHandler.sol b/contracts/multichain/MultichainVaultHandler.sol index 4cdc8ac29..0e9a1942f 100644 --- a/contracts/multichain/MultichainVaultHandler.sol +++ b/contracts/multichain/MultichainVaultHandler.sol @@ -47,17 +47,17 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu * Executes the multicall for the given args * The multicall arguments contains the function calls to be executed (e.g. createDeposit, createOrder, createWithdrawal, etc) * @param account user address on the source chain - * @param multichainId chain id of the destination chain + * @param srcChainId chain id of the source chain * @param multicallArgs array of bytes containing the multicall arguments */ function executeMulticall( address account, - uint256 multichainId, + uint256 srcChainId, bytes[] calldata multicallArgs ) external onlyController { // execute multicall exchangeRouter.multicall(multicallArgs); - MultichainEventUtils.emitMultichainMessage(eventEmitter, account, multichainId); + MultichainEventUtils.emitMultichainMessage(eventEmitter, account, srcChainId); } } diff --git a/test/multichain/LayerZeroProvider.ts b/test/multichain/LayerZeroProvider.ts index 934a4dd20..26f81996d 100644 --- a/test/multichain/LayerZeroProvider.ts +++ b/test/multichain/LayerZeroProvider.ts @@ -17,7 +17,7 @@ describe("LayerZeroProvider", () => { }); it("lzCompose", async () => { - const multichainId = 1; + const srcChainId = 1; const amountUsdc = expandDecimals(50, 6); // mint usdc to users and approve StargatePool to spend it @@ -25,7 +25,7 @@ describe("LayerZeroProvider", () => { await usdc.connect(user0).approve(mockStargatePool.address, amountUsdc); // encoded message must match the decoded message in MultichainProviderUtils.decodeDeposit(message) - const message0 = encodeData(["address", "address", "uint256"], [user0.address, usdc.address, multichainId]); + const message0 = encodeData(["address", "address", "uint256"], [user0.address, usdc.address, srcChainId]); // StargatePool would deliver usdc to LayerZeroProvider contract and call LayerZeroProvider.lzCompose await mockStargatePool.connect(user0).sendToken(usdc.address, layerZeroProvider.address, amountUsdc, message0); From 726965394a03a5a091322b844b1245cddb96af48 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 10 Feb 2025 10:44:51 +0200 Subject: [PATCH 033/205] reorder method params --- contracts/multichain/MultichainRouter.sol | 2 +- contracts/multichain/MultichainUtils.sol | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 26b4027cf..cbd9ac450 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -130,7 +130,7 @@ contract MultichainRouter is GelatoRelayRouter { if (srcChainId == 0) { router.pluginTransfer(token, account, receiver, amount); } else { - MultichainUtils.transferOut(dataStore, eventEmitter, srcChainId, token, account, receiver, amount); + MultichainUtils.transferOut(dataStore, eventEmitter, token, account, receiver, amount, srcChainId); } } // TODO: double-check residualFee diff --git a/contracts/multichain/MultichainUtils.sol b/contracts/multichain/MultichainUtils.sol index 916829b28..047662b76 100644 --- a/contracts/multichain/MultichainUtils.sol +++ b/contracts/multichain/MultichainUtils.sol @@ -53,11 +53,11 @@ library MultichainUtils { function transferOut( DataStore dataStore, EventEmitter eventEmitter, - uint256 srcChainId, // TODO: do we need to emit this? address token, address account, address receiver, - uint256 amount + uint256 amount, + uint256 srcChainId // TODO: do we need to emit this? ) internal { if (amount == 0) { revert Errors.EmptyMultichainTransferOutAmount(); From b27ba9eedbf37742dba49fc9ae40d9fb2cbcea2c Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 10 Feb 2025 11:37:16 +0200 Subject: [PATCH 034/205] Record residualFee under user's multichain balance when the residualFeeReceiver is the multichainVault, remove comments --- contracts/multichain/MultichainRouter.sol | 14 +++----------- contracts/multichain/MultichainUtils.sol | 2 +- contracts/router/relay/BaseGelatoRelayRouter.sol | 1 - 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index cbd9ac450..1fe25e9e6 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -105,11 +105,6 @@ contract MultichainRouter is GelatoRelayRouter { params.createDepositParams.srcChainId ); - // On create step: can deduct relay fee fully or partially depending on user’s MultichainVault balance, save any excess pending relay fees and validate that the user has sufficient position collateral to pay for the remaining relay fee - // On execute step: Deduct pending relay fees from user’s position collateral - // TODO: confirm partial fee deduction logic - // A: this is in case of increase pos - // pay relay fee tokens from MultichainVault to DepositVault and decrease user's multichain balance params.createDepositParams.executionFee = _handleRelay( contracts, @@ -133,13 +128,10 @@ contract MultichainRouter is GelatoRelayRouter { MultichainUtils.transferOut(dataStore, eventEmitter, token, account, receiver, amount, srcChainId); } } - // TODO: double-check residualFee + function _transferResidualFee(address wnt, address residualFeeReceiver, uint256 residualFee, address account, uint256 srcChainId) internal override { - if (srcChainId == 0) { - // sent residualFee to residualFeeReceiver (i.e. DepositVault) - TokenUtils.transfer(dataStore, wnt, residualFeeReceiver, residualFee); - } else { - // sent residualFee to MultichainVault and increase user's multichain balance + TokenUtils.transfer(dataStore, wnt, residualFeeReceiver, residualFee); + if (residualFeeReceiver == address(multichainVault)) { MultichainUtils.recordTransferIn(dataStore, eventEmitter, multichainVault, wnt, account, srcChainId); } } diff --git a/contracts/multichain/MultichainUtils.sol b/contracts/multichain/MultichainUtils.sol index 047662b76..b499797ed 100644 --- a/contracts/multichain/MultichainUtils.sol +++ b/contracts/multichain/MultichainUtils.sol @@ -57,7 +57,7 @@ library MultichainUtils { address account, address receiver, uint256 amount, - uint256 srcChainId // TODO: do we need to emit this? + uint256 srcChainId ) internal { if (amount == 0) { revert Errors.EmptyMultichainTransferOutAmount(); diff --git a/contracts/router/relay/BaseGelatoRelayRouter.sol b/contracts/router/relay/BaseGelatoRelayRouter.sol index b2fd716b3..ce0549f82 100644 --- a/contracts/router/relay/BaseGelatoRelayRouter.sol +++ b/contracts/router/relay/BaseGelatoRelayRouter.sol @@ -311,7 +311,6 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, _transferRelayFeeCapped(outputAmount); - // TODO: should it be named remainingFee as it's intended to always include RelayFee + executionFee? uint256 residualFee = outputAmount - _getFee(); // for create orders the residual fee is sent to the order vault // for update orders the residual fee could be sent to the order vault if order's execution fee should be increased From 756fba7d51cf4e48a1142d2b20ee62e36d72a5b8 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 10 Feb 2025 13:16:49 +0200 Subject: [PATCH 035/205] Add srcChiainId field to CreateOrderParams struct --- contracts/fee/FeeSwapUtils.sol | 3 ++- contracts/order/IBaseOrderUtils.sol | 1 + contracts/order/OrderUtils.sol | 1 + contracts/router/relay/GelatoRelayRouter.sol | 7 ++++--- contracts/router/relay/SubaccountGelatoRelayRouter.sol | 7 ++++--- test/router/ExchangeRouter.ts | 2 ++ 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/contracts/fee/FeeSwapUtils.sol b/contracts/fee/FeeSwapUtils.sol index 09bffaf68..8bdcc4d6a 100644 --- a/contracts/fee/FeeSwapUtils.sol +++ b/contracts/fee/FeeSwapUtils.sol @@ -130,7 +130,8 @@ library FeeSwapUtils { executionFee, // executionFee maxCallbackGasLimit, // callbackGasLimit minOut, // minOutputAmount - 0 // validFromTime + 0, // validFromTime + 0 // srcChainId ); IBaseOrderUtils.CreateOrderParams memory params = IBaseOrderUtils.CreateOrderParams( diff --git a/contracts/order/IBaseOrderUtils.sol b/contracts/order/IBaseOrderUtils.sol index 0e33abb4e..30e139906 100644 --- a/contracts/order/IBaseOrderUtils.sol +++ b/contracts/order/IBaseOrderUtils.sol @@ -52,5 +52,6 @@ interface IBaseOrderUtils { uint256 callbackGasLimit; uint256 minOutputAmount; uint256 validFromTime; + uint256 srcChainId; } } diff --git a/contracts/order/OrderUtils.sol b/contracts/order/OrderUtils.sol index e9756bfb9..67f958914 100644 --- a/contracts/order/OrderUtils.sol +++ b/contracts/order/OrderUtils.sol @@ -153,6 +153,7 @@ library OrderUtils { order.setCallbackGasLimit(params.numbers.callbackGasLimit); order.setMinOutputAmount(params.numbers.minOutputAmount); order.setValidFromTime(params.numbers.validFromTime); + order.setSrcChainId(params.numbers.srcChainId); order.setIsLong(params.isLong); order.setShouldUnwrapNativeToken(params.shouldUnwrapNativeToken); order.setAutoCancel(params.autoCancel); diff --git a/contracts/router/relay/GelatoRelayRouter.sol b/contracts/router/relay/GelatoRelayRouter.sol index 470f1ec0e..3f0a73b99 100644 --- a/contracts/router/relay/GelatoRelayRouter.sol +++ b/contracts/router/relay/GelatoRelayRouter.sol @@ -30,13 +30,13 @@ contract GelatoRelayRouter is BaseGelatoRelayRouter { bytes32 public constant CREATE_ORDER_TYPEHASH = keccak256( bytes( - "CreateOrder(uint256 collateralDeltaAmount,CreateOrderAddresses addresses,CreateOrderNumbers numbers,uint256 orderType,uint256 decreasePositionSwapType,bool isLong,bool shouldUnwrapNativeToken,bool autoCancel,bytes32 referralCode,bytes32 relayParams)CreateOrderAddresses(address receiver,address cancellationReceiver,address callbackContract,address uiFeeReceiver,address market,address initialCollateralToken,address[] swapPath)CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime)" + "CreateOrder(uint256 collateralDeltaAmount,CreateOrderAddresses addresses,CreateOrderNumbers numbers,uint256 orderType,uint256 decreasePositionSwapType,bool isLong,bool shouldUnwrapNativeToken,bool autoCancel,bytes32 referralCode,bytes32 relayParams)CreateOrderAddresses(address receiver,address cancellationReceiver,address callbackContract,address uiFeeReceiver,address market,address initialCollateralToken,address[] swapPath)CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime,uint256 srcChainId)" ) ); bytes32 public constant CREATE_ORDER_NUMBERS_TYPEHASH = keccak256( bytes( - "CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime)" + "CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime,uint256 srcChainId)" ) ); bytes32 public constant CREATE_ORDER_ADDRESSES_TYPEHASH = @@ -172,7 +172,8 @@ contract GelatoRelayRouter is BaseGelatoRelayRouter { numbers.executionFee, numbers.callbackGasLimit, numbers.minOutputAmount, - numbers.validFromTime + numbers.validFromTime, + numbers.srcChainId ) ); } diff --git a/contracts/router/relay/SubaccountGelatoRelayRouter.sol b/contracts/router/relay/SubaccountGelatoRelayRouter.sol index df05d37b9..ed17a7d2d 100644 --- a/contracts/router/relay/SubaccountGelatoRelayRouter.sol +++ b/contracts/router/relay/SubaccountGelatoRelayRouter.sol @@ -39,13 +39,13 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { bytes32 public constant CREATE_ORDER_TYPEHASH = keccak256( bytes( - "CreateOrder(uint256 collateralDeltaAmount,address account,CreateOrderAddresses addresses,CreateOrderNumbers numbers,uint256 orderType,uint256 decreasePositionSwapType,bool isLong,bool shouldUnwrapNativeToken,bool autoCancel,bytes32 referralCode,bytes32 relayParams,bytes32 subaccountApproval)CreateOrderAddresses(address receiver,address cancellationReceiver,address callbackContract,address uiFeeReceiver,address market,address initialCollateralToken,address[] swapPath)CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime)" + "CreateOrder(uint256 collateralDeltaAmount,address account,CreateOrderAddresses addresses,CreateOrderNumbers numbers,uint256 orderType,uint256 decreasePositionSwapType,bool isLong,bool shouldUnwrapNativeToken,bool autoCancel,bytes32 referralCode,bytes32 relayParams,bytes32 subaccountApproval)CreateOrderAddresses(address receiver,address cancellationReceiver,address callbackContract,address uiFeeReceiver,address market,address initialCollateralToken,address[] swapPath)CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime,uint256 srcChainId)" ) ); bytes32 public constant CREATE_ORDER_NUMBERS_TYPEHASH = keccak256( bytes( - "CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime)" + "CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime,uint256 srcChainId)" ) ); bytes32 public constant CREATE_ORDER_ADDRESSES_TYPEHASH = @@ -312,7 +312,8 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { numbers.executionFee, numbers.callbackGasLimit, numbers.minOutputAmount, - numbers.validFromTime + numbers.validFromTime, + numbers.srcChainId ) ); } diff --git a/test/router/ExchangeRouter.ts b/test/router/ExchangeRouter.ts index 22491e56f..be33997b6 100644 --- a/test/router/ExchangeRouter.ts +++ b/test/router/ExchangeRouter.ts @@ -139,6 +139,7 @@ describe("ExchangeRouter", () => { callbackGasLimit: "200000", minOutputAmount: 700, validFromTime: 0, + srcChainId: 1, }, orderType: OrderType.LimitIncrease, decreasePositionSwapType: DecreasePositionSwapType.SwapCollateralTokenToPnlToken, @@ -170,6 +171,7 @@ describe("ExchangeRouter", () => { expect(order.numbers.executionFee).eq(expandDecimals(1, 18)); expect(order.numbers.callbackGasLimit).eq("200000"); expect(order.numbers.minOutputAmount).eq(700); + expect(order.numbers.srcChainId).eq(1); expect(order.flags.isLong).eq(true); expect(order.flags.shouldUnwrapNativeToken).eq(true); From 06afc345c4188c9a6da6bc4639bb46992ec1dd65 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 10 Feb 2025 14:02:19 +0200 Subject: [PATCH 036/205] gasless merge fixes --- contracts/multichain/MultichainRouter.sol | 14 +++++--------- test/router/SubaccountRouter.ts | 8 +++++++- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 1fe25e9e6..72baa6e51 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -53,10 +53,11 @@ contract MultichainRouter is GelatoRelayRouter { Oracle _oracle, IOrderHandler _orderHandler, OrderVault _orderVault, + IExternalHandler _externalHandler, IDepositHandler _depositHandler, DepositVault _depositVault, MultichainVault _multichainVault - ) GelatoRelayRouter(_router, _dataStore, _eventEmitter, _oracle, _orderHandler, _orderVault) { + ) GelatoRelayRouter(_router, _dataStore, _eventEmitter, _oracle, _orderHandler, _orderVault, _externalHandler) { depositVault = _depositVault; depositHandler = _depositHandler; multichainVault = _multichainVault; @@ -74,12 +75,11 @@ contract MultichainRouter is GelatoRelayRouter { bytes32 structHash = _getMultichainCreateDepositStructHash(relayParams, params); _validateCall(relayParams, account, structHash); - return _createDeposit(relayParams.tokenPermits, relayParams.fee, account, params); + return _createDeposit(relayParams, account, params); } function _createDeposit( - TokenPermit[] calldata tokenPermits, - RelayFeeParams calldata fee, + RelayParams calldata relayParams, address account, MultichainCreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee ) internal returns (bytes32) { @@ -108,12 +108,8 @@ contract MultichainRouter is GelatoRelayRouter { // pay relay fee tokens from MultichainVault to DepositVault and decrease user's multichain balance params.createDepositParams.executionFee = _handleRelay( contracts, - tokenPermits, - fee, // feeAmount is relayFee + executionFee - params.createDepositParams.srcChainId, + relayParams, account, - // if initialLongTokenAmount or initialShortTokenAmount is wnt then executionFee will be subracted (in DepositUtils.createDeposit) from one of them - // otherwise executionFee amount of wnt must be sent to DepositVault => it means the residualFeeReceiver should be the DepositVault address(depositVault) // residualFeeReceiver ); diff --git a/test/router/SubaccountRouter.ts b/test/router/SubaccountRouter.ts index ed795d1b6..e971c090f 100644 --- a/test/router/SubaccountRouter.ts +++ b/test/router/SubaccountRouter.ts @@ -158,6 +158,7 @@ describe("SubaccountRouter", () => { callbackGasLimit: "200000", minOutputAmount: 700, validFromTime: 0, + srcChainId: 0, }, orderType: OrderType.Liquidation, decreasePositionSwapType: DecreasePositionSwapType.SwapCollateralTokenToPnlToken, @@ -332,6 +333,7 @@ describe("SubaccountRouter", () => { callbackGasLimit: "200000", minOutputAmount: 700, validFromTime: 0, + srcChainId: 0, }, orderType: OrderType.MarketIncrease, decreasePositionSwapType: DecreasePositionSwapType.SwapCollateralTokenToPnlToken, @@ -474,6 +476,7 @@ describe("SubaccountRouter", () => { callbackGasLimit: "200000", minOutputAmount: 700, validFromTime: 800, + srcChainId: 1, }, orderType: OrderType.LimitIncrease, decreasePositionSwapType: DecreasePositionSwapType.SwapCollateralTokenToPnlToken, @@ -510,6 +513,7 @@ describe("SubaccountRouter", () => { expect(order.numbers.triggerPrice).eq(expandDecimals(4800, 12)); expect(order.numbers.minOutputAmount).eq(700); expect(order.numbers.validFromTime).eq(800); + expect(order.numbers.srcChainId).eq(1); expect(order._dataList).deep.eq(dataList); }); @@ -619,6 +623,7 @@ describe("SubaccountRouter", () => { callbackGasLimit: "200000", minOutputAmount: 700, validFromTime: 800, + srcChainId: 1, }, orderType: OrderType.LimitIncrease, decreasePositionSwapType: DecreasePositionSwapType.SwapCollateralTokenToPnlToken, @@ -655,6 +660,7 @@ describe("SubaccountRouter", () => { expect(order.numbers.triggerPrice).eq(expandDecimals(4800, 12)); expect(order.numbers.minOutputAmount).eq(700); expect(order.numbers.validFromTime).eq(800); + expect(order.numbers.srcChainId).eq(1); expect(order._dataList).deep.eq(dataList); }); @@ -664,7 +670,7 @@ describe("SubaccountRouter", () => { await subaccountRouter.connect(subaccount).cancelOrder(orderKey); - expect(initialWntBalance0.sub(await wnt.balanceOf(user0.address))).closeTo("1579799104730528", "10000000000000"); // 0.001579799104730528 ETH + expect(initialWntBalance0.sub(await wnt.balanceOf(user0.address))).closeTo("1598971241887446", "10000000000000"); // 0.001598971241887446 ETH expect(await usdc.balanceOf(user0.address)).eq(expandDecimals(101, 6)); From 6f0199be2d2638ec406b736f087280e28af1816c Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 10 Feb 2025 22:46:31 +0200 Subject: [PATCH 037/205] Implement multichain createWithdrawal --- contracts/multichain/MultichainRouter.sol | 143 +++++++++++++++++++++- 1 file changed, 139 insertions(+), 4 deletions(-) diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 72baa6e51..bf2dd069c 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -6,6 +6,9 @@ import "../router/relay/GelatoRelayRouter.sol"; import "../deposit/DepositUtils.sol"; import "../deposit/DepositVault.sol"; import "../exchange/IDepositHandler.sol"; +import "../withdrawal/WithdrawalUtils.sol"; +import "../exchange/WithdrawalHandler.sol"; +import "../withdrawal/WithdrawalVault.sol"; import "./MultichainUtils.sol"; @@ -17,6 +20,13 @@ contract MultichainRouter is GelatoRelayRouter { DepositUtils.CreateDepositParams createDepositParams; } + struct MultichainCreateWithdrawalParams { + uint256 desChainId; + uint256 longTokenAmount; + uint256 shortTokenAmount; + WithdrawalUtils.CreateWithdrawalParams createWithdrawalParams; + } + bytes32 public constant CREATE_DEPOSIT_TYPEHASH = keccak256( bytes( @@ -42,9 +52,32 @@ contract MultichainRouter is GelatoRelayRouter { ) ); + bytes32 public constant CREATE_WITHDRAWAL_TYPEHASH = + keccak256( + bytes( + "CreateWithdrawal(CreateWithdrawalParams params,bytes32 relayParams)CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ) + ); + + bytes32 public constant MULTICHAIN_CREATE_WITHDRAWAL_PARAMS_TYPEHASH = + keccak256( + bytes( + "MultichainCreateWithdrawalParams(uint256 desChainId,uint256 longTokenAmount,uint256 shortTokenAmount,CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ) + ); + + bytes32 public constant CREATE_WITHDRAWAL_PARAMS_TYPEHASH = + keccak256( + bytes( + "CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ) + ); + DepositVault depositVault; IDepositHandler depositHandler; MultichainVault multichainVault; + WithdrawalHandler withdrawalHandler; + WithdrawalVault withdrawalVault; constructor( Router _router, @@ -56,11 +89,15 @@ contract MultichainRouter is GelatoRelayRouter { IExternalHandler _externalHandler, IDepositHandler _depositHandler, DepositVault _depositVault, - MultichainVault _multichainVault + MultichainVault _multichainVault, + WithdrawalHandler _withdrawalHandler, + WithdrawalVault _withdrawalVault ) GelatoRelayRouter(_router, _dataStore, _eventEmitter, _oracle, _orderHandler, _orderVault, _externalHandler) { depositVault = _depositVault; depositHandler = _depositHandler; multichainVault = _multichainVault; + withdrawalHandler = _withdrawalHandler; + withdrawalVault = _withdrawalVault; } function createDeposit( @@ -91,15 +128,15 @@ contract MultichainRouter is GelatoRelayRouter { // transfer long & short tokens from MultichainVault to DepositVault and decrement user's multichain balance _sendTokens( - params.createDepositParams.addresses.initialLongToken, account, + params.createDepositParams.addresses.initialLongToken, address(depositVault), // receiver params.longTokenAmount, params.createDepositParams.srcChainId ); _sendTokens( - params.createDepositParams.addresses.initialShortToken, account, + params.createDepositParams.addresses.initialShortToken, address(depositVault), // receiver params.shortTokenAmount, params.createDepositParams.srcChainId @@ -116,6 +153,52 @@ contract MultichainRouter is GelatoRelayRouter { return depositHandler.createDeposit(account, params.createDepositParams); } + function createWithdrawal( + RelayParams calldata relayParams, + address account, + MultichainCreateWithdrawalParams memory params + ) external nonReentrant onlyGelatoRelay returns (bytes32) { + if (params.desChainId != block.chainid) { + revert Errors.InvalidDestinationChainId(); + } + + bytes32 structHash = _getMultichainCreateWithdrawalStructHash(relayParams, params); + _validateCall(relayParams, account, structHash); + + return _createWithdrawal(relayParams, account, params); + } + + function _createWithdrawal( + RelayParams calldata relayParams, + address account, + MultichainCreateWithdrawalParams memory params + ) internal returns (bytes32) { + Contracts memory contracts = Contracts({ + dataStore: dataStore, + eventEmitter: eventEmitter, + bank: withdrawalVault + }); + + // user already bridged the GM / GLV tokens to the MultichainVault and balance was increased + // transfer the GM / GLV tokens from multichainVault to withdrawalVault + _sendTokens( + account, + params.createWithdrawalParams.market, + address(withdrawalVault), // receiver + params.longTokenAmount, // TODO: should amount be provided by the user, or everything should be withdrawn? + params.createWithdrawalParams.srcChainId + ); + + params.createWithdrawalParams.executionFee = _handleRelay( + contracts, + relayParams, + account, + address(withdrawalVault) + ); + + return withdrawalHandler.createWithdrawal(account, params.createWithdrawalParams); + } + function _sendTokens(address account, address token, address receiver, uint256 amount, uint256 srcChainId) internal override { AccountUtils.validateReceiver(receiver); if (srcChainId == 0) { @@ -200,7 +283,59 @@ contract MultichainRouter is GelatoRelayRouter { ); } - function createWithdrawal() external nonReentrant onlyGelatoRelay {} + function _getMultichainCreateWithdrawalStructHash( + RelayParams calldata relayParams, + MultichainCreateWithdrawalParams memory params + ) internal view returns (bytes32) { + bytes32 relayParamsHash = keccak256(abi.encode(relayParams)); + return + keccak256( + abi.encode( + CREATE_WITHDRAWAL_TYPEHASH, + _getMultichainCreateWithdrawalParamsStructHash(params), + relayParamsHash + ) + ); + } + + function _getMultichainCreateWithdrawalParamsStructHash( + MultichainCreateWithdrawalParams memory params + ) internal view returns (bytes32) { + return + keccak256( + abi.encode( + MULTICHAIN_CREATE_WITHDRAWAL_PARAMS_TYPEHASH, + block.chainid, + params.longTokenAmount, + params.shortTokenAmount, + _getCreateWithdrawalParamsStructHash(params.createWithdrawalParams) + ) + ); + } + + function _getCreateWithdrawalParamsStructHash( + WithdrawalUtils.CreateWithdrawalParams memory params + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + CREATE_WITHDRAWAL_PARAMS_TYPEHASH, + params.receiver, + params.callbackContract, + params.uiFeeReceiver, + params.market, + keccak256(abi.encodePacked(params.longTokenSwapPath)), + keccak256(abi.encodePacked(params.shortTokenSwapPath)), + params.minLongTokenAmount, + params.minShortTokenAmount, + params.shouldUnwrapNativeToken, + params.executionFee, + params.callbackGasLimit, + params.srcChainId, + keccak256(abi.encodePacked(params.dataList)) + ) + ); + } function createGlvDeposit() external nonReentrant onlyGelatoRelay {} From 6b086053cc19e8086484da51013c0bc06767ae87 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 11 Feb 2025 09:42:48 +0200 Subject: [PATCH 038/205] Use multichainVault as withdrawal receiver for GM/GLV for source chain transfers, record output tokens under user's multichain balance --- contracts/exchange/GlvHandler.sol | 1 + contracts/exchange/WithdrawalHandler.sol | 4 ++++ .../glv/glvWithdrawal/GlvWithdrawalUtils.sol | 2 ++ contracts/multichain/MultichainRouter.sol | 24 ++++++++++++------- contracts/shift/ShiftUtils.sol | 1 + .../withdrawal/ExecuteWithdrawalUtils.sol | 8 +++++++ deploy/deployWithdrawalHandler.ts | 2 +- 7 files changed, 32 insertions(+), 10 deletions(-) diff --git a/contracts/exchange/GlvHandler.sol b/contracts/exchange/GlvHandler.sol index 9e2d0f447..2b7df5804 100644 --- a/contracts/exchange/GlvHandler.sol +++ b/contracts/exchange/GlvHandler.sol @@ -181,6 +181,7 @@ contract GlvHandler is BaseHandler, ReentrancyGuard { key: key, dataStore: dataStore, eventEmitter: eventEmitter, + multichainVault: multichainVault, glvVault: glvVault, oracle: oracle, startingGas: startingGas, diff --git a/contracts/exchange/WithdrawalHandler.sol b/contracts/exchange/WithdrawalHandler.sol index 92f038a07..90e264dd9 100644 --- a/contracts/exchange/WithdrawalHandler.sol +++ b/contracts/exchange/WithdrawalHandler.sol @@ -20,6 +20,7 @@ import "./IWithdrawalHandler.sol"; contract WithdrawalHandler is IWithdrawalHandler, BaseHandler { using Withdrawal for Withdrawal.Props; + MultichainVault public immutable multichainVault; WithdrawalVault public immutable withdrawalVault; constructor( @@ -27,8 +28,10 @@ contract WithdrawalHandler is IWithdrawalHandler, BaseHandler { DataStore _dataStore, EventEmitter _eventEmitter, Oracle _oracle, + MultichainVault _multichainVault, WithdrawalVault _withdrawalVault ) BaseHandler(_roleStore, _dataStore, _eventEmitter, _oracle) { + multichainVault = _multichainVault; withdrawalVault = _withdrawalVault; } @@ -206,6 +209,7 @@ contract WithdrawalHandler is IWithdrawalHandler, BaseHandler { ExecuteWithdrawalUtils.ExecuteWithdrawalParams memory params = ExecuteWithdrawalUtils.ExecuteWithdrawalParams( dataStore, eventEmitter, + multichainVault, withdrawalVault, oracle, key, diff --git a/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol b/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol index d3f1ea45c..b3103b38a 100644 --- a/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol +++ b/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol @@ -38,6 +38,7 @@ library GlvWithdrawalUtils { struct ExecuteGlvWithdrawalParams { DataStore dataStore; EventEmitter eventEmitter; + MultichainVault multichainVault; GlvVault glvVault; Oracle oracle; bytes32 key; @@ -257,6 +258,7 @@ library GlvWithdrawalUtils { .ExecuteWithdrawalParams({ dataStore: params.dataStore, eventEmitter: params.eventEmitter, + multichainVault: params.multichainVault, withdrawalVault: WithdrawalVault(payable(params.glvVault)), oracle: params.oracle, key: withdrawalKey, diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index bf2dd069c..ee74142b8 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -22,8 +22,7 @@ contract MultichainRouter is GelatoRelayRouter { struct MultichainCreateWithdrawalParams { uint256 desChainId; - uint256 longTokenAmount; - uint256 shortTokenAmount; + uint256 tokenAmount; WithdrawalUtils.CreateWithdrawalParams createWithdrawalParams; } @@ -62,7 +61,7 @@ contract MultichainRouter is GelatoRelayRouter { bytes32 public constant MULTICHAIN_CREATE_WITHDRAWAL_PARAMS_TYPEHASH = keccak256( bytes( - "MultichainCreateWithdrawalParams(uint256 desChainId,uint256 longTokenAmount,uint256 shortTokenAmount,CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + "MultichainCreateWithdrawalParams(uint256 desChainId,uint256 tokenAmount,CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" ) ); @@ -153,10 +152,13 @@ contract MultichainRouter is GelatoRelayRouter { return depositHandler.createDeposit(account, params.createDepositParams); } + // does a WithdrawalUtils.createWithdrawal (receiver is multichainVault if srcChainId != 0) + // keeper will do a ExecuteWithdrawalUtils.executeWithdrawal and outputToken + secondaryOutputToken will be sent to receiver (or multichainVault if srcChainId != 0) + // TODO: MultichainProvider should initiate the bridging (e.g. LayerZeroProvider.sendTokens()) or will there be another action triggering the bridging? function createWithdrawal( RelayParams calldata relayParams, address account, - MultichainCreateWithdrawalParams memory params + MultichainCreateWithdrawalParams memory params // can't use calldata because need to modify params.addresses.receiver & params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { if (params.desChainId != block.chainid) { revert Errors.InvalidDestinationChainId(); @@ -171,7 +173,7 @@ contract MultichainRouter is GelatoRelayRouter { function _createWithdrawal( RelayParams calldata relayParams, address account, - MultichainCreateWithdrawalParams memory params + MultichainCreateWithdrawalParams memory params // can't use calldata because need to modify params.addresses.receiver & params.numbers.executionFee ) internal returns (bytes32) { Contracts memory contracts = Contracts({ dataStore: dataStore, @@ -179,13 +181,18 @@ contract MultichainRouter is GelatoRelayRouter { bank: withdrawalVault }); + // for multichain withdrawals, the output tokens are sent to multichainVault and user's multichain balance is increased + if (params.createWithdrawalParams.srcChainId != 0) { + params.createWithdrawalParams.receiver = address(multichainVault); + } + // user already bridged the GM / GLV tokens to the MultichainVault and balance was increased // transfer the GM / GLV tokens from multichainVault to withdrawalVault _sendTokens( account, params.createWithdrawalParams.market, address(withdrawalVault), // receiver - params.longTokenAmount, // TODO: should amount be provided by the user, or everything should be withdrawn? + params.tokenAmount, // TODO: should remove MultichainCreateWithdrawalParams.tokenAmount and send everything instead? (i.e. sent entire GM/GLV multichain balance to WithdrawalVault) params.createWithdrawalParams.srcChainId ); @@ -193,7 +200,7 @@ contract MultichainRouter is GelatoRelayRouter { contracts, relayParams, account, - address(withdrawalVault) + address(withdrawalVault) // residualFeeReceiver ); return withdrawalHandler.createWithdrawal(account, params.createWithdrawalParams); @@ -306,8 +313,7 @@ contract MultichainRouter is GelatoRelayRouter { abi.encode( MULTICHAIN_CREATE_WITHDRAWAL_PARAMS_TYPEHASH, block.chainid, - params.longTokenAmount, - params.shortTokenAmount, + params.tokenAmount, _getCreateWithdrawalParamsStructHash(params.createWithdrawalParams) ) ); diff --git a/contracts/shift/ShiftUtils.sol b/contracts/shift/ShiftUtils.sol index de002eaae..f5a9bad9a 100644 --- a/contracts/shift/ShiftUtils.sol +++ b/contracts/shift/ShiftUtils.sol @@ -225,6 +225,7 @@ library ShiftUtils { cache.executeWithdrawalParams = ExecuteWithdrawalUtils.ExecuteWithdrawalParams( params.dataStore, params.eventEmitter, + params.multichainVault, WithdrawalVault(payable(params.shiftVault)), params.oracle, cache.withdrawalKey, diff --git a/contracts/withdrawal/ExecuteWithdrawalUtils.sol b/contracts/withdrawal/ExecuteWithdrawalUtils.sol index a4b83b6e5..0cbd4f685 100644 --- a/contracts/withdrawal/ExecuteWithdrawalUtils.sol +++ b/contracts/withdrawal/ExecuteWithdrawalUtils.sol @@ -4,6 +4,8 @@ pragma solidity ^0.8.0; import "../data/DataStore.sol"; +import "../multichain/MultichainUtils.sol"; + import "./WithdrawalVault.sol"; import "./WithdrawalStoreUtils.sol"; import "./WithdrawalEventUtils.sol"; @@ -35,6 +37,7 @@ library ExecuteWithdrawalUtils { struct ExecuteWithdrawalParams { DataStore dataStore; EventEmitter eventEmitter; + MultichainVault multichainVault; WithdrawalVault withdrawalVault; Oracle oracle; bytes32 key; @@ -166,6 +169,11 @@ library ExecuteWithdrawalUtils { withdrawal.receiver() ); + if (withdrawal.srcChainId() != 0) { + MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, cache.result.outputToken, withdrawal.account(), withdrawal.srcChainId()); + MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, cache.result.secondaryOutputToken, withdrawal.account(), withdrawal.srcChainId()); + } + return cache.result; } diff --git a/deploy/deployWithdrawalHandler.ts b/deploy/deployWithdrawalHandler.ts index da69f187c..29095777d 100644 --- a/deploy/deployWithdrawalHandler.ts +++ b/deploy/deployWithdrawalHandler.ts @@ -1,7 +1,7 @@ import { grantRoleIfNotGranted } from "../utils/role"; import { createDeployFunction } from "../utils/deploy"; -const constructorContracts = ["RoleStore", "DataStore", "EventEmitter", "Oracle", "WithdrawalVault"]; +const constructorContracts = ["RoleStore", "DataStore", "EventEmitter", "Oracle", "MultichainVault", "WithdrawalVault"]; const func = createDeployFunction({ contractName: "WithdrawalHandler", From 7afe63d8160456a2467306565c12d70c51e60e3f Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 11 Feb 2025 13:12:28 +0200 Subject: [PATCH 039/205] Implement multichain createGlvDeposit --- contracts/glv/glvDeposit/GlvDepositUtils.sol | 10 +- contracts/multichain/MultichainRouter.sol | 150 ++++++++++++++++++- 2 files changed, 154 insertions(+), 6 deletions(-) diff --git a/contracts/glv/glvDeposit/GlvDepositUtils.sol b/contracts/glv/glvDeposit/GlvDepositUtils.sol index d150be1c5..1be07dc0a 100644 --- a/contracts/glv/glvDeposit/GlvDepositUtils.sol +++ b/contracts/glv/glvDeposit/GlvDepositUtils.sol @@ -4,6 +4,8 @@ pragma solidity ^0.8.0; import "../../deposit/ExecuteDepositUtils.sol"; +import "../../multichain/MultichainUtils.sol"; + import "../../nonce/NonceUtils.sol"; import "../GlvVault.sol"; @@ -248,7 +250,13 @@ library GlvDepositUtils { revert Errors.MinGlvTokens(cache.mintAmount, glvDeposit.minGlvTokens()); } - GlvToken(payable(glvDeposit.glv())).mint(glvDeposit.receiver(), cache.mintAmount); + if (glvDeposit.srcChainId() == 0) { + GlvToken(payable(glvDeposit.glv())).mint(glvDeposit.receiver(), cache.mintAmount); + } else { + GlvToken(payable(glvDeposit.glv())).mint(address(params.multichainVault), cache.mintAmount); + // MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, glvDeposit.glv(), glvDeposit.receiver(), glvDeposit.srcChainId()); // TODO: fix GlvDepositUtils size (maybe make MultichainUtils functions external) + } + cache.market = MarketUtils.getEnabledMarket(params.dataStore, glvDeposit.market()); cache.marketPoolValueInfo = MarketUtils.getPoolValueInfo( diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index ee74142b8..a975f13fd 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -9,6 +9,8 @@ import "../exchange/IDepositHandler.sol"; import "../withdrawal/WithdrawalUtils.sol"; import "../exchange/WithdrawalHandler.sol"; import "../withdrawal/WithdrawalVault.sol"; +import "../exchange/GlvHandler.sol"; +import "../glv/GlvVault.sol"; import "./MultichainUtils.sol"; @@ -26,6 +28,13 @@ contract MultichainRouter is GelatoRelayRouter { WithdrawalUtils.CreateWithdrawalParams createWithdrawalParams; } + struct MultichainCreateGlvDepositParams { + uint256 desChainId; + uint256 longTokenAmount; + uint256 shortTokenAmount; + GlvDepositUtils.CreateGlvDepositParams createGlvDepositParams; + } + bytes32 public constant CREATE_DEPOSIT_TYPEHASH = keccak256( bytes( @@ -41,7 +50,7 @@ contract MultichainRouter is GelatoRelayRouter { bytes32 public constant MULTICHAIN_CREATE_DEPOSIT_PARAMS_TYPEHASH = keccak256( bytes( - "MultichainCreateDepositParams(uint256 desChainId,uint256 longTokenAmount,uint256 shortTokenAmount,CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + "MultichainCreateDepositParams(uint256 desChainId,uint256 longTokenAmount,uint256 shortTokenAmount,CreateDepositParams params)CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" ) ); bytes32 public constant CREATE_DEPOSIT_PARAMS_ADDRESSES_TYPEHASH = @@ -57,14 +66,12 @@ contract MultichainRouter is GelatoRelayRouter { "CreateWithdrawal(CreateWithdrawalParams params,bytes32 relayParams)CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" ) ); - bytes32 public constant MULTICHAIN_CREATE_WITHDRAWAL_PARAMS_TYPEHASH = keccak256( bytes( - "MultichainCreateWithdrawalParams(uint256 desChainId,uint256 tokenAmount,CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + "MultichainCreateWithdrawalParams(uint256 desChainId,uint256 tokenAmount,CreateWithdrawalParams params)CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" ) ); - bytes32 public constant CREATE_WITHDRAWAL_PARAMS_TYPEHASH = keccak256( bytes( @@ -72,11 +79,32 @@ contract MultichainRouter is GelatoRelayRouter { ) ); + bytes32 public constant CREATE_GLV_DEPOSIT_TYPEHASH = + keccak256( + bytes( + "CreateGlvDeposit(CreateGlvDepositParams params,bytes32 relayParams)CreateGlvDepositParams(address account,address market,address initialLongToken,address initialShortToken,uint256 srcChainId,bytes32[] dataList)" + ) + ); + bytes32 public constant MULTICHAIN_CREATE_GLV_DEPOSIT_PARAMS_TYPEHASH = + keccak256( + bytes( + "MultichainCreateGlvDepositParams(uint256 desChainId,uint256 tokenAmount,CreateGlvDepositParams params)CreateGlvDepositParams(address glv,address market,address receiver,address callbackContract,address uiFeeReceiver,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minGlvTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bool shouldUnwrapNativeToken,bool isMarketTokenDeposit,bytes32[] dataList)" + ) + ); + bytes32 public constant CREATE_GLV_DEPOSIT_PARAMS_TYPEHASH = + keccak256( + bytes( + "CreateGlvDepositParams(address glv,address market,address receiver,address callbackContract,address uiFeeReceiver,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minGlvTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bool shouldUnwrapNativeToken,bool isMarketTokenDeposit,bytes32[] dataList)" + ) + ); + DepositVault depositVault; IDepositHandler depositHandler; MultichainVault multichainVault; WithdrawalHandler withdrawalHandler; WithdrawalVault withdrawalVault; + GlvHandler public glvHandler; + GlvVault public glvVault; constructor( Router _router, @@ -91,12 +119,16 @@ contract MultichainRouter is GelatoRelayRouter { MultichainVault _multichainVault, WithdrawalHandler _withdrawalHandler, WithdrawalVault _withdrawalVault + // GlvHandler _glvHandler, // TODO: place in a struct to fix stack to deep error + // GlvVault _glvVault ) GelatoRelayRouter(_router, _dataStore, _eventEmitter, _oracle, _orderHandler, _orderVault, _externalHandler) { depositVault = _depositVault; depositHandler = _depositHandler; multichainVault = _multichainVault; withdrawalHandler = _withdrawalHandler; withdrawalVault = _withdrawalVault; + // glvHandler = _glvHandler; + // glvVault = _glvVault; } function createDeposit( @@ -206,6 +238,59 @@ contract MultichainRouter is GelatoRelayRouter { return withdrawalHandler.createWithdrawal(account, params.createWithdrawalParams); } + function createGlvDeposit( + RelayParams calldata relayParams, + address account, + MultichainCreateGlvDepositParams memory params + ) external nonReentrant onlyGelatoRelay returns (bytes32) { + if (params.desChainId != block.chainid) { + revert Errors.InvalidDestinationChainId(); + } + + bytes32 structHash = _getMultichainCreateGlvDepositStructHash(relayParams, params); + _validateCall(relayParams, account, structHash); + + return _createGlvDeposit(relayParams, account, params); + } + + function _createGlvDeposit( + RelayParams calldata relayParams, + address account, + MultichainCreateGlvDepositParams memory params + ) internal returns (bytes32) { + Contracts memory contracts = Contracts({ + dataStore: dataStore, + eventEmitter: eventEmitter, + bank: glvVault + }); + + // transfer long & short tokens from MultichainVault to GlvVault and decrement user's multichain balance + _sendTokens( + account, + params.createGlvDepositParams.initialLongToken, + address(glvVault), + params.longTokenAmount, + params.createGlvDepositParams.srcChainId + ); + _sendTokens( + account, + params.createGlvDepositParams.initialShortToken, + address(glvVault), + params.shortTokenAmount, + params.createGlvDepositParams.srcChainId + ); + + // pay relay fee tokens from MultichainVault to GlvVault and decrease user's multichain balance + params.createGlvDepositParams.executionFee = _handleRelay( + contracts, + relayParams, + account, + address(glvVault) + ); + + return glvHandler.createGlvDeposit(account, params.createGlvDepositParams); + } + function _sendTokens(address account, address token, address receiver, uint256 amount, uint256 srcChainId) internal override { AccountUtils.validateReceiver(receiver); if (srcChainId == 0) { @@ -343,7 +428,62 @@ contract MultichainRouter is GelatoRelayRouter { ); } - function createGlvDeposit() external nonReentrant onlyGelatoRelay {} + function _getMultichainCreateGlvDepositStructHash( + RelayParams calldata relayParams, + MultichainCreateGlvDepositParams memory params + ) internal pure returns (bytes32) { + bytes32 relayParamsHash = keccak256(abi.encode(relayParams)); + return + keccak256( + abi.encode( + CREATE_GLV_DEPOSIT_TYPEHASH, + _getMultichainCreateGlvDepositParamsStructHash(params), + relayParamsHash + ) + ); + } + + function _getMultichainCreateGlvDepositParamsStructHash( + MultichainCreateGlvDepositParams memory params + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + MULTICHAIN_CREATE_GLV_DEPOSIT_PARAMS_TYPEHASH, + params.desChainId, + params.longTokenAmount, + params.shortTokenAmount, + _getCreateGlvDepositParamsStructHash(params.createGlvDepositParams) + ) + ); + } + + function _getCreateGlvDepositParamsStructHash( + GlvDepositUtils.CreateGlvDepositParams memory params + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + CREATE_GLV_DEPOSIT_PARAMS_TYPEHASH, + params.glv, + params.market, + params.receiver, + params.callbackContract, + params.uiFeeReceiver, + // params.initialLongToken, + // params.initialShortToken, + // keccak256(abi.encodePacked(params.longTokenSwapPath)), + // keccak256(abi.encodePacked(params.shortTokenSwapPath)), // TODO: split CreateGlvDepositParams into groups to fix slot too deep error + params.minGlvTokens, + params.executionFee, + params.callbackGasLimit, + params.srcChainId, + params.shouldUnwrapNativeToken, + params.isMarketTokenDeposit, + keccak256(abi.encodePacked(params.dataList)) + ) + ); + } function createGlvWithdrawal() external nonReentrant onlyGelatoRelay {} From c891d25ff62ed35d0b281689d8823798b7a716af Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 11 Feb 2025 13:27:12 +0200 Subject: [PATCH 040/205] Use MultichainUtils as external lib to avoid increasing the size of the contracts using it --- contracts/glv/glvDeposit/GlvDepositUtils.sol | 2 +- contracts/multichain/MultichainUtils.sol | 4 ++-- deploy/deployExecuteDepositUtils.ts | 1 + deploy/deployExecuteWithdrawalUtils.ts | 1 + deploy/deployGlvDepositUtils.ts | 1 + deploy/deployLayerZeroProvider.ts | 1 + deploy/deployMultichainUtils.ts | 7 +++++++ utils/fixture.ts | 2 ++ 8 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 deploy/deployMultichainUtils.ts diff --git a/contracts/glv/glvDeposit/GlvDepositUtils.sol b/contracts/glv/glvDeposit/GlvDepositUtils.sol index 1be07dc0a..6745c6738 100644 --- a/contracts/glv/glvDeposit/GlvDepositUtils.sol +++ b/contracts/glv/glvDeposit/GlvDepositUtils.sol @@ -254,7 +254,7 @@ library GlvDepositUtils { GlvToken(payable(glvDeposit.glv())).mint(glvDeposit.receiver(), cache.mintAmount); } else { GlvToken(payable(glvDeposit.glv())).mint(address(params.multichainVault), cache.mintAmount); - // MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, glvDeposit.glv(), glvDeposit.receiver(), glvDeposit.srcChainId()); // TODO: fix GlvDepositUtils size (maybe make MultichainUtils functions external) + MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, glvDeposit.glv(), glvDeposit.receiver(), glvDeposit.srcChainId()); } diff --git a/contracts/multichain/MultichainUtils.sol b/contracts/multichain/MultichainUtils.sol index b499797ed..9b53616b6 100644 --- a/contracts/multichain/MultichainUtils.sol +++ b/contracts/multichain/MultichainUtils.sol @@ -31,7 +31,7 @@ library MultichainUtils { address token, address account, uint256 srcChainId - ) internal { + ) external { // token should have been transferred to multichainVault by IMultichainProvider uint256 amount = multichainVault.recordTransferIn(token); if (amount == 0) { @@ -58,7 +58,7 @@ library MultichainUtils { address receiver, uint256 amount, uint256 srcChainId - ) internal { + ) external { if (amount == 0) { revert Errors.EmptyMultichainTransferOutAmount(); } diff --git a/deploy/deployExecuteDepositUtils.ts b/deploy/deployExecuteDepositUtils.ts index d1d8ba72f..3b428b188 100644 --- a/deploy/deployExecuteDepositUtils.ts +++ b/deploy/deployExecuteDepositUtils.ts @@ -13,6 +13,7 @@ const func = createDeployFunction({ "SwapUtils", "SwapPricingUtils", "PositionUtils", + "MultichainUtils", ], }); diff --git a/deploy/deployExecuteWithdrawalUtils.ts b/deploy/deployExecuteWithdrawalUtils.ts index 4591cfc27..b54360865 100644 --- a/deploy/deployExecuteWithdrawalUtils.ts +++ b/deploy/deployExecuteWithdrawalUtils.ts @@ -13,6 +13,7 @@ const func = createDeployFunction({ "SwapUtils", "SwapPricingUtils", "PositionUtils", + "MultichainUtils", ], }); diff --git a/deploy/deployGlvDepositUtils.ts b/deploy/deployGlvDepositUtils.ts index 8e40f8649..b1214e0a7 100644 --- a/deploy/deployGlvDepositUtils.ts +++ b/deploy/deployGlvDepositUtils.ts @@ -12,6 +12,7 @@ const func = createDeployFunction({ "GlvDepositStoreUtils", "GlvDepositCalc", "MarketStoreUtils", + "MultichainUtils", ], }); diff --git a/deploy/deployLayerZeroProvider.ts b/deploy/deployLayerZeroProvider.ts index d51737311..02f1df321 100644 --- a/deploy/deployLayerZeroProvider.ts +++ b/deploy/deployLayerZeroProvider.ts @@ -5,6 +5,7 @@ const constructorContracts = ["DataStore", "EventEmitter", "MultichainVault"]; const func = createDeployFunction({ contractName: "LayerZeroProvider", + libraryNames: ["MultichainUtils"], dependencyNames: constructorContracts, getDeployArgs: async ({ dependencyContracts }) => { return constructorContracts.map((dependencyName) => dependencyContracts[dependencyName].address); diff --git a/deploy/deployMultichainUtils.ts b/deploy/deployMultichainUtils.ts new file mode 100644 index 000000000..6a37ba456 --- /dev/null +++ b/deploy/deployMultichainUtils.ts @@ -0,0 +1,7 @@ +import { createDeployFunction } from "../utils/deploy"; + +const func = createDeployFunction({ + contractName: "MultichainUtils", +}); + +export default func; diff --git a/utils/fixture.ts b/utils/fixture.ts index f8d0a123b..d9129e1f2 100644 --- a/utils/fixture.ts +++ b/utils/fixture.ts @@ -119,6 +119,7 @@ export async function deployFixture() { const mockVaultV1 = await hre.ethers.getContract("MockVaultV1"); const multichainVault = await hre.ethers.getContract("MultichainVault"); const multichainVaultHandler = await hre.ethers.getContract("MultichainVaultHandler"); + const multichainUtils = await hre.ethers.getContract("MultichainUtils"); const layerZeroProvider = await hre.ethers.getContract("LayerZeroProvider"); const mockStargatePool = await hre.ethers.getContract("MockStargatePool"); @@ -329,6 +330,7 @@ export async function deployFixture() { mockVaultV1, multichainVault, multichainVaultHandler, + multichainUtils, layerZeroProvider, mockStargatePool, }, From 4e958e78ecf4124758adf48cd6a4c91360fd0aed Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 11 Feb 2025 18:02:32 +0200 Subject: [PATCH 041/205] Implement multichain createGlvWithdrawal --- .../glv/glvWithdrawal/GlvWithdrawalUtils.sol | 5 + contracts/multichain/MultichainRouter.sol | 150 +++++++++++++++++- deploy/deployGlvWithdrawalUtils.ts | 1 + 3 files changed, 151 insertions(+), 5 deletions(-) diff --git a/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol b/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol index b3103b38a..21f98a5e7 100644 --- a/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol +++ b/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol @@ -207,6 +207,11 @@ library GlvWithdrawalUtils { params.keeper, glvWithdrawal.receiver() ); + + if (glvWithdrawal.srcChainId() != 0) { + MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, withdrawalResult.outputToken, glvWithdrawal.account(), glvWithdrawal.srcChainId()); + MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, withdrawalResult.secondaryOutputToken, glvWithdrawal.account(), glvWithdrawal.srcChainId()); + } } function _processMarketWithdrawal( diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index a975f13fd..1d997f2ae 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -35,6 +35,12 @@ contract MultichainRouter is GelatoRelayRouter { GlvDepositUtils.CreateGlvDepositParams createGlvDepositParams; } + struct MultichainCreateGlvWithdrawalParams { + uint256 desChainId; + uint256 glvTokenAmount; + GlvWithdrawalUtils.CreateGlvWithdrawalParams createGlvWithdrawalParams; + } + bytes32 public constant CREATE_DEPOSIT_TYPEHASH = keccak256( bytes( @@ -98,6 +104,29 @@ contract MultichainRouter is GelatoRelayRouter { ) ); + bytes32 public constant CREATE_GLV_WITHDRAWAL_TYPEHASH = + keccak256( + bytes( + "CreateGlvWithdrawal(CreateGlvWithdrawalParams params,bytes32 relayParams)" + "CreateGlvWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ) + ); + + bytes32 public constant MULTICHAIN_CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH = + keccak256( + bytes( + "MultichainCreateGlvWithdrawalParams(uint256 desChainId,uint256 glvTokenAmount,CreateGlvWithdrawalParams params)" + "CreateGlvWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ) + ); + + bytes32 public constant CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH = + keccak256( + bytes( + "CreateGlvWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ) + ); + DepositVault depositVault; IDepositHandler depositHandler; MultichainVault multichainVault; @@ -218,13 +247,13 @@ contract MultichainRouter is GelatoRelayRouter { params.createWithdrawalParams.receiver = address(multichainVault); } - // user already bridged the GM / GLV tokens to the MultichainVault and balance was increased - // transfer the GM / GLV tokens from multichainVault to withdrawalVault + // user already bridged the GM tokens to the MultichainVault and balance was increased + // transfer the GM tokens from MultichainVault to WithdrawalVault _sendTokens( account, params.createWithdrawalParams.market, address(withdrawalVault), // receiver - params.tokenAmount, // TODO: should remove MultichainCreateWithdrawalParams.tokenAmount and send everything instead? (i.e. sent entire GM/GLV multichain balance to WithdrawalVault) + params.tokenAmount, // TODO: should remove MultichainCreateWithdrawalParams.tokenAmount and send everything instead? (i.e. sent entire GM multichain balance to WithdrawalVault) params.createWithdrawalParams.srcChainId ); @@ -268,14 +297,14 @@ contract MultichainRouter is GelatoRelayRouter { _sendTokens( account, params.createGlvDepositParams.initialLongToken, - address(glvVault), + address(glvVault), // receiver params.longTokenAmount, params.createGlvDepositParams.srcChainId ); _sendTokens( account, params.createGlvDepositParams.initialShortToken, - address(glvVault), + address(glvVault), // receiver params.shortTokenAmount, params.createGlvDepositParams.srcChainId ); @@ -291,6 +320,63 @@ contract MultichainRouter is GelatoRelayRouter { return glvHandler.createGlvDeposit(account, params.createGlvDepositParams); } + function createGlvWithdrawal( + RelayParams calldata relayParams, + address account, + MultichainCreateGlvWithdrawalParams memory params + ) external nonReentrant onlyGelatoRelay returns (bytes32) { + if (params.desChainId != block.chainid) { + revert Errors.InvalidDestinationChainId(); + } + + bytes32 structHash = _getMultichainCreateGlvWithdrawalStructHash(relayParams, params); + _validateCall(relayParams, account, structHash); + + return _createGlvWithdrawal(relayParams, account, params); + } + + function _createGlvWithdrawal( + RelayParams calldata relayParams, + address account, + MultichainCreateGlvWithdrawalParams memory params + ) internal returns (bytes32) { + Contracts memory contracts = Contracts({ + dataStore: dataStore, + eventEmitter: eventEmitter, + bank: glvVault + }); + + // for multichain glv withdrawals, the output tokens are sent to multichainVault and user's multichain balance is increased + if (params.createGlvWithdrawalParams.srcChainId != 0) { + params.createGlvWithdrawalParams.receiver = address(multichainVault); + } + + // transfer GLV tokens from MultichainVault to GlvVault + _sendTokens( + account, + params.createGlvWithdrawalParams.glv, + address(glvVault), // receiver + params.glvTokenAmount, + params.createGlvWithdrawalParams.srcChainId + ); + + // pay relay fee tokens from MultichainVault to GlvVault and decrease user's multichain balance + params.createGlvWithdrawalParams.executionFee = _handleRelay( + contracts, + relayParams, + account, + address(glvVault) // residualFeeReceiver + ); + + return GlvWithdrawalUtils.createGlvWithdrawal( + dataStore, + eventEmitter, + glvVault, + account, + params.createGlvWithdrawalParams + ); + } + function _sendTokens(address account, address token, address receiver, uint256 amount, uint256 srcChainId) internal override { AccountUtils.validateReceiver(receiver); if (srcChainId == 0) { @@ -485,6 +571,60 @@ contract MultichainRouter is GelatoRelayRouter { ); } + function _getMultichainCreateGlvWithdrawalStructHash( + RelayParams calldata relayParams, + MultichainCreateGlvWithdrawalParams memory params + ) internal view returns (bytes32) { + bytes32 relayParamsHash = keccak256(abi.encode(relayParams)); + return + keccak256( + abi.encode( + CREATE_GLV_WITHDRAWAL_TYPEHASH, + _getMultichainCreateGlvWithdrawalParamsStructHash(params), + relayParamsHash + ) + ); + } + + function _getMultichainCreateGlvWithdrawalParamsStructHash( + MultichainCreateGlvWithdrawalParams memory params + ) internal view returns (bytes32) { + return + keccak256( + abi.encode( + MULTICHAIN_CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH, + block.chainid, + params.glvTokenAmount, + _getCreateGlvWithdrawalParamsStructHash(params.createGlvWithdrawalParams) + ) + ); + } + + function _getCreateGlvWithdrawalParamsStructHash( + GlvWithdrawalUtils.CreateGlvWithdrawalParams memory params + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH, + params.receiver, + params.callbackContract, + params.uiFeeReceiver, + params.market, + params.glv, + keccak256(abi.encodePacked(params.longTokenSwapPath)), + keccak256(abi.encodePacked(params.shortTokenSwapPath)), + params.minLongTokenAmount, + params.minShortTokenAmount, + // params.shouldUnwrapNativeToken, // TODO: split CreateGlvWithdrawalParams into groups to fix slot too deep error + params.executionFee, + params.callbackGasLimit, + params.srcChainId, + keccak256(abi.encodePacked(params.dataList)) + ) + ); + } + function createGlvWithdrawal() external nonReentrant onlyGelatoRelay {} function createShift() external nonReentrant onlyGelatoRelay {} diff --git a/deploy/deployGlvWithdrawalUtils.ts b/deploy/deployGlvWithdrawalUtils.ts index 1fd82a64f..f3635fea5 100644 --- a/deploy/deployGlvWithdrawalUtils.ts +++ b/deploy/deployGlvWithdrawalUtils.ts @@ -11,6 +11,7 @@ const func = createDeployFunction({ "MarketUtils", "ExecuteWithdrawalUtils", "WithdrawalEventUtils", + "MultichainUtils", ], }); From ec51450074801fe3cc11044942314e85bcd86bbc Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 12 Feb 2025 14:13:23 +0200 Subject: [PATCH 042/205] Fix function params order --- contracts/deposit/ExecuteDepositUtils.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/deposit/ExecuteDepositUtils.sol b/contracts/deposit/ExecuteDepositUtils.sol index 2f45bff94..24d488dec 100644 --- a/contracts/deposit/ExecuteDepositUtils.sol +++ b/contracts/deposit/ExecuteDepositUtils.sol @@ -517,7 +517,7 @@ library ExecuteDepositUtils { } else { // mint GM tokens to MultichainVault and increase receiver's multichain GM balance MarketToken(payable(_params.market.marketToken)).mint(address(params.multichainVault), mintAmount); - MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, _params.receiver, _params.market.marketToken, 0); // srcChainId is the current block.chainId + MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, _params.market.marketToken, _params.receiver, 0); // srcChainId is the current block.chainId } return mintAmount; From c2af843ad06679ad917848021e3dab0c2d435241 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 12 Feb 2025 14:20:17 +0200 Subject: [PATCH 043/205] Implement multichain createShift --- contracts/multichain/MultichainRouter.sol | 122 +++++++++++++++++++++- contracts/shift/ShiftUtils.sol | 8 +- deploy/deployShiftUtils.ts | 1 + 3 files changed, 128 insertions(+), 3 deletions(-) diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 1d997f2ae..86d4a13ba 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -41,6 +41,12 @@ contract MultichainRouter is GelatoRelayRouter { GlvWithdrawalUtils.CreateGlvWithdrawalParams createGlvWithdrawalParams; } + struct MultichainCreateShiftParams { + uint256 desChainId; + uint256 marketTokenAmount; + ShiftUtils.CreateShiftParams createShiftParams; + } + bytes32 public constant CREATE_DEPOSIT_TYPEHASH = keccak256( bytes( @@ -127,6 +133,18 @@ contract MultichainRouter is GelatoRelayRouter { ) ); + bytes32 public constant CREATE_SHIFT_TYPEHASH = keccak256( + "CreateShift(CreateShiftParams params,bytes32 relayParams)CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ); + + bytes32 public constant MULTICHAIN_CREATE_SHIFT_PARAMS_TYPEHASH = keccak256( + "MultichainCreateShiftParams(uint256 desChainId,uint256 marketTokenAmount,CreateShiftParams params)CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ); + + bytes32 public constant CREATE_SHIFT_PARAMS_TYPEHASH = keccak256( + "CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ); + DepositVault depositVault; IDepositHandler depositHandler; MultichainVault multichainVault; @@ -134,6 +152,7 @@ contract MultichainRouter is GelatoRelayRouter { WithdrawalVault withdrawalVault; GlvHandler public glvHandler; GlvVault public glvVault; + ShiftVault public shiftVault; constructor( Router _router, @@ -150,6 +169,7 @@ contract MultichainRouter is GelatoRelayRouter { WithdrawalVault _withdrawalVault // GlvHandler _glvHandler, // TODO: place in a struct to fix stack to deep error // GlvVault _glvVault + // ShiftVault _shiftVault ) GelatoRelayRouter(_router, _dataStore, _eventEmitter, _oracle, _orderHandler, _orderVault, _externalHandler) { depositVault = _depositVault; depositHandler = _depositHandler; @@ -158,6 +178,7 @@ contract MultichainRouter is GelatoRelayRouter { withdrawalVault = _withdrawalVault; // glvHandler = _glvHandler; // glvVault = _glvVault; + // shiftVault = _shiftVault; } function createDeposit( @@ -242,7 +263,7 @@ contract MultichainRouter is GelatoRelayRouter { bank: withdrawalVault }); - // for multichain withdrawals, the output tokens are sent to multichainVault and user's multichain balance is increased + // for multichain withdrawals, the output tokens should be sent to multichainVault and user's multichain balance should be increased if (params.createWithdrawalParams.srcChainId != 0) { params.createWithdrawalParams.receiver = address(multichainVault); } @@ -346,7 +367,7 @@ contract MultichainRouter is GelatoRelayRouter { bank: glvVault }); - // for multichain glv withdrawals, the output tokens are sent to multichainVault and user's multichain balance is increased + // for multichain glv withdrawals, the output tokens should be sent to multichainVault and user's multichain balance should be increased if (params.createGlvWithdrawalParams.srcChainId != 0) { params.createGlvWithdrawalParams.receiver = address(multichainVault); } @@ -377,6 +398,56 @@ contract MultichainRouter is GelatoRelayRouter { ); } + function createShift( + RelayParams calldata relayParams, + address account, + MultichainCreateShiftParams memory params + ) external nonReentrant onlyGelatoRelay returns (bytes32) { + if (params.desChainId != block.chainid) { + revert Errors.InvalidDestinationChainId(); + } + + bytes32 structHash = _getMultichainCreateShiftStructHash(relayParams, params); + _validateCall(relayParams, account, structHash); + + return _createShift(relayParams, account, params); + } + + function _createShift( + RelayParams calldata relayParams, + address account, + MultichainCreateShiftParams memory params + ) internal returns (bytes32) { + Contracts memory contracts = Contracts({ + dataStore: dataStore, + eventEmitter: eventEmitter, + bank: shiftVault + }); + + _sendTokens( + account, + params.createShiftParams.fromMarket, + address(shiftVault), // receiver + params.marketTokenAmount, + params.createShiftParams.srcChainId + ); + + params.createShiftParams.executionFee = _handleRelay( + contracts, + relayParams, + account, + address(shiftVault) + ); + + return ShiftUtils.createShift( + dataStore, + eventEmitter, + shiftVault, + account, + params.createShiftParams + ); + } + function _sendTokens(address account, address token, address receiver, uint256 amount, uint256 srcChainId) internal override { AccountUtils.validateReceiver(receiver); if (srcChainId == 0) { @@ -625,6 +696,53 @@ contract MultichainRouter is GelatoRelayRouter { ); } + function _getMultichainCreateShiftStructHash( + RelayParams calldata relayParams, + MultichainCreateShiftParams memory params + ) internal view returns (bytes32) { + bytes32 relayParamsHash = keccak256(abi.encode(relayParams)); + return keccak256( + abi.encode( + CREATE_SHIFT_TYPEHASH, + _getMultichainCreateShiftParamsStructHash(params), + relayParamsHash + ) + ); + } + + function _getMultichainCreateShiftParamsStructHash( + MultichainCreateShiftParams memory params + ) internal view returns (bytes32) { + return keccak256( + abi.encode( + MULTICHAIN_CREATE_SHIFT_PARAMS_TYPEHASH, + block.chainid, + params.marketTokenAmount, + _getCreateShiftParamsStructHash(params.createShiftParams) + ) + ); + } + + function _getCreateShiftParamsStructHash( + ShiftUtils.CreateShiftParams memory params + ) internal pure returns (bytes32) { + return keccak256( + abi.encode( + CREATE_SHIFT_PARAMS_TYPEHASH, + params.receiver, + params.callbackContract, + params.uiFeeReceiver, + params.fromMarket, + params.toMarket, + params.minMarketTokens, + params.executionFee, + params.callbackGasLimit, + params.srcChainId, + keccak256(abi.encodePacked(params.dataList)) + ) + ); + } + function createGlvWithdrawal() external nonReentrant onlyGelatoRelay {} function createShift() external nonReentrant onlyGelatoRelay {} diff --git a/contracts/shift/ShiftUtils.sol b/contracts/shift/ShiftUtils.sol index f5a9bad9a..cdb8691d4 100644 --- a/contracts/shift/ShiftUtils.sol +++ b/contracts/shift/ShiftUtils.sol @@ -324,9 +324,15 @@ library ShiftUtils { params.startingGas, GasUtils.estimateShiftOraclePriceCount(), params.keeper, - shift.receiver() + shift.srcChainId() == 0 ? shift.receiver() : address(params.multichainVault) ); + // for multichain action, receiver is the multichainVault; increase user's multichain wnt balance for the fee refund + if (shift.srcChainId() != 0) { + address wnt = params.dataStore.getAddress(Keys.WNT); + MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, wnt, shift.receiver(), 0); // srcChainId is the current block.chainId + } + return cache.receivedMarketTokens; } diff --git a/deploy/deployShiftUtils.ts b/deploy/deployShiftUtils.ts index 0737e7998..63bd440cc 100644 --- a/deploy/deployShiftUtils.ts +++ b/deploy/deployShiftUtils.ts @@ -11,6 +11,7 @@ const func = createDeployFunction({ "WithdrawalEventUtils", "ExecuteDepositUtils", "ExecuteWithdrawalUtils", + "MultichainUtils", ], }); From 6882c2c8250ac6fee7f82e1d778634a112a4b77f Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 12 Feb 2025 16:59:35 +0200 Subject: [PATCH 044/205] Fix refund fee receiver for multichain actions --- contracts/deposit/ExecuteDepositUtils.sol | 8 +++++++- contracts/glv/glvDeposit/GlvDepositUtils.sol | 8 +++++++- .../glv/glvWithdrawal/GlvWithdrawalUtils.sol | 7 ++++--- contracts/multichain/MultichainRouter.sol | 13 ------------- contracts/withdrawal/ExecuteWithdrawalUtils.sol | 17 ++++++++++++----- 5 files changed, 30 insertions(+), 23 deletions(-) diff --git a/contracts/deposit/ExecuteDepositUtils.sol b/contracts/deposit/ExecuteDepositUtils.sol index 24d488dec..8cd83e650 100644 --- a/contracts/deposit/ExecuteDepositUtils.sol +++ b/contracts/deposit/ExecuteDepositUtils.sol @@ -294,9 +294,15 @@ library ExecuteDepositUtils { params.startingGas, GasUtils.estimateDepositOraclePriceCount(deposit.longTokenSwapPath().length + deposit.shortTokenSwapPath().length), params.keeper, - deposit.receiver() + deposit.srcChainId() == 0 ? deposit.receiver() : address(params.multichainVault) ); + // for multichain action, receiver is the multichainVault; increase user's multichain wnt balance for the fee refund + if (deposit.srcChainId() != 0) { + address wnt = params.dataStore.getAddress(Keys.WNT); + MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, wnt, deposit.receiver(), 0); // srcChainId is the current block.chainId + } + return cache.receivedMarketTokens; } diff --git a/contracts/glv/glvDeposit/GlvDepositUtils.sol b/contracts/glv/glvDeposit/GlvDepositUtils.sol index 6745c6738..2d002f6f7 100644 --- a/contracts/glv/glvDeposit/GlvDepositUtils.sol +++ b/contracts/glv/glvDeposit/GlvDepositUtils.sol @@ -314,9 +314,15 @@ library GlvDepositUtils { params.startingGas, cache.oraclePriceCount, params.keeper, - glvDeposit.receiver() + glvDeposit.srcChainId() == 0 ? glvDeposit.receiver() : address(params.multichainVault) ); + // for multichain action, receiver is the multichainVault; increase user's multichain wnt balance for the fee refund + if (glvDeposit.srcChainId() != 0) { + address wnt = params.dataStore.getAddress(Keys.WNT); + MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, wnt, glvDeposit.receiver(), 0); // srcChainId is the current block.chainId + } + return cache.mintAmount; } diff --git a/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol b/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol index 21f98a5e7..bb7d651f8 100644 --- a/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol +++ b/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol @@ -205,12 +205,13 @@ library GlvWithdrawalUtils { params.startingGas, cache.oraclePriceCount, params.keeper, - glvWithdrawal.receiver() + glvWithdrawal.srcChainId() == 0 ? glvWithdrawal.receiver() : address(params.multichainVault)//glvWithdrawal.receiver() ); + // for multichain action, receiver is the multichainVault; increase user's multichain wnt balance for the fee refund if (glvWithdrawal.srcChainId() != 0) { - MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, withdrawalResult.outputToken, glvWithdrawal.account(), glvWithdrawal.srcChainId()); - MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, withdrawalResult.secondaryOutputToken, glvWithdrawal.account(), glvWithdrawal.srcChainId()); + address wnt = params.dataStore.getAddress(Keys.WNT); + MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, wnt, glvWithdrawal.receiver(), 0); // srcChainId is the current block.chainId } } diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 86d4a13ba..de1640d6d 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -234,9 +234,6 @@ contract MultichainRouter is GelatoRelayRouter { return depositHandler.createDeposit(account, params.createDepositParams); } - // does a WithdrawalUtils.createWithdrawal (receiver is multichainVault if srcChainId != 0) - // keeper will do a ExecuteWithdrawalUtils.executeWithdrawal and outputToken + secondaryOutputToken will be sent to receiver (or multichainVault if srcChainId != 0) - // TODO: MultichainProvider should initiate the bridging (e.g. LayerZeroProvider.sendTokens()) or will there be another action triggering the bridging? function createWithdrawal( RelayParams calldata relayParams, address account, @@ -263,11 +260,6 @@ contract MultichainRouter is GelatoRelayRouter { bank: withdrawalVault }); - // for multichain withdrawals, the output tokens should be sent to multichainVault and user's multichain balance should be increased - if (params.createWithdrawalParams.srcChainId != 0) { - params.createWithdrawalParams.receiver = address(multichainVault); - } - // user already bridged the GM tokens to the MultichainVault and balance was increased // transfer the GM tokens from MultichainVault to WithdrawalVault _sendTokens( @@ -367,11 +359,6 @@ contract MultichainRouter is GelatoRelayRouter { bank: glvVault }); - // for multichain glv withdrawals, the output tokens should be sent to multichainVault and user's multichain balance should be increased - if (params.createGlvWithdrawalParams.srcChainId != 0) { - params.createGlvWithdrawalParams.receiver = address(multichainVault); - } - // transfer GLV tokens from MultichainVault to GlvVault _sendTokens( account, diff --git a/contracts/withdrawal/ExecuteWithdrawalUtils.sol b/contracts/withdrawal/ExecuteWithdrawalUtils.sol index 0cbd4f685..96a8a1d80 100644 --- a/contracts/withdrawal/ExecuteWithdrawalUtils.sol +++ b/contracts/withdrawal/ExecuteWithdrawalUtils.sol @@ -166,12 +166,13 @@ library ExecuteWithdrawalUtils { params.startingGas, cache.oraclePriceCount, params.keeper, - withdrawal.receiver() + withdrawal.srcChainId() == 0 ? withdrawal.receiver() : address(params.multichainVault) ); + // for multichain action, receiver is the multichainVault; increase user's multichain wnt balance for the fee refund if (withdrawal.srcChainId() != 0) { - MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, cache.result.outputToken, withdrawal.account(), withdrawal.srcChainId()); - MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, cache.result.secondaryOutputToken, withdrawal.account(), withdrawal.srcChainId()); + address wnt = params.dataStore.getAddress(Keys.WNT); + MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, wnt, withdrawal.receiver(), 0); // srcChainId is the current block.chainId } return cache.result; @@ -302,7 +303,7 @@ library ExecuteWithdrawalUtils { cache.longTokenOutputAmount, withdrawal.longTokenSwapPath(), withdrawal.minLongTokenAmount(), - withdrawal.receiver(), + withdrawal.srcChainId() == 0 ? withdrawal.receiver() : address(params.multichainVault), withdrawal.uiFeeReceiver(), withdrawal.shouldUnwrapNativeToken() ); @@ -314,11 +315,17 @@ library ExecuteWithdrawalUtils { cache.shortTokenOutputAmount, withdrawal.shortTokenSwapPath(), withdrawal.minShortTokenAmount(), - withdrawal.receiver(), + withdrawal.srcChainId() == 0 ? withdrawal.receiver() : address(params.multichainVault), withdrawal.uiFeeReceiver(), withdrawal.shouldUnwrapNativeToken() ); + // for multichain action, receiver is the multichainVault; increase user's multichain balances + if (withdrawal.srcChainId() != 0) { + MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, result.outputToken, withdrawal.receiver(), 0); // srcChainId is the current block.chainId + MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, result.secondaryOutputToken, withdrawal.receiver(), 0); // srcChainId is the current block.chainId + } + SwapPricingUtils.emitSwapFeesCollected( params.eventEmitter, params.key, From 5d3822faec0452061dfc789b6d2d74bb30d8afae Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 13 Feb 2025 13:07:53 +0200 Subject: [PATCH 045/205] Refactor BaseGelatoRelayRouter, GelatoRelayRouter, MultichainRouter to outsource relay related constants and functions into an external RelayUtils lib --- contracts/mock/MockGelatoRelay.sol | 4 +- contracts/multichain/MultichainRouter.sol | 466 +------------ .../router/relay/BaseGelatoRelayRouter.sol | 81 +-- contracts/router/relay/GelatoRelayRouter.sol | 146 +--- contracts/router/relay/RelayUtils.sol | 622 ++++++++++++++++++ .../relay/SubaccountGelatoRelayRouter.sol | 30 +- deploy/deployGelatoRelayRouter.ts | 2 +- deploy/deployRelayUtils.ts | 7 + deploy/deploySubaccountGelatoRelayRouter.ts | 2 +- test/router/relay/signatures.ts | 3 + utils/fixture.ts | 2 + 11 files changed, 698 insertions(+), 667 deletions(-) create mode 100644 contracts/router/relay/RelayUtils.sol create mode 100644 deploy/deployRelayUtils.ts diff --git a/contracts/mock/MockGelatoRelay.sol b/contracts/mock/MockGelatoRelay.sol index 03c1add4b..8cc456d7c 100644 --- a/contracts/mock/MockGelatoRelay.sol +++ b/contracts/mock/MockGelatoRelay.sol @@ -26,12 +26,12 @@ contract MockGelatoRelayRouter is GelatoRelayRouter { ) GelatoRelayRouter(_router, _dataStore, _eventEmitter, _oracle, _orderHandler, _orderVault, _externalHandler) {} function testCancelOrderSignature( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, bytes32 key, address account, uint256 chainId ) external view { - bytes32 structHash = _getCancelOrderStructHash(relayParams, key); + bytes32 structHash = RelayUtils.getCancelOrderStructHash(relayParams, key); _handleSignature(structHash, relayParams.signature, account, chainId); } diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index de1640d6d..e00a33959 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -3,10 +3,8 @@ pragma solidity ^0.8.0; import "../router/relay/GelatoRelayRouter.sol"; -import "../deposit/DepositUtils.sol"; import "../deposit/DepositVault.sol"; import "../exchange/IDepositHandler.sol"; -import "../withdrawal/WithdrawalUtils.sol"; import "../exchange/WithdrawalHandler.sol"; import "../withdrawal/WithdrawalVault.sol"; import "../exchange/GlvHandler.sol"; @@ -15,135 +13,6 @@ import "../glv/GlvVault.sol"; import "./MultichainUtils.sol"; contract MultichainRouter is GelatoRelayRouter { - struct MultichainCreateDepositParams { - uint256 desChainId; - uint256 longTokenAmount; - uint256 shortTokenAmount; - DepositUtils.CreateDepositParams createDepositParams; - } - - struct MultichainCreateWithdrawalParams { - uint256 desChainId; - uint256 tokenAmount; - WithdrawalUtils.CreateWithdrawalParams createWithdrawalParams; - } - - struct MultichainCreateGlvDepositParams { - uint256 desChainId; - uint256 longTokenAmount; - uint256 shortTokenAmount; - GlvDepositUtils.CreateGlvDepositParams createGlvDepositParams; - } - - struct MultichainCreateGlvWithdrawalParams { - uint256 desChainId; - uint256 glvTokenAmount; - GlvWithdrawalUtils.CreateGlvWithdrawalParams createGlvWithdrawalParams; - } - - struct MultichainCreateShiftParams { - uint256 desChainId; - uint256 marketTokenAmount; - ShiftUtils.CreateShiftParams createShiftParams; - } - - bytes32 public constant CREATE_DEPOSIT_TYPEHASH = - keccak256( - bytes( - "CreateDeposit(CreateDepositParams params,bytes32 relayParams)CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" - ) - ); - bytes32 public constant CREATE_DEPOSIT_PARAMS_TYPEHASH = - keccak256( - bytes( - "CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" - ) - ); - bytes32 public constant MULTICHAIN_CREATE_DEPOSIT_PARAMS_TYPEHASH = - keccak256( - bytes( - "MultichainCreateDepositParams(uint256 desChainId,uint256 longTokenAmount,uint256 shortTokenAmount,CreateDepositParams params)CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" - ) - ); - bytes32 public constant CREATE_DEPOSIT_PARAMS_ADDRESSES_TYPEHASH = - keccak256( - bytes( - "CreateDepositParamsAdresses(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath)" - ) - ); - - bytes32 public constant CREATE_WITHDRAWAL_TYPEHASH = - keccak256( - bytes( - "CreateWithdrawal(CreateWithdrawalParams params,bytes32 relayParams)CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" - ) - ); - bytes32 public constant MULTICHAIN_CREATE_WITHDRAWAL_PARAMS_TYPEHASH = - keccak256( - bytes( - "MultichainCreateWithdrawalParams(uint256 desChainId,uint256 tokenAmount,CreateWithdrawalParams params)CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" - ) - ); - bytes32 public constant CREATE_WITHDRAWAL_PARAMS_TYPEHASH = - keccak256( - bytes( - "CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" - ) - ); - - bytes32 public constant CREATE_GLV_DEPOSIT_TYPEHASH = - keccak256( - bytes( - "CreateGlvDeposit(CreateGlvDepositParams params,bytes32 relayParams)CreateGlvDepositParams(address account,address market,address initialLongToken,address initialShortToken,uint256 srcChainId,bytes32[] dataList)" - ) - ); - bytes32 public constant MULTICHAIN_CREATE_GLV_DEPOSIT_PARAMS_TYPEHASH = - keccak256( - bytes( - "MultichainCreateGlvDepositParams(uint256 desChainId,uint256 tokenAmount,CreateGlvDepositParams params)CreateGlvDepositParams(address glv,address market,address receiver,address callbackContract,address uiFeeReceiver,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minGlvTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bool shouldUnwrapNativeToken,bool isMarketTokenDeposit,bytes32[] dataList)" - ) - ); - bytes32 public constant CREATE_GLV_DEPOSIT_PARAMS_TYPEHASH = - keccak256( - bytes( - "CreateGlvDepositParams(address glv,address market,address receiver,address callbackContract,address uiFeeReceiver,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minGlvTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bool shouldUnwrapNativeToken,bool isMarketTokenDeposit,bytes32[] dataList)" - ) - ); - - bytes32 public constant CREATE_GLV_WITHDRAWAL_TYPEHASH = - keccak256( - bytes( - "CreateGlvWithdrawal(CreateGlvWithdrawalParams params,bytes32 relayParams)" - "CreateGlvWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" - ) - ); - - bytes32 public constant MULTICHAIN_CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH = - keccak256( - bytes( - "MultichainCreateGlvWithdrawalParams(uint256 desChainId,uint256 glvTokenAmount,CreateGlvWithdrawalParams params)" - "CreateGlvWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" - ) - ); - - bytes32 public constant CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH = - keccak256( - bytes( - "CreateGlvWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" - ) - ); - - bytes32 public constant CREATE_SHIFT_TYPEHASH = keccak256( - "CreateShift(CreateShiftParams params,bytes32 relayParams)CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" - ); - - bytes32 public constant MULTICHAIN_CREATE_SHIFT_PARAMS_TYPEHASH = keccak256( - "MultichainCreateShiftParams(uint256 desChainId,uint256 marketTokenAmount,CreateShiftParams params)CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" - ); - - bytes32 public constant CREATE_SHIFT_PARAMS_TYPEHASH = keccak256( - "CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" - ); DepositVault depositVault; IDepositHandler depositHandler; @@ -182,24 +51,24 @@ contract MultichainRouter is GelatoRelayRouter { } function createDeposit( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, address account, - MultichainCreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee + RelayUtils.MultichainCreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { if (params.desChainId != block.chainid) { revert Errors.InvalidDestinationChainId(); } - bytes32 structHash = _getMultichainCreateDepositStructHash(relayParams, params); + bytes32 structHash = RelayUtils.getMultichainCreateDepositStructHash(relayParams, params); _validateCall(relayParams, account, structHash); return _createDeposit(relayParams, account, params); } function _createDeposit( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, address account, - MultichainCreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee + RelayUtils.MultichainCreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee ) internal returns (bytes32) { Contracts memory contracts = Contracts({ dataStore: dataStore, @@ -235,24 +104,24 @@ contract MultichainRouter is GelatoRelayRouter { } function createWithdrawal( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, address account, - MultichainCreateWithdrawalParams memory params // can't use calldata because need to modify params.addresses.receiver & params.numbers.executionFee + RelayUtils.MultichainCreateWithdrawalParams memory params // can't use calldata because need to modify params.addresses.receiver & params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { if (params.desChainId != block.chainid) { revert Errors.InvalidDestinationChainId(); } - bytes32 structHash = _getMultichainCreateWithdrawalStructHash(relayParams, params); + bytes32 structHash = RelayUtils.getMultichainCreateWithdrawalStructHash(relayParams, params); _validateCall(relayParams, account, structHash); return _createWithdrawal(relayParams, account, params); } function _createWithdrawal( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, address account, - MultichainCreateWithdrawalParams memory params // can't use calldata because need to modify params.addresses.receiver & params.numbers.executionFee + RelayUtils.MultichainCreateWithdrawalParams memory params // can't use calldata because need to modify params.addresses.receiver & params.numbers.executionFee ) internal returns (bytes32) { Contracts memory contracts = Contracts({ dataStore: dataStore, @@ -266,7 +135,7 @@ contract MultichainRouter is GelatoRelayRouter { account, params.createWithdrawalParams.market, address(withdrawalVault), // receiver - params.tokenAmount, // TODO: should remove MultichainCreateWithdrawalParams.tokenAmount and send everything instead? (i.e. sent entire GM multichain balance to WithdrawalVault) + params.tokenAmount, params.createWithdrawalParams.srcChainId ); @@ -281,24 +150,24 @@ contract MultichainRouter is GelatoRelayRouter { } function createGlvDeposit( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, address account, - MultichainCreateGlvDepositParams memory params + RelayUtils.MultichainCreateGlvDepositParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { if (params.desChainId != block.chainid) { revert Errors.InvalidDestinationChainId(); } - bytes32 structHash = _getMultichainCreateGlvDepositStructHash(relayParams, params); + bytes32 structHash = RelayUtils.getMultichainCreateGlvDepositStructHash(relayParams, params); _validateCall(relayParams, account, structHash); return _createGlvDeposit(relayParams, account, params); } function _createGlvDeposit( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, address account, - MultichainCreateGlvDepositParams memory params + RelayUtils.MultichainCreateGlvDepositParams memory params ) internal returns (bytes32) { Contracts memory contracts = Contracts({ dataStore: dataStore, @@ -334,24 +203,24 @@ contract MultichainRouter is GelatoRelayRouter { } function createGlvWithdrawal( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, address account, - MultichainCreateGlvWithdrawalParams memory params + RelayUtils.MultichainCreateGlvWithdrawalParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { if (params.desChainId != block.chainid) { revert Errors.InvalidDestinationChainId(); } - bytes32 structHash = _getMultichainCreateGlvWithdrawalStructHash(relayParams, params); + bytes32 structHash = RelayUtils.getMultichainCreateGlvWithdrawalStructHash(relayParams, params); _validateCall(relayParams, account, structHash); return _createGlvWithdrawal(relayParams, account, params); } function _createGlvWithdrawal( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, address account, - MultichainCreateGlvWithdrawalParams memory params + RelayUtils.MultichainCreateGlvWithdrawalParams memory params ) internal returns (bytes32) { Contracts memory contracts = Contracts({ dataStore: dataStore, @@ -386,24 +255,24 @@ contract MultichainRouter is GelatoRelayRouter { } function createShift( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, address account, - MultichainCreateShiftParams memory params + RelayUtils.MultichainCreateShiftParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { if (params.desChainId != block.chainid) { revert Errors.InvalidDestinationChainId(); } - bytes32 structHash = _getMultichainCreateShiftStructHash(relayParams, params); + bytes32 structHash = RelayUtils.getMultichainCreateShiftStructHash(relayParams, params); _validateCall(relayParams, account, structHash); return _createShift(relayParams, account, params); } function _createShift( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, address account, - MultichainCreateShiftParams memory params + RelayUtils.MultichainCreateShiftParams memory params ) internal returns (bytes32) { Contracts memory contracts = Contracts({ dataStore: dataStore, @@ -450,287 +319,4 @@ contract MultichainRouter is GelatoRelayRouter { MultichainUtils.recordTransferIn(dataStore, eventEmitter, multichainVault, wnt, account, srcChainId); } } - - function _getMultichainCreateDepositStructHash( - RelayParams calldata relayParams, - MultichainCreateDepositParams memory params - ) internal view returns (bytes32) { - bytes32 relayParamsHash = keccak256(abi.encode(relayParams)); - - return - keccak256( - abi.encode( - CREATE_DEPOSIT_TYPEHASH, - _getMultichainCreateDepositParamsStructHash(params), - relayParamsHash - ) - ); - } - - function _getMultichainCreateDepositParamsStructHash( - MultichainCreateDepositParams memory params - ) internal view returns (bytes32) { - return - keccak256( - abi.encode( - MULTICHAIN_CREATE_DEPOSIT_PARAMS_TYPEHASH, - block.chainid, - params.longTokenAmount, - params.shortTokenAmount, - _getCreateDepositParamsStructHash(params.createDepositParams) - ) - ); - } - - function _getCreateDepositParamsStructHash( - DepositUtils.CreateDepositParams memory params - ) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - CREATE_DEPOSIT_PARAMS_TYPEHASH, - _getCreateDepositParamsAdressesStructHash(params.addresses), - params.minMarketTokens, - params.shouldUnwrapNativeToken, - params.executionFee, - params.callbackGasLimit, - params.srcChainId, - keccak256(abi.encodePacked(params.dataList)) - ) - ); - } - - function _getCreateDepositParamsAdressesStructHash( - DepositUtils.CreateDepositParamsAdresses memory addresses - ) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - CREATE_DEPOSIT_PARAMS_ADDRESSES_TYPEHASH, - addresses.receiver, - addresses.callbackContract, - addresses.uiFeeReceiver, - addresses.market, - addresses.initialLongToken, - addresses.initialShortToken, - keccak256(abi.encodePacked(addresses.longTokenSwapPath)), - keccak256(abi.encodePacked(addresses.shortTokenSwapPath)) - ) - ); - } - - function _getMultichainCreateWithdrawalStructHash( - RelayParams calldata relayParams, - MultichainCreateWithdrawalParams memory params - ) internal view returns (bytes32) { - bytes32 relayParamsHash = keccak256(abi.encode(relayParams)); - return - keccak256( - abi.encode( - CREATE_WITHDRAWAL_TYPEHASH, - _getMultichainCreateWithdrawalParamsStructHash(params), - relayParamsHash - ) - ); - } - - function _getMultichainCreateWithdrawalParamsStructHash( - MultichainCreateWithdrawalParams memory params - ) internal view returns (bytes32) { - return - keccak256( - abi.encode( - MULTICHAIN_CREATE_WITHDRAWAL_PARAMS_TYPEHASH, - block.chainid, - params.tokenAmount, - _getCreateWithdrawalParamsStructHash(params.createWithdrawalParams) - ) - ); - } - - function _getCreateWithdrawalParamsStructHash( - WithdrawalUtils.CreateWithdrawalParams memory params - ) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - CREATE_WITHDRAWAL_PARAMS_TYPEHASH, - params.receiver, - params.callbackContract, - params.uiFeeReceiver, - params.market, - keccak256(abi.encodePacked(params.longTokenSwapPath)), - keccak256(abi.encodePacked(params.shortTokenSwapPath)), - params.minLongTokenAmount, - params.minShortTokenAmount, - params.shouldUnwrapNativeToken, - params.executionFee, - params.callbackGasLimit, - params.srcChainId, - keccak256(abi.encodePacked(params.dataList)) - ) - ); - } - - function _getMultichainCreateGlvDepositStructHash( - RelayParams calldata relayParams, - MultichainCreateGlvDepositParams memory params - ) internal pure returns (bytes32) { - bytes32 relayParamsHash = keccak256(abi.encode(relayParams)); - return - keccak256( - abi.encode( - CREATE_GLV_DEPOSIT_TYPEHASH, - _getMultichainCreateGlvDepositParamsStructHash(params), - relayParamsHash - ) - ); - } - - function _getMultichainCreateGlvDepositParamsStructHash( - MultichainCreateGlvDepositParams memory params - ) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - MULTICHAIN_CREATE_GLV_DEPOSIT_PARAMS_TYPEHASH, - params.desChainId, - params.longTokenAmount, - params.shortTokenAmount, - _getCreateGlvDepositParamsStructHash(params.createGlvDepositParams) - ) - ); - } - - function _getCreateGlvDepositParamsStructHash( - GlvDepositUtils.CreateGlvDepositParams memory params - ) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - CREATE_GLV_DEPOSIT_PARAMS_TYPEHASH, - params.glv, - params.market, - params.receiver, - params.callbackContract, - params.uiFeeReceiver, - // params.initialLongToken, - // params.initialShortToken, - // keccak256(abi.encodePacked(params.longTokenSwapPath)), - // keccak256(abi.encodePacked(params.shortTokenSwapPath)), // TODO: split CreateGlvDepositParams into groups to fix slot too deep error - params.minGlvTokens, - params.executionFee, - params.callbackGasLimit, - params.srcChainId, - params.shouldUnwrapNativeToken, - params.isMarketTokenDeposit, - keccak256(abi.encodePacked(params.dataList)) - ) - ); - } - - function _getMultichainCreateGlvWithdrawalStructHash( - RelayParams calldata relayParams, - MultichainCreateGlvWithdrawalParams memory params - ) internal view returns (bytes32) { - bytes32 relayParamsHash = keccak256(abi.encode(relayParams)); - return - keccak256( - abi.encode( - CREATE_GLV_WITHDRAWAL_TYPEHASH, - _getMultichainCreateGlvWithdrawalParamsStructHash(params), - relayParamsHash - ) - ); - } - - function _getMultichainCreateGlvWithdrawalParamsStructHash( - MultichainCreateGlvWithdrawalParams memory params - ) internal view returns (bytes32) { - return - keccak256( - abi.encode( - MULTICHAIN_CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH, - block.chainid, - params.glvTokenAmount, - _getCreateGlvWithdrawalParamsStructHash(params.createGlvWithdrawalParams) - ) - ); - } - - function _getCreateGlvWithdrawalParamsStructHash( - GlvWithdrawalUtils.CreateGlvWithdrawalParams memory params - ) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH, - params.receiver, - params.callbackContract, - params.uiFeeReceiver, - params.market, - params.glv, - keccak256(abi.encodePacked(params.longTokenSwapPath)), - keccak256(abi.encodePacked(params.shortTokenSwapPath)), - params.minLongTokenAmount, - params.minShortTokenAmount, - // params.shouldUnwrapNativeToken, // TODO: split CreateGlvWithdrawalParams into groups to fix slot too deep error - params.executionFee, - params.callbackGasLimit, - params.srcChainId, - keccak256(abi.encodePacked(params.dataList)) - ) - ); - } - - function _getMultichainCreateShiftStructHash( - RelayParams calldata relayParams, - MultichainCreateShiftParams memory params - ) internal view returns (bytes32) { - bytes32 relayParamsHash = keccak256(abi.encode(relayParams)); - return keccak256( - abi.encode( - CREATE_SHIFT_TYPEHASH, - _getMultichainCreateShiftParamsStructHash(params), - relayParamsHash - ) - ); - } - - function _getMultichainCreateShiftParamsStructHash( - MultichainCreateShiftParams memory params - ) internal view returns (bytes32) { - return keccak256( - abi.encode( - MULTICHAIN_CREATE_SHIFT_PARAMS_TYPEHASH, - block.chainid, - params.marketTokenAmount, - _getCreateShiftParamsStructHash(params.createShiftParams) - ) - ); - } - - function _getCreateShiftParamsStructHash( - ShiftUtils.CreateShiftParams memory params - ) internal pure returns (bytes32) { - return keccak256( - abi.encode( - CREATE_SHIFT_PARAMS_TYPEHASH, - params.receiver, - params.callbackContract, - params.uiFeeReceiver, - params.fromMarket, - params.toMarket, - params.minMarketTokens, - params.executionFee, - params.callbackGasLimit, - params.srcChainId, - keccak256(abi.encodePacked(params.dataList)) - ) - ); - } - - function createGlvWithdrawal() external nonReentrant onlyGelatoRelay {} - - function createShift() external nonReentrant onlyGelatoRelay {} } diff --git a/contracts/router/relay/BaseGelatoRelayRouter.sol b/contracts/router/relay/BaseGelatoRelayRouter.sol index 4dad2f6f3..38d74df87 100644 --- a/contracts/router/relay/BaseGelatoRelayRouter.sol +++ b/contracts/router/relay/BaseGelatoRelayRouter.sol @@ -20,54 +20,11 @@ import "../../router/Router.sol"; import "../../swap/SwapUtils.sol"; import "../../token/TokenUtils.sol"; +import "./RelayUtils.sol"; + abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, OracleModule { using Order for Order.Props; - struct TokenPermit { - address owner; - address spender; - uint256 value; - uint256 deadline; - uint8 v; - bytes32 r; - bytes32 s; - address token; - } - - struct ExternalCalls { - address[] externalCallTargets; - bytes[] externalCallDataList; - address[] refundTokens; - address[] refundReceivers; - } - - struct FeeParams { - address feeToken; - uint256 feeAmount; - address[] feeSwapPath; - } - - struct RelayParams { - OracleUtils.SetPricesParams oracleParams; - ExternalCalls externalCalls; - TokenPermit[] tokenPermits; - FeeParams fee; - uint256 userNonce; - uint256 deadline; - bytes signature; - uint256 srcChainId; - } - - // @note all params except account should be part of the corresponding struct hash - struct UpdateOrderParams { - uint256 sizeDeltaUsd; - uint256 acceptablePrice; - uint256 triggerPrice; - uint256 minOutputAmount; - uint256 validFromTime; - bool autoCancel; - } - struct Contracts { DataStore dataStore; EventEmitter eventEmitter; @@ -119,7 +76,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, } function _createOrder( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, address account, uint256 collateralDeltaAmount, IBaseOrderUtils.CreateOrderParams memory params, // can't use calldata because need to modify params.numbers.executionFee @@ -154,10 +111,10 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, } function _updateOrder( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, address account, bytes32 key, - UpdateOrderParams calldata params, + RelayUtils.UpdateOrderParams calldata params, bool increaseExecutionFee, bool isSubaccount ) internal { @@ -193,7 +150,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, ); } - function _cancelOrder(RelayParams calldata relayParams, address account, bytes32 key) internal { + function _cancelOrder(RelayUtils.RelayParams calldata relayParams, address account, bytes32 key) internal { Contracts memory contracts = Contracts({ dataStore: dataStore, eventEmitter: eventEmitter, @@ -217,7 +174,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, function _swapFeeTokens( Contracts memory contracts, address wnt, - FeeParams calldata fee + RelayUtils.FeeParams calldata fee ) internal returns (uint256) { // swap fee tokens to WNT MarketUtils.validateSwapPath(contracts.dataStore, fee.feeSwapPath); @@ -250,7 +207,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, function _handleRelay( Contracts memory contracts, - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, address account, address residualFeeReceiver ) internal returns (uint256) { @@ -262,7 +219,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, return _handleRelayFee(contracts, relayParams, account, residualFeeReceiver); } - function _handleTokenPermits(TokenPermit[] calldata tokenPermits) internal { + function _handleTokenPermits(RelayUtils.TokenPermit[] calldata tokenPermits) internal { // not all tokens support ERC20Permit, for them separate transaction is needed if (tokenPermits.length == 0) { @@ -272,7 +229,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, address _router = address(router); for (uint256 i; i < tokenPermits.length; i++) { - TokenPermit memory permit = tokenPermits[i]; + RelayUtils.TokenPermit memory permit = tokenPermits[i]; if (permit.spender != _router) { // to avoid permitting spending by an incorrect spender for extra safety @@ -295,7 +252,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, function _handleRelayFee( Contracts memory contracts, - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, address account, address residualFeeReceiver ) internal returns (uint256) { @@ -364,7 +321,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, ); } - function _validateCall(RelayParams calldata relayParams, address account, bytes32 structHash) internal { + function _validateCall(RelayUtils.RelayParams calldata relayParams, address account, bytes32 structHash) internal { uint256 srcChainId = relayParams.srcChainId == 0 ? block.chainid : relayParams.srcChainId; bytes32 domainSeparator = _getDomainSeparator(srcChainId); bytes32 digest = ECDSA.toTypedDataHash(domainSeparator, structHash); @@ -387,20 +344,6 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, userNonces[account] = userNonce + 1; } - function _getRelayParamsHash(RelayParams calldata relayParams) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - relayParams.oracleParams, - relayParams.externalCalls, - relayParams.tokenPermits, - relayParams.fee, - relayParams.userNonce, - relayParams.deadline - ) - ); - } - function _validateGaslessFeature() internal view { FeatureUtils.validateFeature(dataStore, Keys.gaslessFeatureDisabledKey(address(this))); } diff --git a/contracts/router/relay/GelatoRelayRouter.sol b/contracts/router/relay/GelatoRelayRouter.sol index 4b31f4ebf..e5845be73 100644 --- a/contracts/router/relay/GelatoRelayRouter.sol +++ b/contracts/router/relay/GelatoRelayRouter.sol @@ -11,40 +11,6 @@ import "../../router/Router.sol"; import "./BaseGelatoRelayRouter.sol"; contract GelatoRelayRouter is BaseGelatoRelayRouter { - bytes32 public constant UPDATE_ORDER_TYPEHASH = - keccak256( - bytes( - "UpdateOrder(bytes32 key,UpdateOrderParams params,bool increaseExecutionFee,bytes32 relayParams)UpdateOrderParams(uint256 sizeDeltaUsd,uint256 acceptablePrice,uint256 triggerPrice,uint256 minOutputAmount,uint256 validFromTime,bool autoCancel)" - ) - ); - bytes32 public constant UPDATE_ORDER_PARAMS_TYPEHASH = - keccak256( - bytes( - "UpdateOrderParams(uint256 sizeDeltaUsd,uint256 acceptablePrice,uint256 triggerPrice,uint256 minOutputAmount,uint256 validFromTime,bool autoCancel)" - ) - ); - - bytes32 public constant CANCEL_ORDER_TYPEHASH = - keccak256(bytes("CancelOrder(bytes32 key,bytes32 relayParams)")); - - bytes32 public constant CREATE_ORDER_TYPEHASH = - keccak256( - bytes( - "CreateOrder(uint256 collateralDeltaAmount,CreateOrderAddresses addresses,CreateOrderNumbers numbers,uint256 orderType,uint256 decreasePositionSwapType,bool isLong,bool shouldUnwrapNativeToken,bool autoCancel,bytes32 referralCode,bytes32 relayParams)CreateOrderAddresses(address receiver,address cancellationReceiver,address callbackContract,address uiFeeReceiver,address market,address initialCollateralToken,address[] swapPath)CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime,uint256 srcChainId)" - ) - ); - bytes32 public constant CREATE_ORDER_NUMBERS_TYPEHASH = - keccak256( - bytes( - "CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime,uint256 srcChainId)" - ) - ); - bytes32 public constant CREATE_ORDER_ADDRESSES_TYPEHASH = - keccak256( - bytes( - "CreateOrderAddresses(address receiver,address cancellationReceiver,address callbackContract,address uiFeeReceiver,address market,address initialCollateralToken,address[] swapPath)" - ) - ); constructor( Router _router, @@ -58,7 +24,7 @@ contract GelatoRelayRouter is BaseGelatoRelayRouter { // @note all params except account should be part of the corresponding struct hash function createOrder( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, address account, uint256 collateralDeltaAmount, IBaseOrderUtils.CreateOrderParams memory params // can't use calldata because need to modify params.numbers.executionFee @@ -70,7 +36,7 @@ contract GelatoRelayRouter is BaseGelatoRelayRouter { returns (bytes32) { _validateGaslessFeature(); - bytes32 structHash = _getCreateOrderStructHash(relayParams, collateralDeltaAmount, params); + bytes32 structHash = RelayUtils.getCreateOrderStructHash(relayParams, collateralDeltaAmount, params); _validateCall(relayParams, account, structHash); return _createOrder(relayParams, account, collateralDeltaAmount, params, false); @@ -78,14 +44,14 @@ contract GelatoRelayRouter is BaseGelatoRelayRouter { // @note all params except account should be part of the corresponding struct hash function updateOrder( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, address account, bytes32 key, - UpdateOrderParams calldata params, + RelayUtils.UpdateOrderParams calldata params, bool increaseExecutionFee ) external nonReentrant withOraclePricesForAtomicAction(relayParams.oracleParams) onlyGelatoRelay { _validateGaslessFeature(); - bytes32 structHash = _getUpdateOrderStructHash(relayParams, key, params, increaseExecutionFee); + bytes32 structHash = RelayUtils.getUpdateOrderStructHash(relayParams, key, params, increaseExecutionFee); _validateCall(relayParams, account, structHash); _updateOrder(relayParams, account, key, params, increaseExecutionFee, false); @@ -93,112 +59,14 @@ contract GelatoRelayRouter is BaseGelatoRelayRouter { // @note all params except account should be part of the corresponding struct hash function cancelOrder( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, address account, bytes32 key ) external nonReentrant withOraclePricesForAtomicAction(relayParams.oracleParams) onlyGelatoRelay { _validateGaslessFeature(); - bytes32 structHash = _getCancelOrderStructHash(relayParams, key); + bytes32 structHash = RelayUtils.getCancelOrderStructHash(relayParams, key); _validateCall(relayParams, account, structHash); _cancelOrder(relayParams, account, key); } - - function _getUpdateOrderStructHash( - RelayParams calldata relayParams, - bytes32 key, - UpdateOrderParams calldata params, - bool increaseExecutionFee - ) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - UPDATE_ORDER_TYPEHASH, - key, - _getUpdateOrderParamsStructHash(params), - increaseExecutionFee, - _getRelayParamsHash(relayParams) - ) - ); - } - - function _getUpdateOrderParamsStructHash(UpdateOrderParams calldata params) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - UPDATE_ORDER_PARAMS_TYPEHASH, - params.sizeDeltaUsd, - params.acceptablePrice, - params.triggerPrice, - params.minOutputAmount, - params.validFromTime, - params.autoCancel - ) - ); - } - - function _getCancelOrderStructHash(RelayParams calldata relayParams, bytes32 key) internal pure returns (bytes32) { - return keccak256(abi.encode(CANCEL_ORDER_TYPEHASH, key, _getRelayParamsHash(relayParams))); - } - - function _getCreateOrderStructHash( - RelayParams calldata relayParams, - uint256 collateralDeltaAmount, - IBaseOrderUtils.CreateOrderParams memory params - ) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - CREATE_ORDER_TYPEHASH, - collateralDeltaAmount, - _getCreateOrderAddressesStructHash(params.addresses), - _getCreateOrderNumbersStructHash(params.numbers), - uint256(params.orderType), - uint256(params.decreasePositionSwapType), - params.isLong, - params.shouldUnwrapNativeToken, - params.autoCancel, - params.referralCode, - _getRelayParamsHash(relayParams) - ) - ); - } - - function _getCreateOrderNumbersStructHash( - IBaseOrderUtils.CreateOrderParamsNumbers memory numbers - ) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - CREATE_ORDER_NUMBERS_TYPEHASH, - numbers.sizeDeltaUsd, - numbers.initialCollateralDeltaAmount, - numbers.triggerPrice, - numbers.acceptablePrice, - numbers.executionFee, - numbers.callbackGasLimit, - numbers.minOutputAmount, - numbers.validFromTime, - numbers.srcChainId - ) - ); - } - - function _getCreateOrderAddressesStructHash( - IBaseOrderUtils.CreateOrderParamsAddresses memory addresses - ) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - CREATE_ORDER_ADDRESSES_TYPEHASH, - addresses.receiver, - addresses.cancellationReceiver, - addresses.callbackContract, - addresses.uiFeeReceiver, - addresses.market, - addresses.initialCollateralToken, - keccak256(abi.encodePacked(addresses.swapPath)) - ) - ); - } } diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol new file mode 100644 index 000000000..23eed50d8 --- /dev/null +++ b/contracts/router/relay/RelayUtils.sol @@ -0,0 +1,622 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; + +import "../../oracle/OracleUtils.sol"; +import "../../order/IBaseOrderUtils.sol"; + +import "../../deposit/DepositUtils.sol"; +import "../../withdrawal/WithdrawalUtils.sol"; +import "../../glv/glvDeposit/GlvDepositUtils.sol"; +import "../../glv/glvWithdrawal/GlvWithdrawalUtils.sol"; +import "../../shift/ShiftUtils.sol"; + +library RelayUtils { + struct TokenPermit { + address owner; + address spender; + uint256 value; + uint256 deadline; + uint8 v; + bytes32 r; + bytes32 s; + address token; + } + + struct ExternalCalls { + address[] externalCallTargets; + bytes[] externalCallDataList; + address[] refundTokens; + address[] refundReceivers; + } + + struct FeeParams { + address feeToken; + uint256 feeAmount; + address[] feeSwapPath; + } + + struct RelayParams { + OracleUtils.SetPricesParams oracleParams; + ExternalCalls externalCalls; + TokenPermit[] tokenPermits; + FeeParams fee; + uint256 userNonce; + uint256 deadline; + bytes signature; + uint256 srcChainId; + } + + // @note all params except account should be part of the corresponding struct hash + struct UpdateOrderParams { + uint256 sizeDeltaUsd; + uint256 acceptablePrice; + uint256 triggerPrice; + uint256 minOutputAmount; + uint256 validFromTime; + bool autoCancel; + } + + struct MultichainCreateDepositParams { + uint256 desChainId; + uint256 longTokenAmount; + uint256 shortTokenAmount; + DepositUtils.CreateDepositParams createDepositParams; + } + + struct MultichainCreateWithdrawalParams { + uint256 desChainId; + uint256 tokenAmount; + WithdrawalUtils.CreateWithdrawalParams createWithdrawalParams; + } + + struct MultichainCreateGlvDepositParams { + uint256 desChainId; + uint256 longTokenAmount; + uint256 shortTokenAmount; + GlvDepositUtils.CreateGlvDepositParams createGlvDepositParams; + } + + struct MultichainCreateGlvWithdrawalParams { + uint256 desChainId; + uint256 glvTokenAmount; + GlvWithdrawalUtils.CreateGlvWithdrawalParams createGlvWithdrawalParams; + } + + struct MultichainCreateShiftParams { + uint256 desChainId; + uint256 marketTokenAmount; + ShiftUtils.CreateShiftParams createShiftParams; + } + + //////////////////// ORDER //////////////////// + + bytes32 public constant UPDATE_ORDER_TYPEHASH = + keccak256( + bytes( + "UpdateOrder(bytes32 key,UpdateOrderParams params,bool increaseExecutionFee,bytes32 relayParams)UpdateOrderParams(uint256 sizeDeltaUsd,uint256 acceptablePrice,uint256 triggerPrice,uint256 minOutputAmount,uint256 validFromTime,bool autoCancel)" + ) + ); + bytes32 public constant UPDATE_ORDER_PARAMS_TYPEHASH = + keccak256( + bytes( + "UpdateOrderParams(uint256 sizeDeltaUsd,uint256 acceptablePrice,uint256 triggerPrice,uint256 minOutputAmount,uint256 validFromTime,bool autoCancel)" + ) + ); + + bytes32 public constant CANCEL_ORDER_TYPEHASH = + keccak256(bytes("CancelOrder(bytes32 key,bytes32 relayParams)")); + + bytes32 public constant CREATE_ORDER_TYPEHASH = + keccak256( + bytes( + "CreateOrder(uint256 collateralDeltaAmount,CreateOrderAddresses addresses,CreateOrderNumbers numbers,uint256 orderType,uint256 decreasePositionSwapType,bool isLong,bool shouldUnwrapNativeToken,bool autoCancel,bytes32 referralCode,bytes32 relayParams)CreateOrderAddresses(address receiver,address cancellationReceiver,address callbackContract,address uiFeeReceiver,address market,address initialCollateralToken,address[] swapPath)CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime,uint256 srcChainId)" + ) + ); + bytes32 public constant CREATE_ORDER_NUMBERS_TYPEHASH = + keccak256( + bytes( + "CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime,uint256 srcChainId)" + ) + ); + bytes32 public constant CREATE_ORDER_ADDRESSES_TYPEHASH = + keccak256( + bytes( + "CreateOrderAddresses(address receiver,address cancellationReceiver,address callbackContract,address uiFeeReceiver,address market,address initialCollateralToken,address[] swapPath)" + ) + ); + + //////////////////// MULTICHAIN //////////////////// + + bytes32 public constant CREATE_DEPOSIT_TYPEHASH = + keccak256( + bytes( + "CreateDeposit(CreateDepositParams params,bytes32 relayParams)CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ) + ); + bytes32 public constant CREATE_DEPOSIT_PARAMS_TYPEHASH = + keccak256( + bytes( + "CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ) + ); + bytes32 public constant MULTICHAIN_CREATE_DEPOSIT_PARAMS_TYPEHASH = + keccak256( + bytes( + "MultichainCreateDepositParams(uint256 desChainId,uint256 longTokenAmount,uint256 shortTokenAmount,CreateDepositParams params)CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ) + ); + bytes32 public constant CREATE_DEPOSIT_PARAMS_ADDRESSES_TYPEHASH = + keccak256( + bytes( + "CreateDepositParamsAdresses(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath)" + ) + ); + + bytes32 public constant CREATE_WITHDRAWAL_TYPEHASH = + keccak256( + bytes( + "CreateWithdrawal(CreateWithdrawalParams params,bytes32 relayParams)CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ) + ); + bytes32 public constant MULTICHAIN_CREATE_WITHDRAWAL_PARAMS_TYPEHASH = + keccak256( + bytes( + "MultichainCreateWithdrawalParams(uint256 desChainId,uint256 tokenAmount,CreateWithdrawalParams params)CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ) + ); + bytes32 public constant CREATE_WITHDRAWAL_PARAMS_TYPEHASH = + keccak256( + bytes( + "CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ) + ); + + bytes32 public constant CREATE_GLV_DEPOSIT_TYPEHASH = + keccak256( + bytes( + "CreateGlvDeposit(CreateGlvDepositParams params,bytes32 relayParams)CreateGlvDepositParams(address account,address market,address initialLongToken,address initialShortToken,uint256 srcChainId,bytes32[] dataList)" + ) + ); + bytes32 public constant MULTICHAIN_CREATE_GLV_DEPOSIT_PARAMS_TYPEHASH = + keccak256( + bytes( + "MultichainCreateGlvDepositParams(uint256 desChainId,uint256 tokenAmount,CreateGlvDepositParams params)CreateGlvDepositParams(address glv,address market,address receiver,address callbackContract,address uiFeeReceiver,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minGlvTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bool shouldUnwrapNativeToken,bool isMarketTokenDeposit,bytes32[] dataList)" + ) + ); + bytes32 public constant CREATE_GLV_DEPOSIT_PARAMS_TYPEHASH = + keccak256( + bytes( + "CreateGlvDepositParams(address glv,address market,address receiver,address callbackContract,address uiFeeReceiver,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minGlvTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bool shouldUnwrapNativeToken,bool isMarketTokenDeposit,bytes32[] dataList)" + ) + ); + + bytes32 public constant CREATE_GLV_WITHDRAWAL_TYPEHASH = + keccak256( + bytes( + "CreateGlvWithdrawal(CreateGlvWithdrawalParams params,bytes32 relayParams)" + "CreateGlvWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ) + ); + + bytes32 public constant MULTICHAIN_CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH = + keccak256( + bytes( + "MultichainCreateGlvWithdrawalParams(uint256 desChainId,uint256 glvTokenAmount,CreateGlvWithdrawalParams params)" + "CreateGlvWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ) + ); + + bytes32 public constant CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH = + keccak256( + bytes( + "CreateGlvWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ) + ); + + bytes32 public constant CREATE_SHIFT_TYPEHASH = keccak256( + "CreateShift(CreateShiftParams params,bytes32 relayParams)CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ); + + bytes32 public constant MULTICHAIN_CREATE_SHIFT_PARAMS_TYPEHASH = keccak256( + "MultichainCreateShiftParams(uint256 desChainId,uint256 marketTokenAmount,CreateShiftParams params)CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ); + + bytes32 public constant CREATE_SHIFT_PARAMS_TYPEHASH = keccak256( + "CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ); + + //////////////////// ORDER //////////////////// + + function _getRelayParamsHash(RelayParams calldata relayParams) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + relayParams.oracleParams, + relayParams.externalCalls, + relayParams.tokenPermits, + relayParams.fee, + relayParams.userNonce, + relayParams.deadline + ) + ); + } + + function getUpdateOrderStructHash( + RelayParams calldata relayParams, + bytes32 key, + UpdateOrderParams calldata params, + bool increaseExecutionFee + ) external pure returns (bytes32) { + return + keccak256( + abi.encode( + UPDATE_ORDER_TYPEHASH, + key, + _getUpdateOrderParamsStructHash(params), + increaseExecutionFee, + _getRelayParamsHash(relayParams) + ) + ); + } + + function _getUpdateOrderParamsStructHash(UpdateOrderParams calldata params) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + UPDATE_ORDER_PARAMS_TYPEHASH, + params.sizeDeltaUsd, + params.acceptablePrice, + params.triggerPrice, + params.minOutputAmount, + params.validFromTime, + params.autoCancel + ) + ); + } + + function getCancelOrderStructHash(RelayParams calldata relayParams, bytes32 key) external pure returns (bytes32) { + return keccak256(abi.encode(CANCEL_ORDER_TYPEHASH, key, _getRelayParamsHash(relayParams))); + } + + function getCreateOrderStructHash( + RelayParams calldata relayParams, + uint256 collateralDeltaAmount, + IBaseOrderUtils.CreateOrderParams memory params + ) external pure returns (bytes32) { + return + keccak256( + abi.encode( + CREATE_ORDER_TYPEHASH, + collateralDeltaAmount, + _getCreateOrderAddressesStructHash(params.addresses), + _getCreateOrderNumbersStructHash(params.numbers), + uint256(params.orderType), + uint256(params.decreasePositionSwapType), + params.isLong, + params.shouldUnwrapNativeToken, + params.autoCancel, + params.referralCode, + _getRelayParamsHash(relayParams) + ) + ); + } + + function _getCreateOrderNumbersStructHash( + IBaseOrderUtils.CreateOrderParamsNumbers memory numbers + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + CREATE_ORDER_NUMBERS_TYPEHASH, + numbers.sizeDeltaUsd, + numbers.initialCollateralDeltaAmount, + numbers.triggerPrice, + numbers.acceptablePrice, + numbers.executionFee, + numbers.callbackGasLimit, + numbers.minOutputAmount, + numbers.validFromTime, + numbers.srcChainId + ) + ); + } + + function _getCreateOrderAddressesStructHash( + IBaseOrderUtils.CreateOrderParamsAddresses memory addresses + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + CREATE_ORDER_ADDRESSES_TYPEHASH, + addresses.receiver, + addresses.cancellationReceiver, + addresses.callbackContract, + addresses.uiFeeReceiver, + addresses.market, + addresses.initialCollateralToken, + keccak256(abi.encodePacked(addresses.swapPath)) + ) + ); + } + + //////////////////// MULTICHAIN //////////////////// + + function getMultichainCreateDepositStructHash( + RelayParams calldata relayParams, + MultichainCreateDepositParams memory params + ) external view returns (bytes32) { + bytes32 relayParamsHash = _getRelayParamsHash(relayParams); + return + keccak256( + abi.encode( + CREATE_DEPOSIT_TYPEHASH, + _getMultichainCreateDepositParamsStructHash(params), + relayParamsHash + ) + ); + } + + function _getMultichainCreateDepositParamsStructHash( + MultichainCreateDepositParams memory params + ) internal view returns (bytes32) { + return + keccak256( + abi.encode( + MULTICHAIN_CREATE_DEPOSIT_PARAMS_TYPEHASH, + block.chainid, + params.longTokenAmount, + params.shortTokenAmount, + _getCreateDepositParamsStructHash(params.createDepositParams) + ) + ); + } + + function _getCreateDepositParamsStructHash( + DepositUtils.CreateDepositParams memory params + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + CREATE_DEPOSIT_PARAMS_TYPEHASH, + _getCreateDepositParamsAdressesStructHash(params.addresses), + params.minMarketTokens, + params.shouldUnwrapNativeToken, + params.executionFee, + params.callbackGasLimit, + params.srcChainId, + keccak256(abi.encodePacked(params.dataList)) + ) + ); + } + + function _getCreateDepositParamsAdressesStructHash( + DepositUtils.CreateDepositParamsAdresses memory addresses + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + CREATE_DEPOSIT_PARAMS_ADDRESSES_TYPEHASH, + addresses.receiver, + addresses.callbackContract, + addresses.uiFeeReceiver, + addresses.market, + addresses.initialLongToken, + addresses.initialShortToken, + keccak256(abi.encodePacked(addresses.longTokenSwapPath)), + keccak256(abi.encodePacked(addresses.shortTokenSwapPath)) + ) + ); + } + + function getMultichainCreateWithdrawalStructHash( + RelayParams calldata relayParams, + MultichainCreateWithdrawalParams memory params + ) external view returns (bytes32) { + bytes32 relayParamsHash = _getRelayParamsHash(relayParams); + return + keccak256( + abi.encode( + CREATE_WITHDRAWAL_TYPEHASH, + _getMultichainCreateWithdrawalParamsStructHash(params), + relayParamsHash + ) + ); + } + + function _getMultichainCreateWithdrawalParamsStructHash( + MultichainCreateWithdrawalParams memory params + ) internal view returns (bytes32) { + return + keccak256( + abi.encode( + MULTICHAIN_CREATE_WITHDRAWAL_PARAMS_TYPEHASH, + block.chainid, + params.tokenAmount, + _getCreateWithdrawalParamsStructHash(params.createWithdrawalParams) + ) + ); + } + + function _getCreateWithdrawalParamsStructHash( + WithdrawalUtils.CreateWithdrawalParams memory params + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + CREATE_WITHDRAWAL_PARAMS_TYPEHASH, + params.receiver, + params.callbackContract, + params.uiFeeReceiver, + params.market, + keccak256(abi.encodePacked(params.longTokenSwapPath)), + keccak256(abi.encodePacked(params.shortTokenSwapPath)), + params.minLongTokenAmount, + params.minShortTokenAmount, + params.shouldUnwrapNativeToken, + params.executionFee, + params.callbackGasLimit, + params.srcChainId, + keccak256(abi.encodePacked(params.dataList)) + ) + ); + } + + function getMultichainCreateGlvDepositStructHash( + RelayParams calldata relayParams, + MultichainCreateGlvDepositParams memory params + ) external pure returns (bytes32) { + bytes32 relayParamsHash = _getRelayParamsHash(relayParams); + return + keccak256( + abi.encode( + CREATE_GLV_DEPOSIT_TYPEHASH, + _getMultichainCreateGlvDepositParamsStructHash(params), + relayParamsHash + ) + ); + } + + function _getMultichainCreateGlvDepositParamsStructHash( + MultichainCreateGlvDepositParams memory params + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + MULTICHAIN_CREATE_GLV_DEPOSIT_PARAMS_TYPEHASH, + params.desChainId, + params.longTokenAmount, + params.shortTokenAmount, + _getCreateGlvDepositParamsStructHash(params.createGlvDepositParams) + ) + ); + } + + function _getCreateGlvDepositParamsStructHash( + GlvDepositUtils.CreateGlvDepositParams memory params + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + CREATE_GLV_DEPOSIT_PARAMS_TYPEHASH, + params.glv, + params.market, + params.receiver, + params.callbackContract, + params.uiFeeReceiver, + // params.initialLongToken, + // params.initialShortToken, + // keccak256(abi.encodePacked(params.longTokenSwapPath)), + // keccak256(abi.encodePacked(params.shortTokenSwapPath)), // TODO: split CreateGlvDepositParams into groups to fix slot too deep error + params.minGlvTokens, + params.executionFee, + params.callbackGasLimit, + params.srcChainId, + params.shouldUnwrapNativeToken, + params.isMarketTokenDeposit, + keccak256(abi.encodePacked(params.dataList)) + ) + ); + } + + function getMultichainCreateGlvWithdrawalStructHash( + RelayParams calldata relayParams, + MultichainCreateGlvWithdrawalParams memory params + ) external view returns (bytes32) { + bytes32 relayParamsHash = _getRelayParamsHash(relayParams); + return + keccak256( + abi.encode( + CREATE_GLV_WITHDRAWAL_TYPEHASH, + _getMultichainCreateGlvWithdrawalParamsStructHash(params), + relayParamsHash + ) + ); + } + + function _getMultichainCreateGlvWithdrawalParamsStructHash( + MultichainCreateGlvWithdrawalParams memory params + ) internal view returns (bytes32) { + return + keccak256( + abi.encode( + MULTICHAIN_CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH, + block.chainid, + params.glvTokenAmount, + _getCreateGlvWithdrawalParamsStructHash(params.createGlvWithdrawalParams) + ) + ); + } + + function _getCreateGlvWithdrawalParamsStructHash( + GlvWithdrawalUtils.CreateGlvWithdrawalParams memory params + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH, + params.receiver, + params.callbackContract, + params.uiFeeReceiver, + params.market, + params.glv, + keccak256(abi.encodePacked(params.longTokenSwapPath)), + keccak256(abi.encodePacked(params.shortTokenSwapPath)), + params.minLongTokenAmount, + params.minShortTokenAmount, + // params.shouldUnwrapNativeToken, // TODO: split CreateGlvWithdrawalParams into groups to fix slot too deep error + params.executionFee, + params.callbackGasLimit, + params.srcChainId, + keccak256(abi.encodePacked(params.dataList)) + ) + ); + } + + function getMultichainCreateShiftStructHash( + RelayParams calldata relayParams, + MultichainCreateShiftParams memory params + ) external view returns (bytes32) { + bytes32 relayParamsHash = _getRelayParamsHash(relayParams); + return keccak256( + abi.encode( + CREATE_SHIFT_TYPEHASH, + _getMultichainCreateShiftParamsStructHash(params), + relayParamsHash + ) + ); + } + + function _getMultichainCreateShiftParamsStructHash( + MultichainCreateShiftParams memory params + ) internal view returns (bytes32) { + return keccak256( + abi.encode( + MULTICHAIN_CREATE_SHIFT_PARAMS_TYPEHASH, + block.chainid, + params.marketTokenAmount, + _getCreateShiftParamsStructHash(params.createShiftParams) + ) + ); + } + + function _getCreateShiftParamsStructHash( + ShiftUtils.CreateShiftParams memory params + ) internal pure returns (bytes32) { + return keccak256( + abi.encode( + CREATE_SHIFT_PARAMS_TYPEHASH, + params.receiver, + params.callbackContract, + params.uiFeeReceiver, + params.fromMarket, + params.toMarket, + params.minMarketTokens, + params.executionFee, + params.callbackGasLimit, + params.srcChainId, + keccak256(abi.encodePacked(params.dataList)) + ) + ); + } +} diff --git a/contracts/router/relay/SubaccountGelatoRelayRouter.sol b/contracts/router/relay/SubaccountGelatoRelayRouter.sol index 77852f2b4..573efe934 100644 --- a/contracts/router/relay/SubaccountGelatoRelayRouter.sol +++ b/contracts/router/relay/SubaccountGelatoRelayRouter.sol @@ -79,7 +79,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { // @note all params except subaccount should be part of the corresponding struct hash function createOrder( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, SubaccountApproval calldata subaccountApproval, address account, // main account address subaccount, @@ -116,12 +116,12 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { // @note all params except subaccount should be part of the corresponding struct hash function updateOrder( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, SubaccountApproval calldata subaccountApproval, address account, // main account address subaccount, bytes32 key, - UpdateOrderParams calldata params, + RelayUtils.UpdateOrderParams calldata params, bool increaseExecutionFee ) external nonReentrant withOraclePricesForAtomicAction(relayParams.oracleParams) onlyGelatoRelay { _validateGaslessFeature(); @@ -133,7 +133,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { // @note all params except subaccount should be part of the corresponding struct hash function cancelOrder( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, SubaccountApproval calldata subaccountApproval, address account, // main account address subaccount, @@ -148,7 +148,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { // @note all params except account should be part of the corresponding struct hash function removeSubaccount( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, address account, address subaccount ) external nonReentrant { @@ -231,10 +231,10 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { } function _getRemoveSubaccountStructHash( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, address subaccount ) internal pure returns (bytes32) { - return keccak256(abi.encode(REMOVE_SUBACCOUNT_TYPEHASH, subaccount, _getRelayParamsHash(relayParams))); + return keccak256(abi.encode(REMOVE_SUBACCOUNT_TYPEHASH, subaccount, RelayUtils._getRelayParamsHash(relayParams))); } function _getSubaccountApprovalStructHash( @@ -256,13 +256,13 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { } function _getCreateOrderStructHash( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, SubaccountApproval calldata subaccountApproval, address account, uint256 collateralDeltaAmount, IBaseOrderUtils.CreateOrderParams memory params ) internal pure returns (bytes32) { - bytes32 relayParamsHash = _getRelayParamsHash(relayParams); + bytes32 relayParamsHash = RelayUtils._getRelayParamsHash(relayParams); bytes32 subaccountApprovalHash = keccak256(abi.encode(subaccountApproval)); return @@ -324,11 +324,11 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { } function _getUpdateOrderStructHash( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, SubaccountApproval calldata subaccountApproval, address account, bytes32 key, - UpdateOrderParams calldata params, + RelayUtils.UpdateOrderParams calldata params, bool increaseExecutionFee ) internal pure returns (bytes32) { return @@ -339,13 +339,13 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { key, _getUpdateOrderParamsStructHash(params), increaseExecutionFee, - _getRelayParamsHash(relayParams), + RelayUtils._getRelayParamsHash(relayParams), keccak256(abi.encode(subaccountApproval)) ) ); } - function _getUpdateOrderParamsStructHash(UpdateOrderParams calldata params) internal pure returns (bytes32) { + function _getUpdateOrderParamsStructHash(RelayUtils.UpdateOrderParams calldata params) internal pure returns (bytes32) { return keccak256( abi.encode( @@ -361,7 +361,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { } function _getCancelOrderStructHash( - RelayParams calldata relayParams, + RelayUtils.RelayParams calldata relayParams, SubaccountApproval calldata subaccountApproval, address account, bytes32 key @@ -372,7 +372,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { CANCEL_ORDER_TYPEHASH, account, key, - _getRelayParamsHash(relayParams), + RelayUtils._getRelayParamsHash(relayParams), keccak256(abi.encode(subaccountApproval)) ) ); diff --git a/deploy/deployGelatoRelayRouter.ts b/deploy/deployGelatoRelayRouter.ts index dbc0ea3e9..b95d53133 100644 --- a/deploy/deployGelatoRelayRouter.ts +++ b/deploy/deployGelatoRelayRouter.ts @@ -17,7 +17,7 @@ const func = createDeployFunction({ getDeployArgs: async ({ dependencyContracts }) => { return constructorContracts.map((dependencyName) => dependencyContracts[dependencyName].address); }, - libraryNames: ["MarketStoreUtils", "MarketUtils", "OrderStoreUtils", "SwapUtils"], + libraryNames: ["MarketStoreUtils", "MarketUtils", "OrderStoreUtils", "SwapUtils", "RelayUtils"], afterDeploy: async ({ deployedContract }) => { await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); await grantRoleIfNotGranted(deployedContract.address, "ROUTER_PLUGIN"); diff --git a/deploy/deployRelayUtils.ts b/deploy/deployRelayUtils.ts new file mode 100644 index 000000000..ad4ebf403 --- /dev/null +++ b/deploy/deployRelayUtils.ts @@ -0,0 +1,7 @@ +import { createDeployFunction } from "../utils/deploy"; + +const func = createDeployFunction({ + contractName: "RelayUtils", +}); + +export default func; diff --git a/deploy/deploySubaccountGelatoRelayRouter.ts b/deploy/deploySubaccountGelatoRelayRouter.ts index c14f34187..760c3706b 100644 --- a/deploy/deploySubaccountGelatoRelayRouter.ts +++ b/deploy/deploySubaccountGelatoRelayRouter.ts @@ -17,7 +17,7 @@ const func = createDeployFunction({ getDeployArgs: async ({ dependencyContracts }) => { return constructorContracts.map((dependencyName) => dependencyContracts[dependencyName].address); }, - libraryNames: ["MarketStoreUtils", "MarketUtils", "OrderStoreUtils", "SwapUtils", "SubaccountUtils"], + libraryNames: ["MarketStoreUtils", "MarketUtils", "OrderStoreUtils", "SwapUtils", "SubaccountUtils", "RelayUtils"], afterDeploy: async ({ deployedContract }) => { await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); await grantRoleIfNotGranted(deployedContract.address, "ROUTER_PLUGIN"); diff --git a/test/router/relay/signatures.ts b/test/router/relay/signatures.ts index 380ba1fcb..180dd14fa 100644 --- a/test/router/relay/signatures.ts +++ b/test/router/relay/signatures.ts @@ -21,6 +21,7 @@ describe("Relay signatures", () => { marketStoreUtils, orderStoreUtils, swapUtils, + relayUtils, mockContract; beforeEach(async () => { @@ -36,6 +37,7 @@ describe("Relay signatures", () => { marketStoreUtils, orderStoreUtils, swapUtils, + relayUtils, } = fixture.contracts); }); @@ -56,6 +58,7 @@ describe("Relay signatures", () => { MarketStoreUtils: marketStoreUtils.address, OrderStoreUtils: orderStoreUtils.address, SwapUtils: swapUtils.address, + RelayUtils: relayUtils.address, }, } ); diff --git a/utils/fixture.ts b/utils/fixture.ts index d9129e1f2..c56abb326 100644 --- a/utils/fixture.ts +++ b/utils/fixture.ts @@ -98,6 +98,7 @@ export async function deployFixture() { const gelatoRelayRouter = await hre.ethers.getContract("GelatoRelayRouter"); const subaccountGelatoRelayRouter = await hre.ethers.getContract("SubaccountGelatoRelayRouter"); const subaccountRouter = await hre.ethers.getContract("SubaccountRouter"); + const relayUtils = await hre.ethers.getContract("RelayUtils"); const oracle = await hre.ethers.getContract("Oracle"); const gmOracleProvider = await hre.ethers.getContract("GmOracleProvider"); const chainlinkPriceFeedProvider = await hre.ethers.getContract("ChainlinkPriceFeedProvider"); @@ -281,6 +282,7 @@ export async function deployFixture() { gelatoRelayRouter, subaccountGelatoRelayRouter, subaccountRouter, + relayUtils, oracle, gmOracleProvider, chainlinkPriceFeedProvider, From 967b56c20715e1d07f19a88f4ae114fbef2e2477 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 13 Feb 2025 14:01:01 +0200 Subject: [PATCH 046/205] Refactor MultichainRouter by spliting it's logic into MultichainGmRouter and MultichainGlvRouter to reduce contract size --- contracts/multichain/MultichainGlvRouter.sol | 128 ++++++++ contracts/multichain/MultichainGmRouter.sol | 183 +++++++++++ contracts/multichain/MultichainRouter.sol | 315 ++----------------- 3 files changed, 335 insertions(+), 291 deletions(-) create mode 100644 contracts/multichain/MultichainGlvRouter.sol create mode 100644 contracts/multichain/MultichainGmRouter.sol diff --git a/contracts/multichain/MultichainGlvRouter.sol b/contracts/multichain/MultichainGlvRouter.sol new file mode 100644 index 000000000..13e6836b0 --- /dev/null +++ b/contracts/multichain/MultichainGlvRouter.sol @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; + +import "../exchange/GlvHandler.sol"; +import "../glv/GlvVault.sol"; + +import "./MultichainRouter.sol"; + +contract MultichainGlvRouter is MultichainRouter { + + GlvVault public immutable glvVault; + GlvHandler public immutable glvHandler; + + constructor( + BaseConstructorParams memory params, + GlvHandler _glvHandler, + GlvVault _glvVault + ) MultichainRouter(params) { + glvHandler = _glvHandler; + glvVault = _glvVault; + } + + function createGlvDeposit( + RelayUtils.RelayParams calldata relayParams, + address account, + RelayUtils.MultichainCreateGlvDepositParams memory params + ) external nonReentrant onlyGelatoRelay returns (bytes32) { + if (params.desChainId != block.chainid) { + revert Errors.InvalidDestinationChainId(); + } + + bytes32 structHash = RelayUtils.getMultichainCreateGlvDepositStructHash(relayParams, params); + _validateCall(relayParams, account, structHash); + + return _createGlvDeposit(relayParams, account, params); + } + + function _createGlvDeposit( + RelayUtils.RelayParams calldata relayParams, + address account, + RelayUtils.MultichainCreateGlvDepositParams memory params + ) internal returns (bytes32) { + Contracts memory contracts = Contracts({ + dataStore: dataStore, + eventEmitter: eventEmitter, + bank: glvVault + }); + + // transfer long & short tokens from MultichainVault to GlvVault and decrement user's multichain balance + _sendTokens( + account, + params.createGlvDepositParams.initialLongToken, + address(glvVault), // receiver + params.longTokenAmount, + params.createGlvDepositParams.srcChainId + ); + _sendTokens( + account, + params.createGlvDepositParams.initialShortToken, + address(glvVault), // receiver + params.shortTokenAmount, + params.createGlvDepositParams.srcChainId + ); + + // pay relay fee tokens from MultichainVault to GlvVault and decrease user's multichain balance + params.createGlvDepositParams.executionFee = _handleRelay( + contracts, + relayParams, + account, + address(glvVault) + ); + + return glvHandler.createGlvDeposit(account, params.createGlvDepositParams); + } + + function createGlvWithdrawal( + RelayUtils.RelayParams calldata relayParams, + address account, + RelayUtils.MultichainCreateGlvWithdrawalParams memory params + ) external nonReentrant onlyGelatoRelay returns (bytes32) { + if (params.desChainId != block.chainid) { + revert Errors.InvalidDestinationChainId(); + } + + bytes32 structHash = RelayUtils.getMultichainCreateGlvWithdrawalStructHash(relayParams, params); + _validateCall(relayParams, account, structHash); + + return _createGlvWithdrawal(relayParams, account, params); + } + + function _createGlvWithdrawal( + RelayUtils.RelayParams calldata relayParams, + address account, + RelayUtils.MultichainCreateGlvWithdrawalParams memory params + ) internal returns (bytes32) { + Contracts memory contracts = Contracts({ + dataStore: dataStore, + eventEmitter: eventEmitter, + bank: glvVault + }); + + // transfer GLV tokens from MultichainVault to GlvVault + _sendTokens( + account, + params.createGlvWithdrawalParams.glv, + address(glvVault), // receiver + params.glvTokenAmount, + params.createGlvWithdrawalParams.srcChainId + ); + + // pay relay fee tokens from MultichainVault to GlvVault and decrease user's multichain balance + params.createGlvWithdrawalParams.executionFee = _handleRelay( + contracts, + relayParams, + account, + address(glvVault) // residualFeeReceiver + ); + + return GlvWithdrawalUtils.createGlvWithdrawal( + dataStore, + eventEmitter, + glvVault, + account, + params.createGlvWithdrawalParams + ); + } +} diff --git a/contracts/multichain/MultichainGmRouter.sol b/contracts/multichain/MultichainGmRouter.sol new file mode 100644 index 000000000..a23432303 --- /dev/null +++ b/contracts/multichain/MultichainGmRouter.sol @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; + +import "../deposit/DepositVault.sol"; +import "../exchange/IDepositHandler.sol"; +import "../exchange/WithdrawalHandler.sol"; +import "../withdrawal/WithdrawalVault.sol"; + +import "./MultichainRouter.sol"; + +contract MultichainGmRouter is MultichainRouter { + + DepositVault public immutable depositVault; + IDepositHandler public immutable depositHandler; + WithdrawalVault public immutable withdrawalVault; + WithdrawalHandler public immutable withdrawalHandler; + ShiftVault public immutable shiftVault; + + constructor( + BaseConstructorParams memory params, + DepositVault _depositVault, + IDepositHandler _depositHandler, + WithdrawalVault _withdrawalVault, + WithdrawalHandler _withdrawalHandler, + ShiftVault _shiftVault + ) MultichainRouter(params) { + depositVault = _depositVault; + depositHandler = _depositHandler; + withdrawalVault = _withdrawalVault; + withdrawalHandler = _withdrawalHandler; + shiftVault = _shiftVault; + } + + function createDeposit( + RelayUtils.RelayParams calldata relayParams, + address account, + RelayUtils.MultichainCreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee + ) external nonReentrant onlyGelatoRelay returns (bytes32) { + if (params.desChainId != block.chainid) { + revert Errors.InvalidDestinationChainId(); + } + + bytes32 structHash = RelayUtils.getMultichainCreateDepositStructHash(relayParams, params); + _validateCall(relayParams, account, structHash); + + return _createDeposit(relayParams, account, params); + } + + function _createDeposit( + RelayUtils.RelayParams calldata relayParams, + address account, + RelayUtils.MultichainCreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee + ) internal returns (bytes32) { + Contracts memory contracts = Contracts({ + dataStore: dataStore, + eventEmitter: eventEmitter, + bank: depositVault + }); + + // transfer long & short tokens from MultichainVault to DepositVault and decrement user's multichain balance + _sendTokens( + account, + params.createDepositParams.addresses.initialLongToken, + address(depositVault), // receiver + params.longTokenAmount, + params.createDepositParams.srcChainId + ); + _sendTokens( + account, + params.createDepositParams.addresses.initialShortToken, + address(depositVault), // receiver + params.shortTokenAmount, + params.createDepositParams.srcChainId + ); + + // pay relay fee tokens from MultichainVault to DepositVault and decrease user's multichain balance + params.createDepositParams.executionFee = _handleRelay( + contracts, + relayParams, + account, + address(depositVault) // residualFeeReceiver + ); + + return depositHandler.createDeposit(account, params.createDepositParams); + } + + function createWithdrawal( + RelayUtils.RelayParams calldata relayParams, + address account, + RelayUtils.MultichainCreateWithdrawalParams memory params // can't use calldata because need to modify params.addresses.receiver & params.numbers.executionFee + ) external nonReentrant onlyGelatoRelay returns (bytes32) { + if (params.desChainId != block.chainid) { + revert Errors.InvalidDestinationChainId(); + } + + bytes32 structHash = RelayUtils.getMultichainCreateWithdrawalStructHash(relayParams, params); + _validateCall(relayParams, account, structHash); + + return _createWithdrawal(relayParams, account, params); + } + + function _createWithdrawal( + RelayUtils.RelayParams calldata relayParams, + address account, + RelayUtils.MultichainCreateWithdrawalParams memory params // can't use calldata because need to modify params.addresses.receiver & params.numbers.executionFee + ) internal returns (bytes32) { + Contracts memory contracts = Contracts({ + dataStore: dataStore, + eventEmitter: eventEmitter, + bank: withdrawalVault + }); + + // user already bridged the GM tokens to the MultichainVault and balance was increased + // transfer the GM tokens from MultichainVault to WithdrawalVault + _sendTokens( + account, + params.createWithdrawalParams.market, + address(withdrawalVault), // receiver + params.tokenAmount, + params.createWithdrawalParams.srcChainId + ); + + params.createWithdrawalParams.executionFee = _handleRelay( + contracts, + relayParams, + account, + address(withdrawalVault) // residualFeeReceiver + ); + + return withdrawalHandler.createWithdrawal(account, params.createWithdrawalParams); + } + + function createShift( + RelayUtils.RelayParams calldata relayParams, + address account, + RelayUtils.MultichainCreateShiftParams memory params + ) external nonReentrant onlyGelatoRelay returns (bytes32) { + if (params.desChainId != block.chainid) { + revert Errors.InvalidDestinationChainId(); + } + + bytes32 structHash = RelayUtils.getMultichainCreateShiftStructHash(relayParams, params); + _validateCall(relayParams, account, structHash); + + return _createShift(relayParams, account, params); + } + + function _createShift( + RelayUtils.RelayParams calldata relayParams, + address account, + RelayUtils.MultichainCreateShiftParams memory params + ) internal returns (bytes32) { + Contracts memory contracts = Contracts({ + dataStore: dataStore, + eventEmitter: eventEmitter, + bank: shiftVault + }); + + _sendTokens( + account, + params.createShiftParams.fromMarket, + address(shiftVault), // receiver + params.marketTokenAmount, + params.createShiftParams.srcChainId + ); + + params.createShiftParams.executionFee = _handleRelay( + contracts, + relayParams, + account, + address(shiftVault) + ); + + return ShiftUtils.createShift( + dataStore, + eventEmitter, + shiftVault, + account, + params.createShiftParams + ); + } +} diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index e00a33959..520261bd2 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -3,305 +3,38 @@ pragma solidity ^0.8.0; import "../router/relay/GelatoRelayRouter.sol"; -import "../deposit/DepositVault.sol"; -import "../exchange/IDepositHandler.sol"; -import "../exchange/WithdrawalHandler.sol"; -import "../withdrawal/WithdrawalVault.sol"; -import "../exchange/GlvHandler.sol"; -import "../glv/GlvVault.sol"; import "./MultichainUtils.sol"; contract MultichainRouter is GelatoRelayRouter { - DepositVault depositVault; - IDepositHandler depositHandler; - MultichainVault multichainVault; - WithdrawalHandler withdrawalHandler; - WithdrawalVault withdrawalVault; - GlvHandler public glvHandler; - GlvVault public glvVault; - ShiftVault public shiftVault; - - constructor( - Router _router, - DataStore _dataStore, - EventEmitter _eventEmitter, - Oracle _oracle, - IOrderHandler _orderHandler, - OrderVault _orderVault, - IExternalHandler _externalHandler, - IDepositHandler _depositHandler, - DepositVault _depositVault, - MultichainVault _multichainVault, - WithdrawalHandler _withdrawalHandler, - WithdrawalVault _withdrawalVault - // GlvHandler _glvHandler, // TODO: place in a struct to fix stack to deep error - // GlvVault _glvVault - // ShiftVault _shiftVault - ) GelatoRelayRouter(_router, _dataStore, _eventEmitter, _oracle, _orderHandler, _orderVault, _externalHandler) { - depositVault = _depositVault; - depositHandler = _depositHandler; - multichainVault = _multichainVault; - withdrawalHandler = _withdrawalHandler; - withdrawalVault = _withdrawalVault; - // glvHandler = _glvHandler; - // glvVault = _glvVault; - // shiftVault = _shiftVault; - } - - function createDeposit( - RelayUtils.RelayParams calldata relayParams, - address account, - RelayUtils.MultichainCreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee - ) external nonReentrant onlyGelatoRelay returns (bytes32) { - if (params.desChainId != block.chainid) { - revert Errors.InvalidDestinationChainId(); - } - - bytes32 structHash = RelayUtils.getMultichainCreateDepositStructHash(relayParams, params); - _validateCall(relayParams, account, structHash); - - return _createDeposit(relayParams, account, params); - } - - function _createDeposit( - RelayUtils.RelayParams calldata relayParams, - address account, - RelayUtils.MultichainCreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee - ) internal returns (bytes32) { - Contracts memory contracts = Contracts({ - dataStore: dataStore, - eventEmitter: eventEmitter, - bank: depositVault - }); - - // transfer long & short tokens from MultichainVault to DepositVault and decrement user's multichain balance - _sendTokens( - account, - params.createDepositParams.addresses.initialLongToken, - address(depositVault), // receiver - params.longTokenAmount, - params.createDepositParams.srcChainId - ); - _sendTokens( - account, - params.createDepositParams.addresses.initialShortToken, - address(depositVault), // receiver - params.shortTokenAmount, - params.createDepositParams.srcChainId - ); - - // pay relay fee tokens from MultichainVault to DepositVault and decrease user's multichain balance - params.createDepositParams.executionFee = _handleRelay( - contracts, - relayParams, - account, - address(depositVault) // residualFeeReceiver - ); - - return depositHandler.createDeposit(account, params.createDepositParams); - } - - function createWithdrawal( - RelayUtils.RelayParams calldata relayParams, - address account, - RelayUtils.MultichainCreateWithdrawalParams memory params // can't use calldata because need to modify params.addresses.receiver & params.numbers.executionFee - ) external nonReentrant onlyGelatoRelay returns (bytes32) { - if (params.desChainId != block.chainid) { - revert Errors.InvalidDestinationChainId(); - } - - bytes32 structHash = RelayUtils.getMultichainCreateWithdrawalStructHash(relayParams, params); - _validateCall(relayParams, account, structHash); - - return _createWithdrawal(relayParams, account, params); - } - - function _createWithdrawal( - RelayUtils.RelayParams calldata relayParams, - address account, - RelayUtils.MultichainCreateWithdrawalParams memory params // can't use calldata because need to modify params.addresses.receiver & params.numbers.executionFee - ) internal returns (bytes32) { - Contracts memory contracts = Contracts({ - dataStore: dataStore, - eventEmitter: eventEmitter, - bank: withdrawalVault - }); - - // user already bridged the GM tokens to the MultichainVault and balance was increased - // transfer the GM tokens from MultichainVault to WithdrawalVault - _sendTokens( - account, - params.createWithdrawalParams.market, - address(withdrawalVault), // receiver - params.tokenAmount, - params.createWithdrawalParams.srcChainId - ); - - params.createWithdrawalParams.executionFee = _handleRelay( - contracts, - relayParams, - account, - address(withdrawalVault) // residualFeeReceiver - ); - - return withdrawalHandler.createWithdrawal(account, params.createWithdrawalParams); - } - - function createGlvDeposit( - RelayUtils.RelayParams calldata relayParams, - address account, - RelayUtils.MultichainCreateGlvDepositParams memory params - ) external nonReentrant onlyGelatoRelay returns (bytes32) { - if (params.desChainId != block.chainid) { - revert Errors.InvalidDestinationChainId(); - } - - bytes32 structHash = RelayUtils.getMultichainCreateGlvDepositStructHash(relayParams, params); - _validateCall(relayParams, account, structHash); - - return _createGlvDeposit(relayParams, account, params); - } - - function _createGlvDeposit( - RelayUtils.RelayParams calldata relayParams, - address account, - RelayUtils.MultichainCreateGlvDepositParams memory params - ) internal returns (bytes32) { - Contracts memory contracts = Contracts({ - dataStore: dataStore, - eventEmitter: eventEmitter, - bank: glvVault - }); - - // transfer long & short tokens from MultichainVault to GlvVault and decrement user's multichain balance - _sendTokens( - account, - params.createGlvDepositParams.initialLongToken, - address(glvVault), // receiver - params.longTokenAmount, - params.createGlvDepositParams.srcChainId - ); - _sendTokens( - account, - params.createGlvDepositParams.initialShortToken, - address(glvVault), // receiver - params.shortTokenAmount, - params.createGlvDepositParams.srcChainId - ); - - // pay relay fee tokens from MultichainVault to GlvVault and decrease user's multichain balance - params.createGlvDepositParams.executionFee = _handleRelay( - contracts, - relayParams, - account, - address(glvVault) - ); - - return glvHandler.createGlvDeposit(account, params.createGlvDepositParams); + struct BaseConstructorParams { + Router router; + DataStore dataStore; + EventEmitter eventEmitter; + Oracle oracle; + OrderVault orderVault; + IOrderHandler orderHandler; + IExternalHandler externalHandler; + MultichainVault multichainVault; } - function createGlvWithdrawal( - RelayUtils.RelayParams calldata relayParams, - address account, - RelayUtils.MultichainCreateGlvWithdrawalParams memory params - ) external nonReentrant onlyGelatoRelay returns (bytes32) { - if (params.desChainId != block.chainid) { - revert Errors.InvalidDestinationChainId(); - } - - bytes32 structHash = RelayUtils.getMultichainCreateGlvWithdrawalStructHash(relayParams, params); - _validateCall(relayParams, account, structHash); - - return _createGlvWithdrawal(relayParams, account, params); - } - - function _createGlvWithdrawal( - RelayUtils.RelayParams calldata relayParams, - address account, - RelayUtils.MultichainCreateGlvWithdrawalParams memory params - ) internal returns (bytes32) { - Contracts memory contracts = Contracts({ - dataStore: dataStore, - eventEmitter: eventEmitter, - bank: glvVault - }); - - // transfer GLV tokens from MultichainVault to GlvVault - _sendTokens( - account, - params.createGlvWithdrawalParams.glv, - address(glvVault), // receiver - params.glvTokenAmount, - params.createGlvWithdrawalParams.srcChainId - ); + MultichainVault public immutable multichainVault; - // pay relay fee tokens from MultichainVault to GlvVault and decrease user's multichain balance - params.createGlvWithdrawalParams.executionFee = _handleRelay( - contracts, - relayParams, - account, - address(glvVault) // residualFeeReceiver - ); - - return GlvWithdrawalUtils.createGlvWithdrawal( - dataStore, - eventEmitter, - glvVault, - account, - params.createGlvWithdrawalParams - ); - } - - function createShift( - RelayUtils.RelayParams calldata relayParams, - address account, - RelayUtils.MultichainCreateShiftParams memory params - ) external nonReentrant onlyGelatoRelay returns (bytes32) { - if (params.desChainId != block.chainid) { - revert Errors.InvalidDestinationChainId(); - } - - bytes32 structHash = RelayUtils.getMultichainCreateShiftStructHash(relayParams, params); - _validateCall(relayParams, account, structHash); - - return _createShift(relayParams, account, params); - } - - function _createShift( - RelayUtils.RelayParams calldata relayParams, - address account, - RelayUtils.MultichainCreateShiftParams memory params - ) internal returns (bytes32) { - Contracts memory contracts = Contracts({ - dataStore: dataStore, - eventEmitter: eventEmitter, - bank: shiftVault - }); - - _sendTokens( - account, - params.createShiftParams.fromMarket, - address(shiftVault), // receiver - params.marketTokenAmount, - params.createShiftParams.srcChainId - ); - - params.createShiftParams.executionFee = _handleRelay( - contracts, - relayParams, - account, - address(shiftVault) - ); - - return ShiftUtils.createShift( - dataStore, - eventEmitter, - shiftVault, - account, - params.createShiftParams - ); + constructor( + BaseConstructorParams memory params + ) + GelatoRelayRouter( + params.router, + params.dataStore, + params.eventEmitter, + params.oracle, + params.orderHandler, + params.orderVault, + params.externalHandler + ) + { + multichainVault = params.multichainVault; } function _sendTokens(address account, address token, address receiver, uint256 amount, uint256 srcChainId) internal override { From 8b7a0f20dee2e6bc5dbbf87e620d65a9bf6c961a Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 13 Feb 2025 15:08:17 +0200 Subject: [PATCH 047/205] Add TransferRequest[] param to create* multichain actions and _sendTokens by looping on this array. Refactor all related typehashes and helper functions. --- contracts/multichain/MultichainGlvRouter.sol | 35 ++--- contracts/multichain/MultichainGmRouter.sol | 49 ++----- contracts/multichain/MultichainRouter.sol | 7 + contracts/router/relay/RelayUtils.sol | 133 +++++++++++-------- 4 files changed, 104 insertions(+), 120 deletions(-) diff --git a/contracts/multichain/MultichainGlvRouter.sol b/contracts/multichain/MultichainGlvRouter.sol index 13e6836b0..ddfa090aa 100644 --- a/contracts/multichain/MultichainGlvRouter.sol +++ b/contracts/multichain/MultichainGlvRouter.sol @@ -24,15 +24,18 @@ contract MultichainGlvRouter is MultichainRouter { function createGlvDeposit( RelayUtils.RelayParams calldata relayParams, address account, + RelayUtils.TransferRequest[] calldata transferRequests, RelayUtils.MultichainCreateGlvDepositParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { if (params.desChainId != block.chainid) { revert Errors.InvalidDestinationChainId(); } - bytes32 structHash = RelayUtils.getMultichainCreateGlvDepositStructHash(relayParams, params); + bytes32 structHash = RelayUtils.getMultichainCreateGlvDepositStructHash(relayParams, transferRequests, params); _validateCall(relayParams, account, structHash); + _processTransferRequests(account, transferRequests, params.createGlvDepositParams.srcChainId); + return _createGlvDeposit(relayParams, account, params); } @@ -47,22 +50,6 @@ contract MultichainGlvRouter is MultichainRouter { bank: glvVault }); - // transfer long & short tokens from MultichainVault to GlvVault and decrement user's multichain balance - _sendTokens( - account, - params.createGlvDepositParams.initialLongToken, - address(glvVault), // receiver - params.longTokenAmount, - params.createGlvDepositParams.srcChainId - ); - _sendTokens( - account, - params.createGlvDepositParams.initialShortToken, - address(glvVault), // receiver - params.shortTokenAmount, - params.createGlvDepositParams.srcChainId - ); - // pay relay fee tokens from MultichainVault to GlvVault and decrease user's multichain balance params.createGlvDepositParams.executionFee = _handleRelay( contracts, @@ -77,15 +64,18 @@ contract MultichainGlvRouter is MultichainRouter { function createGlvWithdrawal( RelayUtils.RelayParams calldata relayParams, address account, + RelayUtils.TransferRequest[] calldata transferRequests, RelayUtils.MultichainCreateGlvWithdrawalParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { if (params.desChainId != block.chainid) { revert Errors.InvalidDestinationChainId(); } - bytes32 structHash = RelayUtils.getMultichainCreateGlvWithdrawalStructHash(relayParams, params); + bytes32 structHash = RelayUtils.getMultichainCreateGlvWithdrawalStructHash(relayParams, transferRequests, params); _validateCall(relayParams, account, structHash); + _processTransferRequests(account, transferRequests, params.createGlvWithdrawalParams.srcChainId); + return _createGlvWithdrawal(relayParams, account, params); } @@ -100,15 +90,6 @@ contract MultichainGlvRouter is MultichainRouter { bank: glvVault }); - // transfer GLV tokens from MultichainVault to GlvVault - _sendTokens( - account, - params.createGlvWithdrawalParams.glv, - address(glvVault), // receiver - params.glvTokenAmount, - params.createGlvWithdrawalParams.srcChainId - ); - // pay relay fee tokens from MultichainVault to GlvVault and decrease user's multichain balance params.createGlvWithdrawalParams.executionFee = _handleRelay( contracts, diff --git a/contracts/multichain/MultichainGmRouter.sol b/contracts/multichain/MultichainGmRouter.sol index a23432303..e54c703a1 100644 --- a/contracts/multichain/MultichainGmRouter.sol +++ b/contracts/multichain/MultichainGmRouter.sol @@ -35,15 +35,18 @@ contract MultichainGmRouter is MultichainRouter { function createDeposit( RelayUtils.RelayParams calldata relayParams, address account, + RelayUtils.TransferRequest[] calldata transferRequests, RelayUtils.MultichainCreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { if (params.desChainId != block.chainid) { revert Errors.InvalidDestinationChainId(); } - bytes32 structHash = RelayUtils.getMultichainCreateDepositStructHash(relayParams, params); + bytes32 structHash = RelayUtils.getMultichainCreateDepositStructHash(relayParams, transferRequests, params); _validateCall(relayParams, account, structHash); + _processTransferRequests(account, transferRequests, params.createDepositParams.srcChainId); + return _createDeposit(relayParams, account, params); } @@ -58,22 +61,6 @@ contract MultichainGmRouter is MultichainRouter { bank: depositVault }); - // transfer long & short tokens from MultichainVault to DepositVault and decrement user's multichain balance - _sendTokens( - account, - params.createDepositParams.addresses.initialLongToken, - address(depositVault), // receiver - params.longTokenAmount, - params.createDepositParams.srcChainId - ); - _sendTokens( - account, - params.createDepositParams.addresses.initialShortToken, - address(depositVault), // receiver - params.shortTokenAmount, - params.createDepositParams.srcChainId - ); - // pay relay fee tokens from MultichainVault to DepositVault and decrease user's multichain balance params.createDepositParams.executionFee = _handleRelay( contracts, @@ -88,15 +75,18 @@ contract MultichainGmRouter is MultichainRouter { function createWithdrawal( RelayUtils.RelayParams calldata relayParams, address account, + RelayUtils.TransferRequest[] calldata transferRequests, RelayUtils.MultichainCreateWithdrawalParams memory params // can't use calldata because need to modify params.addresses.receiver & params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { if (params.desChainId != block.chainid) { revert Errors.InvalidDestinationChainId(); } - bytes32 structHash = RelayUtils.getMultichainCreateWithdrawalStructHash(relayParams, params); + bytes32 structHash = RelayUtils.getMultichainCreateWithdrawalStructHash(relayParams, transferRequests, params); _validateCall(relayParams, account, structHash); + _processTransferRequests(account, transferRequests, params.createWithdrawalParams.srcChainId); + return _createWithdrawal(relayParams, account, params); } @@ -111,16 +101,6 @@ contract MultichainGmRouter is MultichainRouter { bank: withdrawalVault }); - // user already bridged the GM tokens to the MultichainVault and balance was increased - // transfer the GM tokens from MultichainVault to WithdrawalVault - _sendTokens( - account, - params.createWithdrawalParams.market, - address(withdrawalVault), // receiver - params.tokenAmount, - params.createWithdrawalParams.srcChainId - ); - params.createWithdrawalParams.executionFee = _handleRelay( contracts, relayParams, @@ -134,15 +114,18 @@ contract MultichainGmRouter is MultichainRouter { function createShift( RelayUtils.RelayParams calldata relayParams, address account, + RelayUtils.TransferRequest[] calldata transferRequests, RelayUtils.MultichainCreateShiftParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { if (params.desChainId != block.chainid) { revert Errors.InvalidDestinationChainId(); } - bytes32 structHash = RelayUtils.getMultichainCreateShiftStructHash(relayParams, params); + bytes32 structHash = RelayUtils.getMultichainCreateShiftStructHash(relayParams, transferRequests, params); _validateCall(relayParams, account, structHash); + _processTransferRequests(account, transferRequests, params.createShiftParams.srcChainId); + return _createShift(relayParams, account, params); } @@ -157,14 +140,6 @@ contract MultichainGmRouter is MultichainRouter { bank: shiftVault }); - _sendTokens( - account, - params.createShiftParams.fromMarket, - address(shiftVault), // receiver - params.marketTokenAmount, - params.createShiftParams.srcChainId - ); - params.createShiftParams.executionFee = _handleRelay( contracts, relayParams, diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 520261bd2..21f97c4fb 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -37,6 +37,13 @@ contract MultichainRouter is GelatoRelayRouter { multichainVault = params.multichainVault; } + function _processTransferRequests(address account, RelayUtils.TransferRequest[] calldata transferRequests, uint256 srcChainId) internal { + for (uint256 i = 0; i < transferRequests.length; i++) { + RelayUtils.TransferRequest calldata transferRequest = transferRequests[i]; + _sendTokens(account, transferRequest.token, transferRequest.receiver, transferRequest.amount, srcChainId); + } + } + function _sendTokens(address account, address token, address receiver, uint256 amount, uint256 srcChainId) internal override { AccountUtils.validateReceiver(receiver); if (srcChainId == 0) { diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index 23eed50d8..eeb949a2d 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -57,35 +57,34 @@ library RelayUtils { bool autoCancel; } + struct TransferRequest { + address token; + address receiver; + uint256 amount; + } + struct MultichainCreateDepositParams { uint256 desChainId; - uint256 longTokenAmount; - uint256 shortTokenAmount; DepositUtils.CreateDepositParams createDepositParams; } struct MultichainCreateWithdrawalParams { uint256 desChainId; - uint256 tokenAmount; WithdrawalUtils.CreateWithdrawalParams createWithdrawalParams; } struct MultichainCreateGlvDepositParams { uint256 desChainId; - uint256 longTokenAmount; - uint256 shortTokenAmount; GlvDepositUtils.CreateGlvDepositParams createGlvDepositParams; } struct MultichainCreateGlvWithdrawalParams { uint256 desChainId; - uint256 glvTokenAmount; GlvWithdrawalUtils.CreateGlvWithdrawalParams createGlvWithdrawalParams; } struct MultichainCreateShiftParams { uint256 desChainId; - uint256 marketTokenAmount; ShiftUtils.CreateShiftParams createShiftParams; } @@ -143,7 +142,7 @@ library RelayUtils { bytes32 public constant MULTICHAIN_CREATE_DEPOSIT_PARAMS_TYPEHASH = keccak256( bytes( - "MultichainCreateDepositParams(uint256 desChainId,uint256 longTokenAmount,uint256 shortTokenAmount,CreateDepositParams params)CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + "MultichainCreateDepositParams(uint256 desChainId,CreateDepositParams params)CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" ) ); bytes32 public constant CREATE_DEPOSIT_PARAMS_ADDRESSES_TYPEHASH = @@ -162,7 +161,7 @@ library RelayUtils { bytes32 public constant MULTICHAIN_CREATE_WITHDRAWAL_PARAMS_TYPEHASH = keccak256( bytes( - "MultichainCreateWithdrawalParams(uint256 desChainId,uint256 tokenAmount,CreateWithdrawalParams params)CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + "MultichainCreateWithdrawalParams(uint256 desChainId,CreateWithdrawalParams params)CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" ) ); bytes32 public constant CREATE_WITHDRAWAL_PARAMS_TYPEHASH = @@ -181,7 +180,7 @@ library RelayUtils { bytes32 public constant MULTICHAIN_CREATE_GLV_DEPOSIT_PARAMS_TYPEHASH = keccak256( bytes( - "MultichainCreateGlvDepositParams(uint256 desChainId,uint256 tokenAmount,CreateGlvDepositParams params)CreateGlvDepositParams(address glv,address market,address receiver,address callbackContract,address uiFeeReceiver,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minGlvTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bool shouldUnwrapNativeToken,bool isMarketTokenDeposit,bytes32[] dataList)" + "MultichainCreateGlvDepositParams(uint256 desChainId,CreateGlvDepositParams params)CreateGlvDepositParams(address glv,address market,address receiver,address callbackContract,address uiFeeReceiver,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minGlvTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bool shouldUnwrapNativeToken,bool isMarketTokenDeposit,bytes32[] dataList)" ) ); bytes32 public constant CREATE_GLV_DEPOSIT_PARAMS_TYPEHASH = @@ -202,7 +201,7 @@ library RelayUtils { bytes32 public constant MULTICHAIN_CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH = keccak256( bytes( - "MultichainCreateGlvWithdrawalParams(uint256 desChainId,uint256 glvTokenAmount,CreateGlvWithdrawalParams params)" + "MultichainCreateGlvWithdrawalParams(uint256 desChainId,CreateGlvWithdrawalParams params)" "CreateGlvWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" ) ); @@ -219,13 +218,16 @@ library RelayUtils { ); bytes32 public constant MULTICHAIN_CREATE_SHIFT_PARAMS_TYPEHASH = keccak256( - "MultichainCreateShiftParams(uint256 desChainId,uint256 marketTokenAmount,CreateShiftParams params)CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + "MultichainCreateShiftParams(uint256 desChainId,CreateShiftParams params)CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" ); bytes32 public constant CREATE_SHIFT_PARAMS_TYPEHASH = keccak256( "CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" ); + bytes32 public constant TRANSFER_REQUEST_TYPEHASH = + keccak256("TransferRequest(address token,address receiver,uint256 amount)"); + //////////////////// ORDER //////////////////// function _getRelayParamsHash(RelayParams calldata relayParams) internal pure returns (bytes32) { @@ -344,17 +346,18 @@ library RelayUtils { function getMultichainCreateDepositStructHash( RelayParams calldata relayParams, + TransferRequest[] calldata transferRequests, MultichainCreateDepositParams memory params ) external view returns (bytes32) { - bytes32 relayParamsHash = _getRelayParamsHash(relayParams); - return - keccak256( - abi.encode( - CREATE_DEPOSIT_TYPEHASH, - _getMultichainCreateDepositParamsStructHash(params), - relayParamsHash - ) - ); + bytes32 relayParamsHash = _getRelayParamsHash(relayParams); + return keccak256( + abi.encode( + CREATE_DEPOSIT_TYPEHASH, + _getMultichainCreateDepositParamsStructHash(params), + _getTransferRequestsHash(transferRequests), + relayParamsHash + ) + ); } function _getMultichainCreateDepositParamsStructHash( @@ -365,8 +368,6 @@ library RelayUtils { abi.encode( MULTICHAIN_CREATE_DEPOSIT_PARAMS_TYPEHASH, block.chainid, - params.longTokenAmount, - params.shortTokenAmount, _getCreateDepositParamsStructHash(params.createDepositParams) ) ); @@ -411,17 +412,18 @@ library RelayUtils { function getMultichainCreateWithdrawalStructHash( RelayParams calldata relayParams, + TransferRequest[] calldata transferRequests, MultichainCreateWithdrawalParams memory params ) external view returns (bytes32) { - bytes32 relayParamsHash = _getRelayParamsHash(relayParams); - return - keccak256( - abi.encode( - CREATE_WITHDRAWAL_TYPEHASH, - _getMultichainCreateWithdrawalParamsStructHash(params), - relayParamsHash - ) - ); + bytes32 relayParamsHash = _getRelayParamsHash(relayParams); + return keccak256( + abi.encode( + CREATE_WITHDRAWAL_TYPEHASH, + _getMultichainCreateWithdrawalParamsStructHash(params), + _getTransferRequestsHash(transferRequests), + relayParamsHash + ) + ); } function _getMultichainCreateWithdrawalParamsStructHash( @@ -432,7 +434,6 @@ library RelayUtils { abi.encode( MULTICHAIN_CREATE_WITHDRAWAL_PARAMS_TYPEHASH, block.chainid, - params.tokenAmount, _getCreateWithdrawalParamsStructHash(params.createWithdrawalParams) ) ); @@ -464,17 +465,18 @@ library RelayUtils { function getMultichainCreateGlvDepositStructHash( RelayParams calldata relayParams, + TransferRequest[] calldata transferRequests, MultichainCreateGlvDepositParams memory params ) external pure returns (bytes32) { - bytes32 relayParamsHash = _getRelayParamsHash(relayParams); - return - keccak256( - abi.encode( - CREATE_GLV_DEPOSIT_TYPEHASH, - _getMultichainCreateGlvDepositParamsStructHash(params), - relayParamsHash - ) - ); + bytes32 relayParamsHash = _getRelayParamsHash(relayParams); + return keccak256( + abi.encode( + CREATE_GLV_DEPOSIT_TYPEHASH, + _getMultichainCreateGlvDepositParamsStructHash(params), + _getTransferRequestsHash(transferRequests), + relayParamsHash + ) + ); } function _getMultichainCreateGlvDepositParamsStructHash( @@ -485,8 +487,6 @@ library RelayUtils { abi.encode( MULTICHAIN_CREATE_GLV_DEPOSIT_PARAMS_TYPEHASH, params.desChainId, - params.longTokenAmount, - params.shortTokenAmount, _getCreateGlvDepositParamsStructHash(params.createGlvDepositParams) ) ); @@ -521,17 +521,18 @@ library RelayUtils { function getMultichainCreateGlvWithdrawalStructHash( RelayParams calldata relayParams, + TransferRequest[] calldata transferRequests, MultichainCreateGlvWithdrawalParams memory params ) external view returns (bytes32) { - bytes32 relayParamsHash = _getRelayParamsHash(relayParams); - return - keccak256( - abi.encode( - CREATE_GLV_WITHDRAWAL_TYPEHASH, - _getMultichainCreateGlvWithdrawalParamsStructHash(params), - relayParamsHash - ) - ); + bytes32 relayParamsHash = _getRelayParamsHash(relayParams); + return keccak256( + abi.encode( + CREATE_GLV_WITHDRAWAL_TYPEHASH, + _getMultichainCreateGlvWithdrawalParamsStructHash(params), + _getTransferRequestsHash(transferRequests), + relayParamsHash + ) + ); } function _getMultichainCreateGlvWithdrawalParamsStructHash( @@ -542,7 +543,6 @@ library RelayUtils { abi.encode( MULTICHAIN_CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH, block.chainid, - params.glvTokenAmount, _getCreateGlvWithdrawalParamsStructHash(params.createGlvWithdrawalParams) ) ); @@ -575,13 +575,15 @@ library RelayUtils { function getMultichainCreateShiftStructHash( RelayParams calldata relayParams, + TransferRequest[] calldata transferRequests, MultichainCreateShiftParams memory params ) external view returns (bytes32) { - bytes32 relayParamsHash = _getRelayParamsHash(relayParams); + bytes32 relayParamsHash = _getRelayParamsHash(relayParams); return keccak256( abi.encode( CREATE_SHIFT_TYPEHASH, _getMultichainCreateShiftParamsStructHash(params), + _getTransferRequestsHash(transferRequests), relayParamsHash ) ); @@ -594,7 +596,6 @@ library RelayUtils { abi.encode( MULTICHAIN_CREATE_SHIFT_PARAMS_TYPEHASH, block.chainid, - params.marketTokenAmount, _getCreateShiftParamsStructHash(params.createShiftParams) ) ); @@ -619,4 +620,24 @@ library RelayUtils { ) ); } + + function _getTransferRequestStructHash(TransferRequest memory request) internal pure returns (bytes32) { + return keccak256( + abi.encode( + TRANSFER_REQUEST_TYPEHASH, + request.token, + request.receiver, + request.amount + ) + ); + } + + // TODO: double-check typehash is correctly generated + function _getTransferRequestsHash(TransferRequest[] calldata requests) internal pure returns (bytes32) { + bytes32[] memory hashes = new bytes32[](requests.length); + for (uint256 i = 0; i < requests.length; i++) { + hashes[i] = _getTransferRequestStructHash(requests[i]); + } + return keccak256(abi.encodePacked(hashes)); + } } From ce8c97121b06c8b62dab934b1e1793bad24150ad Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 13 Feb 2025 16:23:32 +0200 Subject: [PATCH 048/205] Refactor GlvDepositUtils to move the "execute" part into ExecuteGlvDepositUtils to reduce contract size --- contracts/exchange/GlvHandler.sol | 5 +- .../glv/glvDeposit/ExecuteGlvDepositUtils.sol | 236 ++++++++++++++++++ contracts/glv/glvDeposit/GlvDepositUtils.sol | 220 ---------------- deploy/deployExecuteGlvDepositUtils.ts | 19 ++ deploy/deployGlvHandler.ts | 1 + 5 files changed, 259 insertions(+), 222 deletions(-) create mode 100644 contracts/glv/glvDeposit/ExecuteGlvDepositUtils.sol create mode 100644 deploy/deployExecuteGlvDepositUtils.ts diff --git a/contracts/exchange/GlvHandler.sol b/contracts/exchange/GlvHandler.sol index 2b7df5804..0232841b2 100644 --- a/contracts/exchange/GlvHandler.sol +++ b/contracts/exchange/GlvHandler.sol @@ -7,6 +7,7 @@ import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "./BaseHandler.sol"; import "../glv/glvDeposit/GlvDepositUtils.sol"; +import "../glv/glvDeposit/ExecuteGlvDepositUtils.sol"; import "../glv/glvWithdrawal/GlvWithdrawalUtils.sol"; import "../glv/glvShift/GlvShiftUtils.sol"; @@ -72,7 +73,7 @@ contract GlvHandler is BaseHandler, ReentrancyGuard { FeatureUtils.validateFeature(dataStore, Keys.executeGlvDepositFeatureDisabledKey(address(this))); - GlvDepositUtils.ExecuteGlvDepositParams memory params = GlvDepositUtils.ExecuteGlvDepositParams({ + ExecuteGlvDepositUtils.ExecuteGlvDepositParams memory params = ExecuteGlvDepositUtils.ExecuteGlvDepositParams({ key: key, dataStore: dataStore, eventEmitter: eventEmitter, @@ -83,7 +84,7 @@ contract GlvHandler is BaseHandler, ReentrancyGuard { keeper: keeper }); - GlvDepositUtils.executeGlvDeposit(params, glvDeposit); + ExecuteGlvDepositUtils.executeGlvDeposit(params, glvDeposit); } function _handleGlvDepositError(bytes32 key, uint256 startingGas, bytes memory reasonBytes) internal { diff --git a/contracts/glv/glvDeposit/ExecuteGlvDepositUtils.sol b/contracts/glv/glvDeposit/ExecuteGlvDepositUtils.sol new file mode 100644 index 000000000..1e89a2591 --- /dev/null +++ b/contracts/glv/glvDeposit/ExecuteGlvDepositUtils.sol @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; + +import "../../deposit/ExecuteDepositUtils.sol"; + +import "../../multichain/MultichainUtils.sol"; + +import "../../nonce/NonceUtils.sol"; + +import "../GlvVault.sol"; +import "../GlvUtils.sol"; +import "./GlvDepositEventUtils.sol"; +import "./GlvDepositStoreUtils.sol"; +import "./GlvDepositCalc.sol"; + +library ExecuteGlvDepositUtils { + using GlvDeposit for GlvDeposit.Props; + using Deposit for Deposit.Props; + using SafeCast for int256; + using EventUtils for EventUtils.UintItems; + + struct ExecuteGlvDepositParams { + DataStore dataStore; + EventEmitter eventEmitter; + MultichainVault multichainVault; + GlvVault glvVault; + Oracle oracle; + bytes32 key; + uint256 startingGas; + address keeper; + } + + struct ExecuteGlvDepositCache { + Market.Props market; + MarketPoolValueInfo.Props marketPoolValueInfo; + uint256 marketTokenSupply; + uint256 receivedMarketTokens; + uint256 mintAmount; + uint256 marketCount; + uint256 oraclePriceCount; + uint256 glvValue; + uint256 glvSupply; + } + + function executeGlvDeposit( + ExecuteGlvDepositParams memory params, + GlvDeposit.Props memory glvDeposit + ) external returns (uint256) { + // 63/64 gas is forwarded to external calls, reduce the startingGas to account for this + params.startingGas -= gasleft() / 63; + + GlvDepositStoreUtils.remove(params.dataStore, params.key, glvDeposit.account()); + + // should be called before any tokens are minted + GlvDepositCalc.validateFirstGlvDeposit(params.dataStore, glvDeposit); + + ExecuteGlvDepositCache memory cache; + + cache.receivedMarketTokens = _processMarketDeposit(params, glvDeposit, params.glvVault); + + // glvValue should be calculated after funds are deposited into GM market + // but before GLV syncs GM token balance for glvValue to account for + // slightly increased GM market price because of paid fees + cache.glvValue = GlvUtils.getGlvValue( + params.dataStore, + params.oracle, + glvDeposit.glv(), + true // maximize + ); + GlvToken(payable(glvDeposit.glv())).syncTokenBalance(glvDeposit.market()); + + cache.glvSupply = GlvToken(payable(glvDeposit.glv())).totalSupply(); + cache.mintAmount = GlvDepositCalc.getMintAmount( + params.dataStore, + params.oracle, + glvDeposit, + cache.receivedMarketTokens, + cache.glvValue, + cache.glvSupply + ); + if (cache.mintAmount < glvDeposit.minGlvTokens()) { + revert Errors.MinGlvTokens(cache.mintAmount, glvDeposit.minGlvTokens()); + } + + if (glvDeposit.srcChainId() == 0) { + GlvToken(payable(glvDeposit.glv())).mint(glvDeposit.receiver(), cache.mintAmount); + } else { + GlvToken(payable(glvDeposit.glv())).mint(address(params.multichainVault), cache.mintAmount); + MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, glvDeposit.glv(), glvDeposit.receiver(), glvDeposit.srcChainId()); + } + + + cache.market = MarketUtils.getEnabledMarket(params.dataStore, glvDeposit.market()); + cache.marketPoolValueInfo = MarketUtils.getPoolValueInfo( + params.dataStore, + cache.market, + params.oracle.getPrimaryPrice(cache.market.indexToken), + params.oracle.getPrimaryPrice(cache.market.longToken), + params.oracle.getPrimaryPrice(cache.market.shortToken), + Keys.MAX_PNL_FACTOR_FOR_DEPOSITS, + true // maximize + ); + cache.marketTokenSupply = MarketUtils.getMarketTokenSupply(MarketToken(payable(glvDeposit.market()))); + + GlvUtils.validateGlvMarketTokenBalance( + params.dataStore, + glvDeposit.glv(), + cache.market, + cache.marketPoolValueInfo.poolValue.toUint256(), + cache.marketTokenSupply + ); + + GlvDepositEventUtils.emitGlvDepositExecuted( + params.eventEmitter, + params.key, + glvDeposit.account(), + cache.mintAmount + ); + + cache.glvValue = GlvUtils.getGlvValue( + params.dataStore, + params.oracle, + glvDeposit.glv(), + true // maximize + ); + cache.glvSupply = GlvToken(payable(glvDeposit.glv())).totalSupply(); + GlvEventUtils.emitGlvValueUpdated(params.eventEmitter, glvDeposit.glv(), cache.glvValue, cache.glvSupply); + + EventUtils.EventLogData memory eventData; + eventData.uintItems.initItems(1); + eventData.uintItems.setItem(0, "receivedGlvTokens", cache.mintAmount); + CallbackUtils.afterGlvDepositExecution(params.key, glvDeposit, eventData); + + cache.marketCount = GlvUtils.getGlvMarketCount(params.dataStore, glvDeposit.glv()); + cache.oraclePriceCount = GasUtils.estimateGlvDepositOraclePriceCount( + cache.marketCount, + glvDeposit.longTokenSwapPath().length + glvDeposit.shortTokenSwapPath().length + ); + GasUtils.payExecutionFee( + params.dataStore, + params.eventEmitter, + params.glvVault, + params.key, + glvDeposit.callbackContract(), + glvDeposit.executionFee(), + params.startingGas, + cache.oraclePriceCount, + params.keeper, + glvDeposit.srcChainId() == 0 ? glvDeposit.receiver() : address(params.multichainVault) + ); + + // TODO: fix GlvDepositUtils contract size error and uncomment the lines bellow + // // for multichain action, receiver is the multichainVault; increase user's multichain wnt balance for the fee refund + // if (glvDeposit.srcChainId() != 0) { + // address wnt = params.dataStore.getAddress(Keys.WNT); + // MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, wnt, glvDeposit.receiver(), 0); // srcChainId is the current block.chainId + // } + + return cache.mintAmount; + } + + + function _processMarketDeposit( + ExecuteGlvDepositParams memory params, + GlvDeposit.Props memory glvDeposit, + GlvVault glvVault + ) private returns (uint256) { + if (glvDeposit.isMarketTokenDeposit()) { + Market.Props memory market = MarketUtils.getEnabledMarket(params.dataStore, glvDeposit.market()); + + MarketUtils.MarketPrices memory marketPrices = MarketUtils.MarketPrices( + params.oracle.getPrimaryPrice(market.indexToken), + params.oracle.getPrimaryPrice(market.longToken), + params.oracle.getPrimaryPrice(market.shortToken) + ); + MarketUtils.validateMaxPnl( + params.dataStore, + market, + marketPrices, + Keys.MAX_PNL_FACTOR_FOR_WITHDRAWALS, + Keys.MAX_PNL_FACTOR_FOR_WITHDRAWALS + ); + + // user deposited GM tokens + glvVault.transferOut(glvDeposit.market(), glvDeposit.glv(), glvDeposit.marketTokenAmount()); + return glvDeposit.marketTokenAmount(); + } + + Deposit.Props memory deposit = Deposit.Props( + Deposit.Addresses({ + account: glvDeposit.glv(), + receiver: glvDeposit.glv(), + callbackContract: address(0), + uiFeeReceiver: glvDeposit.uiFeeReceiver(), + market: glvDeposit.market(), + initialLongToken: glvDeposit.initialLongToken(), + initialShortToken: glvDeposit.initialShortToken(), + longTokenSwapPath: glvDeposit.longTokenSwapPath(), + shortTokenSwapPath: glvDeposit.shortTokenSwapPath() + }), + Deposit.Numbers({ + initialLongTokenAmount: glvDeposit.initialLongTokenAmount(), + initialShortTokenAmount: glvDeposit.initialShortTokenAmount(), + minMarketTokens: 0, + updatedAtTime: glvDeposit.updatedAtTime(), + executionFee: 0, + callbackGasLimit: 0, + srcChainId: 0 + }), + Deposit.Flags({shouldUnwrapNativeToken: false}), + new bytes32[](0) // dataList + ); + + bytes32 depositKey = NonceUtils.getNextKey(params.dataStore); + params.dataStore.addBytes32(Keys.DEPOSIT_LIST, depositKey); + DepositEventUtils.emitDepositCreated(params.eventEmitter, depositKey, deposit, Deposit.DepositType.Glv); + + ExecuteDepositUtils.ExecuteDepositParams memory executeDepositParams = ExecuteDepositUtils.ExecuteDepositParams( + params.dataStore, + params.eventEmitter, + params.multichainVault, + DepositVault(payable(params.glvVault)), + params.oracle, + depositKey, + params.keeper, + params.startingGas, + ISwapPricingUtils.SwapPricingType.Deposit, + true, // includeVirtualInventoryImpact + glvDeposit.srcChainId() + ); + + uint256 receivedMarketTokens = ExecuteDepositUtils.executeDeposit(executeDepositParams, deposit); + return receivedMarketTokens; + } +} diff --git a/contracts/glv/glvDeposit/GlvDepositUtils.sol b/contracts/glv/glvDeposit/GlvDepositUtils.sol index 2d002f6f7..7abd66270 100644 --- a/contracts/glv/glvDeposit/GlvDepositUtils.sol +++ b/contracts/glv/glvDeposit/GlvDepositUtils.sol @@ -4,22 +4,15 @@ pragma solidity ^0.8.0; import "../../deposit/ExecuteDepositUtils.sol"; -import "../../multichain/MultichainUtils.sol"; - import "../../nonce/NonceUtils.sol"; import "../GlvVault.sol"; import "../GlvUtils.sol"; import "./GlvDepositEventUtils.sol"; import "./GlvDepositStoreUtils.sol"; -import "./GlvDepositCalc.sol"; library GlvDepositUtils { using GlvDeposit for GlvDeposit.Props; - using Deposit for Deposit.Props; - using SafeCast for int256; - using SafeCast for uint256; - using EventUtils for EventUtils.UintItems; struct CreateGlvDepositParams { address glv; @@ -46,29 +39,6 @@ library GlvDepositUtils { uint256 initialShortTokenAmount; } - struct ExecuteGlvDepositParams { - DataStore dataStore; - EventEmitter eventEmitter; - MultichainVault multichainVault; - GlvVault glvVault; - Oracle oracle; - bytes32 key; - uint256 startingGas; - address keeper; - } - - struct ExecuteGlvDepositCache { - Market.Props market; - MarketPoolValueInfo.Props marketPoolValueInfo; - uint256 marketTokenSupply; - uint256 receivedMarketTokens; - uint256 mintAmount; - uint256 marketCount; - uint256 oraclePriceCount; - uint256 glvValue; - uint256 glvSupply; - } - struct CancelGlvDepositParams { DataStore dataStore; EventEmitter eventEmitter; @@ -210,196 +180,6 @@ library GlvDepositUtils { return key; } - function executeGlvDeposit( - ExecuteGlvDepositParams memory params, - GlvDeposit.Props memory glvDeposit - ) external returns (uint256) { - // 63/64 gas is forwarded to external calls, reduce the startingGas to account for this - params.startingGas -= gasleft() / 63; - - GlvDepositStoreUtils.remove(params.dataStore, params.key, glvDeposit.account()); - - // should be called before any tokens are minted - GlvDepositCalc.validateFirstGlvDeposit(params.dataStore, glvDeposit); - - ExecuteGlvDepositCache memory cache; - - cache.receivedMarketTokens = _processMarketDeposit(params, glvDeposit, params.glvVault); - - // glvValue should be calculated after funds are deposited into GM market - // but before GLV syncs GM token balance for glvValue to account for - // slightly increased GM market price because of paid fees - cache.glvValue = GlvUtils.getGlvValue( - params.dataStore, - params.oracle, - glvDeposit.glv(), - true // maximize - ); - GlvToken(payable(glvDeposit.glv())).syncTokenBalance(glvDeposit.market()); - - cache.glvSupply = GlvToken(payable(glvDeposit.glv())).totalSupply(); - cache.mintAmount = GlvDepositCalc.getMintAmount( - params.dataStore, - params.oracle, - glvDeposit, - cache.receivedMarketTokens, - cache.glvValue, - cache.glvSupply - ); - if (cache.mintAmount < glvDeposit.minGlvTokens()) { - revert Errors.MinGlvTokens(cache.mintAmount, glvDeposit.minGlvTokens()); - } - - if (glvDeposit.srcChainId() == 0) { - GlvToken(payable(glvDeposit.glv())).mint(glvDeposit.receiver(), cache.mintAmount); - } else { - GlvToken(payable(glvDeposit.glv())).mint(address(params.multichainVault), cache.mintAmount); - MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, glvDeposit.glv(), glvDeposit.receiver(), glvDeposit.srcChainId()); - } - - - cache.market = MarketUtils.getEnabledMarket(params.dataStore, glvDeposit.market()); - cache.marketPoolValueInfo = MarketUtils.getPoolValueInfo( - params.dataStore, - cache.market, - params.oracle.getPrimaryPrice(cache.market.indexToken), - params.oracle.getPrimaryPrice(cache.market.longToken), - params.oracle.getPrimaryPrice(cache.market.shortToken), - Keys.MAX_PNL_FACTOR_FOR_DEPOSITS, - true // maximize - ); - cache.marketTokenSupply = MarketUtils.getMarketTokenSupply(MarketToken(payable(glvDeposit.market()))); - - GlvUtils.validateGlvMarketTokenBalance( - params.dataStore, - glvDeposit.glv(), - cache.market, - cache.marketPoolValueInfo.poolValue.toUint256(), - cache.marketTokenSupply - ); - - GlvDepositEventUtils.emitGlvDepositExecuted( - params.eventEmitter, - params.key, - glvDeposit.account(), - cache.mintAmount - ); - - cache.glvValue = GlvUtils.getGlvValue( - params.dataStore, - params.oracle, - glvDeposit.glv(), - true // maximize - ); - cache.glvSupply = GlvToken(payable(glvDeposit.glv())).totalSupply(); - GlvEventUtils.emitGlvValueUpdated(params.eventEmitter, glvDeposit.glv(), cache.glvValue, cache.glvSupply); - - EventUtils.EventLogData memory eventData; - eventData.uintItems.initItems(1); - eventData.uintItems.setItem(0, "receivedGlvTokens", cache.mintAmount); - CallbackUtils.afterGlvDepositExecution(params.key, glvDeposit, eventData); - - cache.marketCount = GlvUtils.getGlvMarketCount(params.dataStore, glvDeposit.glv()); - cache.oraclePriceCount = GasUtils.estimateGlvDepositOraclePriceCount( - cache.marketCount, - glvDeposit.longTokenSwapPath().length + glvDeposit.shortTokenSwapPath().length - ); - GasUtils.payExecutionFee( - params.dataStore, - params.eventEmitter, - params.glvVault, - params.key, - glvDeposit.callbackContract(), - glvDeposit.executionFee(), - params.startingGas, - cache.oraclePriceCount, - params.keeper, - glvDeposit.srcChainId() == 0 ? glvDeposit.receiver() : address(params.multichainVault) - ); - - // for multichain action, receiver is the multichainVault; increase user's multichain wnt balance for the fee refund - if (glvDeposit.srcChainId() != 0) { - address wnt = params.dataStore.getAddress(Keys.WNT); - MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, wnt, glvDeposit.receiver(), 0); // srcChainId is the current block.chainId - } - - return cache.mintAmount; - } - - - function _processMarketDeposit( - ExecuteGlvDepositParams memory params, - GlvDeposit.Props memory glvDeposit, - GlvVault glvVault - ) private returns (uint256) { - if (glvDeposit.isMarketTokenDeposit()) { - Market.Props memory market = MarketUtils.getEnabledMarket(params.dataStore, glvDeposit.market()); - - MarketUtils.MarketPrices memory marketPrices = MarketUtils.MarketPrices( - params.oracle.getPrimaryPrice(market.indexToken), - params.oracle.getPrimaryPrice(market.longToken), - params.oracle.getPrimaryPrice(market.shortToken) - ); - MarketUtils.validateMaxPnl( - params.dataStore, - market, - marketPrices, - Keys.MAX_PNL_FACTOR_FOR_WITHDRAWALS, - Keys.MAX_PNL_FACTOR_FOR_WITHDRAWALS - ); - - // user deposited GM tokens - glvVault.transferOut(glvDeposit.market(), glvDeposit.glv(), glvDeposit.marketTokenAmount()); - return glvDeposit.marketTokenAmount(); - } - - Deposit.Props memory deposit = Deposit.Props( - Deposit.Addresses({ - account: glvDeposit.glv(), - receiver: glvDeposit.glv(), - callbackContract: address(0), - uiFeeReceiver: glvDeposit.uiFeeReceiver(), - market: glvDeposit.market(), - initialLongToken: glvDeposit.initialLongToken(), - initialShortToken: glvDeposit.initialShortToken(), - longTokenSwapPath: glvDeposit.longTokenSwapPath(), - shortTokenSwapPath: glvDeposit.shortTokenSwapPath() - }), - Deposit.Numbers({ - initialLongTokenAmount: glvDeposit.initialLongTokenAmount(), - initialShortTokenAmount: glvDeposit.initialShortTokenAmount(), - minMarketTokens: 0, - updatedAtTime: glvDeposit.updatedAtTime(), - executionFee: 0, - callbackGasLimit: 0, - srcChainId: 0 - }), - Deposit.Flags({shouldUnwrapNativeToken: false}), - new bytes32[](0) // dataList - ); - - bytes32 depositKey = NonceUtils.getNextKey(params.dataStore); - params.dataStore.addBytes32(Keys.DEPOSIT_LIST, depositKey); - DepositEventUtils.emitDepositCreated(params.eventEmitter, depositKey, deposit, Deposit.DepositType.Glv); - - ExecuteDepositUtils.ExecuteDepositParams memory executeDepositParams = ExecuteDepositUtils.ExecuteDepositParams( - params.dataStore, - params.eventEmitter, - params.multichainVault, - DepositVault(payable(params.glvVault)), - params.oracle, - depositKey, - params.keeper, - params.startingGas, - ISwapPricingUtils.SwapPricingType.Deposit, - true, // includeVirtualInventoryImpact - glvDeposit.srcChainId() - ); - - uint256 receivedMarketTokens = ExecuteDepositUtils.executeDeposit(executeDepositParams, deposit); - return receivedMarketTokens; - } - function cancelGlvDeposit(CancelGlvDepositParams memory params) external { // 63/64 gas is forwarded to external calls, reduce the startingGas to account for this params.startingGas -= gasleft() / 63; diff --git a/deploy/deployExecuteGlvDepositUtils.ts b/deploy/deployExecuteGlvDepositUtils.ts new file mode 100644 index 000000000..7b141840a --- /dev/null +++ b/deploy/deployExecuteGlvDepositUtils.ts @@ -0,0 +1,19 @@ +import { createDeployFunction } from "../utils/deploy"; + +const func = createDeployFunction({ + contractName: "ExecuteGlvDepositUtils", + libraryNames: [ + "MarketUtils", + "GlvUtils", + "DepositEventUtils", + "ExecuteDepositUtils", + "GasUtils", + "GlvDepositEventUtils", + "GlvDepositStoreUtils", + "GlvDepositCalc", + "MarketStoreUtils", + "MultichainUtils", + ], +}); + +export default func; diff --git a/deploy/deployGlvHandler.ts b/deploy/deployGlvHandler.ts index 98eac18ad..db9e93e86 100644 --- a/deploy/deployGlvHandler.ts +++ b/deploy/deployGlvHandler.ts @@ -20,6 +20,7 @@ const func = createDeployFunction({ libraryNames: [ "GlvDepositStoreUtils", "GlvDepositUtils", + "ExecuteGlvDepositUtils", "GlvShiftStoreUtils", "GlvShiftUtils", "GlvUtils", From 01738f2f2acb1427f9adc3a08d73c120ff0ac56f Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 13 Feb 2025 16:27:01 +0200 Subject: [PATCH 049/205] Uncomment relay fee refund logic after solving the contract size error --- contracts/glv/glvDeposit/ExecuteGlvDepositUtils.sol | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/contracts/glv/glvDeposit/ExecuteGlvDepositUtils.sol b/contracts/glv/glvDeposit/ExecuteGlvDepositUtils.sol index 1e89a2591..656695b4f 100644 --- a/contracts/glv/glvDeposit/ExecuteGlvDepositUtils.sol +++ b/contracts/glv/glvDeposit/ExecuteGlvDepositUtils.sol @@ -150,12 +150,11 @@ library ExecuteGlvDepositUtils { glvDeposit.srcChainId() == 0 ? glvDeposit.receiver() : address(params.multichainVault) ); - // TODO: fix GlvDepositUtils contract size error and uncomment the lines bellow - // // for multichain action, receiver is the multichainVault; increase user's multichain wnt balance for the fee refund - // if (glvDeposit.srcChainId() != 0) { - // address wnt = params.dataStore.getAddress(Keys.WNT); - // MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, wnt, glvDeposit.receiver(), 0); // srcChainId is the current block.chainId - // } + // for multichain action, receiver is the multichainVault; increase user's multichain wnt balance for the fee refund + if (glvDeposit.srcChainId() != 0) { + address wnt = params.dataStore.getAddress(Keys.WNT); + MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, wnt, glvDeposit.receiver(), 0); // srcChainId is the current block.chainId + } return cache.mintAmount; } From 78f619d2b482b0fe8aa68a58d3faa48a679e4541 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 13 Feb 2025 22:14:49 +0200 Subject: [PATCH 050/205] Refactor to add addresses subgroup to CreateGlvDepositParams to fix typehash slot too deep error --- contracts/glv/glvDeposit/GlvDepositUtils.sol | 84 +++---- contracts/router/relay/RelayUtils.sol | 219 ++++++++++--------- utils/glv/glvDeposit.ts | 20 +- 3 files changed, 173 insertions(+), 150 deletions(-) diff --git a/contracts/glv/glvDeposit/GlvDepositUtils.sol b/contracts/glv/glvDeposit/GlvDepositUtils.sol index 7abd66270..0d002bb24 100644 --- a/contracts/glv/glvDeposit/GlvDepositUtils.sol +++ b/contracts/glv/glvDeposit/GlvDepositUtils.sol @@ -15,6 +15,17 @@ library GlvDepositUtils { using GlvDeposit for GlvDeposit.Props; struct CreateGlvDepositParams { + CreateGlvDepositParamsAddresses addresses; + uint256 minGlvTokens; + uint256 executionFee; + uint256 callbackGasLimit; + uint256 srcChainId; + bool shouldUnwrapNativeToken; + bool isMarketTokenDeposit; + bytes32[] dataList; + } + + struct CreateGlvDepositParamsAddresses { address glv; address market; address receiver; @@ -24,13 +35,6 @@ library GlvDepositUtils { address initialShortToken; address[] longTokenSwapPath; address[] shortTokenSwapPath; - uint256 minGlvTokens; - uint256 executionFee; - uint256 callbackGasLimit; - uint256 srcChainId; - bool shouldUnwrapNativeToken; - bool isMarketTokenDeposit; - bytes32[] dataList; } struct CreateGlvDepositCache { @@ -60,58 +64,58 @@ library GlvDepositUtils { CreateGlvDepositParams memory params ) external returns (bytes32) { AccountUtils.validateAccount(account); - GlvUtils.validateGlv(dataStore, params.glv); - GlvUtils.validateGlvMarket(dataStore, params.glv, params.market, true); + GlvUtils.validateGlv(dataStore, params.addresses.glv); + GlvUtils.validateGlvMarket(dataStore, params.addresses.glv, params.addresses.market, true); - MarketUtils.validateEnabledMarket(dataStore, params.market); + MarketUtils.validateEnabledMarket(dataStore, params.addresses.market); CreateGlvDepositCache memory cache; if (params.isMarketTokenDeposit) { // user deposited GM tokens - if (params.initialLongToken != address(0)) { - revert Errors.InvalidGlvDepositInitialLongToken(params.initialLongToken); + if (params.addresses.initialLongToken != address(0)) { + revert Errors.InvalidGlvDepositInitialLongToken(params.addresses.initialLongToken); } - if (params.initialShortToken != address(0)) { - revert Errors.InvalidGlvDepositInitialShortToken(params.initialShortToken); + if (params.addresses.initialShortToken != address(0)) { + revert Errors.InvalidGlvDepositInitialShortToken(params.addresses.initialShortToken); } - if (params.longTokenSwapPath.length > 0 || params.shortTokenSwapPath.length > 0) { + if (params.addresses.longTokenSwapPath.length > 0 || params.addresses.shortTokenSwapPath.length > 0) { revert Errors.InvalidGlvDepositSwapPath( - params.longTokenSwapPath.length, - params.shortTokenSwapPath.length + params.addresses.longTokenSwapPath.length, + params.addresses.shortTokenSwapPath.length ); } - cache.marketTokenAmount = glvVault.recordTransferIn(params.market); + cache.marketTokenAmount = glvVault.recordTransferIn(params.addresses.market); if (cache.marketTokenAmount == 0) { revert Errors.EmptyGlvMarketAmount(); } } else { - MarketUtils.validateSwapPath(dataStore, params.longTokenSwapPath); - MarketUtils.validateSwapPath(dataStore, params.shortTokenSwapPath); + MarketUtils.validateSwapPath(dataStore, params.addresses.longTokenSwapPath); + MarketUtils.validateSwapPath(dataStore, params.addresses.shortTokenSwapPath); - if (params.initialLongToken == address(0)) { - revert Errors.InvalidGlvDepositInitialLongToken(params.initialLongToken); + if (params.addresses.initialLongToken == address(0)) { + revert Errors.InvalidGlvDepositInitialLongToken(params.addresses.initialLongToken); } - if (params.initialShortToken == address(0)) { - revert Errors.InvalidGlvDepositInitialShortToken(params.initialShortToken); + if (params.addresses.initialShortToken == address(0)) { + revert Errors.InvalidGlvDepositInitialShortToken(params.addresses.initialShortToken); } // if the initialLongToken and initialShortToken are the same, only the initialLongTokenAmount would // be non-zero, the initialShortTokenAmount would be zero - cache.initialLongTokenAmount = glvVault.recordTransferIn(params.initialLongToken); - if (params.initialShortToken != params.initialLongToken) { - cache.initialShortTokenAmount = glvVault.recordTransferIn(params.initialShortToken); + cache.initialLongTokenAmount = glvVault.recordTransferIn(params.addresses.initialLongToken); + if (params.addresses.initialShortToken != params.addresses.initialLongToken) { + cache.initialShortTokenAmount = glvVault.recordTransferIn(params.addresses.initialShortToken); } } address wnt = TokenUtils.wnt(dataStore); - if (params.initialLongToken == wnt) { + if (params.addresses.initialLongToken == wnt) { if (cache.initialLongTokenAmount < params.executionFee) { revert Errors.InsufficientWntAmountForExecutionFee(cache.initialLongTokenAmount, params.executionFee); } cache.initialLongTokenAmount -= params.executionFee; - } else if (params.initialShortToken == wnt) { + } else if (params.addresses.initialShortToken == wnt) { if (cache.initialShortTokenAmount < params.executionFee) { revert Errors.InsufficientWntAmountForExecutionFee(cache.initialShortTokenAmount, params.executionFee); } @@ -129,20 +133,20 @@ library GlvDepositUtils { revert Errors.EmptyGlvDepositAmounts(); } - AccountUtils.validateReceiver(params.receiver); + AccountUtils.validateReceiver(params.addresses.receiver); GlvDeposit.Props memory glvDeposit = GlvDeposit.Props( GlvDeposit.Addresses({ account: account, - glv: params.glv, - receiver: params.receiver, - callbackContract: params.callbackContract, - uiFeeReceiver: params.uiFeeReceiver, - market: params.market, - initialLongToken: params.initialLongToken, - initialShortToken: params.initialShortToken, - longTokenSwapPath: params.longTokenSwapPath, - shortTokenSwapPath: params.shortTokenSwapPath + glv: params.addresses.glv, + receiver: params.addresses.receiver, + callbackContract: params.addresses.callbackContract, + uiFeeReceiver: params.addresses.uiFeeReceiver, + market: params.addresses.market, + initialLongToken: params.addresses.initialLongToken, + initialShortToken: params.addresses.initialShortToken, + longTokenSwapPath: params.addresses.longTokenSwapPath, + shortTokenSwapPath: params.addresses.shortTokenSwapPath }), GlvDeposit.Numbers({ marketTokenAmount: cache.marketTokenAmount, @@ -167,7 +171,7 @@ library GlvDepositUtils { uint256 estimatedGasLimit = GasUtils.estimateExecuteGlvDepositGasLimit(dataStore, glvDeposit, marketCount); uint256 oraclePriceCount = GasUtils.estimateGlvDepositOraclePriceCount( marketCount, - params.longTokenSwapPath.length + params.shortTokenSwapPath.length + params.addresses.longTokenSwapPath.length + params.addresses.shortTokenSwapPath.length ); GasUtils.validateExecutionFee(dataStore, estimatedGasLimit, params.executionFee, oraclePriceCount); diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index eeb949a2d..8f73c6834 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -103,8 +103,7 @@ library RelayUtils { ) ); - bytes32 public constant CANCEL_ORDER_TYPEHASH = - keccak256(bytes("CancelOrder(bytes32 key,bytes32 relayParams)")); + bytes32 public constant CANCEL_ORDER_TYPEHASH = keccak256(bytes("CancelOrder(bytes32 key,bytes32 relayParams)")); bytes32 public constant CREATE_ORDER_TYPEHASH = keccak256( @@ -183,10 +182,16 @@ library RelayUtils { "MultichainCreateGlvDepositParams(uint256 desChainId,CreateGlvDepositParams params)CreateGlvDepositParams(address glv,address market,address receiver,address callbackContract,address uiFeeReceiver,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minGlvTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bool shouldUnwrapNativeToken,bool isMarketTokenDeposit,bytes32[] dataList)" ) ); + bytes32 public constant CREATE_GLV_DEPOSIT_PARAMS_ADDRESSES_TYPEHASH = + keccak256( + bytes( + "CreateGlvDepositParamsAddresses(address glv,address market,address receiver,address callbackContract,address uiFeeReceiver,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath)" + ) + ); bytes32 public constant CREATE_GLV_DEPOSIT_PARAMS_TYPEHASH = keccak256( bytes( - "CreateGlvDepositParams(address glv,address market,address receiver,address callbackContract,address uiFeeReceiver,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minGlvTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bool shouldUnwrapNativeToken,bool isMarketTokenDeposit,bytes32[] dataList)" + "CreateGlvDepositParams(CreateGlvDepositParamsAddresses addresses,uint256 minGlvTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bool shouldUnwrapNativeToken,bool isMarketTokenDeposit,bytes32[] dataList)CreateGlvDepositParamsAddresses(address glv,address market,address receiver,address callbackContract,address uiFeeReceiver,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath)" ) ); @@ -197,7 +202,6 @@ library RelayUtils { "CreateGlvWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" ) ); - bytes32 public constant MULTICHAIN_CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH = keccak256( bytes( @@ -205,7 +209,6 @@ library RelayUtils { "CreateGlvWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" ) ); - bytes32 public constant CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH = keccak256( bytes( @@ -213,20 +216,27 @@ library RelayUtils { ) ); - bytes32 public constant CREATE_SHIFT_TYPEHASH = keccak256( - "CreateShift(CreateShiftParams params,bytes32 relayParams)CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" - ); - - bytes32 public constant MULTICHAIN_CREATE_SHIFT_PARAMS_TYPEHASH = keccak256( - "MultichainCreateShiftParams(uint256 desChainId,CreateShiftParams params)CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" - ); - - bytes32 public constant CREATE_SHIFT_PARAMS_TYPEHASH = keccak256( - "CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" - ); + bytes32 public constant CREATE_SHIFT_TYPEHASH = + keccak256( + bytes( + "CreateShift(CreateShiftParams params,bytes32 relayParams)CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ) + ); + bytes32 public constant MULTICHAIN_CREATE_SHIFT_PARAMS_TYPEHASH = + keccak256( + bytes( + "MultichainCreateShiftParams(uint256 desChainId,CreateShiftParams params)CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ) + ); + bytes32 public constant CREATE_SHIFT_PARAMS_TYPEHASH = + keccak256( + bytes( + "CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ) + ); bytes32 public constant TRANSFER_REQUEST_TYPEHASH = - keccak256("TransferRequest(address token,address receiver,uint256 amount)"); + keccak256(bytes("TransferRequest(address token,address receiver,uint256 amount)")); //////////////////// ORDER //////////////////// @@ -349,15 +359,15 @@ library RelayUtils { TransferRequest[] calldata transferRequests, MultichainCreateDepositParams memory params ) external view returns (bytes32) { - bytes32 relayParamsHash = _getRelayParamsHash(relayParams); - return keccak256( - abi.encode( - CREATE_DEPOSIT_TYPEHASH, - _getMultichainCreateDepositParamsStructHash(params), - _getTransferRequestsHash(transferRequests), - relayParamsHash - ) - ); + return + keccak256( + abi.encode( + CREATE_DEPOSIT_TYPEHASH, + _getMultichainCreateDepositParamsStructHash(params), + _getTransferRequestsHash(transferRequests), + _getRelayParamsHash(relayParams) + ) + ); } function _getMultichainCreateDepositParamsStructHash( @@ -415,15 +425,15 @@ library RelayUtils { TransferRequest[] calldata transferRequests, MultichainCreateWithdrawalParams memory params ) external view returns (bytes32) { - bytes32 relayParamsHash = _getRelayParamsHash(relayParams); - return keccak256( - abi.encode( - CREATE_WITHDRAWAL_TYPEHASH, - _getMultichainCreateWithdrawalParamsStructHash(params), - _getTransferRequestsHash(transferRequests), - relayParamsHash - ) - ); + return + keccak256( + abi.encode( + CREATE_WITHDRAWAL_TYPEHASH, + _getMultichainCreateWithdrawalParamsStructHash(params), + _getTransferRequestsHash(transferRequests), + _getRelayParamsHash(relayParams) + ) + ); } function _getMultichainCreateWithdrawalParamsStructHash( @@ -468,15 +478,15 @@ library RelayUtils { TransferRequest[] calldata transferRequests, MultichainCreateGlvDepositParams memory params ) external pure returns (bytes32) { - bytes32 relayParamsHash = _getRelayParamsHash(relayParams); - return keccak256( - abi.encode( - CREATE_GLV_DEPOSIT_TYPEHASH, - _getMultichainCreateGlvDepositParamsStructHash(params), - _getTransferRequestsHash(transferRequests), - relayParamsHash - ) - ); + return + keccak256( + abi.encode( + CREATE_GLV_DEPOSIT_TYPEHASH, + _getMultichainCreateGlvDepositParamsStructHash(params), + _getTransferRequestsHash(transferRequests), + _getRelayParamsHash(relayParams) + ) + ); } function _getMultichainCreateGlvDepositParamsStructHash( @@ -499,15 +509,7 @@ library RelayUtils { keccak256( abi.encode( CREATE_GLV_DEPOSIT_PARAMS_TYPEHASH, - params.glv, - params.market, - params.receiver, - params.callbackContract, - params.uiFeeReceiver, - // params.initialLongToken, - // params.initialShortToken, - // keccak256(abi.encodePacked(params.longTokenSwapPath)), - // keccak256(abi.encodePacked(params.shortTokenSwapPath)), // TODO: split CreateGlvDepositParams into groups to fix slot too deep error + _getCreateGlvDepositParamsAddressesStructHash(params.addresses), params.minGlvTokens, params.executionFee, params.callbackGasLimit, @@ -519,20 +521,40 @@ library RelayUtils { ); } + function _getCreateGlvDepositParamsAddressesStructHash( + GlvDepositUtils.CreateGlvDepositParamsAddresses memory addresses + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + CREATE_GLV_DEPOSIT_PARAMS_ADDRESSES_TYPEHASH, + addresses.glv, + addresses.market, + addresses.receiver, + addresses.callbackContract, + addresses.uiFeeReceiver, + addresses.initialLongToken, + addresses.initialShortToken, + keccak256(abi.encodePacked(addresses.longTokenSwapPath)), + keccak256(abi.encodePacked(addresses.shortTokenSwapPath)) + ) + ); + } + function getMultichainCreateGlvWithdrawalStructHash( RelayParams calldata relayParams, TransferRequest[] calldata transferRequests, MultichainCreateGlvWithdrawalParams memory params ) external view returns (bytes32) { - bytes32 relayParamsHash = _getRelayParamsHash(relayParams); - return keccak256( - abi.encode( - CREATE_GLV_WITHDRAWAL_TYPEHASH, - _getMultichainCreateGlvWithdrawalParamsStructHash(params), - _getTransferRequestsHash(transferRequests), - relayParamsHash - ) - ); + return + keccak256( + abi.encode( + CREATE_GLV_WITHDRAWAL_TYPEHASH, + _getMultichainCreateGlvWithdrawalParamsStructHash(params), + _getTransferRequestsHash(transferRequests), + _getRelayParamsHash(relayParams) + ) + ); } function _getMultichainCreateGlvWithdrawalParamsStructHash( @@ -578,58 +600,53 @@ library RelayUtils { TransferRequest[] calldata transferRequests, MultichainCreateShiftParams memory params ) external view returns (bytes32) { - bytes32 relayParamsHash = _getRelayParamsHash(relayParams); - return keccak256( - abi.encode( - CREATE_SHIFT_TYPEHASH, - _getMultichainCreateShiftParamsStructHash(params), - _getTransferRequestsHash(transferRequests), - relayParamsHash - ) - ); + return + keccak256( + abi.encode( + CREATE_SHIFT_TYPEHASH, + _getMultichainCreateShiftParamsStructHash(params), + _getTransferRequestsHash(transferRequests), + _getRelayParamsHash(relayParams) + ) + ); } function _getMultichainCreateShiftParamsStructHash( MultichainCreateShiftParams memory params ) internal view returns (bytes32) { - return keccak256( - abi.encode( - MULTICHAIN_CREATE_SHIFT_PARAMS_TYPEHASH, - block.chainid, - _getCreateShiftParamsStructHash(params.createShiftParams) - ) - ); + return + keccak256( + abi.encode( + MULTICHAIN_CREATE_SHIFT_PARAMS_TYPEHASH, + block.chainid, + _getCreateShiftParamsStructHash(params.createShiftParams) + ) + ); } function _getCreateShiftParamsStructHash( ShiftUtils.CreateShiftParams memory params ) internal pure returns (bytes32) { - return keccak256( - abi.encode( - CREATE_SHIFT_PARAMS_TYPEHASH, - params.receiver, - params.callbackContract, - params.uiFeeReceiver, - params.fromMarket, - params.toMarket, - params.minMarketTokens, - params.executionFee, - params.callbackGasLimit, - params.srcChainId, - keccak256(abi.encodePacked(params.dataList)) - ) - ); + return + keccak256( + abi.encode( + CREATE_SHIFT_PARAMS_TYPEHASH, + params.receiver, + params.callbackContract, + params.uiFeeReceiver, + params.fromMarket, + params.toMarket, + params.minMarketTokens, + params.executionFee, + params.callbackGasLimit, + params.srcChainId, + keccak256(abi.encodePacked(params.dataList)) + ) + ); } function _getTransferRequestStructHash(TransferRequest memory request) internal pure returns (bytes32) { - return keccak256( - abi.encode( - TRANSFER_REQUEST_TYPEHASH, - request.token, - request.receiver, - request.amount - ) - ); + return keccak256(abi.encode(TRANSFER_REQUEST_TYPEHASH, request.token, request.receiver, request.amount)); } // TODO: double-check typehash is correctly generated diff --git a/utils/glv/glvDeposit.ts b/utils/glv/glvDeposit.ts index d09862755..84e75695d 100644 --- a/utils/glv/glvDeposit.ts +++ b/utils/glv/glvDeposit.ts @@ -82,15 +82,17 @@ export async function createGlvDeposit(fixture, overrides: any = {}) { } const params = { - glv, - receiver: receiver.address, - callbackContract: callbackContract.address, - uiFeeReceiver: uiFeeReceiver.address, - market: market.marketToken, - initialLongToken, - initialShortToken, - longTokenSwapPath, - shortTokenSwapPath, + addresses: { + glv, + receiver: receiver.address, + callbackContract: callbackContract.address, + uiFeeReceiver: uiFeeReceiver.address, + market: market.marketToken, + initialLongToken, + initialShortToken, + longTokenSwapPath, + shortTokenSwapPath, + }, marketTokenAmount, minGlvTokens, shouldUnwrapNativeToken, From 9170014e5d775cd872889342f8a685d50db5cb68 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 13 Feb 2025 22:34:39 +0200 Subject: [PATCH 051/205] Refactor to add addresses subgroup to CreateGlvWithdrawalParams to fix typehash slot too deep error --- .../glv/glvWithdrawal/GlvWithdrawalUtils.sol | 48 ++++++++++--------- contracts/router/relay/RelayUtils.sol | 42 +++++++++++----- utils/glv/glvWithdrawal.ts | 16 ++++--- 3 files changed, 64 insertions(+), 42 deletions(-) diff --git a/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol b/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol index bb7d651f8..e38fb665a 100644 --- a/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol +++ b/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol @@ -19,13 +19,7 @@ library GlvWithdrawalUtils { using EventUtils for EventUtils.AddressItems; struct CreateGlvWithdrawalParams { - address receiver; - address callbackContract; - address uiFeeReceiver; - address market; - address glv; - address[] longTokenSwapPath; - address[] shortTokenSwapPath; + CreateGlvWithdrawalParamsAddresses addresses; uint256 minLongTokenAmount; uint256 minShortTokenAmount; bool shouldUnwrapNativeToken; @@ -35,6 +29,16 @@ library GlvWithdrawalUtils { bytes32[] dataList; } + struct CreateGlvWithdrawalParamsAddresses { + address receiver; + address callbackContract; + address uiFeeReceiver; + address market; + address glv; + address[] longTokenSwapPath; + address[] shortTokenSwapPath; + } + struct ExecuteGlvWithdrawalParams { DataStore dataStore; EventEmitter eventEmitter; @@ -72,12 +76,12 @@ library GlvWithdrawalUtils { CreateGlvWithdrawalParams memory params ) external returns (bytes32) { AccountUtils.validateAccount(account); - GlvUtils.validateGlv(dataStore, params.glv); - GlvUtils.validateGlvMarket(dataStore, params.glv, params.market, false); + GlvUtils.validateGlv(dataStore, params.addresses.glv); + GlvUtils.validateGlvMarket(dataStore, params.addresses.glv, params.addresses.market, false); - MarketUtils.validateEnabledMarket(dataStore, params.market); - MarketUtils.validateSwapPath(dataStore, params.longTokenSwapPath); - MarketUtils.validateSwapPath(dataStore, params.shortTokenSwapPath); + MarketUtils.validateEnabledMarket(dataStore, params.addresses.market); + MarketUtils.validateSwapPath(dataStore, params.addresses.longTokenSwapPath); + MarketUtils.validateSwapPath(dataStore, params.addresses.shortTokenSwapPath); address wnt = TokenUtils.wnt(dataStore); uint256 wntAmount = glvVault.recordTransferIn(wnt); @@ -86,9 +90,9 @@ library GlvWithdrawalUtils { } params.executionFee = wntAmount; - AccountUtils.validateReceiver(params.receiver); + AccountUtils.validateReceiver(params.addresses.receiver); - uint256 glvTokenAmount = glvVault.recordTransferIn(params.glv); + uint256 glvTokenAmount = glvVault.recordTransferIn(params.addresses.glv); if (glvTokenAmount == 0) { revert Errors.EmptyGlvWithdrawalAmount(); @@ -97,13 +101,13 @@ library GlvWithdrawalUtils { GlvWithdrawal.Props memory glvWithdrawal = GlvWithdrawal.Props( GlvWithdrawal.Addresses({ account: account, - glv: params.glv, - receiver: params.receiver, - callbackContract: params.callbackContract, - uiFeeReceiver: params.uiFeeReceiver, - market: params.market, - longTokenSwapPath: params.longTokenSwapPath, - shortTokenSwapPath: params.shortTokenSwapPath + glv: params.addresses.glv, + receiver: params.addresses.receiver, + callbackContract: params.addresses.callbackContract, + uiFeeReceiver: params.addresses.uiFeeReceiver, + market: params.addresses.market, + longTokenSwapPath: params.addresses.longTokenSwapPath, + shortTokenSwapPath: params.addresses.shortTokenSwapPath }), GlvWithdrawal.Numbers({ glvTokenAmount: glvTokenAmount, @@ -128,7 +132,7 @@ library GlvWithdrawalUtils { ); uint256 oraclePriceCount = GasUtils.estimateGlvWithdrawalOraclePriceCount( marketCount, - params.longTokenSwapPath.length + params.shortTokenSwapPath.length + params.addresses.longTokenSwapPath.length + params.addresses.shortTokenSwapPath.length ); GasUtils.validateExecutionFee(dataStore, estimatedGasLimit, params.executionFee, oraclePriceCount); diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index 8f73c6834..7dc327c1a 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -198,21 +198,25 @@ library RelayUtils { bytes32 public constant CREATE_GLV_WITHDRAWAL_TYPEHASH = keccak256( bytes( - "CreateGlvWithdrawal(CreateGlvWithdrawalParams params,bytes32 relayParams)" - "CreateGlvWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + "CreateGlvWithdrawal(CreateGlvWithdrawalParams params,bytes32 relayParams)CreateGlvWithdrawalParams(CreateGlvWithdrawalParamsAddresses addresses,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)CreateGlvWithdrawalParamsAddresses(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath)" ) ); bytes32 public constant MULTICHAIN_CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH = keccak256( bytes( - "MultichainCreateGlvWithdrawalParams(uint256 desChainId,CreateGlvWithdrawalParams params)" - "CreateGlvWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + "MultichainCreateGlvWithdrawalParams(uint256 desChainId,CreateGlvWithdrawalParams params)CreateGlvWithdrawalParams(CreateGlvWithdrawalParamsAddresses addresses,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" ) ); bytes32 public constant CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH = keccak256( bytes( - "CreateGlvWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + "CreateGlvWithdrawalParams(CreateGlvWithdrawalParamsAddresses addresses,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + ) + ); + bytes32 public constant CREATE_GLV_WITHDRAWAL_PARAMS_ADDRESSES_TYPEHASH = + keccak256( + bytes( + "CreateGlvWithdrawalParamsAddresses(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath)" ) ); @@ -577,16 +581,10 @@ library RelayUtils { keccak256( abi.encode( CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH, - params.receiver, - params.callbackContract, - params.uiFeeReceiver, - params.market, - params.glv, - keccak256(abi.encodePacked(params.longTokenSwapPath)), - keccak256(abi.encodePacked(params.shortTokenSwapPath)), + _getCreateGlvWithdrawalParamsAddressesStructHash(params.addresses), params.minLongTokenAmount, params.minShortTokenAmount, - // params.shouldUnwrapNativeToken, // TODO: split CreateGlvWithdrawalParams into groups to fix slot too deep error + params.shouldUnwrapNativeToken, params.executionFee, params.callbackGasLimit, params.srcChainId, @@ -595,6 +593,24 @@ library RelayUtils { ); } + function _getCreateGlvWithdrawalParamsAddressesStructHash( + GlvWithdrawalUtils.CreateGlvWithdrawalParamsAddresses memory addresses + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + CREATE_GLV_WITHDRAWAL_PARAMS_ADDRESSES_TYPEHASH, + addresses.receiver, + addresses.callbackContract, + addresses.uiFeeReceiver, + addresses.market, + addresses.glv, + keccak256(abi.encodePacked(addresses.longTokenSwapPath)), + keccak256(abi.encodePacked(addresses.shortTokenSwapPath)) + ) + ); + } + function getMultichainCreateShiftStructHash( RelayParams calldata relayParams, TransferRequest[] calldata transferRequests, diff --git a/utils/glv/glvWithdrawal.ts b/utils/glv/glvWithdrawal.ts index 728ee8893..d870a548a 100644 --- a/utils/glv/glvWithdrawal.ts +++ b/utils/glv/glvWithdrawal.ts @@ -68,13 +68,15 @@ export async function createGlvWithdrawal(fixture, overrides: any = {}) { } const params = { - receiver: receiver.address, - callbackContract: callbackContract.address, - uiFeeReceiver: uiFeeReceiver.address, - glv, - market: market.marketToken, - longTokenSwapPath, - shortTokenSwapPath, + addresses: { + receiver: receiver.address, + callbackContract: callbackContract.address, + uiFeeReceiver: uiFeeReceiver.address, + glv, + market: market.marketToken, + longTokenSwapPath, + shortTokenSwapPath, + }, minLongTokenAmount, minShortTokenAmount, shouldUnwrapNativeToken, From 6e3cf107a7bf658b17457a594da5b305ecfe47e4 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 13 Feb 2025 22:48:15 +0200 Subject: [PATCH 052/205] Remove "immutable" from state variables to reduce contract size bellow the deploying limit --- contracts/multichain/MultichainGmRouter.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts/multichain/MultichainGmRouter.sol b/contracts/multichain/MultichainGmRouter.sol index e54c703a1..1a4a9982a 100644 --- a/contracts/multichain/MultichainGmRouter.sol +++ b/contracts/multichain/MultichainGmRouter.sol @@ -11,11 +11,11 @@ import "./MultichainRouter.sol"; contract MultichainGmRouter is MultichainRouter { - DepositVault public immutable depositVault; - IDepositHandler public immutable depositHandler; - WithdrawalVault public immutable withdrawalVault; - WithdrawalHandler public immutable withdrawalHandler; - ShiftVault public immutable shiftVault; + DepositVault public depositVault; + IDepositHandler public depositHandler; + WithdrawalVault public withdrawalVault; + WithdrawalHandler public withdrawalHandler; + ShiftVault public shiftVault; constructor( BaseConstructorParams memory params, From 7aa648c5bbfda629952495cd5dcd0747d7c75f35 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 14 Feb 2025 09:52:46 +0200 Subject: [PATCH 053/205] Remove srcChainId from RelayParams struct --- contracts/multichain/MultichainGlvRouter.sol | 10 ++++--- contracts/multichain/MultichainGmRouter.sol | 15 ++++++---- contracts/router/relay/GelatoRelayRouter.sol | 11 +++++-- contracts/router/relay/RelayUtils.sol | 1 - .../relay/SubaccountGelatoRelayRouter.sol | 30 ++++++++++++------- 5 files changed, 43 insertions(+), 24 deletions(-) diff --git a/contracts/multichain/MultichainGlvRouter.sol b/contracts/multichain/MultichainGlvRouter.sol index ddfa090aa..a970430ca 100644 --- a/contracts/multichain/MultichainGlvRouter.sol +++ b/contracts/multichain/MultichainGlvRouter.sol @@ -32,7 +32,7 @@ contract MultichainGlvRouter is MultichainRouter { } bytes32 structHash = RelayUtils.getMultichainCreateGlvDepositStructHash(relayParams, transferRequests, params); - _validateCall(relayParams, account, structHash); + _validateCall(relayParams, account, structHash, params.createGlvDepositParams.srcChainId); _processTransferRequests(account, transferRequests, params.createGlvDepositParams.srcChainId); @@ -55,7 +55,8 @@ contract MultichainGlvRouter is MultichainRouter { contracts, relayParams, account, - address(glvVault) + address(glvVault), + params.createGlvDepositParams.srcChainId ); return glvHandler.createGlvDeposit(account, params.createGlvDepositParams); @@ -72,7 +73,7 @@ contract MultichainGlvRouter is MultichainRouter { } bytes32 structHash = RelayUtils.getMultichainCreateGlvWithdrawalStructHash(relayParams, transferRequests, params); - _validateCall(relayParams, account, structHash); + _validateCall(relayParams, account, structHash, params.createGlvWithdrawalParams.srcChainId); _processTransferRequests(account, transferRequests, params.createGlvWithdrawalParams.srcChainId); @@ -95,7 +96,8 @@ contract MultichainGlvRouter is MultichainRouter { contracts, relayParams, account, - address(glvVault) // residualFeeReceiver + address(glvVault), // residualFeeReceiver + params.createGlvWithdrawalParams.srcChainId ); return GlvWithdrawalUtils.createGlvWithdrawal( diff --git a/contracts/multichain/MultichainGmRouter.sol b/contracts/multichain/MultichainGmRouter.sol index 1a4a9982a..6cd3b6f9a 100644 --- a/contracts/multichain/MultichainGmRouter.sol +++ b/contracts/multichain/MultichainGmRouter.sol @@ -43,7 +43,7 @@ contract MultichainGmRouter is MultichainRouter { } bytes32 structHash = RelayUtils.getMultichainCreateDepositStructHash(relayParams, transferRequests, params); - _validateCall(relayParams, account, structHash); + _validateCall(relayParams, account, structHash, params.createDepositParams.srcChainId); _processTransferRequests(account, transferRequests, params.createDepositParams.srcChainId); @@ -66,7 +66,8 @@ contract MultichainGmRouter is MultichainRouter { contracts, relayParams, account, - address(depositVault) // residualFeeReceiver + address(depositVault), // residualFeeReceiver + params.createDepositParams.srcChainId ); return depositHandler.createDeposit(account, params.createDepositParams); @@ -83,7 +84,7 @@ contract MultichainGmRouter is MultichainRouter { } bytes32 structHash = RelayUtils.getMultichainCreateWithdrawalStructHash(relayParams, transferRequests, params); - _validateCall(relayParams, account, structHash); + _validateCall(relayParams, account, structHash, params.createWithdrawalParams.srcChainId); _processTransferRequests(account, transferRequests, params.createWithdrawalParams.srcChainId); @@ -105,7 +106,8 @@ contract MultichainGmRouter is MultichainRouter { contracts, relayParams, account, - address(withdrawalVault) // residualFeeReceiver + address(withdrawalVault), // residualFeeReceiver + params.createWithdrawalParams.srcChainId ); return withdrawalHandler.createWithdrawal(account, params.createWithdrawalParams); @@ -122,7 +124,7 @@ contract MultichainGmRouter is MultichainRouter { } bytes32 structHash = RelayUtils.getMultichainCreateShiftStructHash(relayParams, transferRequests, params); - _validateCall(relayParams, account, structHash); + _validateCall(relayParams, account, structHash, params.createShiftParams.srcChainId); _processTransferRequests(account, transferRequests, params.createShiftParams.srcChainId); @@ -144,7 +146,8 @@ contract MultichainGmRouter is MultichainRouter { contracts, relayParams, account, - address(shiftVault) + address(shiftVault), + params.createShiftParams.srcChainId ); return ShiftUtils.createShift( diff --git a/contracts/router/relay/GelatoRelayRouter.sol b/contracts/router/relay/GelatoRelayRouter.sol index e5845be73..d1e22bd7c 100644 --- a/contracts/router/relay/GelatoRelayRouter.sol +++ b/contracts/router/relay/GelatoRelayRouter.sol @@ -11,6 +11,7 @@ import "../../router/Router.sol"; import "./BaseGelatoRelayRouter.sol"; contract GelatoRelayRouter is BaseGelatoRelayRouter { + using Order for Order.Props; constructor( Router _router, @@ -37,7 +38,7 @@ contract GelatoRelayRouter is BaseGelatoRelayRouter { { _validateGaslessFeature(); bytes32 structHash = RelayUtils.getCreateOrderStructHash(relayParams, collateralDeltaAmount, params); - _validateCall(relayParams, account, structHash); + _validateCall(relayParams, account, structHash, params.numbers.srcChainId); return _createOrder(relayParams, account, collateralDeltaAmount, params, false); } @@ -51,8 +52,10 @@ contract GelatoRelayRouter is BaseGelatoRelayRouter { bool increaseExecutionFee ) external nonReentrant withOraclePricesForAtomicAction(relayParams.oracleParams) onlyGelatoRelay { _validateGaslessFeature(); + bytes32 structHash = RelayUtils.getUpdateOrderStructHash(relayParams, key, params, increaseExecutionFee); - _validateCall(relayParams, account, structHash); + Order.Props memory order = OrderStoreUtils.get(dataStore, key); + _validateCall(relayParams, account, structHash, order.srcChainId()); _updateOrder(relayParams, account, key, params, increaseExecutionFee, false); } @@ -64,8 +67,10 @@ contract GelatoRelayRouter is BaseGelatoRelayRouter { bytes32 key ) external nonReentrant withOraclePricesForAtomicAction(relayParams.oracleParams) onlyGelatoRelay { _validateGaslessFeature(); + bytes32 structHash = RelayUtils.getCancelOrderStructHash(relayParams, key); - _validateCall(relayParams, account, structHash); + Order.Props memory order = OrderStoreUtils.get(dataStore, key); + _validateCall(relayParams, account, structHash, order.srcChainId()); _cancelOrder(relayParams, account, key); } diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index 7dc327c1a..d4e5c3dfd 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -44,7 +44,6 @@ library RelayUtils { uint256 userNonce; uint256 deadline; bytes signature; - uint256 srcChainId; } // @note all params except account should be part of the corresponding struct hash diff --git a/contracts/router/relay/SubaccountGelatoRelayRouter.sol b/contracts/router/relay/SubaccountGelatoRelayRouter.sol index 573efe934..4d27539ff 100644 --- a/contracts/router/relay/SubaccountGelatoRelayRouter.sol +++ b/contracts/router/relay/SubaccountGelatoRelayRouter.sol @@ -9,6 +9,8 @@ import "../../subaccount/SubaccountUtils.sol"; import "./BaseGelatoRelayRouter.sol"; contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { + using Order for Order.Props; + struct SubaccountApproval { address subaccount; bool shouldAdd; @@ -63,7 +65,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { ); bytes32 public constant REMOVE_SUBACCOUNT_TYPEHASH = - keccak256(bytes("RemoveSubaccount(address subaccount,bytes32 relayParams)")); + keccak256(bytes("RemoveSubaccount(address subaccount,uint256 srcChainId,bytes32 relayParams)")); mapping(address => uint256) public subaccountApprovalNonces; @@ -100,7 +102,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { collateralDeltaAmount, params ); - _validateCall(relayParams, subaccount, structHash); + _validateCall(relayParams, subaccount, structHash, params.numbers.srcChainId); _handleSubaccountAction(account, subaccount, Keys.SUBACCOUNT_ORDER_ACTION, subaccountApproval); if (params.addresses.receiver != account) { @@ -125,8 +127,11 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { bool increaseExecutionFee ) external nonReentrant withOraclePricesForAtomicAction(relayParams.oracleParams) onlyGelatoRelay { _validateGaslessFeature(); + bytes32 structHash = _getUpdateOrderStructHash(relayParams, subaccountApproval, account, key, params, increaseExecutionFee); - _validateCall(relayParams, subaccount, structHash); + Order.Props memory order = OrderStoreUtils.get(dataStore, key); + _validateCall(relayParams, subaccount, structHash, order.srcChainId()); + _handleSubaccountAction(account, subaccount, Keys.SUBACCOUNT_ORDER_ACTION, subaccountApproval); _updateOrder(relayParams, account, key, params, increaseExecutionFee, true); } @@ -140,8 +145,11 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { bytes32 key ) external nonReentrant withOraclePricesForAtomicAction(relayParams.oracleParams) onlyGelatoRelay { _validateGaslessFeature(); + bytes32 structHash = _getCancelOrderStructHash(relayParams, subaccountApproval, account, key); - _validateCall(relayParams, subaccount, structHash); + Order.Props memory order = OrderStoreUtils.get(dataStore, key); + _validateCall(relayParams, subaccount, structHash, order.srcChainId()); + _handleSubaccountAction(account, subaccount, Keys.SUBACCOUNT_ORDER_ACTION, subaccountApproval); _cancelOrder(relayParams, account, key); } @@ -150,18 +158,19 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { function removeSubaccount( RelayUtils.RelayParams calldata relayParams, address account, - address subaccount + address subaccount, + uint256 srcChainId ) external nonReentrant { _validateGaslessFeature(); - bytes32 structHash = _getRemoveSubaccountStructHash(relayParams, subaccount); - _validateCall(relayParams, account, structHash); + bytes32 structHash = _getRemoveSubaccountStructHash(relayParams, subaccount, srcChainId); + _validateCall(relayParams, account, structHash, srcChainId); Contracts memory contracts = Contracts({ dataStore: dataStore, eventEmitter: eventEmitter, bank: orderVault }); - _handleRelay(contracts, relayParams, account, account); + _handleRelay(contracts, relayParams, account, account, srcChainId); SubaccountUtils.removeSubaccount(dataStore, eventEmitter, account, subaccount); } @@ -232,9 +241,10 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { function _getRemoveSubaccountStructHash( RelayUtils.RelayParams calldata relayParams, - address subaccount + address subaccount, + uint256 srcChainId ) internal pure returns (bytes32) { - return keccak256(abi.encode(REMOVE_SUBACCOUNT_TYPEHASH, subaccount, RelayUtils._getRelayParamsHash(relayParams))); + return keccak256(abi.encode(REMOVE_SUBACCOUNT_TYPEHASH, subaccount, srcChainId, RelayUtils._getRelayParamsHash(relayParams))); } function _getSubaccountApprovalStructHash( From 9447228e250a5e24766a446317ba3edbccd8acc6 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 14 Feb 2025 11:55:30 +0200 Subject: [PATCH 054/205] Remove outdated contracts --- .../MultichainProviderSignature.sol | 44 ------------- .../multichain/MultichainVaultHandler.sol | 63 ------------------- deploy/deployMultichainHandler.ts | 17 ----- utils/fixture.ts | 2 - 4 files changed, 126 deletions(-) delete mode 100644 contracts/multichain/MultichainProviderSignature.sol delete mode 100644 contracts/multichain/MultichainVaultHandler.sol delete mode 100644 deploy/deployMultichainHandler.ts diff --git a/contracts/multichain/MultichainProviderSignature.sol b/contracts/multichain/MultichainProviderSignature.sol deleted file mode 100644 index e96a7a585..000000000 --- a/contracts/multichain/MultichainProviderSignature.sol +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.0; - -import "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; -import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; - -import { MultichainProviderUtils } from "./MultichainProviderUtils.sol"; - -contract MultichainProviderSignature is EIP712 { - using ECDSA for bytes32; - - string private constant SIGNING_DOMAIN = "MultichainProviderSignatureDomain"; - string private constant SIGNATURE_VERSION = "1"; - - // Define the EIP-712 struct type: - // Message(address token,uint256 amount,address account,uint256 srcChainId,uint32 srcEid) - bytes32 private constant _MESSAGE_TYPEHASH = - keccak256("Message(address token,uint256 amount,address account,uint256 srcChainId,uint32 srcEid)"); - - constructor() EIP712(SIGNING_DOMAIN, SIGNATURE_VERSION) {} - - /** - * Check the signature for a given message - * @param message The ABI encoded parameters (token, amount, account, srcChainId, srcEid). - * @param signature The EIP-712 signature. - */ - function isSigner(bytes calldata message, bytes calldata signature) external view returns (bool) { - // Decode the message - (address token, uint256 amount, address account, uint256 srcChainId, uint32 srcEid) = MultichainProviderUtils - .decodeWithdrawal(message); - - // Build the struct hash - bytes32 structHash = keccak256(abi.encode(_MESSAGE_TYPEHASH, token, amount, account, srcChainId, srcEid)); - - // Get the typed data hash for EIP-712 - bytes32 hash = _hashTypedDataV4(structHash); - - // Recover the signer from the signature - address signer = ECDSA.recover(hash, signature); - - return signer == account; - } -} diff --git a/contracts/multichain/MultichainVaultHandler.sol b/contracts/multichain/MultichainVaultHandler.sol deleted file mode 100644 index 0e9a1942f..000000000 --- a/contracts/multichain/MultichainVaultHandler.sol +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.0; - -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; - -import { RoleStore } from "../role/RoleStore.sol"; -import { RoleModule } from "../role/RoleModule.sol"; -import { DataStore } from "../data/DataStore.sol"; -import { Keys } from "../data/Keys.sol"; -import { Oracle } from "../oracle/Oracle.sol"; -import { OracleModule } from "../oracle/OracleModule.sol"; -import { EventEmitter } from "../event/EventEmitter.sol"; -import { Errors } from "../error/Errors.sol"; -import { GlobalReentrancyGuard } from "../utils/GlobalReentrancyGuard.sol"; -import { ExchangeRouter } from "../router/ExchangeRouter.sol"; - -import { MultichainVault } from "./MultichainVault.sol"; -import { MultichainUtils } from "./MultichainUtils.sol"; -import { MultichainEventUtils } from "./MultichainEventUtils.sol"; - -/** - * @title MultichainVaultHandler - */ -contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModule { - using SafeERC20 for IERC20; - - MultichainVault public multichainVault; - EventEmitter public eventEmitter; - ExchangeRouter public exchangeRouter; - - constructor( - RoleStore _roleStore, - DataStore _dataStore, - EventEmitter _eventEmitter, - Oracle _oracle, - MultichainVault _multichainVault, - ExchangeRouter _exchangeRouter - ) RoleModule(_roleStore) GlobalReentrancyGuard(_dataStore) OracleModule(_oracle) { - multichainVault = _multichainVault; - eventEmitter = _eventEmitter; - exchangeRouter = _exchangeRouter; - } - - /** - * Executes the multicall for the given args - * The multicall arguments contains the function calls to be executed (e.g. createDeposit, createOrder, createWithdrawal, etc) - * @param account user address on the source chain - * @param srcChainId chain id of the source chain - * @param multicallArgs array of bytes containing the multicall arguments - */ - function executeMulticall( - address account, - uint256 srcChainId, - bytes[] calldata multicallArgs - ) external onlyController { - // execute multicall - exchangeRouter.multicall(multicallArgs); - - MultichainEventUtils.emitMultichainMessage(eventEmitter, account, srcChainId); - } -} diff --git a/deploy/deployMultichainHandler.ts b/deploy/deployMultichainHandler.ts deleted file mode 100644 index 3f28c9645..000000000 --- a/deploy/deployMultichainHandler.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { grantRoleIfNotGranted } from "../utils/role"; -import { createDeployFunction } from "../utils/deploy"; - -const constructorContracts = ["RoleStore", "DataStore", "EventEmitter", "Oracle", "MultichainVault", "ExchangeRouter"]; - -const func = createDeployFunction({ - contractName: "MultichainVaultHandler", - dependencyNames: constructorContracts, - getDeployArgs: async ({ dependencyContracts }) => { - return constructorContracts.map((dependencyName) => dependencyContracts[dependencyName].address); - }, - afterDeploy: async ({ deployedContract }) => { - await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); - }, -}); - -export default func; diff --git a/utils/fixture.ts b/utils/fixture.ts index c56abb326..e7eb85e62 100644 --- a/utils/fixture.ts +++ b/utils/fixture.ts @@ -119,7 +119,6 @@ export async function deployFixture() { const feeHandler = await hre.ethers.getContract("FeeHandler"); const mockVaultV1 = await hre.ethers.getContract("MockVaultV1"); const multichainVault = await hre.ethers.getContract("MultichainVault"); - const multichainVaultHandler = await hre.ethers.getContract("MultichainVaultHandler"); const multichainUtils = await hre.ethers.getContract("MultichainUtils"); const layerZeroProvider = await hre.ethers.getContract("LayerZeroProvider"); const mockStargatePool = await hre.ethers.getContract("MockStargatePool"); @@ -331,7 +330,6 @@ export async function deployFixture() { glvReader, mockVaultV1, multichainVault, - multichainVaultHandler, multichainUtils, layerZeroProvider, mockStargatePool, From 576c3ca04fdf3a8584f38ba6aecdb670f55a5920 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 14 Feb 2025 12:17:25 +0200 Subject: [PATCH 055/205] Remove srcChainId from RelayParams struct (continued) --- .../router/relay/BaseGelatoRelayRouter.sol | 30 ++++++++++--------- deploy/deploySubaccountGelatoRelayRouter.ts | 2 +- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/contracts/router/relay/BaseGelatoRelayRouter.sol b/contracts/router/relay/BaseGelatoRelayRouter.sol index 38d74df87..b72aed770 100644 --- a/contracts/router/relay/BaseGelatoRelayRouter.sol +++ b/contracts/router/relay/BaseGelatoRelayRouter.sol @@ -88,7 +88,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, bank: orderVault }); - params.numbers.executionFee = _handleRelay(contracts, relayParams, account, address(contracts.bank)); + params.numbers.executionFee = _handleRelay(contracts, relayParams, account, address(contracts.bank), params.numbers.srcChainId); if ( params.orderType == Order.OrderType.MarketSwap || @@ -102,7 +102,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, params.addresses.initialCollateralToken, address(contracts.bank), collateralDeltaAmount, - relayParams.srcChainId + params.numbers.srcChainId ); } @@ -135,7 +135,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, } address residualFeeReceiver = increaseExecutionFee ? address(contracts.bank) : account; - _handleRelay(contracts, relayParams, account, residualFeeReceiver); + _handleRelay(contracts, relayParams, account, residualFeeReceiver, order.srcChainId()); orderHandler.updateOrder( key, @@ -166,7 +166,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, revert Errors.Unauthorized(account, "account for cancelOrder"); } - _handleRelay(contracts, relayParams, account, account); + _handleRelay(contracts, relayParams, account, account, order.srcChainId()); orderHandler.cancelOrder(key); } @@ -209,14 +209,15 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, Contracts memory contracts, RelayUtils.RelayParams calldata relayParams, address account, - address residualFeeReceiver + address residualFeeReceiver, + uint256 srcChainId ) internal returns (uint256) { if (relayParams.externalCalls.externalCallTargets.length != 0 && relayParams.fee.feeSwapPath.length != 0) { revert Errors.InvalidRelayParams(); } _handleTokenPermits(relayParams.tokenPermits); - return _handleRelayFee(contracts, relayParams, account, residualFeeReceiver); + return _handleRelayFee(contracts, relayParams, account, residualFeeReceiver, srcChainId); } function _handleTokenPermits(RelayUtils.TokenPermit[] calldata tokenPermits) internal { @@ -254,7 +255,8 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, Contracts memory contracts, RelayUtils.RelayParams calldata relayParams, address account, - address residualFeeReceiver + address residualFeeReceiver, + uint256 srcChainId ) internal returns (uint256) { address wnt = TokenUtils.wnt(contracts.dataStore); @@ -265,7 +267,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, // if external calls => send to external handler uint256 outputAmount; if (relayParams.externalCalls.externalCallTargets.length > 0) { - _sendTokens(account, relayParams.fee.feeToken, address(externalHandler), relayParams.fee.feeAmount, relayParams.srcChainId); + _sendTokens(account, relayParams.fee.feeToken, address(externalHandler), relayParams.fee.feeAmount, srcChainId); externalHandler.makeExternalCalls( relayParams.externalCalls.externalCallTargets, relayParams.externalCalls.externalCallDataList, @@ -274,10 +276,10 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, ); outputAmount = ERC20(_getFeeToken()).balanceOf(address(this)); } else if (relayParams.fee.feeSwapPath.length != 0) { - _sendTokens(account, relayParams.fee.feeToken, address(contracts.bank), relayParams.fee.feeAmount, relayParams.srcChainId); + _sendTokens(account, relayParams.fee.feeToken, address(contracts.bank), relayParams.fee.feeAmount, srcChainId); outputAmount = _swapFeeTokens(contracts, wnt, relayParams.fee); } else if (relayParams.fee.feeToken == wnt) { - _sendTokens(account, relayParams.fee.feeToken, address(this), relayParams.fee.feeAmount, relayParams.srcChainId); + _sendTokens(account, relayParams.fee.feeToken, address(this), relayParams.fee.feeAmount, srcChainId); outputAmount = relayParams.fee.feeAmount; } else { revert Errors.UnexpectedRelayFeeToken(_getFeeToken(), wnt); @@ -291,7 +293,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, // for update orders the residual fee could be sent to the order vault if order's execution fee should be increased // otherwise the residual fee is sent back to the user // for other actions the residual fee is sent back to the user - _transferResidualFee(wnt, residualFeeReceiver, residualFee, account, relayParams.srcChainId); + _transferResidualFee(wnt, residualFeeReceiver, residualFee, account, srcChainId); return residualFee; } @@ -321,9 +323,9 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, ); } - function _validateCall(RelayUtils.RelayParams calldata relayParams, address account, bytes32 structHash) internal { - uint256 srcChainId = relayParams.srcChainId == 0 ? block.chainid : relayParams.srcChainId; - bytes32 domainSeparator = _getDomainSeparator(srcChainId); + function _validateCall(RelayUtils.RelayParams calldata relayParams, address account, bytes32 structHash, uint256 srcChainId) internal { + uint256 _srcChainId = srcChainId == 0 ? block.chainid : srcChainId; + bytes32 domainSeparator = _getDomainSeparator(_srcChainId); bytes32 digest = ECDSA.toTypedDataHash(domainSeparator, structHash); _validateSignature(digest, relayParams.signature, account, "call"); diff --git a/deploy/deploySubaccountGelatoRelayRouter.ts b/deploy/deploySubaccountGelatoRelayRouter.ts index 760c3706b..065d029c8 100644 --- a/deploy/deploySubaccountGelatoRelayRouter.ts +++ b/deploy/deploySubaccountGelatoRelayRouter.ts @@ -17,7 +17,7 @@ const func = createDeployFunction({ getDeployArgs: async ({ dependencyContracts }) => { return constructorContracts.map((dependencyName) => dependencyContracts[dependencyName].address); }, - libraryNames: ["MarketStoreUtils", "MarketUtils", "OrderStoreUtils", "SwapUtils", "SubaccountUtils", "RelayUtils"], + libraryNames: ["MarketStoreUtils", "OrderStoreUtils", "SwapUtils", "SubaccountUtils"], afterDeploy: async ({ deployedContract }) => { await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); await grantRoleIfNotGranted(deployedContract.address, "ROUTER_PLUGIN"); From dc952cbd21dfc624c707098dcd6a53ad0f529865 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 14 Feb 2025 15:28:17 +0200 Subject: [PATCH 056/205] Move desChainId from multichain structs into RelayParams struct, remove multichain struct from create functions, remove top level multichain typehases --- contracts/multichain/MultichainGlvRouter.sol | 36 ++--- contracts/multichain/MultichainGmRouter.sol | 54 +++---- contracts/router/relay/RelayUtils.sol | 162 +++---------------- 3 files changed, 67 insertions(+), 185 deletions(-) diff --git a/contracts/multichain/MultichainGlvRouter.sol b/contracts/multichain/MultichainGlvRouter.sol index a970430ca..9c01504c0 100644 --- a/contracts/multichain/MultichainGlvRouter.sol +++ b/contracts/multichain/MultichainGlvRouter.sol @@ -25,16 +25,16 @@ contract MultichainGlvRouter is MultichainRouter { RelayUtils.RelayParams calldata relayParams, address account, RelayUtils.TransferRequest[] calldata transferRequests, - RelayUtils.MultichainCreateGlvDepositParams memory params + GlvDepositUtils.CreateGlvDepositParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { - if (params.desChainId != block.chainid) { + if (relayParams.desChainId != block.chainid) { revert Errors.InvalidDestinationChainId(); } - bytes32 structHash = RelayUtils.getMultichainCreateGlvDepositStructHash(relayParams, transferRequests, params); - _validateCall(relayParams, account, structHash, params.createGlvDepositParams.srcChainId); + bytes32 structHash = RelayUtils.getCreateGlvDepositStructHash(relayParams, transferRequests, params); + _validateCall(relayParams, account, structHash, params.srcChainId); - _processTransferRequests(account, transferRequests, params.createGlvDepositParams.srcChainId); + _processTransferRequests(account, transferRequests, params.srcChainId); return _createGlvDeposit(relayParams, account, params); } @@ -42,7 +42,7 @@ contract MultichainGlvRouter is MultichainRouter { function _createGlvDeposit( RelayUtils.RelayParams calldata relayParams, address account, - RelayUtils.MultichainCreateGlvDepositParams memory params + GlvDepositUtils.CreateGlvDepositParams memory params ) internal returns (bytes32) { Contracts memory contracts = Contracts({ dataStore: dataStore, @@ -51,31 +51,31 @@ contract MultichainGlvRouter is MultichainRouter { }); // pay relay fee tokens from MultichainVault to GlvVault and decrease user's multichain balance - params.createGlvDepositParams.executionFee = _handleRelay( + params.executionFee = _handleRelay( contracts, relayParams, account, address(glvVault), - params.createGlvDepositParams.srcChainId + params.srcChainId ); - return glvHandler.createGlvDeposit(account, params.createGlvDepositParams); + return glvHandler.createGlvDeposit(account, params); } function createGlvWithdrawal( RelayUtils.RelayParams calldata relayParams, address account, RelayUtils.TransferRequest[] calldata transferRequests, - RelayUtils.MultichainCreateGlvWithdrawalParams memory params + GlvWithdrawalUtils.CreateGlvWithdrawalParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { - if (params.desChainId != block.chainid) { + if (relayParams.desChainId != block.chainid) { revert Errors.InvalidDestinationChainId(); } - bytes32 structHash = RelayUtils.getMultichainCreateGlvWithdrawalStructHash(relayParams, transferRequests, params); - _validateCall(relayParams, account, structHash, params.createGlvWithdrawalParams.srcChainId); + bytes32 structHash = RelayUtils.getCreateGlvWithdrawalStructHash(relayParams, transferRequests, params); + _validateCall(relayParams, account, structHash, params.srcChainId); - _processTransferRequests(account, transferRequests, params.createGlvWithdrawalParams.srcChainId); + _processTransferRequests(account, transferRequests, params.srcChainId); return _createGlvWithdrawal(relayParams, account, params); } @@ -83,7 +83,7 @@ contract MultichainGlvRouter is MultichainRouter { function _createGlvWithdrawal( RelayUtils.RelayParams calldata relayParams, address account, - RelayUtils.MultichainCreateGlvWithdrawalParams memory params + GlvWithdrawalUtils.CreateGlvWithdrawalParams memory params ) internal returns (bytes32) { Contracts memory contracts = Contracts({ dataStore: dataStore, @@ -92,12 +92,12 @@ contract MultichainGlvRouter is MultichainRouter { }); // pay relay fee tokens from MultichainVault to GlvVault and decrease user's multichain balance - params.createGlvWithdrawalParams.executionFee = _handleRelay( + params.executionFee = _handleRelay( contracts, relayParams, account, address(glvVault), // residualFeeReceiver - params.createGlvWithdrawalParams.srcChainId + params.srcChainId ); return GlvWithdrawalUtils.createGlvWithdrawal( @@ -105,7 +105,7 @@ contract MultichainGlvRouter is MultichainRouter { eventEmitter, glvVault, account, - params.createGlvWithdrawalParams + params ); } } diff --git a/contracts/multichain/MultichainGmRouter.sol b/contracts/multichain/MultichainGmRouter.sol index 6cd3b6f9a..334bc6855 100644 --- a/contracts/multichain/MultichainGmRouter.sol +++ b/contracts/multichain/MultichainGmRouter.sol @@ -36,16 +36,16 @@ contract MultichainGmRouter is MultichainRouter { RelayUtils.RelayParams calldata relayParams, address account, RelayUtils.TransferRequest[] calldata transferRequests, - RelayUtils.MultichainCreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee + DepositUtils.CreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { - if (params.desChainId != block.chainid) { + if (relayParams.desChainId != block.chainid) { revert Errors.InvalidDestinationChainId(); } - bytes32 structHash = RelayUtils.getMultichainCreateDepositStructHash(relayParams, transferRequests, params); - _validateCall(relayParams, account, structHash, params.createDepositParams.srcChainId); + bytes32 structHash = RelayUtils.getCreateDepositStructHash(relayParams, transferRequests, params); + _validateCall(relayParams, account, structHash, params.srcChainId); - _processTransferRequests(account, transferRequests, params.createDepositParams.srcChainId); + _processTransferRequests(account, transferRequests, params.srcChainId); return _createDeposit(relayParams, account, params); } @@ -53,7 +53,7 @@ contract MultichainGmRouter is MultichainRouter { function _createDeposit( RelayUtils.RelayParams calldata relayParams, address account, - RelayUtils.MultichainCreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee + DepositUtils.CreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee ) internal returns (bytes32) { Contracts memory contracts = Contracts({ dataStore: dataStore, @@ -62,31 +62,31 @@ contract MultichainGmRouter is MultichainRouter { }); // pay relay fee tokens from MultichainVault to DepositVault and decrease user's multichain balance - params.createDepositParams.executionFee = _handleRelay( + params.executionFee = _handleRelay( contracts, relayParams, account, address(depositVault), // residualFeeReceiver - params.createDepositParams.srcChainId + params.srcChainId ); - return depositHandler.createDeposit(account, params.createDepositParams); + return depositHandler.createDeposit(account, params); } function createWithdrawal( RelayUtils.RelayParams calldata relayParams, address account, RelayUtils.TransferRequest[] calldata transferRequests, - RelayUtils.MultichainCreateWithdrawalParams memory params // can't use calldata because need to modify params.addresses.receiver & params.numbers.executionFee + WithdrawalUtils.CreateWithdrawalParams memory params // can't use calldata because need to modify params.addresses.receiver & params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { - if (params.desChainId != block.chainid) { + if (relayParams.desChainId != block.chainid) { revert Errors.InvalidDestinationChainId(); } - bytes32 structHash = RelayUtils.getMultichainCreateWithdrawalStructHash(relayParams, transferRequests, params); - _validateCall(relayParams, account, structHash, params.createWithdrawalParams.srcChainId); + bytes32 structHash = RelayUtils.getCreateWithdrawalStructHash(relayParams, transferRequests, params); + _validateCall(relayParams, account, structHash, params.srcChainId); - _processTransferRequests(account, transferRequests, params.createWithdrawalParams.srcChainId); + _processTransferRequests(account, transferRequests, params.srcChainId); return _createWithdrawal(relayParams, account, params); } @@ -94,7 +94,7 @@ contract MultichainGmRouter is MultichainRouter { function _createWithdrawal( RelayUtils.RelayParams calldata relayParams, address account, - RelayUtils.MultichainCreateWithdrawalParams memory params // can't use calldata because need to modify params.addresses.receiver & params.numbers.executionFee + WithdrawalUtils.CreateWithdrawalParams memory params // can't use calldata because need to modify params.addresses.receiver & params.numbers.executionFee ) internal returns (bytes32) { Contracts memory contracts = Contracts({ dataStore: dataStore, @@ -102,31 +102,31 @@ contract MultichainGmRouter is MultichainRouter { bank: withdrawalVault }); - params.createWithdrawalParams.executionFee = _handleRelay( + params.executionFee = _handleRelay( contracts, relayParams, account, address(withdrawalVault), // residualFeeReceiver - params.createWithdrawalParams.srcChainId + params.srcChainId ); - return withdrawalHandler.createWithdrawal(account, params.createWithdrawalParams); + return withdrawalHandler.createWithdrawal(account, params); } function createShift( RelayUtils.RelayParams calldata relayParams, address account, RelayUtils.TransferRequest[] calldata transferRequests, - RelayUtils.MultichainCreateShiftParams memory params + ShiftUtils.CreateShiftParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { - if (params.desChainId != block.chainid) { + if (relayParams.desChainId != block.chainid) { revert Errors.InvalidDestinationChainId(); } - bytes32 structHash = RelayUtils.getMultichainCreateShiftStructHash(relayParams, transferRequests, params); - _validateCall(relayParams, account, structHash, params.createShiftParams.srcChainId); + bytes32 structHash = RelayUtils.getCreateShiftStructHash(relayParams, transferRequests, params); + _validateCall(relayParams, account, structHash, params.srcChainId); - _processTransferRequests(account, transferRequests, params.createShiftParams.srcChainId); + _processTransferRequests(account, transferRequests, params.srcChainId); return _createShift(relayParams, account, params); } @@ -134,7 +134,7 @@ contract MultichainGmRouter is MultichainRouter { function _createShift( RelayUtils.RelayParams calldata relayParams, address account, - RelayUtils.MultichainCreateShiftParams memory params + ShiftUtils.CreateShiftParams memory params ) internal returns (bytes32) { Contracts memory contracts = Contracts({ dataStore: dataStore, @@ -142,12 +142,12 @@ contract MultichainGmRouter is MultichainRouter { bank: shiftVault }); - params.createShiftParams.executionFee = _handleRelay( + params.executionFee = _handleRelay( contracts, relayParams, account, address(shiftVault), - params.createShiftParams.srcChainId + params.srcChainId ); return ShiftUtils.createShift( @@ -155,7 +155,7 @@ contract MultichainGmRouter is MultichainRouter { eventEmitter, shiftVault, account, - params.createShiftParams + params ); } } diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index d4e5c3dfd..fb7fdbbf7 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -44,6 +44,7 @@ library RelayUtils { uint256 userNonce; uint256 deadline; bytes signature; + uint256 desChainId; } // @note all params except account should be part of the corresponding struct hash @@ -62,31 +63,6 @@ library RelayUtils { uint256 amount; } - struct MultichainCreateDepositParams { - uint256 desChainId; - DepositUtils.CreateDepositParams createDepositParams; - } - - struct MultichainCreateWithdrawalParams { - uint256 desChainId; - WithdrawalUtils.CreateWithdrawalParams createWithdrawalParams; - } - - struct MultichainCreateGlvDepositParams { - uint256 desChainId; - GlvDepositUtils.CreateGlvDepositParams createGlvDepositParams; - } - - struct MultichainCreateGlvWithdrawalParams { - uint256 desChainId; - GlvWithdrawalUtils.CreateGlvWithdrawalParams createGlvWithdrawalParams; - } - - struct MultichainCreateShiftParams { - uint256 desChainId; - ShiftUtils.CreateShiftParams createShiftParams; - } - //////////////////// ORDER //////////////////// bytes32 public constant UPDATE_ORDER_TYPEHASH = @@ -137,12 +113,6 @@ library RelayUtils { "CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" ) ); - bytes32 public constant MULTICHAIN_CREATE_DEPOSIT_PARAMS_TYPEHASH = - keccak256( - bytes( - "MultichainCreateDepositParams(uint256 desChainId,CreateDepositParams params)CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" - ) - ); bytes32 public constant CREATE_DEPOSIT_PARAMS_ADDRESSES_TYPEHASH = keccak256( bytes( @@ -156,12 +126,6 @@ library RelayUtils { "CreateWithdrawal(CreateWithdrawalParams params,bytes32 relayParams)CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" ) ); - bytes32 public constant MULTICHAIN_CREATE_WITHDRAWAL_PARAMS_TYPEHASH = - keccak256( - bytes( - "MultichainCreateWithdrawalParams(uint256 desChainId,CreateWithdrawalParams params)CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" - ) - ); bytes32 public constant CREATE_WITHDRAWAL_PARAMS_TYPEHASH = keccak256( bytes( @@ -175,12 +139,6 @@ library RelayUtils { "CreateGlvDeposit(CreateGlvDepositParams params,bytes32 relayParams)CreateGlvDepositParams(address account,address market,address initialLongToken,address initialShortToken,uint256 srcChainId,bytes32[] dataList)" ) ); - bytes32 public constant MULTICHAIN_CREATE_GLV_DEPOSIT_PARAMS_TYPEHASH = - keccak256( - bytes( - "MultichainCreateGlvDepositParams(uint256 desChainId,CreateGlvDepositParams params)CreateGlvDepositParams(address glv,address market,address receiver,address callbackContract,address uiFeeReceiver,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minGlvTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bool shouldUnwrapNativeToken,bool isMarketTokenDeposit,bytes32[] dataList)" - ) - ); bytes32 public constant CREATE_GLV_DEPOSIT_PARAMS_ADDRESSES_TYPEHASH = keccak256( bytes( @@ -200,12 +158,6 @@ library RelayUtils { "CreateGlvWithdrawal(CreateGlvWithdrawalParams params,bytes32 relayParams)CreateGlvWithdrawalParams(CreateGlvWithdrawalParamsAddresses addresses,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)CreateGlvWithdrawalParamsAddresses(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath)" ) ); - bytes32 public constant MULTICHAIN_CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH = - keccak256( - bytes( - "MultichainCreateGlvWithdrawalParams(uint256 desChainId,CreateGlvWithdrawalParams params)CreateGlvWithdrawalParams(CreateGlvWithdrawalParamsAddresses addresses,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" - ) - ); bytes32 public constant CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH = keccak256( bytes( @@ -225,12 +177,6 @@ library RelayUtils { "CreateShift(CreateShiftParams params,bytes32 relayParams)CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" ) ); - bytes32 public constant MULTICHAIN_CREATE_SHIFT_PARAMS_TYPEHASH = - keccak256( - bytes( - "MultichainCreateShiftParams(uint256 desChainId,CreateShiftParams params)CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" - ) - ); bytes32 public constant CREATE_SHIFT_PARAMS_TYPEHASH = keccak256( bytes( @@ -252,7 +198,8 @@ library RelayUtils { relayParams.tokenPermits, relayParams.fee, relayParams.userNonce, - relayParams.deadline + relayParams.deadline, + relayParams.desChainId ) ); } @@ -357,35 +304,22 @@ library RelayUtils { //////////////////// MULTICHAIN //////////////////// - function getMultichainCreateDepositStructHash( + function getCreateDepositStructHash( RelayParams calldata relayParams, TransferRequest[] calldata transferRequests, - MultichainCreateDepositParams memory params - ) external view returns (bytes32) { + DepositUtils.CreateDepositParams memory params + ) external pure returns (bytes32) { return keccak256( abi.encode( CREATE_DEPOSIT_TYPEHASH, - _getMultichainCreateDepositParamsStructHash(params), + _getCreateDepositParamsStructHash(params), _getTransferRequestsHash(transferRequests), _getRelayParamsHash(relayParams) ) ); } - function _getMultichainCreateDepositParamsStructHash( - MultichainCreateDepositParams memory params - ) internal view returns (bytes32) { - return - keccak256( - abi.encode( - MULTICHAIN_CREATE_DEPOSIT_PARAMS_TYPEHASH, - block.chainid, - _getCreateDepositParamsStructHash(params.createDepositParams) - ) - ); - } - function _getCreateDepositParamsStructHash( DepositUtils.CreateDepositParams memory params ) internal pure returns (bytes32) { @@ -423,35 +357,22 @@ library RelayUtils { ); } - function getMultichainCreateWithdrawalStructHash( + function getCreateWithdrawalStructHash( RelayParams calldata relayParams, TransferRequest[] calldata transferRequests, - MultichainCreateWithdrawalParams memory params - ) external view returns (bytes32) { + WithdrawalUtils.CreateWithdrawalParams memory params + ) external pure returns (bytes32) { return keccak256( abi.encode( CREATE_WITHDRAWAL_TYPEHASH, - _getMultichainCreateWithdrawalParamsStructHash(params), + _getCreateWithdrawalParamsStructHash(params), _getTransferRequestsHash(transferRequests), _getRelayParamsHash(relayParams) ) ); } - function _getMultichainCreateWithdrawalParamsStructHash( - MultichainCreateWithdrawalParams memory params - ) internal view returns (bytes32) { - return - keccak256( - abi.encode( - MULTICHAIN_CREATE_WITHDRAWAL_PARAMS_TYPEHASH, - block.chainid, - _getCreateWithdrawalParamsStructHash(params.createWithdrawalParams) - ) - ); - } - function _getCreateWithdrawalParamsStructHash( WithdrawalUtils.CreateWithdrawalParams memory params ) internal pure returns (bytes32) { @@ -476,35 +397,22 @@ library RelayUtils { ); } - function getMultichainCreateGlvDepositStructHash( + function getCreateGlvDepositStructHash( RelayParams calldata relayParams, TransferRequest[] calldata transferRequests, - MultichainCreateGlvDepositParams memory params + GlvDepositUtils.CreateGlvDepositParams memory params ) external pure returns (bytes32) { return keccak256( abi.encode( CREATE_GLV_DEPOSIT_TYPEHASH, - _getMultichainCreateGlvDepositParamsStructHash(params), + _getCreateGlvDepositParamsStructHash(params), _getTransferRequestsHash(transferRequests), _getRelayParamsHash(relayParams) ) ); } - function _getMultichainCreateGlvDepositParamsStructHash( - MultichainCreateGlvDepositParams memory params - ) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - MULTICHAIN_CREATE_GLV_DEPOSIT_PARAMS_TYPEHASH, - params.desChainId, - _getCreateGlvDepositParamsStructHash(params.createGlvDepositParams) - ) - ); - } - function _getCreateGlvDepositParamsStructHash( GlvDepositUtils.CreateGlvDepositParams memory params ) internal pure returns (bytes32) { @@ -544,35 +452,22 @@ library RelayUtils { ); } - function getMultichainCreateGlvWithdrawalStructHash( + function getCreateGlvWithdrawalStructHash( RelayParams calldata relayParams, TransferRequest[] calldata transferRequests, - MultichainCreateGlvWithdrawalParams memory params - ) external view returns (bytes32) { + GlvWithdrawalUtils.CreateGlvWithdrawalParams memory params + ) external pure returns (bytes32) { return keccak256( abi.encode( CREATE_GLV_WITHDRAWAL_TYPEHASH, - _getMultichainCreateGlvWithdrawalParamsStructHash(params), + _getCreateGlvWithdrawalParamsStructHash(params), _getTransferRequestsHash(transferRequests), _getRelayParamsHash(relayParams) ) ); } - function _getMultichainCreateGlvWithdrawalParamsStructHash( - MultichainCreateGlvWithdrawalParams memory params - ) internal view returns (bytes32) { - return - keccak256( - abi.encode( - MULTICHAIN_CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH, - block.chainid, - _getCreateGlvWithdrawalParamsStructHash(params.createGlvWithdrawalParams) - ) - ); - } - function _getCreateGlvWithdrawalParamsStructHash( GlvWithdrawalUtils.CreateGlvWithdrawalParams memory params ) internal pure returns (bytes32) { @@ -610,35 +505,22 @@ library RelayUtils { ); } - function getMultichainCreateShiftStructHash( + function getCreateShiftStructHash( RelayParams calldata relayParams, TransferRequest[] calldata transferRequests, - MultichainCreateShiftParams memory params - ) external view returns (bytes32) { + ShiftUtils.CreateShiftParams memory params + ) external pure returns (bytes32) { return keccak256( abi.encode( CREATE_SHIFT_TYPEHASH, - _getMultichainCreateShiftParamsStructHash(params), + _getCreateShiftParamsStructHash(params), _getTransferRequestsHash(transferRequests), _getRelayParamsHash(relayParams) ) ); } - function _getMultichainCreateShiftParamsStructHash( - MultichainCreateShiftParams memory params - ) internal view returns (bytes32) { - return - keccak256( - abi.encode( - MULTICHAIN_CREATE_SHIFT_PARAMS_TYPEHASH, - block.chainid, - _getCreateShiftParamsStructHash(params.createShiftParams) - ) - ); - } - function _getCreateShiftParamsStructHash( ShiftUtils.CreateShiftParams memory params ) internal pure returns (bytes32) { From c43d5ddb87d3d8a90fa7f3cf52197636e52ea717 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 14 Feb 2025 15:55:35 +0200 Subject: [PATCH 057/205] Add srcChainId to depositHandler.createDeposit, small DepositUtils refactor to solve stack too deep error --- contracts/deposit/DepositUtils.sol | 13 +++++++------ contracts/exchange/DepositHandler.sol | 2 ++ contracts/exchange/IDepositHandler.sol | 2 +- contracts/migration/GlpMigrator.sol | 2 +- contracts/multichain/MultichainGmRouter.sol | 12 +++++++----- contracts/router/ExchangeRouter.sol | 1 + contracts/router/relay/RelayUtils.sol | 1 - utils/deposit.ts | 3 +-- 8 files changed, 20 insertions(+), 16 deletions(-) diff --git a/contracts/deposit/DepositUtils.sol b/contracts/deposit/DepositUtils.sol index de142bdc7..871302cd4 100644 --- a/contracts/deposit/DepositUtils.sol +++ b/contracts/deposit/DepositUtils.sol @@ -43,7 +43,6 @@ library DepositUtils { bool shouldUnwrapNativeToken; uint256 executionFee; uint256 callbackGasLimit; - uint256 srcChainId; bytes32[] dataList; } @@ -70,6 +69,7 @@ library DepositUtils { EventEmitter eventEmitter, DepositVault depositVault, address account, + uint256 srcChainId, CreateDepositParams memory params ) external returns (bytes32) { AccountUtils.validateAccount(account); @@ -123,7 +123,7 @@ library DepositUtils { Chain.currentTimestamp(), // updatedAtTime params.executionFee, params.callbackGasLimit, - params.srcChainId + srcChainId ), Deposit.Flags( params.shouldUnwrapNativeToken @@ -133,11 +133,12 @@ library DepositUtils { CallbackUtils.validateCallbackGasLimit(dataStore, deposit.callbackGasLimit()); - uint256 estimatedGasLimit = GasUtils.estimateExecuteDepositGasLimit(dataStore, deposit); - uint256 oraclePriceCount = GasUtils.estimateDepositOraclePriceCount( - deposit.longTokenSwapPath().length + deposit.shortTokenSwapPath().length + GasUtils.validateExecutionFee( + dataStore, + GasUtils.estimateExecuteDepositGasLimit(dataStore, deposit), // estimatedGasLimit + params.executionFee, + GasUtils.estimateDepositOraclePriceCount(deposit.longTokenSwapPath().length + deposit.shortTokenSwapPath().length) // oraclePriceCount ); - GasUtils.validateExecutionFee(dataStore, estimatedGasLimit, params.executionFee, oraclePriceCount); bytes32 key = NonceUtils.getNextKey(dataStore); diff --git a/contracts/exchange/DepositHandler.sol b/contracts/exchange/DepositHandler.sol index ef740de9e..bf6824f5e 100644 --- a/contracts/exchange/DepositHandler.sol +++ b/contracts/exchange/DepositHandler.sol @@ -40,6 +40,7 @@ contract DepositHandler is IDepositHandler, BaseHandler { // @param params DepositUtils.CreateDepositParams function createDeposit( address account, + uint256 srcChainId, DepositUtils.CreateDepositParams calldata params ) external override globalNonReentrant onlyController returns (bytes32) { FeatureUtils.validateFeature(dataStore, Keys.createDepositFeatureDisabledKey(address(this))); @@ -50,6 +51,7 @@ contract DepositHandler is IDepositHandler, BaseHandler { eventEmitter, depositVault, account, + srcChainId, params ); } diff --git a/contracts/exchange/IDepositHandler.sol b/contracts/exchange/IDepositHandler.sol index 73eafaf01..fe673f5f5 100644 --- a/contracts/exchange/IDepositHandler.sol +++ b/contracts/exchange/IDepositHandler.sol @@ -6,7 +6,7 @@ import "../deposit/DepositUtils.sol"; import "../oracle/OracleUtils.sol"; interface IDepositHandler { - function createDeposit(address account, DepositUtils.CreateDepositParams calldata params) external returns (bytes32); + function createDeposit(address account, uint256 srcChainId, DepositUtils.CreateDepositParams calldata params) external returns (bytes32); function cancelDeposit(bytes32 key) external; function simulateExecuteDeposit( bytes32 key, diff --git a/contracts/migration/GlpMigrator.sol b/contracts/migration/GlpMigrator.sol index 3d2e0ec36..5108609d5 100644 --- a/contracts/migration/GlpMigrator.sol +++ b/contracts/migration/GlpMigrator.sol @@ -210,12 +210,12 @@ contract GlpMigrator is ReentrancyGuard, RoleModule { false, // shouldUnwrapNativeToken; migrationItem.executionFee, // executionFee; 0, // callbackGasLimit; - 0, // srcChainId new bytes32[](0) // dataList; ); cache.depositKey = depositHandler.createDeposit( account, + 0, // srcChainId depositParams ); diff --git a/contracts/multichain/MultichainGmRouter.sol b/contracts/multichain/MultichainGmRouter.sol index 334bc6855..fe19758b2 100644 --- a/contracts/multichain/MultichainGmRouter.sol +++ b/contracts/multichain/MultichainGmRouter.sol @@ -35,6 +35,7 @@ contract MultichainGmRouter is MultichainRouter { function createDeposit( RelayUtils.RelayParams calldata relayParams, address account, + uint256 srcChainId, RelayUtils.TransferRequest[] calldata transferRequests, DepositUtils.CreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { @@ -43,16 +44,17 @@ contract MultichainGmRouter is MultichainRouter { } bytes32 structHash = RelayUtils.getCreateDepositStructHash(relayParams, transferRequests, params); - _validateCall(relayParams, account, structHash, params.srcChainId); + _validateCall(relayParams, account, structHash, srcChainId); - _processTransferRequests(account, transferRequests, params.srcChainId); + _processTransferRequests(account, transferRequests, srcChainId); - return _createDeposit(relayParams, account, params); + return _createDeposit(relayParams, account, srcChainId, params); } function _createDeposit( RelayUtils.RelayParams calldata relayParams, address account, + uint256 srcChainId, DepositUtils.CreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee ) internal returns (bytes32) { Contracts memory contracts = Contracts({ @@ -67,10 +69,10 @@ contract MultichainGmRouter is MultichainRouter { relayParams, account, address(depositVault), // residualFeeReceiver - params.srcChainId + srcChainId ); - return depositHandler.createDeposit(account, params); + return depositHandler.createDeposit(account, srcChainId, params); } function createWithdrawal( diff --git a/contracts/router/ExchangeRouter.sol b/contracts/router/ExchangeRouter.sol index 683f1ea39..f6c86c0c8 100644 --- a/contracts/router/ExchangeRouter.sol +++ b/contracts/router/ExchangeRouter.sol @@ -128,6 +128,7 @@ contract ExchangeRouter is IExchangeRouter, BaseRouter { return depositHandler.createDeposit( account, + 0, // srcChainId params ); } diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index fb7fdbbf7..fe1d46bd2 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -332,7 +332,6 @@ library RelayUtils { params.shouldUnwrapNativeToken, params.executionFee, params.callbackGasLimit, - params.srcChainId, keccak256(abi.encodePacked(params.dataList)) ) ); diff --git a/utils/deposit.ts b/utils/deposit.ts index dd8faf5f5..863570ccc 100644 --- a/utils/deposit.ts +++ b/utils/deposit.ts @@ -75,12 +75,11 @@ export async function createDeposit(fixture, overrides: any = {}) { shouldUnwrapNativeToken, executionFee, callbackGasLimit, - srcChainId, dataList, }; const txReceipt = await logGasUsage({ - tx: depositHandler.connect(sender).createDeposit(account.address, params), + tx: depositHandler.connect(sender).createDeposit(account.address, srcChainId, params), label: overrides.gasUsageLabel, }); From 059a726ffa5c902088e878901721c6a41aa330c8 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 14 Feb 2025 16:44:02 +0200 Subject: [PATCH 058/205] Add srcChainId to withdrawalHandler.createWithdrawal, small WithdrawalUtils refactor to solve stack too deep error --- contracts/exchange/IWithdrawalHandler.sol | 2 +- contracts/exchange/WithdrawalHandler.sol | 3 +++ contracts/multichain/MultichainGmRouter.sol | 12 +++++++----- contracts/router/ExchangeRouter.sol | 1 + contracts/router/relay/RelayUtils.sol | 1 - contracts/withdrawal/WithdrawalUtils.sol | 8 ++++---- test/router/ExchangeRouter.ts | 3 +-- utils/withdrawal.ts | 3 +-- 8 files changed, 18 insertions(+), 15 deletions(-) diff --git a/contracts/exchange/IWithdrawalHandler.sol b/contracts/exchange/IWithdrawalHandler.sol index 412a26c10..e9871d9a2 100644 --- a/contracts/exchange/IWithdrawalHandler.sol +++ b/contracts/exchange/IWithdrawalHandler.sol @@ -7,7 +7,7 @@ import "../oracle/OracleUtils.sol"; import "../pricing/ISwapPricingUtils.sol"; interface IWithdrawalHandler { - function createWithdrawal(address account, WithdrawalUtils.CreateWithdrawalParams calldata params) external returns (bytes32); + function createWithdrawal(address account, uint256 srcChainId, WithdrawalUtils.CreateWithdrawalParams calldata params) external returns (bytes32); function cancelWithdrawal(bytes32 key) external; function executeAtomicWithdrawal( address account, diff --git a/contracts/exchange/WithdrawalHandler.sol b/contracts/exchange/WithdrawalHandler.sol index 90e264dd9..3ea1973b8 100644 --- a/contracts/exchange/WithdrawalHandler.sol +++ b/contracts/exchange/WithdrawalHandler.sol @@ -40,6 +40,7 @@ contract WithdrawalHandler is IWithdrawalHandler, BaseHandler { // @param params WithdrawalUtils.CreateWithdrawalParams function createWithdrawal( address account, + uint256 srcChainId, WithdrawalUtils.CreateWithdrawalParams calldata params ) external override globalNonReentrant onlyController returns (bytes32) { FeatureUtils.validateFeature(dataStore, Keys.createWithdrawalFeatureDisabledKey(address(this))); @@ -50,6 +51,7 @@ contract WithdrawalHandler is IWithdrawalHandler, BaseHandler { eventEmitter, withdrawalVault, account, + srcChainId, params, false // isAtomicWithdrawal ); @@ -153,6 +155,7 @@ contract WithdrawalHandler is IWithdrawalHandler, BaseHandler { eventEmitter, withdrawalVault, account, + 0, // srcChainId params, true // isAtomicWithdrawal ); diff --git a/contracts/multichain/MultichainGmRouter.sol b/contracts/multichain/MultichainGmRouter.sol index fe19758b2..eaa517efe 100644 --- a/contracts/multichain/MultichainGmRouter.sol +++ b/contracts/multichain/MultichainGmRouter.sol @@ -78,6 +78,7 @@ contract MultichainGmRouter is MultichainRouter { function createWithdrawal( RelayUtils.RelayParams calldata relayParams, address account, + uint256 srcChainId, RelayUtils.TransferRequest[] calldata transferRequests, WithdrawalUtils.CreateWithdrawalParams memory params // can't use calldata because need to modify params.addresses.receiver & params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { @@ -86,16 +87,17 @@ contract MultichainGmRouter is MultichainRouter { } bytes32 structHash = RelayUtils.getCreateWithdrawalStructHash(relayParams, transferRequests, params); - _validateCall(relayParams, account, structHash, params.srcChainId); + _validateCall(relayParams, account, structHash, srcChainId); - _processTransferRequests(account, transferRequests, params.srcChainId); + _processTransferRequests(account, transferRequests, srcChainId); - return _createWithdrawal(relayParams, account, params); + return _createWithdrawal(relayParams, account, srcChainId, params); } function _createWithdrawal( RelayUtils.RelayParams calldata relayParams, address account, + uint256 srcChainId, WithdrawalUtils.CreateWithdrawalParams memory params // can't use calldata because need to modify params.addresses.receiver & params.numbers.executionFee ) internal returns (bytes32) { Contracts memory contracts = Contracts({ @@ -109,10 +111,10 @@ contract MultichainGmRouter is MultichainRouter { relayParams, account, address(withdrawalVault), // residualFeeReceiver - params.srcChainId + srcChainId ); - return withdrawalHandler.createWithdrawal(account, params); + return withdrawalHandler.createWithdrawal(account, srcChainId, params); } function createShift( diff --git a/contracts/router/ExchangeRouter.sol b/contracts/router/ExchangeRouter.sol index f6c86c0c8..45f65c15c 100644 --- a/contracts/router/ExchangeRouter.sol +++ b/contracts/router/ExchangeRouter.sol @@ -167,6 +167,7 @@ contract ExchangeRouter is IExchangeRouter, BaseRouter { return withdrawalHandler.createWithdrawal( account, + 0, // srcChainId params ); } diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index fe1d46bd2..ec7fb8bdb 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -390,7 +390,6 @@ library RelayUtils { params.shouldUnwrapNativeToken, params.executionFee, params.callbackGasLimit, - params.srcChainId, keccak256(abi.encodePacked(params.dataList)) ) ); diff --git a/contracts/withdrawal/WithdrawalUtils.sol b/contracts/withdrawal/WithdrawalUtils.sol index 25094ba42..a18abe558 100644 --- a/contracts/withdrawal/WithdrawalUtils.sol +++ b/contracts/withdrawal/WithdrawalUtils.sol @@ -58,7 +58,6 @@ library WithdrawalUtils { bool shouldUnwrapNativeToken; uint256 executionFee; uint256 callbackGasLimit; - uint256 srcChainId; bytes32[] dataList; } @@ -77,6 +76,7 @@ library WithdrawalUtils { EventEmitter eventEmitter, WithdrawalVault withdrawalVault, address account, + uint256 srcChainId, CreateWithdrawalParams memory params, bool isAtomicWithdrawal ) external returns (bytes32) { @@ -119,7 +119,7 @@ library WithdrawalUtils { Chain.currentTimestamp(), // updatedAtTime params.executionFee, params.callbackGasLimit, - params.srcChainId + srcChainId ), Withdrawal.Flags( params.shouldUnwrapNativeToken @@ -129,9 +129,9 @@ library WithdrawalUtils { CallbackUtils.validateCallbackGasLimit(dataStore, withdrawal.callbackGasLimit()); - uint256 estimatedGasLimit = GasUtils.estimateExecuteWithdrawalGasLimit(dataStore, withdrawal); - uint256 oraclePriceCount = GasUtils.estimateWithdrawalOraclePriceCount(withdrawal.longTokenSwapPath().length + withdrawal.shortTokenSwapPath().length); if (!isAtomicWithdrawal) { + uint256 estimatedGasLimit = GasUtils.estimateExecuteWithdrawalGasLimit(dataStore, withdrawal); + uint256 oraclePriceCount = GasUtils.estimateWithdrawalOraclePriceCount(withdrawal.longTokenSwapPath().length + withdrawal.shortTokenSwapPath().length); GasUtils.validateExecutionFee(dataStore, estimatedGasLimit, params.executionFee, oraclePriceCount); } diff --git a/test/router/ExchangeRouter.ts b/test/router/ExchangeRouter.ts index be33997b6..03bd25b1b 100644 --- a/test/router/ExchangeRouter.ts +++ b/test/router/ExchangeRouter.ts @@ -220,7 +220,6 @@ describe("ExchangeRouter", () => { shouldUnwrapNativeToken: true, executionFee, callbackGasLimit: "200000", - srcChainId: 1, dataList, }, ]), @@ -243,7 +242,7 @@ describe("ExchangeRouter", () => { expect(withdrawal.numbers.minShortTokenAmount).eq(900); expect(withdrawal.numbers.executionFee).eq(expandDecimals(1, 18)); expect(withdrawal.numbers.callbackGasLimit).eq("200000"); - expect(withdrawal.numbers.srcChainId).eq(1); + expect(withdrawal.numbers.srcChainId).eq(0); expect(withdrawal.flags.shouldUnwrapNativeToken).eq(true); expect(withdrawal._dataList).deep.eq(dataList); diff --git a/utils/withdrawal.ts b/utils/withdrawal.ts index 1f0a55229..8c691b106 100644 --- a/utils/withdrawal.ts +++ b/utils/withdrawal.ts @@ -62,12 +62,11 @@ export async function createWithdrawal(fixture, overrides: any = {}) { shouldUnwrapNativeToken, executionFee, callbackGasLimit, - srcChainId, dataList, }; await logGasUsage({ - tx: withdrawalHandler.connect(wallet).createWithdrawal(account.address, params), + tx: withdrawalHandler.connect(wallet).createWithdrawal(account.address, srcChainId, params), label: overrides.gasUsageLabel, }); } From 2a02ee74a7c2dc154c40a9fc51861e42fe11320d Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 14 Feb 2025 16:58:58 +0200 Subject: [PATCH 059/205] Add srcChainId to shiftHandler.createShift --- contracts/exchange/IShiftHandler.sol | 2 +- contracts/exchange/ShiftHandler.sol | 2 ++ contracts/multichain/MultichainGmRouter.sol | 11 +++++++---- contracts/router/ExchangeRouter.sol | 1 + contracts/router/relay/RelayUtils.sol | 1 - contracts/shift/ShiftUtils.sol | 4 ++-- utils/shift.ts | 3 +-- 7 files changed, 14 insertions(+), 10 deletions(-) diff --git a/contracts/exchange/IShiftHandler.sol b/contracts/exchange/IShiftHandler.sol index 9998ca06f..f476b6f18 100644 --- a/contracts/exchange/IShiftHandler.sol +++ b/contracts/exchange/IShiftHandler.sol @@ -6,7 +6,7 @@ import "../shift/ShiftUtils.sol"; import "../oracle/OracleUtils.sol"; interface IShiftHandler { - function createShift(address account, ShiftUtils.CreateShiftParams calldata params) external returns (bytes32); + function createShift(address account, uint256 srcChainId, ShiftUtils.CreateShiftParams calldata params) external returns (bytes32); function cancelShift(bytes32 key) external; function simulateExecuteShift(bytes32 key, OracleUtils.SimulatePricesParams memory params) external; } diff --git a/contracts/exchange/ShiftHandler.sol b/contracts/exchange/ShiftHandler.sol index f19e208f3..d17483aad 100644 --- a/contracts/exchange/ShiftHandler.sol +++ b/contracts/exchange/ShiftHandler.sol @@ -28,6 +28,7 @@ contract ShiftHandler is IShiftHandler, BaseHandler { function createShift( address account, + uint256 srcChainId, ShiftUtils.CreateShiftParams calldata params ) external override globalNonReentrant onlyController returns (bytes32) { FeatureUtils.validateFeature(dataStore, Keys.createShiftFeatureDisabledKey(address(this))); @@ -38,6 +39,7 @@ contract ShiftHandler is IShiftHandler, BaseHandler { eventEmitter, shiftVault, account, + srcChainId, params ); } diff --git a/contracts/multichain/MultichainGmRouter.sol b/contracts/multichain/MultichainGmRouter.sol index eaa517efe..efae3584c 100644 --- a/contracts/multichain/MultichainGmRouter.sol +++ b/contracts/multichain/MultichainGmRouter.sol @@ -120,6 +120,7 @@ contract MultichainGmRouter is MultichainRouter { function createShift( RelayUtils.RelayParams calldata relayParams, address account, + uint256 srcChainId, RelayUtils.TransferRequest[] calldata transferRequests, ShiftUtils.CreateShiftParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { @@ -128,16 +129,17 @@ contract MultichainGmRouter is MultichainRouter { } bytes32 structHash = RelayUtils.getCreateShiftStructHash(relayParams, transferRequests, params); - _validateCall(relayParams, account, structHash, params.srcChainId); + _validateCall(relayParams, account, structHash, srcChainId); - _processTransferRequests(account, transferRequests, params.srcChainId); + _processTransferRequests(account, transferRequests, srcChainId); - return _createShift(relayParams, account, params); + return _createShift(relayParams, account, srcChainId, params); } function _createShift( RelayUtils.RelayParams calldata relayParams, address account, + uint256 srcChainId, ShiftUtils.CreateShiftParams memory params ) internal returns (bytes32) { Contracts memory contracts = Contracts({ @@ -151,7 +153,7 @@ contract MultichainGmRouter is MultichainRouter { relayParams, account, address(shiftVault), - params.srcChainId + srcChainId ); return ShiftUtils.createShift( @@ -159,6 +161,7 @@ contract MultichainGmRouter is MultichainRouter { eventEmitter, shiftVault, account, + srcChainId, params ); } diff --git a/contracts/router/ExchangeRouter.sol b/contracts/router/ExchangeRouter.sol index 45f65c15c..6c19ff88e 100644 --- a/contracts/router/ExchangeRouter.sol +++ b/contracts/router/ExchangeRouter.sol @@ -217,6 +217,7 @@ contract ExchangeRouter is IExchangeRouter, BaseRouter { return shiftHandler.createShift( account, + 0, // srcChainId params ); } diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index ec7fb8bdb..506f87f77 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -534,7 +534,6 @@ library RelayUtils { params.minMarketTokens, params.executionFee, params.callbackGasLimit, - params.srcChainId, keccak256(abi.encodePacked(params.dataList)) ) ); diff --git a/contracts/shift/ShiftUtils.sol b/contracts/shift/ShiftUtils.sol index cdb8691d4..fcb479efe 100644 --- a/contracts/shift/ShiftUtils.sol +++ b/contracts/shift/ShiftUtils.sol @@ -40,7 +40,6 @@ library ShiftUtils { uint256 minMarketTokens; uint256 executionFee; uint256 callbackGasLimit; - uint256 srcChainId; bytes32[] dataList; } @@ -79,6 +78,7 @@ library ShiftUtils { EventEmitter eventEmitter, ShiftVault shiftVault, address account, + uint256 srcChainId, CreateShiftParams memory params ) external returns (bytes32) { AccountUtils.validateAccount(account); @@ -130,7 +130,7 @@ library ShiftUtils { Chain.currentTimestamp(), params.executionFee, params.callbackGasLimit, - params.srcChainId + srcChainId ), params.dataList ); diff --git a/utils/shift.ts b/utils/shift.ts index 7628a7149..b1c92579f 100644 --- a/utils/shift.ts +++ b/utils/shift.ts @@ -56,12 +56,11 @@ export async function createShift(fixture, overrides: any = {}) { minMarketTokens, executionFee, callbackGasLimit, - srcChainId, dataList, }; const txReceipt = await logGasUsage({ - tx: shiftHandler.connect(sender).createShift(account.address, params), + tx: shiftHandler.connect(sender).createShift(account.address, srcChainId, params), label: overrides.gasUsageLabel, }); From 0aadaaf20973a607ba2c83646510f2df15975a50 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 15 Feb 2025 10:44:09 +0200 Subject: [PATCH 060/205] Add srcChainId to glvHandler (createGlvDeposit, createGlvWithdrawal), small GlvWithdrawalUtils refactor to solve stack too deep error --- contracts/exchange/GlvHandler.sol | 6 +++-- contracts/exchange/IGlvHandler.sol | 2 ++ contracts/glv/glvDeposit/GlvDepositUtils.sol | 4 ++-- .../glv/glvWithdrawal/GlvWithdrawalUtils.sol | 16 +++++-------- contracts/multichain/MultichainGlvRouter.sol | 23 +++++++++++-------- contracts/router/GlvRouter.sol | 4 ++-- contracts/router/relay/RelayUtils.sol | 2 -- utils/glv/glvDeposit.ts | 3 +-- utils/glv/glvWithdrawal.ts | 3 +-- 9 files changed, 32 insertions(+), 31 deletions(-) diff --git a/contracts/exchange/GlvHandler.sol b/contracts/exchange/GlvHandler.sol index 0232841b2..7e1f391f8 100644 --- a/contracts/exchange/GlvHandler.sol +++ b/contracts/exchange/GlvHandler.sol @@ -36,12 +36,13 @@ contract GlvHandler is BaseHandler, ReentrancyGuard { function createGlvDeposit( address account, + uint256 srcChainId, GlvDepositUtils.CreateGlvDepositParams calldata params ) external globalNonReentrant onlyController returns (bytes32) { FeatureUtils.validateFeature(dataStore, Keys.createGlvDepositFeatureDisabledKey(address(this))); validateDataListLength(params.dataList.length); - return GlvDepositUtils.createGlvDeposit(dataStore, eventEmitter, glvVault, account, params); + return GlvDepositUtils.createGlvDeposit(dataStore, eventEmitter, glvVault, account, srcChainId, params); } // @key glvDeposit key @@ -133,12 +134,13 @@ contract GlvHandler is BaseHandler, ReentrancyGuard { function createGlvWithdrawal( address account, + uint256 srcChainId, GlvWithdrawalUtils.CreateGlvWithdrawalParams calldata params ) external globalNonReentrant onlyController returns (bytes32) { DataStore _dataStore = dataStore; FeatureUtils.validateFeature(_dataStore, Keys.createGlvWithdrawalFeatureDisabledKey(address(this))); - return GlvWithdrawalUtils.createGlvWithdrawal(_dataStore, eventEmitter, glvVault, account, params); + return GlvWithdrawalUtils.createGlvWithdrawal(_dataStore, eventEmitter, glvVault, account, srcChainId, params); } // @key glvDeposit key diff --git a/contracts/exchange/IGlvHandler.sol b/contracts/exchange/IGlvHandler.sol index 045c5747a..864b667fb 100644 --- a/contracts/exchange/IGlvHandler.sol +++ b/contracts/exchange/IGlvHandler.sol @@ -9,6 +9,7 @@ import "../oracle/OracleUtils.sol"; interface IGlvHandler { function createGlvDeposit( address account, + uint256 srcChainId, GlvDepositUtils.CreateGlvDepositParams calldata params ) external payable returns (bytes32); @@ -18,6 +19,7 @@ interface IGlvHandler { function createGlvWithdrawal( address account, + uint256 srcChainId, GlvWithdrawalUtils.CreateGlvWithdrawalParams calldata params ) external payable returns (bytes32); diff --git a/contracts/glv/glvDeposit/GlvDepositUtils.sol b/contracts/glv/glvDeposit/GlvDepositUtils.sol index 0d002bb24..8ecb4c4b1 100644 --- a/contracts/glv/glvDeposit/GlvDepositUtils.sol +++ b/contracts/glv/glvDeposit/GlvDepositUtils.sol @@ -19,7 +19,6 @@ library GlvDepositUtils { uint256 minGlvTokens; uint256 executionFee; uint256 callbackGasLimit; - uint256 srcChainId; bool shouldUnwrapNativeToken; bool isMarketTokenDeposit; bytes32[] dataList; @@ -61,6 +60,7 @@ library GlvDepositUtils { EventEmitter eventEmitter, GlvVault glvVault, address account, + uint256 srcChainId, CreateGlvDepositParams memory params ) external returns (bytes32) { AccountUtils.validateAccount(account); @@ -156,7 +156,7 @@ library GlvDepositUtils { updatedAtTime: Chain.currentTimestamp(), executionFee: params.executionFee, callbackGasLimit: params.callbackGasLimit, - srcChainId: params.srcChainId + srcChainId: srcChainId }), GlvDeposit.Flags({ shouldUnwrapNativeToken: params.shouldUnwrapNativeToken, diff --git a/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol b/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol index e38fb665a..68177b17b 100644 --- a/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol +++ b/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol @@ -25,7 +25,6 @@ library GlvWithdrawalUtils { bool shouldUnwrapNativeToken; uint256 executionFee; uint256 callbackGasLimit; - uint256 srcChainId; bytes32[] dataList; } @@ -73,6 +72,7 @@ library GlvWithdrawalUtils { EventEmitter eventEmitter, GlvVault glvVault, address account, + uint256 srcChainId, CreateGlvWithdrawalParams memory params ) external returns (bytes32) { AccountUtils.validateAccount(account); @@ -116,7 +116,7 @@ library GlvWithdrawalUtils { updatedAtTime: Chain.currentTimestamp(), executionFee: params.executionFee, callbackGasLimit: params.callbackGasLimit, - srcChainId: params.srcChainId + srcChainId: srcChainId }), GlvWithdrawal.Flags({shouldUnwrapNativeToken: params.shouldUnwrapNativeToken}), params.dataList @@ -125,16 +125,12 @@ library GlvWithdrawalUtils { CallbackUtils.validateCallbackGasLimit(dataStore, params.callbackGasLimit); uint256 marketCount = GlvUtils.getGlvMarketCount(dataStore, glvWithdrawal.glv()); - uint256 estimatedGasLimit = GasUtils.estimateExecuteGlvWithdrawalGasLimit( + GasUtils.validateExecutionFee( dataStore, - glvWithdrawal, - marketCount - ); - uint256 oraclePriceCount = GasUtils.estimateGlvWithdrawalOraclePriceCount( - marketCount, - params.addresses.longTokenSwapPath.length + params.addresses.shortTokenSwapPath.length + GasUtils.estimateExecuteGlvWithdrawalGasLimit(dataStore, glvWithdrawal, marketCount), // estimatedGasLimit + params.executionFee, + GasUtils.estimateGlvWithdrawalOraclePriceCount(marketCount, params.addresses.longTokenSwapPath.length + params.addresses.shortTokenSwapPath.length) // oraclePriceCount ); - GasUtils.validateExecutionFee(dataStore, estimatedGasLimit, params.executionFee, oraclePriceCount); bytes32 key = NonceUtils.getNextKey(dataStore); diff --git a/contracts/multichain/MultichainGlvRouter.sol b/contracts/multichain/MultichainGlvRouter.sol index 9c01504c0..4664df413 100644 --- a/contracts/multichain/MultichainGlvRouter.sol +++ b/contracts/multichain/MultichainGlvRouter.sol @@ -24,6 +24,7 @@ contract MultichainGlvRouter is MultichainRouter { function createGlvDeposit( RelayUtils.RelayParams calldata relayParams, address account, + uint256 srcChainId, RelayUtils.TransferRequest[] calldata transferRequests, GlvDepositUtils.CreateGlvDepositParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { @@ -32,16 +33,17 @@ contract MultichainGlvRouter is MultichainRouter { } bytes32 structHash = RelayUtils.getCreateGlvDepositStructHash(relayParams, transferRequests, params); - _validateCall(relayParams, account, structHash, params.srcChainId); + _validateCall(relayParams, account, structHash, srcChainId); - _processTransferRequests(account, transferRequests, params.srcChainId); + _processTransferRequests(account, transferRequests, srcChainId); - return _createGlvDeposit(relayParams, account, params); + return _createGlvDeposit(relayParams, account, srcChainId, params); } function _createGlvDeposit( RelayUtils.RelayParams calldata relayParams, address account, + uint256 srcChainId, GlvDepositUtils.CreateGlvDepositParams memory params ) internal returns (bytes32) { Contracts memory contracts = Contracts({ @@ -56,15 +58,16 @@ contract MultichainGlvRouter is MultichainRouter { relayParams, account, address(glvVault), - params.srcChainId + srcChainId ); - return glvHandler.createGlvDeposit(account, params); + return glvHandler.createGlvDeposit(account, srcChainId, params); } function createGlvWithdrawal( RelayUtils.RelayParams calldata relayParams, address account, + uint256 srcChainId, RelayUtils.TransferRequest[] calldata transferRequests, GlvWithdrawalUtils.CreateGlvWithdrawalParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { @@ -73,16 +76,17 @@ contract MultichainGlvRouter is MultichainRouter { } bytes32 structHash = RelayUtils.getCreateGlvWithdrawalStructHash(relayParams, transferRequests, params); - _validateCall(relayParams, account, structHash, params.srcChainId); + _validateCall(relayParams, account, structHash, srcChainId); - _processTransferRequests(account, transferRequests, params.srcChainId); + _processTransferRequests(account, transferRequests, srcChainId); - return _createGlvWithdrawal(relayParams, account, params); + return _createGlvWithdrawal(relayParams, account, srcChainId, params); } function _createGlvWithdrawal( RelayUtils.RelayParams calldata relayParams, address account, + uint256 srcChainId, GlvWithdrawalUtils.CreateGlvWithdrawalParams memory params ) internal returns (bytes32) { Contracts memory contracts = Contracts({ @@ -97,7 +101,7 @@ contract MultichainGlvRouter is MultichainRouter { relayParams, account, address(glvVault), // residualFeeReceiver - params.srcChainId + srcChainId ); return GlvWithdrawalUtils.createGlvWithdrawal( @@ -105,6 +109,7 @@ contract MultichainGlvRouter is MultichainRouter { eventEmitter, glvVault, account, + srcChainId, params ); } diff --git a/contracts/router/GlvRouter.sol b/contracts/router/GlvRouter.sol index 7eae15851..55f4e727e 100644 --- a/contracts/router/GlvRouter.sol +++ b/contracts/router/GlvRouter.sol @@ -37,7 +37,7 @@ contract GlvRouter is BaseRouter { ) external payable nonReentrant returns (bytes32) { address account = msg.sender; - return glvHandler.createGlvDeposit(account, params); + return glvHandler.createGlvDeposit(account, 0 /* srcChainId */, params); } function cancelGlvDeposit(bytes32 key) external nonReentrant { @@ -72,7 +72,7 @@ contract GlvRouter is BaseRouter { ) external payable nonReentrant returns (bytes32) { address account = msg.sender; - return glvHandler.createGlvWithdrawal(account, params); + return glvHandler.createGlvWithdrawal(account, 0 /* srcChainId */, params); } function cancelGlvWithdrawal(bytes32 key) external nonReentrant { diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index 506f87f77..c3e28211a 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -422,7 +422,6 @@ library RelayUtils { params.minGlvTokens, params.executionFee, params.callbackGasLimit, - params.srcChainId, params.shouldUnwrapNativeToken, params.isMarketTokenDeposit, keccak256(abi.encodePacked(params.dataList)) @@ -479,7 +478,6 @@ library RelayUtils { params.shouldUnwrapNativeToken, params.executionFee, params.callbackGasLimit, - params.srcChainId, keccak256(abi.encodePacked(params.dataList)) ) ); diff --git a/utils/glv/glvDeposit.ts b/utils/glv/glvDeposit.ts index 84e75695d..318dbc2ae 100644 --- a/utils/glv/glvDeposit.ts +++ b/utils/glv/glvDeposit.ts @@ -98,7 +98,6 @@ export async function createGlvDeposit(fixture, overrides: any = {}) { shouldUnwrapNativeToken, executionFee, callbackGasLimit, - srcChainId, isMarketTokenDeposit, gasUsageLabel, dataList, @@ -113,7 +112,7 @@ export async function createGlvDeposit(fixture, overrides: any = {}) { const txReceipt = await logGasUsage({ tx: useGlvHandler - ? glvHandler.connect(sender).createGlvDeposit(account.address, params) + ? glvHandler.connect(sender).createGlvDeposit(account.address, srcChainId, params) : glvRouter.connect(account).createGlvDeposit(params), label: gasUsageLabel, }); diff --git a/utils/glv/glvWithdrawal.ts b/utils/glv/glvWithdrawal.ts index d870a548a..cf1ebe839 100644 --- a/utils/glv/glvWithdrawal.ts +++ b/utils/glv/glvWithdrawal.ts @@ -82,7 +82,6 @@ export async function createGlvWithdrawal(fixture, overrides: any = {}) { shouldUnwrapNativeToken, executionFee, callbackGasLimit, - srcChainId, dataList, }; @@ -94,7 +93,7 @@ export async function createGlvWithdrawal(fixture, overrides: any = {}) { await logGasUsage({ tx: useGlvHandler - ? glvHandler.connect(sender).createGlvWithdrawal(account.address, params) + ? glvHandler.connect(sender).createGlvWithdrawal(account.address, srcChainId, params) : glvRouter.connect(account).createGlvWithdrawal(params), label: gasUsageLabel, }); From d72a86a3a2cef70062621d3f390d9eb0d1da05dd Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 15 Feb 2025 12:42:43 +0200 Subject: [PATCH 061/205] Add srcChainId to orderHandler.createOrder, small OrderUtils refactor to solve stack too deep error --- contracts/exchange/IOrderHandler.sol | 1 + contracts/exchange/OrderHandler.sol | 2 ++ contracts/fee/FeeSwapUtils.sol | 3 +-- contracts/order/IBaseOrderUtils.sol | 1 - contracts/order/OrderUtils.sol | 9 ++++----- contracts/router/ExchangeRouter.sol | 1 + contracts/router/SubaccountRouter.sol | 1 + contracts/router/relay/BaseGelatoRelayRouter.sol | 7 ++++--- contracts/router/relay/GelatoRelayRouter.sol | 4 ++-- contracts/router/relay/RelayUtils.sol | 3 +-- contracts/router/relay/SubaccountGelatoRelayRouter.sol | 7 +++---- test/router/ExchangeRouter.ts | 3 +-- test/router/SubaccountRouter.ts | 8 ++------ utils/order.ts | 3 +-- 14 files changed, 24 insertions(+), 29 deletions(-) diff --git a/contracts/exchange/IOrderHandler.sol b/contracts/exchange/IOrderHandler.sol index 1ecef1a78..c19f1dd30 100644 --- a/contracts/exchange/IOrderHandler.sol +++ b/contracts/exchange/IOrderHandler.sol @@ -8,6 +8,7 @@ import "../oracle/OracleUtils.sol"; interface IOrderHandler { function createOrder( address account, + uint256 srcChainId, IBaseOrderUtils.CreateOrderParams calldata params, bool shouldValidateMaxExecutionFee ) external returns (bytes32); diff --git a/contracts/exchange/OrderHandler.sol b/contracts/exchange/OrderHandler.sol index b589734f0..2aab30e98 100644 --- a/contracts/exchange/OrderHandler.sol +++ b/contracts/exchange/OrderHandler.sol @@ -38,6 +38,7 @@ contract OrderHandler is IOrderHandler, BaseOrderHandler { // @param params BaseOrderUtils.CreateOrderParams function createOrder( address account, + uint256 srcChainId, IBaseOrderUtils.CreateOrderParams calldata params, bool shouldValidateMaxExecutionFee ) external override globalNonReentrant onlyController returns (bytes32) { @@ -50,6 +51,7 @@ contract OrderHandler is IOrderHandler, BaseOrderHandler { orderVault, referralStorage, account, + srcChainId, params, shouldValidateMaxExecutionFee ); diff --git a/contracts/fee/FeeSwapUtils.sol b/contracts/fee/FeeSwapUtils.sol index 8bdcc4d6a..09bffaf68 100644 --- a/contracts/fee/FeeSwapUtils.sol +++ b/contracts/fee/FeeSwapUtils.sol @@ -130,8 +130,7 @@ library FeeSwapUtils { executionFee, // executionFee maxCallbackGasLimit, // callbackGasLimit minOut, // minOutputAmount - 0, // validFromTime - 0 // srcChainId + 0 // validFromTime ); IBaseOrderUtils.CreateOrderParams memory params = IBaseOrderUtils.CreateOrderParams( diff --git a/contracts/order/IBaseOrderUtils.sol b/contracts/order/IBaseOrderUtils.sol index b89171784..7fca4a8c1 100644 --- a/contracts/order/IBaseOrderUtils.sol +++ b/contracts/order/IBaseOrderUtils.sol @@ -55,6 +55,5 @@ interface IBaseOrderUtils { uint256 callbackGasLimit; uint256 minOutputAmount; uint256 validFromTime; - uint256 srcChainId; } } diff --git a/contracts/order/OrderUtils.sol b/contracts/order/OrderUtils.sol index 67f958914..29eca467b 100644 --- a/contracts/order/OrderUtils.sol +++ b/contracts/order/OrderUtils.sol @@ -64,6 +64,7 @@ library OrderUtils { OrderVault orderVault, IReferralStorage referralStorage, address account, + uint256 srcChainId, IBaseOrderUtils.CreateOrderParams memory params, bool shouldValidateMaxExecutionFee ) external returns (bytes32) { @@ -153,7 +154,7 @@ library OrderUtils { order.setCallbackGasLimit(params.numbers.callbackGasLimit); order.setMinOutputAmount(params.numbers.minOutputAmount); order.setValidFromTime(params.numbers.validFromTime); - order.setSrcChainId(params.numbers.srcChainId); + order.setSrcChainId(srcChainId); order.setIsLong(params.isLong); order.setShouldUnwrapNativeToken(params.shouldUnwrapNativeToken); order.setAutoCancel(params.autoCancel); @@ -167,13 +168,11 @@ library OrderUtils { CallbackUtils.validateCallbackGasLimit(dataStore, order.callbackGasLimit()); - uint256 estimatedGasLimit = GasUtils.estimateExecuteOrderGasLimit(dataStore, order); - uint256 oraclePriceCount = GasUtils.estimateOrderOraclePriceCount(params.addresses.swapPath.length); GasUtils.validateExecutionFee( dataStore, - estimatedGasLimit, + GasUtils.estimateExecuteOrderGasLimit(dataStore, order), // estimatedGasLimit order.executionFee(), - oraclePriceCount, + GasUtils.estimateOrderOraclePriceCount(params.addresses.swapPath.length), // oraclePriceCount shouldValidateMaxExecutionFee ); diff --git a/contracts/router/ExchangeRouter.sol b/contracts/router/ExchangeRouter.sol index 6c19ff88e..f2257d1e7 100644 --- a/contracts/router/ExchangeRouter.sol +++ b/contracts/router/ExchangeRouter.sol @@ -258,6 +258,7 @@ contract ExchangeRouter is IExchangeRouter, BaseRouter { return orderHandler.createOrder( account, + 0, // srcChainId params, false ); diff --git a/contracts/router/SubaccountRouter.sol b/contracts/router/SubaccountRouter.sol index d323f30a3..3913b4f88 100644 --- a/contracts/router/SubaccountRouter.sol +++ b/contracts/router/SubaccountRouter.sol @@ -126,6 +126,7 @@ contract SubaccountRouter is BaseRouter { bytes32 key = orderHandler.createOrder( account, + 0, // srcChainId params, params.addresses.callbackContract != address(0) ); diff --git a/contracts/router/relay/BaseGelatoRelayRouter.sol b/contracts/router/relay/BaseGelatoRelayRouter.sol index b72aed770..be34edfc9 100644 --- a/contracts/router/relay/BaseGelatoRelayRouter.sol +++ b/contracts/router/relay/BaseGelatoRelayRouter.sol @@ -79,6 +79,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, RelayUtils.RelayParams calldata relayParams, address account, uint256 collateralDeltaAmount, + uint256 srcChainId, IBaseOrderUtils.CreateOrderParams memory params, // can't use calldata because need to modify params.numbers.executionFee bool isSubaccount ) internal returns (bytes32) { @@ -88,7 +89,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, bank: orderVault }); - params.numbers.executionFee = _handleRelay(contracts, relayParams, account, address(contracts.bank), params.numbers.srcChainId); + params.numbers.executionFee = _handleRelay(contracts, relayParams, account, address(contracts.bank), srcChainId); if ( params.orderType == Order.OrderType.MarketSwap || @@ -102,12 +103,12 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, params.addresses.initialCollateralToken, address(contracts.bank), collateralDeltaAmount, - params.numbers.srcChainId + srcChainId ); } return - orderHandler.createOrder(account, params, isSubaccount && params.addresses.callbackContract != address(0)); + orderHandler.createOrder(account, srcChainId, params, isSubaccount && params.addresses.callbackContract != address(0)); } function _updateOrder( diff --git a/contracts/router/relay/GelatoRelayRouter.sol b/contracts/router/relay/GelatoRelayRouter.sol index d1e22bd7c..087d62f9f 100644 --- a/contracts/router/relay/GelatoRelayRouter.sol +++ b/contracts/router/relay/GelatoRelayRouter.sol @@ -38,9 +38,9 @@ contract GelatoRelayRouter is BaseGelatoRelayRouter { { _validateGaslessFeature(); bytes32 structHash = RelayUtils.getCreateOrderStructHash(relayParams, collateralDeltaAmount, params); - _validateCall(relayParams, account, structHash, params.numbers.srcChainId); + _validateCall(relayParams, account, structHash, 0 /* srcChainId */); - return _createOrder(relayParams, account, collateralDeltaAmount, params, false); + return _createOrder(relayParams, account, 0 /* srcChainId */, collateralDeltaAmount, params, false); } // @note all params except account should be part of the corresponding struct hash diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index c3e28211a..c5fd253f4 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -278,8 +278,7 @@ library RelayUtils { numbers.executionFee, numbers.callbackGasLimit, numbers.minOutputAmount, - numbers.validFromTime, - numbers.srcChainId + numbers.validFromTime ) ); } diff --git a/contracts/router/relay/SubaccountGelatoRelayRouter.sol b/contracts/router/relay/SubaccountGelatoRelayRouter.sol index 4d27539ff..b513904ea 100644 --- a/contracts/router/relay/SubaccountGelatoRelayRouter.sol +++ b/contracts/router/relay/SubaccountGelatoRelayRouter.sol @@ -102,7 +102,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { collateralDeltaAmount, params ); - _validateCall(relayParams, subaccount, structHash, params.numbers.srcChainId); + _validateCall(relayParams, subaccount, structHash, 0 /* srcChainId */); _handleSubaccountAction(account, subaccount, Keys.SUBACCOUNT_ORDER_ACTION, subaccountApproval); if (params.addresses.receiver != account) { @@ -113,7 +113,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { revert Errors.InvalidCancellationReceiverForSubaccountOrder(params.addresses.cancellationReceiver, account); } - return _createOrder(relayParams, account, collateralDeltaAmount, params, true); + return _createOrder(relayParams, account, 0 /* srcChainId */, collateralDeltaAmount, params, true); } // @note all params except subaccount should be part of the corresponding struct hash @@ -327,8 +327,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { numbers.executionFee, numbers.callbackGasLimit, numbers.minOutputAmount, - numbers.validFromTime, - numbers.srcChainId + numbers.validFromTime ) ); } diff --git a/test/router/ExchangeRouter.ts b/test/router/ExchangeRouter.ts index 03bd25b1b..b093b9fb9 100644 --- a/test/router/ExchangeRouter.ts +++ b/test/router/ExchangeRouter.ts @@ -139,7 +139,6 @@ describe("ExchangeRouter", () => { callbackGasLimit: "200000", minOutputAmount: 700, validFromTime: 0, - srcChainId: 1, }, orderType: OrderType.LimitIncrease, decreasePositionSwapType: DecreasePositionSwapType.SwapCollateralTokenToPnlToken, @@ -171,7 +170,7 @@ describe("ExchangeRouter", () => { expect(order.numbers.executionFee).eq(expandDecimals(1, 18)); expect(order.numbers.callbackGasLimit).eq("200000"); expect(order.numbers.minOutputAmount).eq(700); - expect(order.numbers.srcChainId).eq(1); + expect(order.numbers.srcChainId).eq(0); expect(order.flags.isLong).eq(true); expect(order.flags.shouldUnwrapNativeToken).eq(true); diff --git a/test/router/SubaccountRouter.ts b/test/router/SubaccountRouter.ts index bd96cab00..272e8d5de 100644 --- a/test/router/SubaccountRouter.ts +++ b/test/router/SubaccountRouter.ts @@ -158,7 +158,6 @@ describe("SubaccountRouter", () => { callbackGasLimit: "200000", minOutputAmount: 700, validFromTime: 0, - srcChainId: 0, }, orderType: OrderType.Liquidation, decreasePositionSwapType: DecreasePositionSwapType.SwapCollateralTokenToPnlToken, @@ -351,7 +350,6 @@ describe("SubaccountRouter", () => { callbackGasLimit: "200000", minOutputAmount: 700, validFromTime: 0, - srcChainId: 0, }, orderType: OrderType.MarketIncrease, decreasePositionSwapType: DecreasePositionSwapType.SwapCollateralTokenToPnlToken, @@ -494,7 +492,6 @@ describe("SubaccountRouter", () => { callbackGasLimit: "200000", minOutputAmount: 700, validFromTime: 800, - srcChainId: 1, }, orderType: OrderType.LimitIncrease, decreasePositionSwapType: DecreasePositionSwapType.SwapCollateralTokenToPnlToken, @@ -531,7 +528,7 @@ describe("SubaccountRouter", () => { expect(order.numbers.triggerPrice).eq(expandDecimals(4800, 12)); expect(order.numbers.minOutputAmount).eq(700); expect(order.numbers.validFromTime).eq(800); - expect(order.numbers.srcChainId).eq(1); + expect(order.numbers.srcChainId).eq(0); expect(order._dataList).deep.eq(dataList); }); @@ -641,7 +638,6 @@ describe("SubaccountRouter", () => { callbackGasLimit: "200000", minOutputAmount: 700, validFromTime: 800, - srcChainId: 1, }, orderType: OrderType.LimitIncrease, decreasePositionSwapType: DecreasePositionSwapType.SwapCollateralTokenToPnlToken, @@ -678,7 +674,7 @@ describe("SubaccountRouter", () => { expect(order.numbers.triggerPrice).eq(expandDecimals(4800, 12)); expect(order.numbers.minOutputAmount).eq(700); expect(order.numbers.validFromTime).eq(800); - expect(order.numbers.srcChainId).eq(1); + expect(order.numbers.srcChainId).eq(0); expect(order._dataList).deep.eq(dataList); }); diff --git a/utils/order.ts b/utils/order.ts index 96b8846e4..906141b93 100644 --- a/utils/order.ts +++ b/utils/order.ts @@ -131,7 +131,6 @@ export async function createOrder(fixture, overrides) { callbackGasLimit, minOutputAmount, validFromTime, - srcChainId, }, orderType, decreasePositionSwapType, @@ -143,7 +142,7 @@ export async function createOrder(fixture, overrides) { }; const txReceipt = await logGasUsage({ - tx: orderHandler.connect(sender).createOrder(account.address, params, false), + tx: orderHandler.connect(sender).createOrder(account.address, srcChainId, params, false), label: gasUsageLabel, }); From f3777d0474727a7c65a767532ac5ca871963522c Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 16 Feb 2025 09:23:17 +0200 Subject: [PATCH 062/205] Remove srcChainId from non-multichain orders --- contracts/router/relay/GelatoRelayRouter.sol | 6 ++--- .../relay/SubaccountGelatoRelayRouter.sol | 26 ++++++++----------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/contracts/router/relay/GelatoRelayRouter.sol b/contracts/router/relay/GelatoRelayRouter.sol index 087d62f9f..fc0e6dce8 100644 --- a/contracts/router/relay/GelatoRelayRouter.sol +++ b/contracts/router/relay/GelatoRelayRouter.sol @@ -54,8 +54,7 @@ contract GelatoRelayRouter is BaseGelatoRelayRouter { _validateGaslessFeature(); bytes32 structHash = RelayUtils.getUpdateOrderStructHash(relayParams, key, params, increaseExecutionFee); - Order.Props memory order = OrderStoreUtils.get(dataStore, key); - _validateCall(relayParams, account, structHash, order.srcChainId()); + _validateCall(relayParams, account, structHash, 0 /* srcChainId */); _updateOrder(relayParams, account, key, params, increaseExecutionFee, false); } @@ -69,8 +68,7 @@ contract GelatoRelayRouter is BaseGelatoRelayRouter { _validateGaslessFeature(); bytes32 structHash = RelayUtils.getCancelOrderStructHash(relayParams, key); - Order.Props memory order = OrderStoreUtils.get(dataStore, key); - _validateCall(relayParams, account, structHash, order.srcChainId()); + _validateCall(relayParams, account, structHash, 0 /* srcChainId */); _cancelOrder(relayParams, account, key); } diff --git a/contracts/router/relay/SubaccountGelatoRelayRouter.sol b/contracts/router/relay/SubaccountGelatoRelayRouter.sol index b513904ea..0c264ce77 100644 --- a/contracts/router/relay/SubaccountGelatoRelayRouter.sol +++ b/contracts/router/relay/SubaccountGelatoRelayRouter.sol @@ -41,13 +41,13 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { bytes32 public constant CREATE_ORDER_TYPEHASH = keccak256( bytes( - "CreateOrder(uint256 collateralDeltaAmount,address account,CreateOrderAddresses addresses,CreateOrderNumbers numbers,uint256 orderType,uint256 decreasePositionSwapType,bool isLong,bool shouldUnwrapNativeToken,bool autoCancel,bytes32 referralCode,bytes32 relayParams,bytes32 subaccountApproval)CreateOrderAddresses(address receiver,address cancellationReceiver,address callbackContract,address uiFeeReceiver,address market,address initialCollateralToken,address[] swapPath)CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime,uint256 srcChainId)" + "CreateOrder(uint256 collateralDeltaAmount,address account,CreateOrderAddresses addresses,CreateOrderNumbers numbers,uint256 orderType,uint256 decreasePositionSwapType,bool isLong,bool shouldUnwrapNativeToken,bool autoCancel,bytes32 referralCode,bytes32 relayParams,bytes32 subaccountApproval)CreateOrderAddresses(address receiver,address cancellationReceiver,address callbackContract,address uiFeeReceiver,address market,address initialCollateralToken,address[] swapPath)CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime)" ) ); bytes32 public constant CREATE_ORDER_NUMBERS_TYPEHASH = keccak256( bytes( - "CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime,uint256 srcChainId)" + "CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime)" ) ); bytes32 public constant CREATE_ORDER_ADDRESSES_TYPEHASH = @@ -65,7 +65,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { ); bytes32 public constant REMOVE_SUBACCOUNT_TYPEHASH = - keccak256(bytes("RemoveSubaccount(address subaccount,uint256 srcChainId,bytes32 relayParams)")); + keccak256(bytes("RemoveSubaccount(address subaccount,bytes32 relayParams)")); mapping(address => uint256) public subaccountApprovalNonces; @@ -129,8 +129,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { _validateGaslessFeature(); bytes32 structHash = _getUpdateOrderStructHash(relayParams, subaccountApproval, account, key, params, increaseExecutionFee); - Order.Props memory order = OrderStoreUtils.get(dataStore, key); - _validateCall(relayParams, subaccount, structHash, order.srcChainId()); + _validateCall(relayParams, subaccount, structHash, 0 /* srcChainId */); _handleSubaccountAction(account, subaccount, Keys.SUBACCOUNT_ORDER_ACTION, subaccountApproval); _updateOrder(relayParams, account, key, params, increaseExecutionFee, true); @@ -147,8 +146,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { _validateGaslessFeature(); bytes32 structHash = _getCancelOrderStructHash(relayParams, subaccountApproval, account, key); - Order.Props memory order = OrderStoreUtils.get(dataStore, key); - _validateCall(relayParams, subaccount, structHash, order.srcChainId()); + _validateCall(relayParams, subaccount, structHash, 0 /* srcChainId */); _handleSubaccountAction(account, subaccount, Keys.SUBACCOUNT_ORDER_ACTION, subaccountApproval); _cancelOrder(relayParams, account, key); @@ -158,19 +156,18 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { function removeSubaccount( RelayUtils.RelayParams calldata relayParams, address account, - address subaccount, - uint256 srcChainId + address subaccount ) external nonReentrant { _validateGaslessFeature(); - bytes32 structHash = _getRemoveSubaccountStructHash(relayParams, subaccount, srcChainId); - _validateCall(relayParams, account, structHash, srcChainId); + bytes32 structHash = _getRemoveSubaccountStructHash(relayParams, subaccount); + _validateCall(relayParams, account, structHash, 0 /* srcChainId */); Contracts memory contracts = Contracts({ dataStore: dataStore, eventEmitter: eventEmitter, bank: orderVault }); - _handleRelay(contracts, relayParams, account, account, srcChainId); + _handleRelay(contracts, relayParams, account, account, 0 /* srcChainId */); SubaccountUtils.removeSubaccount(dataStore, eventEmitter, account, subaccount); } @@ -241,10 +238,9 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { function _getRemoveSubaccountStructHash( RelayUtils.RelayParams calldata relayParams, - address subaccount, - uint256 srcChainId + address subaccount ) internal pure returns (bytes32) { - return keccak256(abi.encode(REMOVE_SUBACCOUNT_TYPEHASH, subaccount, srcChainId, RelayUtils._getRelayParamsHash(relayParams))); + return keccak256(abi.encode(REMOVE_SUBACCOUNT_TYPEHASH, subaccount, RelayUtils._getRelayParamsHash(relayParams))); } function _getSubaccountApprovalStructHash( From c1488313a7d351d9ea7c3c4ea9e0ea83d5eb504f Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 16 Feb 2025 09:24:23 +0200 Subject: [PATCH 063/205] Add MultichainGmRouter: createOrder, updateOrder, cancelOrder --- .../multichain/MultichainOrderRouter.sol | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 contracts/multichain/MultichainOrderRouter.sol diff --git a/contracts/multichain/MultichainOrderRouter.sol b/contracts/multichain/MultichainOrderRouter.sol new file mode 100644 index 000000000..3d032bb25 --- /dev/null +++ b/contracts/multichain/MultichainOrderRouter.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; + +import "./MultichainRouter.sol"; + +contract MultichainGmRouter is MultichainRouter { + constructor(BaseConstructorParams memory params) MultichainRouter(params) {} + + // TODO: handle partial fee payment + + function createOrder( + RelayUtils.RelayParams calldata relayParams, + address account, + uint256 srcChainId, + uint256 collateralDeltaAmount, + IBaseOrderUtils.CreateOrderParams memory params // can't use calldata because need to modify params.numbers.executionFee + ) external nonReentrant onlyGelatoRelay returns (bytes32) { + bytes32 structHash = RelayUtils.getCreateOrderStructHash(relayParams, collateralDeltaAmount, params); + _validateCall(relayParams, account, structHash, srcChainId); + + return _createOrder(relayParams, account, srcChainId, collateralDeltaAmount, params, false); + } + + function updateOrder( + RelayUtils.RelayParams calldata relayParams, + address account, + uint256 srcChainId, + bytes32 key, + RelayUtils.UpdateOrderParams calldata params, + bool increaseExecutionFee + ) external nonReentrant withOraclePricesForAtomicAction(relayParams.oracleParams) onlyGelatoRelay { + _validateGaslessFeature(); + + bytes32 structHash = RelayUtils.getUpdateOrderStructHash(relayParams, key, params, increaseExecutionFee); + _validateCall(relayParams, account, structHash, srcChainId); + + _updateOrder(relayParams, account, key, params, increaseExecutionFee, false); + } + + function cancelOrder( + RelayUtils.RelayParams calldata relayParams, + address account, + uint256 srcChainId, + bytes32 key + ) external nonReentrant withOraclePricesForAtomicAction(relayParams.oracleParams) onlyGelatoRelay { + _validateGaslessFeature(); + + bytes32 structHash = RelayUtils.getCancelOrderStructHash(relayParams, key); + _validateCall(relayParams, account, structHash, srcChainId); + + _cancelOrder(relayParams, account, key); + } +} From 2801fbc22fdccb9cbdfc51c8716a93273642710f Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 16 Feb 2025 17:22:54 +0200 Subject: [PATCH 064/205] Rename contract, remove comment, add modifier --- contracts/multichain/MultichainOrderRouter.sol | 4 ++-- contracts/position/Position.sol | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/multichain/MultichainOrderRouter.sol b/contracts/multichain/MultichainOrderRouter.sol index 3d032bb25..74f1e794b 100644 --- a/contracts/multichain/MultichainOrderRouter.sol +++ b/contracts/multichain/MultichainOrderRouter.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; import "./MultichainRouter.sol"; -contract MultichainGmRouter is MultichainRouter { +contract MultichainOrderRouter is MultichainRouter { constructor(BaseConstructorParams memory params) MultichainRouter(params) {} // TODO: handle partial fee payment @@ -15,7 +15,7 @@ contract MultichainGmRouter is MultichainRouter { uint256 srcChainId, uint256 collateralDeltaAmount, IBaseOrderUtils.CreateOrderParams memory params // can't use calldata because need to modify params.numbers.executionFee - ) external nonReentrant onlyGelatoRelay returns (bytes32) { + ) external nonReentrant withOraclePricesForAtomicAction(relayParams.oracleParams) onlyGelatoRelay returns (bytes32) { bytes32 structHash = RelayUtils.getCreateOrderStructHash(relayParams, collateralDeltaAmount, params); _validateCall(relayParams, account, structHash, srcChainId); diff --git a/contracts/position/Position.sol b/contracts/position/Position.sol index 0679fa515..2da0950c0 100644 --- a/contracts/position/Position.sol +++ b/contracts/position/Position.sol @@ -198,7 +198,7 @@ library Position { // @param collateralToken the position's collateralToken // @param isLong whether the position is long or short // @return the position key - function getPositionKey(address _account, address _market, address _collateralToken, bool _isLong/*, uint256 _chainId*/) internal pure returns (bytes32) { + function getPositionKey(address _account, address _market, address _collateralToken, bool _isLong) internal pure returns (bytes32) { bytes32 _key = keccak256(abi.encode(_account, _market, _collateralToken, _isLong)); return _key; From 1d60d13599de7b4c498ff873c7f8a3db92f2edd8 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 16 Feb 2025 17:36:55 +0200 Subject: [PATCH 065/205] gassless merge fix --- contracts/router/relay/BaseGelatoRelayRouter.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/router/relay/BaseGelatoRelayRouter.sol b/contracts/router/relay/BaseGelatoRelayRouter.sol index 7c0e7377f..90b2ae12d 100644 --- a/contracts/router/relay/BaseGelatoRelayRouter.sol +++ b/contracts/router/relay/BaseGelatoRelayRouter.sol @@ -264,9 +264,9 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, // for subaccount orders there is max execution fee validation // malicious actor could send a lot of WNT to the order vault to cause the subaccount order to fail // then malicious actor could get back the WNT by creating an order - uint256 pendingWntAmount = contracts.orderVault.recordTransferIn(wnt); + uint256 pendingWntAmount = contracts.bank.recordTransferIn(wnt); if (pendingWntAmount > 0) { - contracts.orderVault.transferOut(wnt, contracts.dataStore.getAddress(Keys.FEE_RECEIVER), pendingWntAmount); + contracts.bank.transferOut(wnt, contracts.dataStore.getAddress(Keys.FEE_RECEIVER), pendingWntAmount); } if (_getFeeToken() != wnt) { From 149cb5c90b0521cff015223d59c762e446d387c9 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 16 Feb 2025 22:49:53 +0200 Subject: [PATCH 066/205] Refactor: add function to validate destination chain --- contracts/multichain/MultichainGlvRouter.sol | 8 ++------ contracts/multichain/MultichainGmRouter.sol | 12 +++--------- contracts/multichain/MultichainRouter.sol | 6 ++++++ 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/contracts/multichain/MultichainGlvRouter.sol b/contracts/multichain/MultichainGlvRouter.sol index 4664df413..eff0cf2a9 100644 --- a/contracts/multichain/MultichainGlvRouter.sol +++ b/contracts/multichain/MultichainGlvRouter.sol @@ -28,9 +28,7 @@ contract MultichainGlvRouter is MultichainRouter { RelayUtils.TransferRequest[] calldata transferRequests, GlvDepositUtils.CreateGlvDepositParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { - if (relayParams.desChainId != block.chainid) { - revert Errors.InvalidDestinationChainId(); - } + _validateDesChainId(relayParams.desChainId); bytes32 structHash = RelayUtils.getCreateGlvDepositStructHash(relayParams, transferRequests, params); _validateCall(relayParams, account, structHash, srcChainId); @@ -71,9 +69,7 @@ contract MultichainGlvRouter is MultichainRouter { RelayUtils.TransferRequest[] calldata transferRequests, GlvWithdrawalUtils.CreateGlvWithdrawalParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { - if (relayParams.desChainId != block.chainid) { - revert Errors.InvalidDestinationChainId(); - } + _validateDesChainId(relayParams.desChainId); bytes32 structHash = RelayUtils.getCreateGlvWithdrawalStructHash(relayParams, transferRequests, params); _validateCall(relayParams, account, structHash, srcChainId); diff --git a/contracts/multichain/MultichainGmRouter.sol b/contracts/multichain/MultichainGmRouter.sol index efae3584c..de8efe104 100644 --- a/contracts/multichain/MultichainGmRouter.sol +++ b/contracts/multichain/MultichainGmRouter.sol @@ -39,9 +39,7 @@ contract MultichainGmRouter is MultichainRouter { RelayUtils.TransferRequest[] calldata transferRequests, DepositUtils.CreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { - if (relayParams.desChainId != block.chainid) { - revert Errors.InvalidDestinationChainId(); - } + _validateDesChainId(relayParams.desChainId); bytes32 structHash = RelayUtils.getCreateDepositStructHash(relayParams, transferRequests, params); _validateCall(relayParams, account, structHash, srcChainId); @@ -82,9 +80,7 @@ contract MultichainGmRouter is MultichainRouter { RelayUtils.TransferRequest[] calldata transferRequests, WithdrawalUtils.CreateWithdrawalParams memory params // can't use calldata because need to modify params.addresses.receiver & params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { - if (relayParams.desChainId != block.chainid) { - revert Errors.InvalidDestinationChainId(); - } + _validateDesChainId(relayParams.desChainId); bytes32 structHash = RelayUtils.getCreateWithdrawalStructHash(relayParams, transferRequests, params); _validateCall(relayParams, account, structHash, srcChainId); @@ -124,9 +120,7 @@ contract MultichainGmRouter is MultichainRouter { RelayUtils.TransferRequest[] calldata transferRequests, ShiftUtils.CreateShiftParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { - if (relayParams.desChainId != block.chainid) { - revert Errors.InvalidDestinationChainId(); - } + _validateDesChainId(relayParams.desChainId); bytes32 structHash = RelayUtils.getCreateShiftStructHash(relayParams, transferRequests, params); _validateCall(relayParams, account, structHash, srcChainId); diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 21f97c4fb..fb418f25b 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -59,4 +59,10 @@ contract MultichainRouter is GelatoRelayRouter { MultichainUtils.recordTransferIn(dataStore, eventEmitter, multichainVault, wnt, account, srcChainId); } } + + function _validateDesChainId(uint256 desChainId) internal view { + if (desChainId != block.chainid) { + revert Errors.InvalidDestinationChainId(); + } + } } From 2d9f8ad6496ad121dd9f68a4d9bd3a5aedd123d1 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 16 Feb 2025 23:22:04 +0200 Subject: [PATCH 067/205] Add bridgeOut function, validate multichain provider is whitelisted, emit bridge in/out events, add bridgeOut thypehashes --- contracts/config/Config.sol | 1 + contracts/data/Keys.sol | 12 +++++ contracts/error/Errors.sol | 1 + contracts/multichain/IMultichainProvider.sol | 9 +++- contracts/multichain/MultichainEventUtils.sol | 32 ++++++++++--- .../multichain/MultichainOrderRouter.sol | 41 +++++++++++++++- contracts/router/relay/RelayUtils.sol | 47 +++++++++++++++++++ 7 files changed, 134 insertions(+), 9 deletions(-) diff --git a/contracts/config/Config.sol b/contracts/config/Config.sol index b2253682b..083a1781f 100644 --- a/contracts/config/Config.sol +++ b/contracts/config/Config.sol @@ -519,6 +519,7 @@ contract Config is ReentrancyGuard, RoleModule, BasicMulticall { allowedBaseKeys[Keys.DATA_STREAM_SPREAD_REDUCTION_FACTOR] = true; allowedBaseKeys[Keys.MULTICHAIN_BALANCE] = true; + allowedBaseKeys[Keys.IS_MULTICHAIN_PROVIDER_ENABLED] = true; allowedBaseKeys[Keys.MAX_DATA_LENGTH] = true; diff --git a/contracts/data/Keys.sol b/contracts/data/Keys.sol index d366dc481..c1028999a 100644 --- a/contracts/data/Keys.sol +++ b/contracts/data/Keys.sol @@ -479,6 +479,8 @@ library Keys { // @dev key for user's multichain balance bytes32 public constant MULTICHAIN_BALANCE = keccak256(abi.encode("MULTICHAIN_BALANCE")); + // @dev key for user's multichain balance + bytes32 public constant IS_MULTICHAIN_PROVIDER_ENABLED = keccak256(abi.encode("IS_MULTICHAIN_PROVIDER_ENABLED")); // @dev key for the maximum length for data list array of bytes32 bytes32 public constant MAX_DATA_LENGTH = keccak256(abi.encode("MAX_DATA_LENGTH")); @@ -2135,6 +2137,16 @@ library Keys { )); } + // @dev key for whether a multichain provider is enabled + // @param provider the multichain provider + // @return key for whether a multichain provider is enabled + function isMultichainProviderEnabledKey(address provider) internal pure returns (bytes32) { + return keccak256(abi.encode( + IS_MULTICHAIN_PROVIDER_ENABLED, + provider + )); + } + // @dev key for user's multichain balance // @param account the account for which to retreive the user balance key // @param token the token for which to retreive the user balance key diff --git a/contracts/error/Errors.sol b/contracts/error/Errors.sol index 4e086684c..978a9c132 100644 --- a/contracts/error/Errors.sol +++ b/contracts/error/Errors.sol @@ -434,6 +434,7 @@ library Errors { error EmptyMultichainTransferOutAmount(); error InsufficientMultichainBalance(); error InvalidDestinationChainId(); + error InvalidMultichainProvider(address provider); enum SignatureType { Call, diff --git a/contracts/multichain/IMultichainProvider.sol b/contracts/multichain/IMultichainProvider.sol index b943b8904..98f73a669 100644 --- a/contracts/multichain/IMultichainProvider.sol +++ b/contracts/multichain/IMultichainProvider.sol @@ -6,6 +6,11 @@ pragma solidity ^0.8.0; * @title IMultichainProvider */ interface IMultichainProvider { - // lzCompose is LZ specific. If defined here, interface should be named ILayerZeroProvider instead of IMultichainProvider - // function lzCompose(address from, bytes32 guid, bytes calldata message, address executor, bytes calldata extraData) external payable; + function bridgeOut( + address _stargate, + uint32 _dstEid, + address account, + address token, + uint256 amount + ) external; } diff --git a/contracts/multichain/MultichainEventUtils.sol b/contracts/multichain/MultichainEventUtils.sol index 6ac917016..b1c24177b 100644 --- a/contracts/multichain/MultichainEventUtils.sol +++ b/contracts/multichain/MultichainEventUtils.sol @@ -13,6 +13,24 @@ library MultichainEventUtils { using EventUtils for EventUtils.AddressItems; using EventUtils for EventUtils.UintItems; + function emitBridgeIn( + EventEmitter eventEmitter, + address token, + address account, + uint256 srcChainId + ) internal { + EventUtils.EventLogData memory eventData; + + eventData.addressItems.initItems(2); + eventData.addressItems.setItem(0, "token", token); + eventData.addressItems.setItem(1, "account", account); + + eventData.uintItems.initItems(1); + eventData.uintItems.setItem(0, "srcChainId", srcChainId); + + eventEmitter.emitEventLog1("BridgeIn", Cast.toBytes32(account), eventData); + } + function emitMultichainTransferIn( EventEmitter eventEmitter, address token, @@ -32,21 +50,23 @@ library MultichainEventUtils { eventEmitter.emitEventLog1("MultichainTransferIn", Cast.toBytes32(account), eventData); } - - function emitMultichainMessage( + + function emitBridgeOut( EventEmitter eventEmitter, address account, - uint256 srcChainId + address token, + uint256 amount ) internal { EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(1); + eventData.addressItems.initItems(2); eventData.addressItems.setItem(0, "account", account); + eventData.addressItems.setItem(1, "token", token); eventData.uintItems.initItems(1); - eventData.uintItems.setItem(0, "srcChainId", srcChainId); + eventData.uintItems.setItem(0, "amount", amount); - eventEmitter.emitEventLog1("MultichainMessage", Cast.toBytes32(account), eventData); + eventEmitter.emitEventLog1("BridgeOut", Cast.toBytes32(account), eventData); } function emitMultichainTransferOut( diff --git a/contracts/multichain/MultichainOrderRouter.sol b/contracts/multichain/MultichainOrderRouter.sol index 74f1e794b..8515afd77 100644 --- a/contracts/multichain/MultichainOrderRouter.sol +++ b/contracts/multichain/MultichainOrderRouter.sol @@ -3,9 +3,14 @@ pragma solidity ^0.8.0; import "./MultichainRouter.sol"; +import "./IMultichainProvider.sol"; contract MultichainOrderRouter is MultichainRouter { - constructor(BaseConstructorParams memory params) MultichainRouter(params) {} + IMultichainProvider multichainProvider; + + constructor(BaseConstructorParams memory params, IMultichainProvider _multichainProvider) MultichainRouter(params) { + multichainProvider = _multichainProvider; + } // TODO: handle partial fee payment @@ -16,6 +21,8 @@ contract MultichainOrderRouter is MultichainRouter { uint256 collateralDeltaAmount, IBaseOrderUtils.CreateOrderParams memory params // can't use calldata because need to modify params.numbers.executionFee ) external nonReentrant withOraclePricesForAtomicAction(relayParams.oracleParams) onlyGelatoRelay returns (bytes32) { + _validateDesChainId(relayParams.desChainId); + bytes32 structHash = RelayUtils.getCreateOrderStructHash(relayParams, collateralDeltaAmount, params); _validateCall(relayParams, account, structHash, srcChainId); @@ -30,6 +37,7 @@ contract MultichainOrderRouter is MultichainRouter { RelayUtils.UpdateOrderParams calldata params, bool increaseExecutionFee ) external nonReentrant withOraclePricesForAtomicAction(relayParams.oracleParams) onlyGelatoRelay { + _validateDesChainId(relayParams.desChainId); _validateGaslessFeature(); bytes32 structHash = RelayUtils.getUpdateOrderStructHash(relayParams, key, params, increaseExecutionFee); @@ -44,6 +52,7 @@ contract MultichainOrderRouter is MultichainRouter { uint256 srcChainId, bytes32 key ) external nonReentrant withOraclePricesForAtomicAction(relayParams.oracleParams) onlyGelatoRelay { + _validateDesChainId(relayParams.desChainId); _validateGaslessFeature(); bytes32 structHash = RelayUtils.getCancelOrderStructHash(relayParams, key); @@ -51,4 +60,34 @@ contract MultichainOrderRouter is MultichainRouter { _cancelOrder(relayParams, account, key); } + + function bridgeOut( + RelayUtils.RelayParams calldata relayParams, + address provider, + uint32 dstEid, + address account, + uint256 srcChainId, + RelayUtils.BridgeOutParams calldata params + ) external nonReentrant onlyGelatoRelay { + _validateDesChainId(relayParams.desChainId); + _validateMultichainProvider(dataStore, provider); + + bytes32 structHash = RelayUtils.getBridgeOutStructHash(relayParams, params); + _validateCall(relayParams, account, structHash, srcChainId); + + multichainProvider.bridgeOut( + provider, + dstEid, + account, + params.token, + params.amount + ); + } + + function _validateMultichainProvider(DataStore dataStore, address provider) internal view { + bytes32 providerKey = Keys.isMultichainProviderEnabledKey(provider); + if (!dataStore.getBool(providerKey)) { + revert Errors.InvalidMultichainProvider(provider); + } + } } diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index c5fd253f4..37c917026 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -63,6 +63,12 @@ library RelayUtils { uint256 amount; } + struct BridgeOutParams { + address token; + address account; + uint256 amount; + } + //////////////////// ORDER //////////////////// bytes32 public constant UPDATE_ORDER_TYPEHASH = @@ -187,6 +193,20 @@ library RelayUtils { bytes32 public constant TRANSFER_REQUEST_TYPEHASH = keccak256(bytes("TransferRequest(address token,address receiver,uint256 amount)")); + bytes32 public constant BRIDGE_OUT_TYPEHASH = + keccak256( + bytes( + "BridgeOut(BridgeOutParams params,bytes32 relayParams)BridgeOutParams(address token,uint256 amount)" + ) + ); + + bytes32 public constant BRIDGE_OUT_PARAMS_TYPEHASH = + keccak256( + bytes( + "BridgeOutParams(address token,uint256 amount)" + ) + ); + //////////////////// ORDER //////////////////// function _getRelayParamsHash(RelayParams calldata relayParams) internal pure returns (bytes32) { @@ -548,4 +568,31 @@ library RelayUtils { } return keccak256(abi.encodePacked(hashes)); } + + function getBridgeOutStructHash( + RelayParams calldata relayParams, + BridgeOutParams memory params + ) external pure returns (bytes32) { + return + keccak256( + abi.encode( + BRIDGE_OUT_TYPEHASH, + _getBridgeOutParamsStructHash(params), + _getRelayParamsHash(relayParams) + ) + ); + } + + function _getBridgeOutParamsStructHash( + BridgeOutParams memory params + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + BRIDGE_OUT_PARAMS_TYPEHASH, + params.token, + params.amount + ) + ); + } } From 5ae76a0a14f908dc4faaadb626beef948c86bb3b Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 16 Feb 2025 23:22:57 +0200 Subject: [PATCH 068/205] wip provider bridgeOut --- contracts/multichain/LayerZeroProvider.sol | 96 ++++++++++++++++--- .../LayerZeroProviderEventUtils.sol | 27 ------ 2 files changed, 85 insertions(+), 38 deletions(-) diff --git a/contracts/multichain/LayerZeroProvider.sol b/contracts/multichain/LayerZeroProvider.sol index 296321bc6..8edf0665b 100644 --- a/contracts/multichain/LayerZeroProvider.sol +++ b/contracts/multichain/LayerZeroProvider.sol @@ -6,26 +6,29 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ILayerZeroComposer } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroComposer.sol"; -import { EventEmitter } from "../event/EventEmitter.sol"; -import { DataStore } from "../data/DataStore.sol"; +import "../event/EventEmitter.sol"; +import "../data/DataStore.sol"; -import { IMultichainProvider } from "./IMultichainProvider.sol"; -import { MultichainVault } from "./MultichainVault.sol"; -import { MultichainUtils } from "./MultichainUtils.sol"; -import { MultichainProviderUtils } from "./MultichainProviderUtils.sol"; -import { LayerZeroProviderEventUtils } from "./LayerZeroProviderEventUtils.sol"; +import "./IMultichainProvider.sol"; +import "./MultichainVault.sol"; +import "./MultichainUtils.sol"; +import "./MultichainEventUtils.sol"; +import "./MultichainProviderUtils.sol"; +import "./LayerZeroProviderEventUtils.sol"; /** * @title LayerZeroProvider - * Receives tokens and messages from source chains. + * Receives tokens + encoded message from a source chain and bridges tokens back to a source chain. * Defines lzCompose function which: * - is called by the Stargate executor after tokens are delivered to this contract * - forwards the received tokens to MultichainVault and increases user's multichain balance + * Defines bridgeOut function which: + * - sends tokens to the Stargate executor for bridging out to the source chain */ contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer { - DataStore public dataStore; - EventEmitter public eventEmitter; - MultichainVault public multichainVault; + DataStore public immutable dataStore; + EventEmitter public immutable eventEmitter; + MultichainVault public immutable multichainVault; constructor(DataStore _dataStore, EventEmitter _eventEmitter, MultichainVault _multichainVault) { dataStore = _dataStore; @@ -62,6 +65,7 @@ contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer { MultichainUtils.recordTransferIn(dataStore, eventEmitter, multichainVault, token, account, srcChainId); + // TODO: check what LZ contract already emits --> if it already emits the fields bellow, remove this event LayerZeroProviderEventUtils.emitComposedMessageReceived( eventEmitter, srcChainId, @@ -72,10 +76,80 @@ contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer { executor, extraData ); + + MultichainEventUtils.emitBridgeIn( + eventEmitter, + token, + account, + srcChainId + ); } function _transferToVault(address token, address to) private { uint256 amount = IERC20(token).balanceOf(address(this)); IERC20(token).transfer(to, amount); } + + function bridgeOut( + address _stargate, + uint32 _dstEid, + address account, + address token, + uint256 amount + ) external { + IERC20(token).approve(_stargate, amount); + + // IStargate stargate = IStargate(_stargate); + + // (uint256 valueToSend, SendParam memory sendParam, MessagingFee memory messagingFee) = prepareSend( + // stargate, + // amount, + // account, + // _dstEid, + // new bytes(0), // _extraOptions + // new bytes(0) // _composeMsg + // ); + + // (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) = stargate.send{value: valueToSend}( + // sendParam, + // messagingFee, + // account + // ); + + MultichainEventUtils.emitBridgeOut( + eventEmitter, + account, + token, + amount + ); + } + + // function prepareSend( + // IStargate stargate, + // uint256 amount, + // address receiver, + // uint32 _dstEid, + // bytes memory _composeMsg, + // bytes memory _extraOptions + // ) private view returns (uint256 valueToSend, SendParam memory sendParam, MessagingFee memory messagingFee) { + // sendParam = SendParam({ + // dstEid: _dstEid, + // to: addressToBytes32(receiver), + // amountLD: amount, + // minAmountLD: amount, + // extraOptions: _extraOptions, + // composeMsg: _composeMsg, + // oftCmd: "" + // }); + + // (, , OFTReceipt memory receipt) = stargate.quoteOFT(sendParam); + // sendParam.minAmountLD = receipt.amountReceivedLD; + + // messagingFee = stargate.quoteSend(sendParam, false); + // valueToSend = messagingFee.nativeFee; + + // if (stargate.token() == address(0x0)) { + // valueToSend += sendParam.amountLD; + // } + // } } diff --git a/contracts/multichain/LayerZeroProviderEventUtils.sol b/contracts/multichain/LayerZeroProviderEventUtils.sol index 5f395a7e1..b321470c8 100644 --- a/contracts/multichain/LayerZeroProviderEventUtils.sol +++ b/contracts/multichain/LayerZeroProviderEventUtils.sol @@ -44,31 +44,4 @@ library LayerZeroProviderEventUtils { eventEmitter.emitEventLog1("MessageComposedReceived", Cast.toBytes32(account), eventData); } - - function emitWithdrawalReceipt( - EventEmitter eventEmitter, - uint256 srcChainId, - address account, - bytes32 guid, - uint64 nonce, - uint256 nativeFee, - uint256 lzTokenFee, - uint256 amountSentLD, - uint256 amountReceivedLD - ) internal { - EventUtils.EventLogData memory eventData; - - eventData.uintItems.initItems(6); - eventData.uintItems.setItem(0, "nonce", uint256(nonce)); - eventData.uintItems.setItem(1, "nativeFee", nativeFee); - eventData.uintItems.setItem(2, "lzTokenFee", lzTokenFee); - eventData.uintItems.setItem(3, "amountSentLD", amountSentLD); - eventData.uintItems.setItem(4, "amountReceivedLD", amountReceivedLD); - eventData.uintItems.setItem(5, "srcChainId", srcChainId); - - eventData.bytes32Items.initItems(1); - eventData.bytes32Items.setItem(0, "guid", guid); - - eventEmitter.emitEventLog1("WithdrawalReceipt", Cast.toBytes32(account), eventData); - } } From 07e6c9656e86eac5407b6cb4c3c5c230da5addde Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 17 Feb 2025 11:02:11 +0200 Subject: [PATCH 069/205] Fix gasless merge --- contracts/multichain/MultichainGlvRouter.sol | 2 ++ contracts/multichain/MultichainGmRouter.sol | 3 +++ contracts/multichain/MultichainOrderRouter.sol | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/contracts/multichain/MultichainGlvRouter.sol b/contracts/multichain/MultichainGlvRouter.sol index eff0cf2a9..fddff4315 100644 --- a/contracts/multichain/MultichainGlvRouter.sol +++ b/contracts/multichain/MultichainGlvRouter.sol @@ -56,6 +56,7 @@ contract MultichainGlvRouter is MultichainRouter { relayParams, account, address(glvVault), + false, // isSubaccount srcChainId ); @@ -97,6 +98,7 @@ contract MultichainGlvRouter is MultichainRouter { relayParams, account, address(glvVault), // residualFeeReceiver + false, // isSubaccount srcChainId ); diff --git a/contracts/multichain/MultichainGmRouter.sol b/contracts/multichain/MultichainGmRouter.sol index de8efe104..b1712cd7f 100644 --- a/contracts/multichain/MultichainGmRouter.sol +++ b/contracts/multichain/MultichainGmRouter.sol @@ -67,6 +67,7 @@ contract MultichainGmRouter is MultichainRouter { relayParams, account, address(depositVault), // residualFeeReceiver + false, // isSubaccount srcChainId ); @@ -107,6 +108,7 @@ contract MultichainGmRouter is MultichainRouter { relayParams, account, address(withdrawalVault), // residualFeeReceiver + false, // isSubaccount srcChainId ); @@ -147,6 +149,7 @@ contract MultichainGmRouter is MultichainRouter { relayParams, account, address(shiftVault), + false, // isSubaccount srcChainId ); diff --git a/contracts/multichain/MultichainOrderRouter.sol b/contracts/multichain/MultichainOrderRouter.sol index 8515afd77..533ce802b 100644 --- a/contracts/multichain/MultichainOrderRouter.sol +++ b/contracts/multichain/MultichainOrderRouter.sol @@ -58,7 +58,7 @@ contract MultichainOrderRouter is MultichainRouter { bytes32 structHash = RelayUtils.getCancelOrderStructHash(relayParams, key); _validateCall(relayParams, account, structHash, srcChainId); - _cancelOrder(relayParams, account, key); + _cancelOrder(relayParams, account, key, false /* isSubaccount */); } function bridgeOut( From fb303a0265581e6fcdee4ef6eb8682f10e3007d5 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 17 Feb 2025 11:15:29 +0200 Subject: [PATCH 070/205] Add dataList field to relay create order params --- test/router/relay/GelatoRelayRouter.ts | 1 + test/router/relay/SubaccountGelatoRelayRouter.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/test/router/relay/GelatoRelayRouter.ts b/test/router/relay/GelatoRelayRouter.ts index 5ecfaff92..002225f26 100644 --- a/test/router/relay/GelatoRelayRouter.ts +++ b/test/router/relay/GelatoRelayRouter.ts @@ -66,6 +66,7 @@ describe("GelatoRelayRouter", () => { isLong: true, shouldUnwrapNativeToken: true, referralCode, + dataList: [], }; await impersonateAccount(GELATO_RELAY_ADDRESS); diff --git a/test/router/relay/SubaccountGelatoRelayRouter.ts b/test/router/relay/SubaccountGelatoRelayRouter.ts index 515018945..5675b0b22 100644 --- a/test/router/relay/SubaccountGelatoRelayRouter.ts +++ b/test/router/relay/SubaccountGelatoRelayRouter.ts @@ -89,6 +89,7 @@ describe("SubaccountGelatoRelayRouter", () => { isLong: true, shouldUnwrapNativeToken: true, referralCode, + dataList: [], }; enableSubaccount = async () => { From b848483e4d79a0b75ffa207cca920f70c43ee3ae Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 17 Feb 2025 17:14:22 +0200 Subject: [PATCH 071/205] Fix signatures and missing fields for gasless tests --- contracts/router/relay/RelayUtils.sol | 9 +++++---- test/router/relay/GelatoRelayRouter.ts | 7 +++++-- test/router/relay/SubaccountGelatoRelayRouter.ts | 8 ++++++-- utils/relay/gelatoRelay.ts | 3 +++ utils/relay/helpers.ts | 2 ++ utils/relay/subaccountGelatoRelay.ts | 4 ++++ 6 files changed, 25 insertions(+), 8 deletions(-) diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index 37c917026..9ec5225b7 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -89,13 +89,13 @@ library RelayUtils { bytes32 public constant CREATE_ORDER_TYPEHASH = keccak256( bytes( - "CreateOrder(uint256 collateralDeltaAmount,CreateOrderAddresses addresses,CreateOrderNumbers numbers,uint256 orderType,uint256 decreasePositionSwapType,bool isLong,bool shouldUnwrapNativeToken,bool autoCancel,bytes32 referralCode,bytes32 relayParams)CreateOrderAddresses(address receiver,address cancellationReceiver,address callbackContract,address uiFeeReceiver,address market,address initialCollateralToken,address[] swapPath)CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime,uint256 srcChainId)" + "CreateOrder(uint256 collateralDeltaAmount,CreateOrderAddresses addresses,CreateOrderNumbers numbers,uint256 orderType,uint256 decreasePositionSwapType,bool isLong,bool shouldUnwrapNativeToken,bool autoCancel,bytes32 referralCode,bytes32 relayParams)CreateOrderAddresses(address receiver,address cancellationReceiver,address callbackContract,address uiFeeReceiver,address market,address initialCollateralToken,address[] swapPath)CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime)" ) ); bytes32 public constant CREATE_ORDER_NUMBERS_TYPEHASH = keccak256( bytes( - "CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime,uint256 srcChainId)" + "CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime)" ) ); bytes32 public constant CREATE_ORDER_ADDRESSES_TYPEHASH = @@ -218,8 +218,8 @@ library RelayUtils { relayParams.tokenPermits, relayParams.fee, relayParams.userNonce, - relayParams.deadline, - relayParams.desChainId + relayParams.deadline + // relayParams.desChainId // TOOD: should be block.chainid ) ); } @@ -279,6 +279,7 @@ library RelayUtils { params.shouldUnwrapNativeToken, params.autoCancel, params.referralCode, + // keccak256(abi.encodePacked(params.dataList)), _getRelayParamsHash(relayParams) ) ); diff --git a/test/router/relay/GelatoRelayRouter.ts b/test/router/relay/GelatoRelayRouter.ts index 002225f26..31d8aa6ca 100644 --- a/test/router/relay/GelatoRelayRouter.ts +++ b/test/router/relay/GelatoRelayRouter.ts @@ -93,6 +93,7 @@ describe("GelatoRelayRouter", () => { account: user0.address, params: defaultParams, deadline: 9999999999, + desChainId: 0, relayRouter: gelatoRelayRouter, chainId, relayFeeToken: wnt.address, @@ -272,7 +273,7 @@ describe("GelatoRelayRouter", () => { // allowance was set expect(await wnt.allowance(user0.address, router.address)).to.eq( - expandDecimals(1, 18).sub(collateralDeltaAmount).sub(gelatoRelayFee).sub(expandDecimals(1, 15)) + expandDecimals(1, 18).sub(collateralDeltaAmount).sub(gelatoRelayFee).sub(expandDecimals(1, 15)) // TODO: fails --> collateralDeltaAmount is 0 instead of 0.1 ); // relay fee was sent await expectBalance(wnt.address, GELATO_RELAY_ADDRESS, gelatoRelayFee); @@ -289,7 +290,7 @@ describe("GelatoRelayRouter", () => { expect(order.numbers.orderType).eq(OrderType.LimitIncrease); expect(order.numbers.decreasePositionSwapType).eq(DecreasePositionSwapType.SwapCollateralTokenToPnlToken); expect(order.numbers.sizeDeltaUsd).eq(decimalToFloat(1000)); - expect(order.numbers.initialCollateralDeltaAmount).eq(collateralDeltaAmount); + expect(order.numbers.initialCollateralDeltaAmount).eq(collateralDeltaAmount); // TODO: fails --> collateralDeltaAmount is 0 instead of 0.1 expect(order.numbers.triggerPrice).eq(decimalToFloat(4800)); expect(order.numbers.acceptablePrice).eq(decimalToFloat(4900)); expect(order.numbers.executionFee).eq(expandDecimals(1, 15)); @@ -445,6 +446,7 @@ describe("GelatoRelayRouter", () => { }, key: ethers.constants.HashZero, deadline: 9999999999, + desChainId: 0, relayRouter: gelatoRelayRouter, chainId, relayFeeToken: wnt.address, @@ -583,6 +585,7 @@ describe("GelatoRelayRouter", () => { key: ethers.constants.HashZero, account: user0.address, deadline: 9999999999, + desChainId: 0, relayRouter: gelatoRelayRouter, chainId, relayFeeToken: wnt.address, diff --git a/test/router/relay/SubaccountGelatoRelayRouter.ts b/test/router/relay/SubaccountGelatoRelayRouter.ts index 5675b0b22..eb4697589 100644 --- a/test/router/relay/SubaccountGelatoRelayRouter.ts +++ b/test/router/relay/SubaccountGelatoRelayRouter.ts @@ -134,6 +134,7 @@ describe("SubaccountGelatoRelayRouter", () => { subaccount: user0.address, params: defaultCreateOrderParams, deadline: 9999999999, + desChainId: 0, relayRouter: subaccountGelatoRelayRouter, chainId, relayFeeToken: wnt.address, @@ -588,7 +589,7 @@ describe("SubaccountGelatoRelayRouter", () => { // allowance was set expect(await wnt.allowance(user1.address, router.address)).to.eq( - expandDecimals(1, 18).sub(collateralDeltaAmount).sub(gelatoRelayFee).sub(expandDecimals(1, 15)) + expandDecimals(1, 18).sub(collateralDeltaAmount).sub(gelatoRelayFee).sub(expandDecimals(1, 15)) // TODO: fails --> collateralDeltaAmount is 0 instead of 0.1 ); // relay fee was sent await expectBalance(wnt.address, GELATO_RELAY_ADDRESS, gelatoRelayFee); @@ -606,7 +607,7 @@ describe("SubaccountGelatoRelayRouter", () => { expect(order.numbers.orderType).eq(OrderType.LimitIncrease); expect(order.numbers.decreasePositionSwapType).eq(DecreasePositionSwapType.SwapCollateralTokenToPnlToken); expect(order.numbers.sizeDeltaUsd).eq(decimalToFloat(1000)); - expect(order.numbers.initialCollateralDeltaAmount).eq(collateralDeltaAmount); + expect(order.numbers.initialCollateralDeltaAmount).eq(collateralDeltaAmount); // TODO: fails --> collateralDeltaAmount is 0 instead of 0.1 expect(order.numbers.triggerPrice).eq(decimalToFloat(4800)); expect(order.numbers.acceptablePrice).eq(decimalToFloat(4900)); expect(order.numbers.executionFee).eq(expandDecimals(1, 15)); @@ -655,6 +656,7 @@ describe("SubaccountGelatoRelayRouter", () => { autoCancel: false, }, deadline: 9999999999, + desChainId: 0, relayRouter: subaccountGelatoRelayRouter, chainId, relayFeeToken: wnt.address, @@ -852,6 +854,7 @@ describe("SubaccountGelatoRelayRouter", () => { subaccount: user0.address, key: ethers.constants.HashZero, deadline: 9999999999, + desChainId: 0, relayRouter: subaccountGelatoRelayRouter, chainId, relayFeeToken: wnt.address, @@ -940,6 +943,7 @@ describe("SubaccountGelatoRelayRouter", () => { relayFeeToken: wnt.address, relayFeeAmount: expandDecimals(1, 15), deadline: 9999999999, + desChainId: 0, }; }); diff --git a/utils/relay/gelatoRelay.ts b/utils/relay/gelatoRelay.ts index 861739b39..41f0fd48e 100644 --- a/utils/relay/gelatoRelay.ts +++ b/utils/relay/gelatoRelay.ts @@ -35,6 +35,7 @@ export async function sendCreateOrder(p: { signature?: string; userNonce?: BigNumberish; deadline: BigNumberish; + desChainId: BigNumberish; relayRouter: ethers.Contract; chainId: BigNumberish; relayFeeToken: string; @@ -160,6 +161,7 @@ export async function sendUpdateOrder(p: { autoCancel: boolean; }; deadline: BigNumberish; + desChainId: BigNumberish; userNonce?: BigNumberish; relayRouter: ethers.Contract; signature?: string; @@ -257,6 +259,7 @@ export async function sendCancelOrder(p: { chainId: BigNumberish; account: string; deadline: BigNumberish; + desChainId: BigNumberish; userNonce?: BigNumberish; relayRouter: ethers.Contract; signature?: string; diff --git a/utils/relay/helpers.ts b/utils/relay/helpers.ts index 750e3ef6b..35c913683 100644 --- a/utils/relay/helpers.ts +++ b/utils/relay/helpers.ts @@ -15,6 +15,7 @@ export async function getRelayParams(p: { feeParams: any; userNonce?: BigNumberish; deadline: BigNumberish; + desChainId: BigNumberish; relayRouter: ethers.Contract; signer: ethers.Signer; }) { @@ -33,6 +34,7 @@ export async function getRelayParams(p: { fee: p.feeParams, userNonce: p.userNonce, deadline: p.deadline, + desChainId: p.desChainId, }; } diff --git a/utils/relay/subaccountGelatoRelay.ts b/utils/relay/subaccountGelatoRelay.ts index 5ffb9363e..5fbe0bfa1 100644 --- a/utils/relay/subaccountGelatoRelay.ts +++ b/utils/relay/subaccountGelatoRelay.ts @@ -46,6 +46,7 @@ export async function sendCreateOrder(p: { signature?: string; userNonce?: BigNumberish; deadline: BigNumberish; + desChainId: BigNumberish; relayRouter: ethers.Contract; chainId: BigNumberish; relayFeeToken: string; @@ -210,6 +211,7 @@ export async function sendUpdateOrder(p: { autoCancel: boolean; }; deadline: BigNumberish; + desChainId: BigNumberish; userNonce?: BigNumberish; relayRouter: ethers.Contract; signature?: string; @@ -368,6 +370,7 @@ export async function sendCancelOrder(p: { chainId: BigNumberish; account: string; deadline: BigNumberish; + desChainId: BigNumberish; userNonce?: BigNumberish; relayRouter: ethers.Contract; signature?: string; @@ -507,6 +510,7 @@ export async function sendRemoveSubaccount(p: { chainId: BigNumberish; account: string; deadline: BigNumberish; + desChainId: BigNumberish; userNonce?: BigNumberish; relayRouter: ethers.Contract; signature?: string; From 6553e812bf24217caa2e1525ec3eb2b664498b0e Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 17 Feb 2025 18:35:27 +0200 Subject: [PATCH 072/205] Include desChainId into relay signature --- contracts/router/relay/RelayUtils.sol | 4 ++-- utils/relay/helpers.ts | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index 9ec5225b7..53002de2f 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -218,8 +218,8 @@ library RelayUtils { relayParams.tokenPermits, relayParams.fee, relayParams.userNonce, - relayParams.deadline - // relayParams.desChainId // TOOD: should be block.chainid + relayParams.deadline, + relayParams.desChainId // TOOD: should be block.chainid ) ); } diff --git a/utils/relay/helpers.ts b/utils/relay/helpers.ts index 35c913683..210c4b63d 100644 --- a/utils/relay/helpers.ts +++ b/utils/relay/helpers.ts @@ -62,6 +62,7 @@ export function hashRelayParams(relayParams: any) { "tuple(address feeToken, uint256 feeAmount, address[] feeSwapPath)", "uint256", "uint256", + "uint256", ], [ [relayParams.oracleParams.tokens, relayParams.oracleParams.providers, relayParams.oracleParams.data], @@ -84,6 +85,7 @@ export function hashRelayParams(relayParams: any) { [relayParams.fee.feeToken, relayParams.fee.feeAmount, relayParams.fee.feeSwapPath], relayParams.userNonce, relayParams.deadline, + relayParams.desChainId, ] ); From 0f4dca578bc7301c3f2307ac865caa1bf17f54dc Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 17 Feb 2025 18:52:11 +0200 Subject: [PATCH 073/205] Validate desChainId against block.chainid (for same chain actions, chainId and desChainId are the same) --- contracts/router/relay/RelayUtils.sol | 22 +++++++++---------- .../relay/SubaccountGelatoRelayRouter.sol | 8 +++---- test/router/relay/GelatoRelayRouter.ts | 6 ++--- .../relay/SubaccountGelatoRelayRouter.ts | 8 +++---- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index 53002de2f..80ac3814d 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -209,7 +209,7 @@ library RelayUtils { //////////////////// ORDER //////////////////// - function _getRelayParamsHash(RelayParams calldata relayParams) internal pure returns (bytes32) { + function _getRelayParamsHash(RelayParams calldata relayParams) internal view returns (bytes32) { return keccak256( abi.encode( @@ -219,7 +219,7 @@ library RelayUtils { relayParams.fee, relayParams.userNonce, relayParams.deadline, - relayParams.desChainId // TOOD: should be block.chainid + block.chainid ) ); } @@ -229,7 +229,7 @@ library RelayUtils { bytes32 key, UpdateOrderParams calldata params, bool increaseExecutionFee - ) external pure returns (bytes32) { + ) external view returns (bytes32) { return keccak256( abi.encode( @@ -257,7 +257,7 @@ library RelayUtils { ); } - function getCancelOrderStructHash(RelayParams calldata relayParams, bytes32 key) external pure returns (bytes32) { + function getCancelOrderStructHash(RelayParams calldata relayParams, bytes32 key) external view returns (bytes32) { return keccak256(abi.encode(CANCEL_ORDER_TYPEHASH, key, _getRelayParamsHash(relayParams))); } @@ -265,7 +265,7 @@ library RelayUtils { RelayParams calldata relayParams, uint256 collateralDeltaAmount, IBaseOrderUtils.CreateOrderParams memory params - ) external pure returns (bytes32) { + ) external view returns (bytes32) { return keccak256( abi.encode( @@ -328,7 +328,7 @@ library RelayUtils { RelayParams calldata relayParams, TransferRequest[] calldata transferRequests, DepositUtils.CreateDepositParams memory params - ) external pure returns (bytes32) { + ) external view returns (bytes32) { return keccak256( abi.encode( @@ -380,7 +380,7 @@ library RelayUtils { RelayParams calldata relayParams, TransferRequest[] calldata transferRequests, WithdrawalUtils.CreateWithdrawalParams memory params - ) external pure returns (bytes32) { + ) external view returns (bytes32) { return keccak256( abi.encode( @@ -419,7 +419,7 @@ library RelayUtils { RelayParams calldata relayParams, TransferRequest[] calldata transferRequests, GlvDepositUtils.CreateGlvDepositParams memory params - ) external pure returns (bytes32) { + ) external view returns (bytes32) { return keccak256( abi.encode( @@ -473,7 +473,7 @@ library RelayUtils { RelayParams calldata relayParams, TransferRequest[] calldata transferRequests, GlvWithdrawalUtils.CreateGlvWithdrawalParams memory params - ) external pure returns (bytes32) { + ) external view returns (bytes32) { return keccak256( abi.encode( @@ -525,7 +525,7 @@ library RelayUtils { RelayParams calldata relayParams, TransferRequest[] calldata transferRequests, ShiftUtils.CreateShiftParams memory params - ) external pure returns (bytes32) { + ) external view returns (bytes32) { return keccak256( abi.encode( @@ -573,7 +573,7 @@ library RelayUtils { function getBridgeOutStructHash( RelayParams calldata relayParams, BridgeOutParams memory params - ) external pure returns (bytes32) { + ) external view returns (bytes32) { return keccak256( abi.encode( diff --git a/contracts/router/relay/SubaccountGelatoRelayRouter.sol b/contracts/router/relay/SubaccountGelatoRelayRouter.sol index 174d40542..85353d746 100644 --- a/contracts/router/relay/SubaccountGelatoRelayRouter.sol +++ b/contracts/router/relay/SubaccountGelatoRelayRouter.sol @@ -254,7 +254,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { function _getRemoveSubaccountStructHash( RelayUtils.RelayParams calldata relayParams, address subaccount - ) internal pure returns (bytes32) { + ) internal view returns (bytes32) { return keccak256(abi.encode(REMOVE_SUBACCOUNT_TYPEHASH, subaccount, RelayUtils._getRelayParamsHash(relayParams))); } @@ -282,7 +282,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { address account, uint256 collateralDeltaAmount, IBaseOrderUtils.CreateOrderParams memory params - ) internal pure returns (bytes32) { + ) internal view returns (bytes32) { bytes32 relayParamsHash = RelayUtils._getRelayParamsHash(relayParams); bytes32 subaccountApprovalHash = keccak256(abi.encode(subaccountApproval)); @@ -350,7 +350,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { bytes32 key, RelayUtils.UpdateOrderParams calldata params, bool increaseExecutionFee - ) internal pure returns (bytes32) { + ) internal view returns (bytes32) { return keccak256( abi.encode( @@ -385,7 +385,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { SubaccountApproval calldata subaccountApproval, address account, bytes32 key - ) internal pure returns (bytes32) { + ) internal view returns (bytes32) { return keccak256( abi.encode( diff --git a/test/router/relay/GelatoRelayRouter.ts b/test/router/relay/GelatoRelayRouter.ts index 31d8aa6ca..8e14e6f65 100644 --- a/test/router/relay/GelatoRelayRouter.ts +++ b/test/router/relay/GelatoRelayRouter.ts @@ -93,7 +93,7 @@ describe("GelatoRelayRouter", () => { account: user0.address, params: defaultParams, deadline: 9999999999, - desChainId: 0, + desChainId: chainId, // for non-multichain actions, desChainId is the same as chainId relayRouter: gelatoRelayRouter, chainId, relayFeeToken: wnt.address, @@ -446,7 +446,7 @@ describe("GelatoRelayRouter", () => { }, key: ethers.constants.HashZero, deadline: 9999999999, - desChainId: 0, + desChainId: chainId, // for non-multichain actions, desChainId is the same as chainId relayRouter: gelatoRelayRouter, chainId, relayFeeToken: wnt.address, @@ -585,7 +585,7 @@ describe("GelatoRelayRouter", () => { key: ethers.constants.HashZero, account: user0.address, deadline: 9999999999, - desChainId: 0, + desChainId: chainId, // for non-multichain actions, desChainId is the same as chainId relayRouter: gelatoRelayRouter, chainId, relayFeeToken: wnt.address, diff --git a/test/router/relay/SubaccountGelatoRelayRouter.ts b/test/router/relay/SubaccountGelatoRelayRouter.ts index eb4697589..cea79589a 100644 --- a/test/router/relay/SubaccountGelatoRelayRouter.ts +++ b/test/router/relay/SubaccountGelatoRelayRouter.ts @@ -134,7 +134,7 @@ describe("SubaccountGelatoRelayRouter", () => { subaccount: user0.address, params: defaultCreateOrderParams, deadline: 9999999999, - desChainId: 0, + desChainId: chainId, // for non-multichain actions, desChainId is the same as chainId relayRouter: subaccountGelatoRelayRouter, chainId, relayFeeToken: wnt.address, @@ -656,7 +656,7 @@ describe("SubaccountGelatoRelayRouter", () => { autoCancel: false, }, deadline: 9999999999, - desChainId: 0, + desChainId: chainId, // for non-multichain actions, desChainId is the same as chainId relayRouter: subaccountGelatoRelayRouter, chainId, relayFeeToken: wnt.address, @@ -854,7 +854,7 @@ describe("SubaccountGelatoRelayRouter", () => { subaccount: user0.address, key: ethers.constants.HashZero, deadline: 9999999999, - desChainId: 0, + desChainId: chainId, // for non-multichain actions, desChainId is the same as chainId relayRouter: subaccountGelatoRelayRouter, chainId, relayFeeToken: wnt.address, @@ -943,7 +943,7 @@ describe("SubaccountGelatoRelayRouter", () => { relayFeeToken: wnt.address, relayFeeAmount: expandDecimals(1, 15), deadline: 9999999999, - desChainId: 0, + desChainId: chainId, // for non-multichain actions, desChainId is the same as chainId }; }); From 749a4133bf5c57fa79dde2928254cecc30f69226 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 17 Feb 2025 21:00:59 +0200 Subject: [PATCH 074/205] Include dataList into relay signature, remove srcChainId from multichain typehashes --- contracts/router/relay/RelayUtils.sol | 24 +++++++++---------- .../relay/SubaccountGelatoRelayRouter.sol | 3 ++- utils/relay/gelatoRelay.ts | 2 ++ utils/relay/subaccountGelatoRelay.ts | 2 ++ 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index 80ac3814d..ffedd567d 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -89,7 +89,7 @@ library RelayUtils { bytes32 public constant CREATE_ORDER_TYPEHASH = keccak256( bytes( - "CreateOrder(uint256 collateralDeltaAmount,CreateOrderAddresses addresses,CreateOrderNumbers numbers,uint256 orderType,uint256 decreasePositionSwapType,bool isLong,bool shouldUnwrapNativeToken,bool autoCancel,bytes32 referralCode,bytes32 relayParams)CreateOrderAddresses(address receiver,address cancellationReceiver,address callbackContract,address uiFeeReceiver,address market,address initialCollateralToken,address[] swapPath)CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime)" + "CreateOrder(uint256 collateralDeltaAmount,CreateOrderAddresses addresses,CreateOrderNumbers numbers,uint256 orderType,uint256 decreasePositionSwapType,bool isLong,bool shouldUnwrapNativeToken,bool autoCancel,bytes32 referralCode,bytes32[] dataList,bytes32 relayParams)CreateOrderAddresses(address receiver,address cancellationReceiver,address callbackContract,address uiFeeReceiver,address market,address initialCollateralToken,address[] swapPath)CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime)" ) ); bytes32 public constant CREATE_ORDER_NUMBERS_TYPEHASH = @@ -110,13 +110,13 @@ library RelayUtils { bytes32 public constant CREATE_DEPOSIT_TYPEHASH = keccak256( bytes( - "CreateDeposit(CreateDepositParams params,bytes32 relayParams)CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + "CreateDeposit(CreateDepositParams params,bytes32 relayParams)CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" ) ); bytes32 public constant CREATE_DEPOSIT_PARAMS_TYPEHASH = keccak256( bytes( - "CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + "CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" ) ); bytes32 public constant CREATE_DEPOSIT_PARAMS_ADDRESSES_TYPEHASH = @@ -129,20 +129,20 @@ library RelayUtils { bytes32 public constant CREATE_WITHDRAWAL_TYPEHASH = keccak256( bytes( - "CreateWithdrawal(CreateWithdrawalParams params,bytes32 relayParams)CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + "CreateWithdrawal(CreateWithdrawalParams params,bytes32 relayParams)CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" ) ); bytes32 public constant CREATE_WITHDRAWAL_PARAMS_TYPEHASH = keccak256( bytes( - "CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + "CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" ) ); bytes32 public constant CREATE_GLV_DEPOSIT_TYPEHASH = keccak256( bytes( - "CreateGlvDeposit(CreateGlvDepositParams params,bytes32 relayParams)CreateGlvDepositParams(address account,address market,address initialLongToken,address initialShortToken,uint256 srcChainId,bytes32[] dataList)" + "CreateGlvDeposit(CreateGlvDepositParams params,bytes32 relayParams)CreateGlvDepositParams(address account,address market,address initialLongToken,address initialShortToken,bytes32[] dataList)" ) ); bytes32 public constant CREATE_GLV_DEPOSIT_PARAMS_ADDRESSES_TYPEHASH = @@ -154,20 +154,20 @@ library RelayUtils { bytes32 public constant CREATE_GLV_DEPOSIT_PARAMS_TYPEHASH = keccak256( bytes( - "CreateGlvDepositParams(CreateGlvDepositParamsAddresses addresses,uint256 minGlvTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bool shouldUnwrapNativeToken,bool isMarketTokenDeposit,bytes32[] dataList)CreateGlvDepositParamsAddresses(address glv,address market,address receiver,address callbackContract,address uiFeeReceiver,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath)" + "CreateGlvDepositParams(CreateGlvDepositParamsAddresses addresses,uint256 minGlvTokens,uint256 executionFee,uint256 callbackGasLimit,bool shouldUnwrapNativeToken,bool isMarketTokenDeposit,bytes32[] dataList)CreateGlvDepositParamsAddresses(address glv,address market,address receiver,address callbackContract,address uiFeeReceiver,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath)" ) ); bytes32 public constant CREATE_GLV_WITHDRAWAL_TYPEHASH = keccak256( bytes( - "CreateGlvWithdrawal(CreateGlvWithdrawalParams params,bytes32 relayParams)CreateGlvWithdrawalParams(CreateGlvWithdrawalParamsAddresses addresses,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)CreateGlvWithdrawalParamsAddresses(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath)" + "CreateGlvWithdrawal(CreateGlvWithdrawalParams params,bytes32 relayParams)CreateGlvWithdrawalParams(CreateGlvWithdrawalParamsAddresses addresses,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)CreateGlvWithdrawalParamsAddresses(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath)" ) ); bytes32 public constant CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH = keccak256( bytes( - "CreateGlvWithdrawalParams(CreateGlvWithdrawalParamsAddresses addresses,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + "CreateGlvWithdrawalParams(CreateGlvWithdrawalParamsAddresses addresses,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" ) ); bytes32 public constant CREATE_GLV_WITHDRAWAL_PARAMS_ADDRESSES_TYPEHASH = @@ -180,13 +180,13 @@ library RelayUtils { bytes32 public constant CREATE_SHIFT_TYPEHASH = keccak256( bytes( - "CreateShift(CreateShiftParams params,bytes32 relayParams)CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + "CreateShift(CreateShiftParams params,bytes32 relayParams)CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" ) ); bytes32 public constant CREATE_SHIFT_PARAMS_TYPEHASH = keccak256( bytes( - "CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,uint256 srcChainId,bytes32[] dataList)" + "CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" ) ); @@ -279,7 +279,7 @@ library RelayUtils { params.shouldUnwrapNativeToken, params.autoCancel, params.referralCode, - // keccak256(abi.encodePacked(params.dataList)), + keccak256(abi.encodePacked(params.dataList)), _getRelayParamsHash(relayParams) ) ); diff --git a/contracts/router/relay/SubaccountGelatoRelayRouter.sol b/contracts/router/relay/SubaccountGelatoRelayRouter.sol index 85353d746..7784438da 100644 --- a/contracts/router/relay/SubaccountGelatoRelayRouter.sol +++ b/contracts/router/relay/SubaccountGelatoRelayRouter.sol @@ -41,7 +41,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { bytes32 public constant CREATE_ORDER_TYPEHASH = keccak256( bytes( - "CreateOrder(uint256 collateralDeltaAmount,address account,CreateOrderAddresses addresses,CreateOrderNumbers numbers,uint256 orderType,uint256 decreasePositionSwapType,bool isLong,bool shouldUnwrapNativeToken,bool autoCancel,bytes32 referralCode,bytes32 relayParams,bytes32 subaccountApproval)CreateOrderAddresses(address receiver,address cancellationReceiver,address callbackContract,address uiFeeReceiver,address market,address initialCollateralToken,address[] swapPath)CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime)" + "CreateOrder(uint256 collateralDeltaAmount,address account,CreateOrderAddresses addresses,CreateOrderNumbers numbers,uint256 orderType,uint256 decreasePositionSwapType,bool isLong,bool shouldUnwrapNativeToken,bool autoCancel,bytes32 referralCode,bytes32[] dataList,bytes32 relayParams,bytes32 subaccountApproval)CreateOrderAddresses(address receiver,address cancellationReceiver,address callbackContract,address uiFeeReceiver,address market,address initialCollateralToken,address[] swapPath)CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime)" ) ); bytes32 public constant CREATE_ORDER_NUMBERS_TYPEHASH = @@ -300,6 +300,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { params.shouldUnwrapNativeToken, params.autoCancel, params.referralCode, + keccak256(abi.encodePacked(params.dataList)), relayParamsHash, subaccountApprovalHash ) diff --git a/utils/relay/gelatoRelay.ts b/utils/relay/gelatoRelay.ts index 41f0fd48e..63019aee9 100644 --- a/utils/relay/gelatoRelay.ts +++ b/utils/relay/gelatoRelay.ts @@ -86,6 +86,7 @@ async function getCreateOrderSignature({ { name: "shouldUnwrapNativeToken", type: "bool" }, { name: "autoCancel", type: "bool" }, { name: "referralCode", type: "bytes32" }, + { name: "dataList", type: "bytes32[]" }, { name: "relayParams", type: "bytes32" }, ], CreateOrderAddresses: [ @@ -124,6 +125,7 @@ async function getCreateOrderSignature({ shouldUnwrapNativeToken: params.shouldUnwrapNativeToken, autoCancel: false, referralCode: params.referralCode, + dataList: params.dataList, relayParams: hashRelayParams(relayParams), }; diff --git a/utils/relay/subaccountGelatoRelay.ts b/utils/relay/subaccountGelatoRelay.ts index 5fbe0bfa1..65527902c 100644 --- a/utils/relay/subaccountGelatoRelay.ts +++ b/utils/relay/subaccountGelatoRelay.ts @@ -117,6 +117,7 @@ async function getCreateOrderSignature({ { name: "shouldUnwrapNativeToken", type: "bool" }, { name: "autoCancel", type: "bool" }, { name: "referralCode", type: "bytes32" }, + { name: "dataList", type: "bytes32[]" }, { name: "relayParams", type: "bytes32" }, { name: "subaccountApproval", type: "bytes32" }, ], @@ -153,6 +154,7 @@ async function getCreateOrderSignature({ shouldUnwrapNativeToken: params.shouldUnwrapNativeToken, autoCancel: false, referralCode: params.referralCode, + dataList: params.dataList, relayParams: hashRelayParams(relayParams), subaccountApproval: hashSubaccountApproval(subaccountApproval), }; From 2b9883bb0d7f85bdb9eebb9fc103335aa1d449c9 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 18 Feb 2025 09:47:24 +0200 Subject: [PATCH 075/205] Add deploy script for MultichainGmRouter --- deploy/deployMultichainGmRouter.ts | 48 ++++++++++++++++++++++++++++++ utils/fixture.ts | 2 ++ 2 files changed, 50 insertions(+) create mode 100644 deploy/deployMultichainGmRouter.ts diff --git a/deploy/deployMultichainGmRouter.ts b/deploy/deployMultichainGmRouter.ts new file mode 100644 index 000000000..40a13964b --- /dev/null +++ b/deploy/deployMultichainGmRouter.ts @@ -0,0 +1,48 @@ +import { grantRoleIfNotGranted } from "../utils/role"; +import { createDeployFunction } from "../utils/deploy"; + +const baseConstructorContracts = [ + "Router", + "DataStore", + "EventEmitter", + "Oracle", + "OrderVault", + "OrderHandler", + "ExternalHandler", + "MultichainVault", +]; + +const gmConstructorContracts = ["DepositVault", "DepositHandler", "WithdrawalVault", "WithdrawalHandler", "ShiftVault"]; + +const func = createDeployFunction({ + contractName: "MultichainGmRouter", + dependencyNames: [...baseConstructorContracts, ...gmConstructorContracts], + getDeployArgs: async ({ dependencyContracts }) => { + const baseParams = { + router: dependencyContracts.Router.address, + dataStore: dependencyContracts.DataStore.address, + eventEmitter: dependencyContracts.EventEmitter.address, + oracle: dependencyContracts.Oracle.address, + orderVault: dependencyContracts.OrderVault.address, + orderHandler: dependencyContracts.OrderHandler.address, + externalHandler: dependencyContracts.ExternalHandler.address, + multichainVault: dependencyContracts.MultichainVault.address, + }; + + return [ + baseParams, + dependencyContracts.DepositVault.address, + dependencyContracts.DepositHandler.address, + dependencyContracts.WithdrawalVault.address, + dependencyContracts.WithdrawalHandler.address, + dependencyContracts.ShiftVault.address, + ]; + }, + libraryNames: ["MarketStoreUtils", "MultichainUtils", "OrderStoreUtils", "RelayUtils", "ShiftUtils", "SwapUtils"], + afterDeploy: async ({ deployedContract }) => { + await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); + await grantRoleIfNotGranted(deployedContract.address, "ROUTER_PLUGIN"); + }, +}); + +export default func; diff --git a/utils/fixture.ts b/utils/fixture.ts index e7eb85e62..a08855a16 100644 --- a/utils/fixture.ts +++ b/utils/fixture.ts @@ -98,6 +98,7 @@ export async function deployFixture() { const gelatoRelayRouter = await hre.ethers.getContract("GelatoRelayRouter"); const subaccountGelatoRelayRouter = await hre.ethers.getContract("SubaccountGelatoRelayRouter"); const subaccountRouter = await hre.ethers.getContract("SubaccountRouter"); + const multichainGmRouter = await hre.ethers.getContract("MultichainGmRouter"); const relayUtils = await hre.ethers.getContract("RelayUtils"); const oracle = await hre.ethers.getContract("Oracle"); const gmOracleProvider = await hre.ethers.getContract("GmOracleProvider"); @@ -281,6 +282,7 @@ export async function deployFixture() { gelatoRelayRouter, subaccountGelatoRelayRouter, subaccountRouter, + multichainGmRouter, relayUtils, oracle, gmOracleProvider, From a7c8e43f782b1a6724e96b3ac25507f006192e59 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 18 Feb 2025 23:29:53 +0200 Subject: [PATCH 076/205] Fix 'from' param for transferOut --- contracts/multichain/MultichainRouter.sol | 4 ++-- contracts/multichain/MultichainUtils.sol | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index fb418f25b..d8013e1f9 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -6,7 +6,7 @@ import "../router/relay/GelatoRelayRouter.sol"; import "./MultichainUtils.sol"; -contract MultichainRouter is GelatoRelayRouter { +abstract contract MultichainRouter is GelatoRelayRouter { struct BaseConstructorParams { Router router; @@ -49,7 +49,7 @@ contract MultichainRouter is GelatoRelayRouter { if (srcChainId == 0) { router.pluginTransfer(token, account, receiver, amount); } else { - MultichainUtils.transferOut(dataStore, eventEmitter, token, account, receiver, amount, srcChainId); + MultichainUtils.transferOut(dataStore, eventEmitter, multichainVault, token, account, receiver, amount, srcChainId); } } diff --git a/contracts/multichain/MultichainUtils.sol b/contracts/multichain/MultichainUtils.sol index 9b53616b6..5554df422 100644 --- a/contracts/multichain/MultichainUtils.sol +++ b/contracts/multichain/MultichainUtils.sol @@ -53,6 +53,7 @@ library MultichainUtils { function transferOut( DataStore dataStore, EventEmitter eventEmitter, + MultichainVault multichainVault, address token, address account, address receiver, @@ -70,7 +71,7 @@ library MultichainUtils { revert Errors.InsufficientMultichainBalance(); } - IERC20(token).safeTransferFrom(account, receiver, amount); + IERC20(token).safeTransferFrom(address(multichainVault), receiver, amount); dataStore.decrementUint(Keys.multichainBalanceKey(account, token), amount); MultichainEventUtils.emitMultichainTransferOut(eventEmitter, token, account, amount, srcChainId); } From afca6e5910928e05ef1a944692864d6045ebb0a1 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 18 Feb 2025 23:35:45 +0200 Subject: [PATCH 077/205] Fix multichain createDeposit typehash (without transferRequests) --- contracts/router/relay/RelayUtils.sol | 37 ++++++++------------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index ffedd567d..99a9be372 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -110,19 +110,14 @@ library RelayUtils { bytes32 public constant CREATE_DEPOSIT_TYPEHASH = keccak256( bytes( - "CreateDeposit(CreateDepositParams params,bytes32 relayParams)CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" + "CreateDeposit(CreateDepositAddresses addresses,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList,bytes32 relayParams)CreateDepositAddresses(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath)" + // TODO: "CreateDeposit(TransferRequest[] transferRequests, CreateDepositAddresses addresses,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList,bytes32 relayParams)TransferRequest(address token,address receiver,uint256 amount)CreateDepositAddresses(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath)" ) ); - bytes32 public constant CREATE_DEPOSIT_PARAMS_TYPEHASH = + bytes32 public constant CREATE_DEPOSIT_ADDRESSES_TYPEHASH = keccak256( bytes( - "CreateDepositParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" - ) - ); - bytes32 public constant CREATE_DEPOSIT_PARAMS_ADDRESSES_TYPEHASH = - keccak256( - bytes( - "CreateDepositParamsAdresses(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath)" + "CreateDepositAddresses(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath)" ) ); @@ -219,7 +214,7 @@ library RelayUtils { relayParams.fee, relayParams.userNonce, relayParams.deadline, - block.chainid + block.chainid // TODO: could actually be relayParams.desChainId. desChainId is already checked against block.chainid in _validateDesChainId ) ); } @@ -333,26 +328,14 @@ library RelayUtils { keccak256( abi.encode( CREATE_DEPOSIT_TYPEHASH, - _getCreateDepositParamsStructHash(params), - _getTransferRequestsHash(transferRequests), - _getRelayParamsHash(relayParams) - ) - ); - } - - function _getCreateDepositParamsStructHash( - DepositUtils.CreateDepositParams memory params - ) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - CREATE_DEPOSIT_PARAMS_TYPEHASH, + // _getTransferRequestsHash(transferRequests), // TODO: fix transferRequests hashing _getCreateDepositParamsAdressesStructHash(params.addresses), params.minMarketTokens, params.shouldUnwrapNativeToken, params.executionFee, params.callbackGasLimit, - keccak256(abi.encodePacked(params.dataList)) + keccak256(abi.encodePacked(params.dataList)), + _getRelayParamsHash(relayParams) ) ); } @@ -363,7 +346,7 @@ library RelayUtils { return keccak256( abi.encode( - CREATE_DEPOSIT_PARAMS_ADDRESSES_TYPEHASH, + CREATE_DEPOSIT_ADDRESSES_TYPEHASH, addresses.receiver, addresses.callbackContract, addresses.uiFeeReceiver, @@ -557,7 +540,7 @@ library RelayUtils { ); } - function _getTransferRequestStructHash(TransferRequest memory request) internal pure returns (bytes32) { + function _getTransferRequestStructHash(TransferRequest calldata request) internal pure returns (bytes32) { return keccak256(abi.encode(TRANSFER_REQUEST_TYPEHASH, request.token, request.receiver, request.amount)); } From fdaa75287734228a1e490e956b89b8316967aa9a Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 18 Feb 2025 23:38:09 +0200 Subject: [PATCH 078/205] WIP test multichain createDeposit flow --- contracts/mock/MockStargatePool.sol | 6 +- test/multichain/MultichainGmRouter.ts | 177 ++++++++++++++++++++++++++ utils/relay/multichain.ts | 126 ++++++++++++++++++ 3 files changed, 306 insertions(+), 3 deletions(-) create mode 100644 test/multichain/MultichainGmRouter.ts create mode 100644 utils/relay/multichain.ts diff --git a/contracts/mock/MockStargatePool.sol b/contracts/mock/MockStargatePool.sol index 813298d7b..628095a2c 100644 --- a/contracts/mock/MockStargatePool.sol +++ b/contracts/mock/MockStargatePool.sol @@ -6,14 +6,14 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { LayerZeroProvider } from "../multichain/LayerZeroProvider.sol"; contract MockStargatePool { - function sendToken(address token, address recipientContract, uint256 amount, bytes calldata message) external { - IERC20(token).transferFrom(msg.sender, recipientContract, amount); + function sendToken(address token, address multichainProvider, uint256 amount, bytes calldata message) external { + IERC20(token).transferFrom(msg.sender, multichainProvider, amount); address from = address(this); bytes32 guid = bytes32(0); address executor = msg.sender; bytes memory extraData = bytes(""); - LayerZeroProvider(recipientContract).lzCompose(from, guid, message, executor, extraData); + LayerZeroProvider(multichainProvider).lzCompose(from, guid, message, executor, extraData); } } diff --git a/test/multichain/MultichainGmRouter.ts b/test/multichain/MultichainGmRouter.ts new file mode 100644 index 000000000..e067db52a --- /dev/null +++ b/test/multichain/MultichainGmRouter.ts @@ -0,0 +1,177 @@ +import { expect } from "chai"; +import { impersonateAccount, setBalance } from "@nomicfoundation/hardhat-network-helpers"; + +import { expandDecimals } from "../../utils/math"; +import { deployFixture } from "../../utils/fixture"; +import { GELATO_RELAY_ADDRESS } from "../../utils/relay/addresses"; +import { getTokenPermit } from "../../utils/relay/tokenPermit"; +import { sendCreateDeposit } from "../../utils/relay/multichain"; +import * as keys from "../../utils/keys"; + +describe("MultichainGmRouter", () => { + let fixture; + let user0, user1, user2; + let reader, + dataStore, + router, + multichainGmRouter, + multichainVault, + depositVault, + ethUsdMarket, + wnt, + usdc, + layerZeroProvider, + mockStargatePool; + let relaySigner; + let chainId; + + let defaultParams; + let createDepositParams: Parameters[0]; + + const wntAmount = expandDecimals(10, 18); + const usdcAmount = expandDecimals(50000, 6); + + beforeEach(async () => { + fixture = await deployFixture(); + ({ user0, user1, user2 } = fixture.accounts); + ({ + reader, + dataStore, + router, + multichainGmRouter, + multichainVault, + depositVault, + ethUsdMarket, + wnt, + usdc, + layerZeroProvider, + mockStargatePool, + } = fixture.contracts); + + defaultParams = { + addresses: { + receiver: user0.address, + callbackContract: user1.address, + uiFeeReceiver: user2.address, + market: ethUsdMarket.marketToken, + initialLongToken: ethUsdMarket.longToken, + initialShortToken: ethUsdMarket.shortToken, + longTokenSwapPath: [ethUsdMarket.marketToken], + shortTokenSwapPath: [ethUsdMarket.marketToken], + }, + minMarketTokens: 0, + shouldUnwrapNativeToken: false, + executionFee: 0, + callbackGasLimit: "200000", + dataList: [], + }; + + await impersonateAccount(GELATO_RELAY_ADDRESS); + await setBalance(GELATO_RELAY_ADDRESS, expandDecimals(100, 18)); + await usdc.mint(user0.address, expandDecimals(1, 30)); // very large amount + await wnt.mint(user0.address, expandDecimals(1, 30)); // very large amount + + relaySigner = await hre.ethers.getSigner(GELATO_RELAY_ADDRESS); + chainId = await hre.ethers.provider.getNetwork().then((network) => network.chainId); + + createDepositParams = { + sender: relaySigner, + signer: user0, + feeParams: { + feeToken: wnt.address, + feeAmount: expandDecimals(2, 15), // 0.002 ETH + feeSwapPath: [], + }, + tokenPermits: [], + transferRequests: [ + { + token: wnt.address, + receiver: depositVault.address, + amount: expandDecimals(1, 18), + }, + { + token: usdc.address, + receiver: depositVault.address, + amount: expandDecimals(5000, 6), + }, + ], + account: user0.address, + params: defaultParams, + deadline: 9999999999, + chainId, + srcChainId: 0, + desChainId: chainId, // for non-multichain actions, desChainId and srcChainId are the same + relayRouter: multichainGmRouter, + relayFeeToken: wnt.address, + relayFeeAmount: expandDecimals(1, 15), + }; + + // mock wnt bridging (increase user's wnt multichain balance) + const encodedMessageEth = ethers.utils.defaultAbiCoder.encode( + ["address", "address", "uint256"], + [user0.address, wnt.address, createDepositParams.srcChainId] + ); + await wnt.connect(user0).approve(mockStargatePool.address, wntAmount); + await mockStargatePool + .connect(user0) + .sendToken(wnt.address, layerZeroProvider.address, wntAmount, encodedMessageEth); + // mock usdc bridging (increase user's usdc multichain balance) + const encodedMessageUsdc = ethers.utils.defaultAbiCoder.encode( + ["address", "address", "uint256"], + [user0.address, usdc.address, createDepositParams.srcChainId] + ); + await usdc.connect(user0).approve(mockStargatePool.address, usdcAmount); + await mockStargatePool + .connect(user0) + .sendToken(usdc.address, layerZeroProvider.address, usdcAmount, encodedMessageUsdc); + }); + + describe("createDeposit", () => { + it("creates deposit and sends relayer fee", async () => { + expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, wnt.address))).to.eq(wntAmount); // 10 ETH + expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, usdc.address))).to.eq(usdcAmount); // 50k USDC + + console.log("user0 address: %s", user0.address); + console.log("GELATO_RELAY_ADDRESS: %s", GELATO_RELAY_ADDRESS); + console.log("router address: %s", router.address); + console.log("multichainGmRouter address: %s", multichainGmRouter.address); + console.log("multichainVault address: %s", multichainVault.address); + console.log("depositVault address: %s", depositVault.address); + + const tokenPermits = [ + await getTokenPermit( + wnt, + user0, + multichainGmRouter.address, // reverts with InvalidPermitSpender if not Router + createDepositParams.transferRequests[0].amount, + 0, + 9999999999, + chainId + ), + await getTokenPermit( + usdc, + user0, + multichainGmRouter.address, // reverts with InvalidPermitSpender if not Router + createDepositParams.transferRequests[1].amount, + 0, + 9999999999, + chainId + ), + ]; + + // TODO: fix 'ERC20: insufficient allowance' error + // might be due to permits or the multichianGmRouter not being able to spend from multichainVault + await sendCreateDeposit({ + ...createDepositParams, + tokenPermits, + }); + + expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, wnt.address))).to.eq( + wntAmount.sub(createDepositParams.transferRequests[0].amount) + ); // 9 ETH + expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, usdc.address))).to.eq( + usdcAmount.sub(createDepositParams.transferRequests[1].amount) + ); // 45k USDC + }); + }); +}); diff --git a/utils/relay/multichain.ts b/utils/relay/multichain.ts new file mode 100644 index 000000000..40bb5dcc9 --- /dev/null +++ b/utils/relay/multichain.ts @@ -0,0 +1,126 @@ +import { BigNumberish, ethers } from "ethers"; +import { GELATO_RELAY_ADDRESS } from "./addresses"; +import { hashRelayParams, signTypedData } from "./helpers"; +import { getDomain } from "./helpers"; +import { getRelayParams } from "./helpers"; + +export async function sendCreateDeposit(p: { + signer: ethers.Signer; + sender: ethers.Signer; + oracleParams?: { + tokens: string[]; + providers: string[]; + data: string[]; + }; + externalCalls?: { + externalCallTargets: string[]; + externalCallDataList: string[]; + refundTokens: string[]; + refundReceivers: string[]; + }; + tokenPermits?: { + token: string; + spender: string; + value: BigNumberish; + deadline: BigNumberish; + }[]; + feeParams: { + feeToken: string; + feeAmount: BigNumberish; + feeSwapPath: string[]; + }; + transferRequests: { + token: string; + receiver: string; + amount: BigNumberish; + }[]; + account: string; + params: any; + signature?: string; + userNonce?: BigNumberish; + deadline: BigNumberish; + chainId: BigNumberish; + srcChainId: BigNumberish; + desChainId: BigNumberish; + relayRouter: ethers.Contract; + relayFeeToken: string; + relayFeeAmount: BigNumberish; +}) { + const relayParams = await getRelayParams(p); + + let signature = p.signature; + if (!signature) { + signature = await getCreateDepositSignature({ ...p, relayParams, verifyingContract: p.relayRouter.address }); + } + + const createDepositCalldata = p.relayRouter.interface.encodeFunctionData("createDeposit", [ + { ...relayParams, signature }, + p.account, + p.srcChainId, + p.transferRequests, + p.params, + ]); + const calldata = ethers.utils.solidityPack( + ["bytes", "address", "address", "uint256"], + [createDepositCalldata, GELATO_RELAY_ADDRESS, p.relayFeeToken, p.relayFeeAmount] + ); + return p.sender.sendTransaction({ + to: p.relayRouter.address, + data: calldata, + }); +} + +async function getCreateDepositSignature({ + signer, + relayParams, + transferRequests, + verifyingContract, + params, + chainId, +}) { + if (relayParams.userNonce === undefined) { + throw new Error("userNonce is required"); + } + const types = { + CreateDeposit: [ + // { name: "transferRequests", type: "TransferRequest[]" }, + { name: "addresses", type: "CreateDepositAddresses" }, + { name: "minMarketTokens", type: "uint256" }, + { name: "shouldUnwrapNativeToken", type: "bool" }, + { name: "executionFee", type: "uint256" }, + { name: "callbackGasLimit", type: "uint256" }, + { name: "dataList", type: "bytes32[]" }, + { name: "relayParams", type: "bytes32" }, + ], + CreateDepositAddresses: [ + { name: "receiver", type: "address" }, + { name: "callbackContract", type: "address" }, + { name: "uiFeeReceiver", type: "address" }, + { name: "market", type: "address" }, + { name: "initialLongToken", type: "address" }, + { name: "initialShortToken", type: "address" }, + { name: "longTokenSwapPath", type: "address[]" }, + { name: "shortTokenSwapPath", type: "address[]" }, + ], + // TransferRequest: [ + // { name: "token", type: "address" }, + // { name: "receiver", type: "address" }, + // { name: "amount", type: "uint256" }, + // ], + }; + + const domain = getDomain(chainId, verifyingContract); + + const typedData = { + // transferRequests, + addresses: params.addresses, + minMarketTokens: params.minMarketTokens, + shouldUnwrapNativeToken: params.shouldUnwrapNativeToken, + executionFee: params.executionFee, + callbackGasLimit: params.callbackGasLimit, + dataList: params.dataList, + relayParams: hashRelayParams(relayParams), + }; + + return signTypedData(signer, domain, types, typedData); +} From 76260fe4a5951b31e2102a566b2d59438d102a60 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 18 Feb 2025 23:50:33 +0200 Subject: [PATCH 079/205] change token permit nonce and srcChainId --- test/multichain/MultichainGmRouter.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/multichain/MultichainGmRouter.ts b/test/multichain/MultichainGmRouter.ts index e067db52a..0e3653f3a 100644 --- a/test/multichain/MultichainGmRouter.ts +++ b/test/multichain/MultichainGmRouter.ts @@ -99,7 +99,7 @@ describe("MultichainGmRouter", () => { params: defaultParams, deadline: 9999999999, chainId, - srcChainId: 0, + srcChainId: chainId, // 0 would mean same chain action desChainId: chainId, // for non-multichain actions, desChainId and srcChainId are the same relayRouter: multichainGmRouter, relayFeeToken: wnt.address, @@ -153,7 +153,7 @@ describe("MultichainGmRouter", () => { user0, multichainGmRouter.address, // reverts with InvalidPermitSpender if not Router createDepositParams.transferRequests[1].amount, - 0, + 1, 9999999999, chainId ), From b4fd7aef691f1e4f007a42df5f1ca3978da9d2aa Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 19 Feb 2025 11:10:49 +0200 Subject: [PATCH 080/205] Add provider specific function param on bridgeOut --- contracts/multichain/IMultichainProvider.sol | 8 ++++---- contracts/multichain/LayerZeroProvider.sol | 20 ++++++++++++------- .../multichain/MultichainOrderRouter.sol | 12 +++++------ contracts/router/relay/RelayUtils.sol | 2 +- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/contracts/multichain/IMultichainProvider.sol b/contracts/multichain/IMultichainProvider.sol index 98f73a669..d7950d9ff 100644 --- a/contracts/multichain/IMultichainProvider.sol +++ b/contracts/multichain/IMultichainProvider.sol @@ -7,10 +7,10 @@ pragma solidity ^0.8.0; */ interface IMultichainProvider { function bridgeOut( - address _stargate, - uint32 _dstEid, - address account, + address provider, + address receiver, address token, - uint256 amount + uint256 amount, + bytes calldata data ) external; } diff --git a/contracts/multichain/LayerZeroProvider.sol b/contracts/multichain/LayerZeroProvider.sol index 8edf0665b..a879f624c 100644 --- a/contracts/multichain/LayerZeroProvider.sol +++ b/contracts/multichain/LayerZeroProvider.sol @@ -26,6 +26,11 @@ import "./LayerZeroProviderEventUtils.sol"; * - sends tokens to the Stargate executor for bridging out to the source chain */ contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer { + + struct LayerZeroBridgeOutParams { + bytes32 dstEid; + } + DataStore public immutable dataStore; EventEmitter public immutable eventEmitter; MultichainVault public immutable multichainVault; @@ -92,20 +97,21 @@ contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer { function bridgeOut( address _stargate, - uint32 _dstEid, - address account, + address receiver, address token, - uint256 amount + uint256 amount, + bytes calldata data ) external { IERC20(token).approve(_stargate, amount); // IStargate stargate = IStargate(_stargate); + // LayerZeroBridgeOutParams memory info = abi.decode(data, (LayerZeroBridgeOutParams)); // (uint256 valueToSend, SendParam memory sendParam, MessagingFee memory messagingFee) = prepareSend( // stargate, // amount, - // account, - // _dstEid, + // receiver, + // info.dstEid, // new bytes(0), // _extraOptions // new bytes(0) // _composeMsg // ); @@ -113,12 +119,12 @@ contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer { // (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) = stargate.send{value: valueToSend}( // sendParam, // messagingFee, - // account + // receiver // ); MultichainEventUtils.emitBridgeOut( eventEmitter, - account, + receiver, token, amount ); diff --git a/contracts/multichain/MultichainOrderRouter.sol b/contracts/multichain/MultichainOrderRouter.sol index 533ce802b..ad9121eb7 100644 --- a/contracts/multichain/MultichainOrderRouter.sol +++ b/contracts/multichain/MultichainOrderRouter.sol @@ -64,23 +64,23 @@ contract MultichainOrderRouter is MultichainRouter { function bridgeOut( RelayUtils.RelayParams calldata relayParams, address provider, - uint32 dstEid, - address account, + address receiver, uint256 srcChainId, + bytes calldata data, // encoded provider specific data e.g. dstEid RelayUtils.BridgeOutParams calldata params ) external nonReentrant onlyGelatoRelay { _validateDesChainId(relayParams.desChainId); _validateMultichainProvider(dataStore, provider); bytes32 structHash = RelayUtils.getBridgeOutStructHash(relayParams, params); - _validateCall(relayParams, account, structHash, srcChainId); + _validateCall(relayParams, receiver, structHash, srcChainId); multichainProvider.bridgeOut( provider, - dstEid, - account, + receiver, params.token, - params.amount + params.amount, + data ); } diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index 99a9be372..c4dc867f9 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -65,7 +65,7 @@ library RelayUtils { struct BridgeOutParams { address token; - address account; + address receiver; uint256 amount; } From 957a63b978d4d0f3193caaa182f464ce795824b9 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 19 Feb 2025 11:13:10 +0200 Subject: [PATCH 081/205] Fix multichain transferOut logic --- contracts/multichain/MultichainUtils.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/multichain/MultichainUtils.sol b/contracts/multichain/MultichainUtils.sol index 5554df422..8d8f07e2e 100644 --- a/contracts/multichain/MultichainUtils.sol +++ b/contracts/multichain/MultichainUtils.sol @@ -71,7 +71,7 @@ library MultichainUtils { revert Errors.InsufficientMultichainBalance(); } - IERC20(token).safeTransferFrom(address(multichainVault), receiver, amount); + multichainVault.transferOut(token, receiver, amount); dataStore.decrementUint(Keys.multichainBalanceKey(account, token), amount); MultichainEventUtils.emitMultichainTransferOut(eventEmitter, token, account, amount, srcChainId); } From 170376a4a67b37b2a970b5a9acd155f3972d89a8 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 19 Feb 2025 11:30:41 +0200 Subject: [PATCH 082/205] Remove double validation for desChainId --- contracts/router/relay/RelayUtils.sol | 22 +++++++++---------- .../relay/SubaccountGelatoRelayRouter.sol | 8 +++---- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index c4dc867f9..6bdd2332a 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -204,7 +204,7 @@ library RelayUtils { //////////////////// ORDER //////////////////// - function _getRelayParamsHash(RelayParams calldata relayParams) internal view returns (bytes32) { + function _getRelayParamsHash(RelayParams calldata relayParams) internal pure returns (bytes32) { return keccak256( abi.encode( @@ -214,7 +214,7 @@ library RelayUtils { relayParams.fee, relayParams.userNonce, relayParams.deadline, - block.chainid // TODO: could actually be relayParams.desChainId. desChainId is already checked against block.chainid in _validateDesChainId + relayParams.desChainId ) ); } @@ -224,7 +224,7 @@ library RelayUtils { bytes32 key, UpdateOrderParams calldata params, bool increaseExecutionFee - ) external view returns (bytes32) { + ) external pure returns (bytes32) { return keccak256( abi.encode( @@ -252,7 +252,7 @@ library RelayUtils { ); } - function getCancelOrderStructHash(RelayParams calldata relayParams, bytes32 key) external view returns (bytes32) { + function getCancelOrderStructHash(RelayParams calldata relayParams, bytes32 key) external pure returns (bytes32) { return keccak256(abi.encode(CANCEL_ORDER_TYPEHASH, key, _getRelayParamsHash(relayParams))); } @@ -260,7 +260,7 @@ library RelayUtils { RelayParams calldata relayParams, uint256 collateralDeltaAmount, IBaseOrderUtils.CreateOrderParams memory params - ) external view returns (bytes32) { + ) external pure returns (bytes32) { return keccak256( abi.encode( @@ -323,7 +323,7 @@ library RelayUtils { RelayParams calldata relayParams, TransferRequest[] calldata transferRequests, DepositUtils.CreateDepositParams memory params - ) external view returns (bytes32) { + ) external pure returns (bytes32) { return keccak256( abi.encode( @@ -363,7 +363,7 @@ library RelayUtils { RelayParams calldata relayParams, TransferRequest[] calldata transferRequests, WithdrawalUtils.CreateWithdrawalParams memory params - ) external view returns (bytes32) { + ) external pure returns (bytes32) { return keccak256( abi.encode( @@ -402,7 +402,7 @@ library RelayUtils { RelayParams calldata relayParams, TransferRequest[] calldata transferRequests, GlvDepositUtils.CreateGlvDepositParams memory params - ) external view returns (bytes32) { + ) external pure returns (bytes32) { return keccak256( abi.encode( @@ -456,7 +456,7 @@ library RelayUtils { RelayParams calldata relayParams, TransferRequest[] calldata transferRequests, GlvWithdrawalUtils.CreateGlvWithdrawalParams memory params - ) external view returns (bytes32) { + ) external pure returns (bytes32) { return keccak256( abi.encode( @@ -508,7 +508,7 @@ library RelayUtils { RelayParams calldata relayParams, TransferRequest[] calldata transferRequests, ShiftUtils.CreateShiftParams memory params - ) external view returns (bytes32) { + ) external pure returns (bytes32) { return keccak256( abi.encode( @@ -556,7 +556,7 @@ library RelayUtils { function getBridgeOutStructHash( RelayParams calldata relayParams, BridgeOutParams memory params - ) external view returns (bytes32) { + ) external pure returns (bytes32) { return keccak256( abi.encode( diff --git a/contracts/router/relay/SubaccountGelatoRelayRouter.sol b/contracts/router/relay/SubaccountGelatoRelayRouter.sol index 7784438da..27a34b826 100644 --- a/contracts/router/relay/SubaccountGelatoRelayRouter.sol +++ b/contracts/router/relay/SubaccountGelatoRelayRouter.sol @@ -254,7 +254,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { function _getRemoveSubaccountStructHash( RelayUtils.RelayParams calldata relayParams, address subaccount - ) internal view returns (bytes32) { + ) internal pure returns (bytes32) { return keccak256(abi.encode(REMOVE_SUBACCOUNT_TYPEHASH, subaccount, RelayUtils._getRelayParamsHash(relayParams))); } @@ -282,7 +282,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { address account, uint256 collateralDeltaAmount, IBaseOrderUtils.CreateOrderParams memory params - ) internal view returns (bytes32) { + ) internal pure returns (bytes32) { bytes32 relayParamsHash = RelayUtils._getRelayParamsHash(relayParams); bytes32 subaccountApprovalHash = keccak256(abi.encode(subaccountApproval)); @@ -351,7 +351,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { bytes32 key, RelayUtils.UpdateOrderParams calldata params, bool increaseExecutionFee - ) internal view returns (bytes32) { + ) internal pure returns (bytes32) { return keccak256( abi.encode( @@ -386,7 +386,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { SubaccountApproval calldata subaccountApproval, address account, bytes32 key - ) internal view returns (bytes32) { + ) internal pure returns (bytes32) { return keccak256( abi.encode( From 15b0f4c62a21d0ffec619c667ab82f2d9f90f99f Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 19 Feb 2025 14:14:20 +0200 Subject: [PATCH 083/205] Change transferRequests structure, update typehashes --- contracts/error/Errors.sol | 1 + contracts/multichain/MultichainGlvRouter.sol | 4 +- contracts/multichain/MultichainGmRouter.sol | 6 +- contracts/multichain/MultichainRouter.sol | 20 ++++- contracts/router/relay/RelayUtils.sol | 86 ++++++++------------ test/multichain/MultichainGmRouter.ts | 45 ++-------- utils/relay/multichain.ts | 25 +++--- 7 files changed, 74 insertions(+), 113 deletions(-) diff --git a/contracts/error/Errors.sol b/contracts/error/Errors.sol index 9658c109d..812f8f427 100644 --- a/contracts/error/Errors.sol +++ b/contracts/error/Errors.sol @@ -430,6 +430,7 @@ library Errors { error EmptyMarketPrice(address market); // Multichain errors + error InvalidTransferRequestsLength(); error EmptyMultichainTransferInAmount(); error EmptyMultichainTransferOutAmount(); error InsufficientMultichainBalance(); diff --git a/contracts/multichain/MultichainGlvRouter.sol b/contracts/multichain/MultichainGlvRouter.sol index fddff4315..aaf9230f9 100644 --- a/contracts/multichain/MultichainGlvRouter.sol +++ b/contracts/multichain/MultichainGlvRouter.sol @@ -25,7 +25,7 @@ contract MultichainGlvRouter is MultichainRouter { RelayUtils.RelayParams calldata relayParams, address account, uint256 srcChainId, - RelayUtils.TransferRequest[] calldata transferRequests, + RelayUtils.TransferRequests calldata transferRequests, GlvDepositUtils.CreateGlvDepositParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { _validateDesChainId(relayParams.desChainId); @@ -67,7 +67,7 @@ contract MultichainGlvRouter is MultichainRouter { RelayUtils.RelayParams calldata relayParams, address account, uint256 srcChainId, - RelayUtils.TransferRequest[] calldata transferRequests, + RelayUtils.TransferRequests calldata transferRequests, GlvWithdrawalUtils.CreateGlvWithdrawalParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { _validateDesChainId(relayParams.desChainId); diff --git a/contracts/multichain/MultichainGmRouter.sol b/contracts/multichain/MultichainGmRouter.sol index b1712cd7f..22ffe884c 100644 --- a/contracts/multichain/MultichainGmRouter.sol +++ b/contracts/multichain/MultichainGmRouter.sol @@ -36,7 +36,7 @@ contract MultichainGmRouter is MultichainRouter { RelayUtils.RelayParams calldata relayParams, address account, uint256 srcChainId, - RelayUtils.TransferRequest[] calldata transferRequests, + RelayUtils.TransferRequests calldata transferRequests, DepositUtils.CreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { _validateDesChainId(relayParams.desChainId); @@ -78,7 +78,7 @@ contract MultichainGmRouter is MultichainRouter { RelayUtils.RelayParams calldata relayParams, address account, uint256 srcChainId, - RelayUtils.TransferRequest[] calldata transferRequests, + RelayUtils.TransferRequests calldata transferRequests, WithdrawalUtils.CreateWithdrawalParams memory params // can't use calldata because need to modify params.addresses.receiver & params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { _validateDesChainId(relayParams.desChainId); @@ -119,7 +119,7 @@ contract MultichainGmRouter is MultichainRouter { RelayUtils.RelayParams calldata relayParams, address account, uint256 srcChainId, - RelayUtils.TransferRequest[] calldata transferRequests, + RelayUtils.TransferRequests calldata transferRequests, ShiftUtils.CreateShiftParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { _validateDesChainId(relayParams.desChainId); diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index d8013e1f9..4b7099ce6 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -37,10 +37,22 @@ abstract contract MultichainRouter is GelatoRelayRouter { multichainVault = params.multichainVault; } - function _processTransferRequests(address account, RelayUtils.TransferRequest[] calldata transferRequests, uint256 srcChainId) internal { - for (uint256 i = 0; i < transferRequests.length; i++) { - RelayUtils.TransferRequest calldata transferRequest = transferRequests[i]; - _sendTokens(account, transferRequest.token, transferRequest.receiver, transferRequest.amount, srcChainId); + function _processTransferRequests(address account, RelayUtils.TransferRequests calldata transferRequests, uint256 srcChainId) internal { + if ( + transferRequests.tokens.length != transferRequests.receivers.length || + transferRequests.tokens.length != transferRequests.amounts.length + ) { + revert Errors.InvalidTransferRequestsLength(); + } + + for (uint256 i = 0; i < transferRequests.tokens.length; i++) { + _sendTokens( + account, + transferRequests.tokens[i], + transferRequests.receivers[i], + transferRequests.amounts[i], + srcChainId + ); } } diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index 6bdd2332a..c0145c291 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -57,10 +57,10 @@ library RelayUtils { bool autoCancel; } - struct TransferRequest { - address token; - address receiver; - uint256 amount; + struct TransferRequests { + address[] tokens; + address[] receivers; + uint256[] amounts; } struct BridgeOutParams { @@ -110,8 +110,7 @@ library RelayUtils { bytes32 public constant CREATE_DEPOSIT_TYPEHASH = keccak256( bytes( - "CreateDeposit(CreateDepositAddresses addresses,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList,bytes32 relayParams)CreateDepositAddresses(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath)" - // TODO: "CreateDeposit(TransferRequest[] transferRequests, CreateDepositAddresses addresses,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList,bytes32 relayParams)TransferRequest(address token,address receiver,uint256 amount)CreateDepositAddresses(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath)" + "CreateDeposit(address[] transferTokens,address[] transferReceivers,uint256[] transferAmounts,CreateDepositAddresses addresses,uint256 minMarketTokens,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList,bytes32 relayParams)CreateDepositAddresses(address receiver,address callbackContract,address uiFeeReceiver,address market,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath)" ) ); bytes32 public constant CREATE_DEPOSIT_ADDRESSES_TYPEHASH = @@ -124,9 +123,10 @@ library RelayUtils { bytes32 public constant CREATE_WITHDRAWAL_TYPEHASH = keccak256( bytes( - "CreateWithdrawal(CreateWithdrawalParams params,bytes32 relayParams)CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" + "CreateWithdrawal(address[] transferTokens,address[] transferReceivers,uint256[] transferAmounts,CreateWithdrawalParams params,bytes32 relayParams)CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" ) ); + bytes32 public constant CREATE_WITHDRAWAL_PARAMS_TYPEHASH = keccak256( bytes( @@ -137,7 +137,7 @@ library RelayUtils { bytes32 public constant CREATE_GLV_DEPOSIT_TYPEHASH = keccak256( bytes( - "CreateGlvDeposit(CreateGlvDepositParams params,bytes32 relayParams)CreateGlvDepositParams(address account,address market,address initialLongToken,address initialShortToken,bytes32[] dataList)" + "CreateGlvDeposit(address[] transferTokens,address[] transferReceivers,uint256[] transferAmounts,CreateGlvDepositParams params,bytes32 relayParams)CreateGlvDepositParams(CreateGlvDepositParamsAddresses addresses,uint256 minGlvTokens,uint256 executionFee,uint256 callbackGasLimit,bool shouldUnwrapNativeToken,bool isMarketTokenDeposit,bytes32[] dataList)" ) ); bytes32 public constant CREATE_GLV_DEPOSIT_PARAMS_ADDRESSES_TYPEHASH = @@ -156,7 +156,7 @@ library RelayUtils { bytes32 public constant CREATE_GLV_WITHDRAWAL_TYPEHASH = keccak256( bytes( - "CreateGlvWithdrawal(CreateGlvWithdrawalParams params,bytes32 relayParams)CreateGlvWithdrawalParams(CreateGlvWithdrawalParamsAddresses addresses,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)CreateGlvWithdrawalParamsAddresses(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath)" + "CreateGlvWithdrawal(address[] transferTokens,address[] transferReceivers,uint256[] transferAmounts,CreateGlvWithdrawalParams params,bytes32 relayParams)CreateGlvWithdrawalParams(CreateGlvWithdrawalParamsAddresses addresses,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)CreateGlvWithdrawalParamsAddresses(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath)" ) ); bytes32 public constant CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH = @@ -175,7 +175,7 @@ library RelayUtils { bytes32 public constant CREATE_SHIFT_TYPEHASH = keccak256( bytes( - "CreateShift(CreateShiftParams params,bytes32 relayParams)CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" + "CreateShift(address[] transferTokens,address[] transferReceivers,uint256[] transferAmounts,CreateShiftParams params,bytes32 relayParams)CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" ) ); bytes32 public constant CREATE_SHIFT_PARAMS_TYPEHASH = @@ -185,8 +185,8 @@ library RelayUtils { ) ); - bytes32 public constant TRANSFER_REQUEST_TYPEHASH = - keccak256(bytes("TransferRequest(address token,address receiver,uint256 amount)")); + bytes32 public constant TRANSFER_REQUESTS_TYPEHASH = + keccak256(bytes("TransferRequests(address[] tokens,address[] receivers,uint256[] amounts)")); bytes32 public constant BRIDGE_OUT_TYPEHASH = keccak256( @@ -321,14 +321,16 @@ library RelayUtils { function getCreateDepositStructHash( RelayParams calldata relayParams, - TransferRequest[] calldata transferRequests, + TransferRequests calldata transferRequests, DepositUtils.CreateDepositParams memory params ) external pure returns (bytes32) { return keccak256( abi.encode( CREATE_DEPOSIT_TYPEHASH, - // _getTransferRequestsHash(transferRequests), // TODO: fix transferRequests hashing + keccak256(abi.encodePacked(transferRequests.tokens)), + keccak256(abi.encodePacked(transferRequests.receivers)), + keccak256(abi.encodePacked(transferRequests.amounts)), _getCreateDepositParamsAdressesStructHash(params.addresses), params.minMarketTokens, params.shouldUnwrapNativeToken, @@ -361,15 +363,17 @@ library RelayUtils { function getCreateWithdrawalStructHash( RelayParams calldata relayParams, - TransferRequest[] calldata transferRequests, + TransferRequests calldata transferRequests, WithdrawalUtils.CreateWithdrawalParams memory params ) external pure returns (bytes32) { return keccak256( abi.encode( CREATE_WITHDRAWAL_TYPEHASH, + keccak256(abi.encodePacked(transferRequests.tokens)), + keccak256(abi.encodePacked(transferRequests.receivers)), + keccak256(abi.encodePacked(transferRequests.amounts)), _getCreateWithdrawalParamsStructHash(params), - _getTransferRequestsHash(transferRequests), _getRelayParamsHash(relayParams) ) ); @@ -400,15 +404,17 @@ library RelayUtils { function getCreateGlvDepositStructHash( RelayParams calldata relayParams, - TransferRequest[] calldata transferRequests, + TransferRequests calldata transferRequests, GlvDepositUtils.CreateGlvDepositParams memory params ) external pure returns (bytes32) { return keccak256( abi.encode( CREATE_GLV_DEPOSIT_TYPEHASH, + keccak256(abi.encodePacked(transferRequests.tokens)), + keccak256(abi.encodePacked(transferRequests.receivers)), + keccak256(abi.encodePacked(transferRequests.amounts)), _getCreateGlvDepositParamsStructHash(params), - _getTransferRequestsHash(transferRequests), _getRelayParamsHash(relayParams) ) ); @@ -454,15 +460,17 @@ library RelayUtils { function getCreateGlvWithdrawalStructHash( RelayParams calldata relayParams, - TransferRequest[] calldata transferRequests, + TransferRequests calldata transferRequests, GlvWithdrawalUtils.CreateGlvWithdrawalParams memory params ) external pure returns (bytes32) { return keccak256( abi.encode( CREATE_GLV_WITHDRAWAL_TYPEHASH, + keccak256(abi.encodePacked(transferRequests.tokens)), + keccak256(abi.encodePacked(transferRequests.receivers)), + keccak256(abi.encodePacked(transferRequests.amounts)), _getCreateGlvWithdrawalParamsStructHash(params), - _getTransferRequestsHash(transferRequests), _getRelayParamsHash(relayParams) ) ); @@ -506,15 +514,17 @@ library RelayUtils { function getCreateShiftStructHash( RelayParams calldata relayParams, - TransferRequest[] calldata transferRequests, + TransferRequests calldata transferRequests, ShiftUtils.CreateShiftParams memory params ) external pure returns (bytes32) { return keccak256( abi.encode( CREATE_SHIFT_TYPEHASH, + keccak256(abi.encodePacked(transferRequests.tokens)), + keccak256(abi.encodePacked(transferRequests.receivers)), + keccak256(abi.encodePacked(transferRequests.amounts)), _getCreateShiftParamsStructHash(params), - _getTransferRequestsHash(transferRequests), _getRelayParamsHash(relayParams) ) ); @@ -540,43 +550,17 @@ library RelayUtils { ); } - function _getTransferRequestStructHash(TransferRequest calldata request) internal pure returns (bytes32) { - return keccak256(abi.encode(TRANSFER_REQUEST_TYPEHASH, request.token, request.receiver, request.amount)); - } - - // TODO: double-check typehash is correctly generated - function _getTransferRequestsHash(TransferRequest[] calldata requests) internal pure returns (bytes32) { - bytes32[] memory hashes = new bytes32[](requests.length); - for (uint256 i = 0; i < requests.length; i++) { - hashes[i] = _getTransferRequestStructHash(requests[i]); - } - return keccak256(abi.encodePacked(hashes)); - } - function getBridgeOutStructHash( RelayParams calldata relayParams, BridgeOutParams memory params ) external pure returns (bytes32) { return keccak256( - abi.encode( - BRIDGE_OUT_TYPEHASH, - _getBridgeOutParamsStructHash(params), - _getRelayParamsHash(relayParams) - ) + abi.encode(BRIDGE_OUT_TYPEHASH, _getBridgeOutParamsStructHash(params), _getRelayParamsHash(relayParams)) ); } - function _getBridgeOutParamsStructHash( - BridgeOutParams memory params - ) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - BRIDGE_OUT_PARAMS_TYPEHASH, - params.token, - params.amount - ) - ); + function _getBridgeOutParamsStructHash(BridgeOutParams memory params) internal pure returns (bytes32) { + return keccak256(abi.encode(BRIDGE_OUT_PARAMS_TYPEHASH, params.token, params.amount)); } } diff --git a/test/multichain/MultichainGmRouter.ts b/test/multichain/MultichainGmRouter.ts index 0e3653f3a..fd91ace63 100644 --- a/test/multichain/MultichainGmRouter.ts +++ b/test/multichain/MultichainGmRouter.ts @@ -83,18 +83,11 @@ describe("MultichainGmRouter", () => { feeSwapPath: [], }, tokenPermits: [], - transferRequests: [ - { - token: wnt.address, - receiver: depositVault.address, - amount: expandDecimals(1, 18), - }, - { - token: usdc.address, - receiver: depositVault.address, - amount: expandDecimals(5000, 6), - }, - ], + transferRequests: { + tokens: [wnt.address, usdc.address], + receivers: [multichainVault.address, multichainVault.address], + amounts: [expandDecimals(1, 18), expandDecimals(50000, 6)], + }, account: user0.address, params: defaultParams, deadline: 9999999999, @@ -138,33 +131,7 @@ describe("MultichainGmRouter", () => { console.log("multichainVault address: %s", multichainVault.address); console.log("depositVault address: %s", depositVault.address); - const tokenPermits = [ - await getTokenPermit( - wnt, - user0, - multichainGmRouter.address, // reverts with InvalidPermitSpender if not Router - createDepositParams.transferRequests[0].amount, - 0, - 9999999999, - chainId - ), - await getTokenPermit( - usdc, - user0, - multichainGmRouter.address, // reverts with InvalidPermitSpender if not Router - createDepositParams.transferRequests[1].amount, - 1, - 9999999999, - chainId - ), - ]; - - // TODO: fix 'ERC20: insufficient allowance' error - // might be due to permits or the multichianGmRouter not being able to spend from multichainVault - await sendCreateDeposit({ - ...createDepositParams, - tokenPermits, - }); + await sendCreateDeposit(createDepositParams); expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, wnt.address))).to.eq( wntAmount.sub(createDepositParams.transferRequests[0].amount) diff --git a/utils/relay/multichain.ts b/utils/relay/multichain.ts index 40bb5dcc9..ca12d113a 100644 --- a/utils/relay/multichain.ts +++ b/utils/relay/multichain.ts @@ -30,10 +30,10 @@ export async function sendCreateDeposit(p: { feeSwapPath: string[]; }; transferRequests: { - token: string; - receiver: string; - amount: BigNumberish; - }[]; + tokens: string[]; + receivers: string[]; + amounts: BigNumberish[]; + }; account: string; params: any; signature?: string; @@ -83,7 +83,9 @@ async function getCreateDepositSignature({ } const types = { CreateDeposit: [ - // { name: "transferRequests", type: "TransferRequest[]" }, + { name: "transferTokens", type: "address[]" }, + { name: "transferReceivers", type: "address[]" }, + { name: "transferAmounts", type: "uint256[]" }, { name: "addresses", type: "CreateDepositAddresses" }, { name: "minMarketTokens", type: "uint256" }, { name: "shouldUnwrapNativeToken", type: "bool" }, @@ -102,17 +104,11 @@ async function getCreateDepositSignature({ { name: "longTokenSwapPath", type: "address[]" }, { name: "shortTokenSwapPath", type: "address[]" }, ], - // TransferRequest: [ - // { name: "token", type: "address" }, - // { name: "receiver", type: "address" }, - // { name: "amount", type: "uint256" }, - // ], }; - - const domain = getDomain(chainId, verifyingContract); - const typedData = { - // transferRequests, + transferTokens: transferRequests.tokens, + transferReceivers: transferRequests.receivers, + transferAmounts: transferRequests.amounts, addresses: params.addresses, minMarketTokens: params.minMarketTokens, shouldUnwrapNativeToken: params.shouldUnwrapNativeToken, @@ -121,6 +117,7 @@ async function getCreateDepositSignature({ dataList: params.dataList, relayParams: hashRelayParams(relayParams), }; + const domain = getDomain(chainId, verifyingContract); return signTypedData(signer, domain, types, typedData); } From 81baa9fbea4f6fb4fb2f4dbc6f7c30846c870769 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 19 Feb 2025 22:24:11 +0200 Subject: [PATCH 084/205] Test multichain deposit --- test/multichain/MultichainGmRouter.ts | 90 +++++++++++++++------------ 1 file changed, 51 insertions(+), 39 deletions(-) diff --git a/test/multichain/MultichainGmRouter.ts b/test/multichain/MultichainGmRouter.ts index fd91ace63..6bbe39acf 100644 --- a/test/multichain/MultichainGmRouter.ts +++ b/test/multichain/MultichainGmRouter.ts @@ -4,42 +4,27 @@ import { impersonateAccount, setBalance } from "@nomicfoundation/hardhat-network import { expandDecimals } from "../../utils/math"; import { deployFixture } from "../../utils/fixture"; import { GELATO_RELAY_ADDRESS } from "../../utils/relay/addresses"; -import { getTokenPermit } from "../../utils/relay/tokenPermit"; import { sendCreateDeposit } from "../../utils/relay/multichain"; import * as keys from "../../utils/keys"; +import { getDepositKeys } from "../../utils/deposit"; describe("MultichainGmRouter", () => { let fixture; - let user0, user1, user2; - let reader, - dataStore, - router, - multichainGmRouter, - multichainVault, - depositVault, - ethUsdMarket, - wnt, - usdc, - layerZeroProvider, - mockStargatePool; + let user0, user1, user2, user3; + let reader, dataStore, multichainGmRouter, depositVault, ethUsdMarket, wnt, usdc, layerZeroProvider, mockStargatePool; let relaySigner; let chainId; let defaultParams; let createDepositParams: Parameters[0]; - const wntAmount = expandDecimals(10, 18); - const usdcAmount = expandDecimals(50000, 6); - beforeEach(async () => { fixture = await deployFixture(); - ({ user0, user1, user2 } = fixture.accounts); + ({ user0, user1, user2, user3 } = fixture.accounts); ({ reader, dataStore, - router, multichainGmRouter, - multichainVault, depositVault, ethUsdMarket, wnt, @@ -67,9 +52,7 @@ describe("MultichainGmRouter", () => { }; await impersonateAccount(GELATO_RELAY_ADDRESS); - await setBalance(GELATO_RELAY_ADDRESS, expandDecimals(100, 18)); - await usdc.mint(user0.address, expandDecimals(1, 30)); // very large amount - await wnt.mint(user0.address, expandDecimals(1, 30)); // very large amount + await setBalance(GELATO_RELAY_ADDRESS, expandDecimals(1, 16)); // 0.01 ETH to pay tx fees relaySigner = await hre.ethers.getSigner(GELATO_RELAY_ADDRESS); chainId = await hre.ethers.provider.getNetwork().then((network) => network.chainId); @@ -79,14 +62,14 @@ describe("MultichainGmRouter", () => { signer: user0, feeParams: { feeToken: wnt.address, - feeAmount: expandDecimals(2, 15), // 0.002 ETH + feeAmount: expandDecimals(5, 15), // 0.005 ETH feeSwapPath: [], }, tokenPermits: [], transferRequests: { tokens: [wnt.address, usdc.address], - receivers: [multichainVault.address, multichainVault.address], - amounts: [expandDecimals(1, 18), expandDecimals(50000, 6)], + receivers: [depositVault.address, depositVault.address], + amounts: [expandDecimals(1, 18), expandDecimals(5000, 6)], }, account: user0.address, params: defaultParams, @@ -96,9 +79,16 @@ describe("MultichainGmRouter", () => { desChainId: chainId, // for non-multichain actions, desChainId and srcChainId are the same relayRouter: multichainGmRouter, relayFeeToken: wnt.address, - relayFeeAmount: expandDecimals(1, 15), + relayFeeAmount: expandDecimals(2, 15), // 0.002 ETH }; + await dataStore.setAddress(keys.FEE_RECEIVER, user3.address); + + const wntAmount = expandDecimals(10, 18); + const usdcAmount = expandDecimals(50000, 6); + await wnt.mint(user0.address, wntAmount); + await usdc.mint(user0.address, usdcAmount); + // mock wnt bridging (increase user's wnt multichain balance) const encodedMessageEth = ethers.utils.defaultAbiCoder.encode( ["address", "address", "uint256"], @@ -121,24 +111,46 @@ describe("MultichainGmRouter", () => { describe("createDeposit", () => { it("creates deposit and sends relayer fee", async () => { - expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, wnt.address))).to.eq(wntAmount); // 10 ETH - expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, usdc.address))).to.eq(usdcAmount); // 50k USDC - - console.log("user0 address: %s", user0.address); - console.log("GELATO_RELAY_ADDRESS: %s", GELATO_RELAY_ADDRESS); - console.log("router address: %s", router.address); - console.log("multichainGmRouter address: %s", multichainGmRouter.address); - console.log("multichainVault address: %s", multichainVault.address); - console.log("depositVault address: %s", depositVault.address); + expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, wnt.address))).to.eq( + expandDecimals(10, 18) + ); // 10 ETH + expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, usdc.address))).to.eq( + expandDecimals(50_000, 6) + ); // 50.000 USDC + expect(await wnt.balanceOf(GELATO_RELAY_ADDRESS)).to.eq(0); + expect(await wnt.balanceOf(user3.address)).eq(0); // FEE_RECEIVER await sendCreateDeposit(createDepositParams); + // user's multichain balance was decreased by the deposit amounts + 0.005 ETH fee expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, wnt.address))).to.eq( - wntAmount.sub(createDepositParams.transferRequests[0].amount) - ); // 9 ETH + expandDecimals(8_995, 15) + ); // 10 - 1 - 0.005 = 8.995 ETH expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, usdc.address))).to.eq( - usdcAmount.sub(createDepositParams.transferRequests[1].amount) - ); // 45k USDC + expandDecimals(45_000, 6) + ); // 50.000 - 5.000 = 45.000 USDC + expect(await wnt.balanceOf(GELATO_RELAY_ADDRESS)).to.eq(expandDecimals(2, 15)); // 0.002 ETH (createDepositParams.relayFeeAmount) + // TODO: why is FEE_RECEIVER getting 1 WNT? + expect(await wnt.balanceOf(user3.address)).eq(expandDecimals(1, 18)); // FEE_RECEIVER + + const depositKeys = await getDepositKeys(dataStore, 0, 1); + const deposit = await reader.getDeposit(dataStore.address, depositKeys[0]); + expect(deposit.addresses.account).eq(user0.address); + expect(deposit.addresses.receiver).eq(defaultParams.addresses.receiver); + expect(deposit.addresses.callbackContract).eq(defaultParams.addresses.callbackContract); + expect(deposit.addresses.market).eq(defaultParams.addresses.market); + expect(deposit.addresses.initialLongToken).eq(defaultParams.addresses.initialLongToken); + expect(deposit.addresses.initialShortToken).eq(defaultParams.addresses.initialShortToken); + expect(deposit.addresses.longTokenSwapPath).deep.eq(defaultParams.addresses.longTokenSwapPath); + expect(deposit.addresses.shortTokenSwapPath).deep.eq(defaultParams.addresses.shortTokenSwapPath); + // TODO: why initialLongTokenAmount is not 1 ETH? + expect(deposit.numbers.initialLongTokenAmount).eq(0); + expect(deposit.numbers.initialShortTokenAmount).eq(expandDecimals(5000, 6)); + expect(deposit.numbers.minMarketTokens).eq(defaultParams.minMarketTokens); + expect(deposit.numbers.executionFee).eq(expandDecimals(3, 15)); // 0.005 - 0.002 = 0.003 ETH (createDepositParams.feeParams.feeAmount - createDepositParams.relayFeeAmount) + expect(deposit.numbers.callbackGasLimit).eq(defaultParams.callbackGasLimit); + expect(deposit.flags.shouldUnwrapNativeToken).eq(defaultParams.shouldUnwrapNativeToken); + expect(deposit._dataList).deep.eq(defaultParams.dataList); }); }); }); From c7d699d009afd99365dd7cb7bd0e35640cb3267b Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 20 Feb 2025 09:25:36 +0200 Subject: [PATCH 085/205] Fix ETH deposit and fee payment --- contracts/multichain/MultichainGmRouter.sol | 8 ++++--- test/multichain/MultichainGmRouter.ts | 24 ++++++++++----------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/contracts/multichain/MultichainGmRouter.sol b/contracts/multichain/MultichainGmRouter.sol index 22ffe884c..53b648621 100644 --- a/contracts/multichain/MultichainGmRouter.sol +++ b/contracts/multichain/MultichainGmRouter.sol @@ -44,15 +44,14 @@ contract MultichainGmRouter is MultichainRouter { bytes32 structHash = RelayUtils.getCreateDepositStructHash(relayParams, transferRequests, params); _validateCall(relayParams, account, structHash, srcChainId); - _processTransferRequests(account, transferRequests, srcChainId); - - return _createDeposit(relayParams, account, srcChainId, params); + return _createDeposit(relayParams, account, srcChainId, transferRequests, params); } function _createDeposit( RelayUtils.RelayParams calldata relayParams, address account, uint256 srcChainId, + RelayUtils.TransferRequests calldata transferRequests, DepositUtils.CreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee ) internal returns (bytes32) { Contracts memory contracts = Contracts({ @@ -71,6 +70,9 @@ contract MultichainGmRouter is MultichainRouter { srcChainId ); + // process transfer requests after relay fee is paid, otherwise all wnt amount will be recorder as relay fee + _processTransferRequests(account, transferRequests, srcChainId); + return depositHandler.createDeposit(account, srcChainId, params); } diff --git a/test/multichain/MultichainGmRouter.ts b/test/multichain/MultichainGmRouter.ts index 6bbe39acf..5b30ac2fa 100644 --- a/test/multichain/MultichainGmRouter.ts +++ b/test/multichain/MultichainGmRouter.ts @@ -69,7 +69,7 @@ describe("MultichainGmRouter", () => { transferRequests: { tokens: [wnt.address, usdc.address], receivers: [depositVault.address, depositVault.address], - amounts: [expandDecimals(1, 18), expandDecimals(5000, 6)], + amounts: [expandDecimals(1, 18), expandDecimals(5_000, 6)], }, account: user0.address, params: defaultParams, @@ -84,8 +84,8 @@ describe("MultichainGmRouter", () => { await dataStore.setAddress(keys.FEE_RECEIVER, user3.address); - const wntAmount = expandDecimals(10, 18); - const usdcAmount = expandDecimals(50000, 6); + const wntAmount = expandDecimals(10, 18); // 10 ETH + const usdcAmount = expandDecimals(50_000, 6); // 50,000 USDC await wnt.mint(user0.address, wntAmount); await usdc.mint(user0.address, usdcAmount); @@ -116,7 +116,7 @@ describe("MultichainGmRouter", () => { ); // 10 ETH expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, usdc.address))).to.eq( expandDecimals(50_000, 6) - ); // 50.000 USDC + ); // 50,000 USDC expect(await wnt.balanceOf(GELATO_RELAY_ADDRESS)).to.eq(0); expect(await wnt.balanceOf(user3.address)).eq(0); // FEE_RECEIVER @@ -128,10 +128,9 @@ describe("MultichainGmRouter", () => { ); // 10 - 1 - 0.005 = 8.995 ETH expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, usdc.address))).to.eq( expandDecimals(45_000, 6) - ); // 50.000 - 5.000 = 45.000 USDC - expect(await wnt.balanceOf(GELATO_RELAY_ADDRESS)).to.eq(expandDecimals(2, 15)); // 0.002 ETH (createDepositParams.relayFeeAmount) - // TODO: why is FEE_RECEIVER getting 1 WNT? - expect(await wnt.balanceOf(user3.address)).eq(expandDecimals(1, 18)); // FEE_RECEIVER + ); // 50,000 - 5,000 = 45,000 USDC + expect(await wnt.balanceOf(GELATO_RELAY_ADDRESS)).to.eq(createDepositParams.relayFeeAmount); // 0.002 ETH + expect(await wnt.balanceOf(user3.address)).eq(0); // FEE_RECEIVER const depositKeys = await getDepositKeys(dataStore, 0, 1); const deposit = await reader.getDeposit(dataStore.address, depositKeys[0]); @@ -143,11 +142,12 @@ describe("MultichainGmRouter", () => { expect(deposit.addresses.initialShortToken).eq(defaultParams.addresses.initialShortToken); expect(deposit.addresses.longTokenSwapPath).deep.eq(defaultParams.addresses.longTokenSwapPath); expect(deposit.addresses.shortTokenSwapPath).deep.eq(defaultParams.addresses.shortTokenSwapPath); - // TODO: why initialLongTokenAmount is not 1 ETH? - expect(deposit.numbers.initialLongTokenAmount).eq(0); - expect(deposit.numbers.initialShortTokenAmount).eq(expandDecimals(5000, 6)); + expect(deposit.numbers.initialLongTokenAmount).eq(createDepositParams.transferRequests.amounts[0]); // 1 ETH + expect(deposit.numbers.initialShortTokenAmount).eq(createDepositParams.transferRequests.amounts[1]); // 5,000 USDC expect(deposit.numbers.minMarketTokens).eq(defaultParams.minMarketTokens); - expect(deposit.numbers.executionFee).eq(expandDecimals(3, 15)); // 0.005 - 0.002 = 0.003 ETH (createDepositParams.feeParams.feeAmount - createDepositParams.relayFeeAmount) + expect(deposit.numbers.executionFee).eq( + createDepositParams.feeParams.feeAmount.sub(createDepositParams.relayFeeAmount) + ); // 0.005 - 0.002 = 0.003 ETH expect(deposit.numbers.callbackGasLimit).eq(defaultParams.callbackGasLimit); expect(deposit.flags.shouldUnwrapNativeToken).eq(defaultParams.shouldUnwrapNativeToken); expect(deposit._dataList).deep.eq(defaultParams.dataList); From 3447978d69c4a029369e6a2e0238dad283341df8 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 20 Feb 2025 12:42:18 +0200 Subject: [PATCH 086/205] Split CreateWithdrawalParams into subgroups to solve relay typehash error --- contracts/exchange/WithdrawalHandler.sol | 8 ++--- contracts/router/relay/RelayUtils.sol | 43 ++++++++++++------------ contracts/withdrawal/WithdrawalUtils.sol | 38 +++++++++++---------- test/router/ExchangeRouter.ts | 14 ++++---- utils/withdrawal.ts | 28 ++++++++------- 5 files changed, 70 insertions(+), 61 deletions(-) diff --git a/contracts/exchange/WithdrawalHandler.sol b/contracts/exchange/WithdrawalHandler.sol index 3ea1973b8..b0b3923e2 100644 --- a/contracts/exchange/WithdrawalHandler.sol +++ b/contracts/exchange/WithdrawalHandler.sol @@ -141,12 +141,12 @@ contract WithdrawalHandler is IWithdrawalHandler, BaseHandler { oracle.validateSequencerUp(); if ( - params.longTokenSwapPath.length != 0 || - params.shortTokenSwapPath.length != 0 + params.addresses.longTokenSwapPath.length != 0 || + params.addresses.shortTokenSwapPath.length != 0 ) { revert Errors.SwapsNotAllowedForAtomicWithdrawal( - params.longTokenSwapPath.length, - params.shortTokenSwapPath.length + params.addresses.longTokenSwapPath.length, + params.addresses.shortTokenSwapPath.length ); } diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index c0145c291..2a375da5d 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -123,14 +123,13 @@ library RelayUtils { bytes32 public constant CREATE_WITHDRAWAL_TYPEHASH = keccak256( bytes( - "CreateWithdrawal(address[] transferTokens,address[] transferReceivers,uint256[] transferAmounts,CreateWithdrawalParams params,bytes32 relayParams)CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" + "CreateWithdrawal(address[] transferTokens,address[] transferReceivers,uint256[] transferAmounts,CreateWithdrawalAddresses addresses,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList,bytes32 relayParams)CreateWithdrawalAddresses(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath)" ) ); - - bytes32 public constant CREATE_WITHDRAWAL_PARAMS_TYPEHASH = + bytes32 public constant CREATE_WITHDRAWAL_ADDRESSES_TYPEHASH = keccak256( bytes( - "CreateWithdrawalParams(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" + "CreateWithdrawalAddresses(address receiver,address callbackContract,address uiFeeReceiver,address market,address[] longTokenSwapPath,address[] shortTokenSwapPath)" ) ); @@ -331,7 +330,7 @@ library RelayUtils { keccak256(abi.encodePacked(transferRequests.tokens)), keccak256(abi.encodePacked(transferRequests.receivers)), keccak256(abi.encodePacked(transferRequests.amounts)), - _getCreateDepositParamsAdressesStructHash(params.addresses), + _getCreateDepositAdressesStructHash(params.addresses), params.minMarketTokens, params.shouldUnwrapNativeToken, params.executionFee, @@ -342,7 +341,7 @@ library RelayUtils { ); } - function _getCreateDepositParamsAdressesStructHash( + function _getCreateDepositAdressesStructHash( DepositUtils.CreateDepositParamsAdresses memory addresses ) internal pure returns (bytes32) { return @@ -373,31 +372,31 @@ library RelayUtils { keccak256(abi.encodePacked(transferRequests.tokens)), keccak256(abi.encodePacked(transferRequests.receivers)), keccak256(abi.encodePacked(transferRequests.amounts)), - _getCreateWithdrawalParamsStructHash(params), + _getCreateWithdrawalAddressesStructHash(params.addresses), + params.minLongTokenAmount, + params.minShortTokenAmount, + params.shouldUnwrapNativeToken, + params.executionFee, + params.callbackGasLimit, + keccak256(abi.encodePacked(params.dataList)), _getRelayParamsHash(relayParams) ) ); } - function _getCreateWithdrawalParamsStructHash( - WithdrawalUtils.CreateWithdrawalParams memory params + function _getCreateWithdrawalAddressesStructHash( + WithdrawalUtils.CreateWithdrawalParamsAddresses memory addresses ) internal pure returns (bytes32) { return keccak256( abi.encode( - CREATE_WITHDRAWAL_PARAMS_TYPEHASH, - params.receiver, - params.callbackContract, - params.uiFeeReceiver, - params.market, - keccak256(abi.encodePacked(params.longTokenSwapPath)), - keccak256(abi.encodePacked(params.shortTokenSwapPath)), - params.minLongTokenAmount, - params.minShortTokenAmount, - params.shouldUnwrapNativeToken, - params.executionFee, - params.callbackGasLimit, - keccak256(abi.encodePacked(params.dataList)) + CREATE_WITHDRAWAL_ADDRESSES_TYPEHASH, + addresses.receiver, + addresses.callbackContract, + addresses.uiFeeReceiver, + addresses.market, + keccak256(abi.encodePacked(addresses.longTokenSwapPath)), + keccak256(abi.encodePacked(addresses.shortTokenSwapPath)) ) ); } diff --git a/contracts/withdrawal/WithdrawalUtils.sol b/contracts/withdrawal/WithdrawalUtils.sol index a18abe558..dec337c88 100644 --- a/contracts/withdrawal/WithdrawalUtils.sol +++ b/contracts/withdrawal/WithdrawalUtils.sol @@ -47,12 +47,7 @@ library WithdrawalUtils { * @param callbackGasLimit The gas limit for calling the callback contract. */ struct CreateWithdrawalParams { - address receiver; - address callbackContract; - address uiFeeReceiver; - address market; - address[] longTokenSwapPath; - address[] shortTokenSwapPath; + CreateWithdrawalParamsAddresses addresses; uint256 minLongTokenAmount; uint256 minShortTokenAmount; bool shouldUnwrapNativeToken; @@ -61,6 +56,15 @@ library WithdrawalUtils { bytes32[] dataList; } + struct CreateWithdrawalParamsAddresses { + address receiver; + address callbackContract; + address uiFeeReceiver; + address market; + address[] longTokenSwapPath; + address[] shortTokenSwapPath; + } + /** * @dev Creates a withdrawal in the withdrawal store. * @@ -89,28 +93,28 @@ library WithdrawalUtils { revert Errors.InsufficientWntAmountForExecutionFee(wntAmount, params.executionFee); } - AccountUtils.validateReceiver(params.receiver); + AccountUtils.validateReceiver(params.addresses.receiver); - uint256 marketTokenAmount = withdrawalVault.recordTransferIn(params.market); + uint256 marketTokenAmount = withdrawalVault.recordTransferIn(params.addresses.market); if (marketTokenAmount == 0) { revert Errors.EmptyWithdrawalAmount(); } params.executionFee = wntAmount; - MarketUtils.validateEnabledMarket(dataStore, params.market); - MarketUtils.validateSwapPath(dataStore, params.longTokenSwapPath); - MarketUtils.validateSwapPath(dataStore, params.shortTokenSwapPath); + MarketUtils.validateEnabledMarket(dataStore, params.addresses.market); + MarketUtils.validateSwapPath(dataStore, params.addresses.longTokenSwapPath); + MarketUtils.validateSwapPath(dataStore, params.addresses.shortTokenSwapPath); Withdrawal.Props memory withdrawal = Withdrawal.Props( Withdrawal.Addresses( account, - params.receiver, - params.callbackContract, - params.uiFeeReceiver, - params.market, - params.longTokenSwapPath, - params.shortTokenSwapPath + params.addresses.receiver, + params.addresses.callbackContract, + params.addresses.uiFeeReceiver, + params.addresses.market, + params.addresses.longTokenSwapPath, + params.addresses.shortTokenSwapPath ), Withdrawal.Numbers( marketTokenAmount, diff --git a/test/router/ExchangeRouter.ts b/test/router/ExchangeRouter.ts index b093b9fb9..a2f619dd9 100644 --- a/test/router/ExchangeRouter.ts +++ b/test/router/ExchangeRouter.ts @@ -207,12 +207,14 @@ describe("ExchangeRouter", () => { ]), exchangeRouter.interface.encodeFunctionData("createWithdrawal", [ { - receiver: user1.address, - callbackContract: user2.address, - uiFeeReceiver: user3.address, - market: ethUsdMarket.marketToken, - longTokenSwapPath: [], - shortTokenSwapPath: [], + addresses: { + receiver: user1.address, + callbackContract: user2.address, + uiFeeReceiver: user3.address, + market: ethUsdMarket.marketToken, + longTokenSwapPath: [], + shortTokenSwapPath: [], + }, marketTokenAmount: 700, minLongTokenAmount: 800, minShortTokenAmount: 900, diff --git a/utils/withdrawal.ts b/utils/withdrawal.ts index 8c691b106..314dab40d 100644 --- a/utils/withdrawal.ts +++ b/utils/withdrawal.ts @@ -50,12 +50,14 @@ export async function createWithdrawal(fixture, overrides: any = {}) { await marketToken.connect(account).transfer(withdrawalVault.address, marketTokenAmount); const params = { - receiver: receiver.address, - callbackContract: callbackContract.address, - uiFeeReceiver: uiFeeReceiver.address, - market: market.marketToken, - longTokenSwapPath, - shortTokenSwapPath, + addresses: { + receiver: receiver.address, + callbackContract: callbackContract.address, + uiFeeReceiver: uiFeeReceiver.address, + market: market.marketToken, + longTokenSwapPath, + shortTokenSwapPath, + }, marketTokenAmount, minLongTokenAmount, minShortTokenAmount, @@ -149,12 +151,14 @@ export async function executeAtomicWithdrawal(fixture, overrides: any = {}) { await marketToken.connect(account).transfer(withdrawalVault.address, marketTokenAmount); const params = { - receiver: receiver.address, - callbackContract: callbackContract.address, - uiFeeReceiver: uiFeeReceiver.address, - market: market.marketToken, - longTokenSwapPath, - shortTokenSwapPath, + addresses: { + receiver: receiver.address, + callbackContract: callbackContract.address, + uiFeeReceiver: uiFeeReceiver.address, + market: market.marketToken, + longTokenSwapPath, + shortTokenSwapPath, + }, marketTokenAmount, minLongTokenAmount, minShortTokenAmount, From 082d847bf900839da1b79420888e0595b8265904 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 20 Feb 2025 20:51:17 +0200 Subject: [PATCH 087/205] Add error params --- contracts/error/Errors.sol | 4 ++-- contracts/multichain/MultichainRouter.sol | 2 +- contracts/multichain/MultichainUtils.sol | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/error/Errors.sol b/contracts/error/Errors.sol index 812f8f427..d871ef7b6 100644 --- a/contracts/error/Errors.sol +++ b/contracts/error/Errors.sol @@ -433,8 +433,8 @@ library Errors { error InvalidTransferRequestsLength(); error EmptyMultichainTransferInAmount(); error EmptyMultichainTransferOutAmount(); - error InsufficientMultichainBalance(); - error InvalidDestinationChainId(); + error InsufficientMultichainBalance(address token, uint256 balance, uint256 amount); + error InvalidDestinationChainId(uint256 desChainId); error InvalidMultichainProvider(address provider); enum SignatureType { diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 4b7099ce6..762f48d92 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -74,7 +74,7 @@ abstract contract MultichainRouter is GelatoRelayRouter { function _validateDesChainId(uint256 desChainId) internal view { if (desChainId != block.chainid) { - revert Errors.InvalidDestinationChainId(); + revert Errors.InvalidDestinationChainId(desChainId); } } } diff --git a/contracts/multichain/MultichainUtils.sol b/contracts/multichain/MultichainUtils.sol index 8d8f07e2e..86b498da8 100644 --- a/contracts/multichain/MultichainUtils.sol +++ b/contracts/multichain/MultichainUtils.sol @@ -68,7 +68,7 @@ library MultichainUtils { uint256 balance = dataStore.getUint(balanceKey); if (balance < amount) { - revert Errors.InsufficientMultichainBalance(); + revert Errors.InsufficientMultichainBalance(token, balance, amount); } multichainVault.transferOut(token, receiver, amount); From b252c9f50028385d6564699011e338a0a86ebe73 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 20 Feb 2025 21:37:24 +0200 Subject: [PATCH 088/205] WIP test multichain withdrawal --- test/multichain/MultichainGmRouter.ts | 153 ++++++++++++++++++++++---- utils/relay/multichain.ts | 122 +++++++++++++++++++- 2 files changed, 247 insertions(+), 28 deletions(-) diff --git a/test/multichain/MultichainGmRouter.ts b/test/multichain/MultichainGmRouter.ts index 5b30ac2fa..7161f3a29 100644 --- a/test/multichain/MultichainGmRouter.ts +++ b/test/multichain/MultichainGmRouter.ts @@ -4,14 +4,25 @@ import { impersonateAccount, setBalance } from "@nomicfoundation/hardhat-network import { expandDecimals } from "../../utils/math"; import { deployFixture } from "../../utils/fixture"; import { GELATO_RELAY_ADDRESS } from "../../utils/relay/addresses"; -import { sendCreateDeposit } from "../../utils/relay/multichain"; +import { sendCreateDeposit, sendCreateWithdrawal } from "../../utils/relay/multichain"; import * as keys from "../../utils/keys"; -import { getDepositKeys } from "../../utils/deposit"; +import { executeDeposit, getDepositCount, getDepositKeys } from "../../utils/deposit"; +import { getWithdrawalCount, getWithdrawalKeys } from "../../utils/withdrawal"; +import { getBalanceOf } from "../../utils/token"; describe("MultichainGmRouter", () => { let fixture; let user0, user1, user2, user3; - let reader, dataStore, multichainGmRouter, depositVault, ethUsdMarket, wnt, usdc, layerZeroProvider, mockStargatePool; + let reader, + dataStore, + multichainGmRouter, + depositVault, + withdrawalVault, + ethUsdMarket, + wnt, + usdc, + layerZeroProvider, + mockStargatePool; let relaySigner; let chainId; @@ -26,6 +37,7 @@ describe("MultichainGmRouter", () => { dataStore, multichainGmRouter, depositVault, + withdrawalVault, ethUsdMarket, wnt, usdc, @@ -35,7 +47,7 @@ describe("MultichainGmRouter", () => { defaultParams = { addresses: { - receiver: user0.address, + receiver: user1.address, callbackContract: user1.address, uiFeeReceiver: user2.address, market: ethUsdMarket.marketToken, @@ -44,7 +56,7 @@ describe("MultichainGmRouter", () => { longTokenSwapPath: [ethUsdMarket.marketToken], shortTokenSwapPath: [ethUsdMarket.marketToken], }, - minMarketTokens: 0, + minMarketTokens: 100, shouldUnwrapNativeToken: false, executionFee: 0, callbackGasLimit: "200000", @@ -65,11 +77,10 @@ describe("MultichainGmRouter", () => { feeAmount: expandDecimals(5, 15), // 0.005 ETH feeSwapPath: [], }, - tokenPermits: [], transferRequests: { tokens: [wnt.address, usdc.address], receivers: [depositVault.address, depositVault.address], - amounts: [expandDecimals(1, 18), expandDecimals(5_000, 6)], + amounts: [expandDecimals(10, 18), expandDecimals(50_000, 6)], }, account: user0.address, params: defaultParams, @@ -84,8 +95,8 @@ describe("MultichainGmRouter", () => { await dataStore.setAddress(keys.FEE_RECEIVER, user3.address); - const wntAmount = expandDecimals(10, 18); // 10 ETH - const usdcAmount = expandDecimals(50_000, 6); // 50,000 USDC + const wntAmount = expandDecimals(15, 18); // 15 ETH + const usdcAmount = expandDecimals(75_000, 6); // 75,000 USDC await wnt.mint(user0.address, wntAmount); await usdc.mint(user0.address, usdcAmount); @@ -112,11 +123,11 @@ describe("MultichainGmRouter", () => { describe("createDeposit", () => { it("creates deposit and sends relayer fee", async () => { expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, wnt.address))).to.eq( - expandDecimals(10, 18) - ); // 10 ETH + expandDecimals(15, 18) + ); // 15 ETH expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, usdc.address))).to.eq( - expandDecimals(50_000, 6) - ); // 50,000 USDC + expandDecimals(75_000, 6) + ); // 75,000 USDC expect(await wnt.balanceOf(GELATO_RELAY_ADDRESS)).to.eq(0); expect(await wnt.balanceOf(user3.address)).eq(0); // FEE_RECEIVER @@ -124,11 +135,14 @@ describe("MultichainGmRouter", () => { // user's multichain balance was decreased by the deposit amounts + 0.005 ETH fee expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, wnt.address))).to.eq( - expandDecimals(8_995, 15) - ); // 10 - 1 - 0.005 = 8.995 ETH + // fee is paid first, transfers are proccessed afterwards => user must bridge deposit + fee + // TODO: should the 0.005 fee be taken from deposit instead of user's multichain balance + // e.g. if there are exactly 10 WNT in user's multichain balance and does a 10 WNT deposit, tx fails because there are no additional funds to pay the fee + expandDecimals(4_995, 15) + ); // 15 - 10 - 0.005 = 4.995 ETH expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, usdc.address))).to.eq( - expandDecimals(45_000, 6) - ); // 50,000 - 5,000 = 45,000 USDC + expandDecimals(25_000, 6) + ); // 75,000 - 50,000 = 25,000 USDC expect(await wnt.balanceOf(GELATO_RELAY_ADDRESS)).to.eq(createDepositParams.relayFeeAmount); // 0.002 ETH expect(await wnt.balanceOf(user3.address)).eq(0); // FEE_RECEIVER @@ -138,19 +152,114 @@ describe("MultichainGmRouter", () => { expect(deposit.addresses.receiver).eq(defaultParams.addresses.receiver); expect(deposit.addresses.callbackContract).eq(defaultParams.addresses.callbackContract); expect(deposit.addresses.market).eq(defaultParams.addresses.market); - expect(deposit.addresses.initialLongToken).eq(defaultParams.addresses.initialLongToken); - expect(deposit.addresses.initialShortToken).eq(defaultParams.addresses.initialShortToken); + expect(deposit.addresses.initialLongToken).eq(createDepositParams.transferRequests.tokens[0]); + expect(deposit.addresses.initialShortToken).eq(createDepositParams.transferRequests.tokens[1]); expect(deposit.addresses.longTokenSwapPath).deep.eq(defaultParams.addresses.longTokenSwapPath); expect(deposit.addresses.shortTokenSwapPath).deep.eq(defaultParams.addresses.shortTokenSwapPath); - expect(deposit.numbers.initialLongTokenAmount).eq(createDepositParams.transferRequests.amounts[0]); // 1 ETH - expect(deposit.numbers.initialShortTokenAmount).eq(createDepositParams.transferRequests.amounts[1]); // 5,000 USDC + expect(deposit.numbers.initialLongTokenAmount).eq(createDepositParams.transferRequests.amounts[0]); // 10 ETH + expect(deposit.numbers.initialShortTokenAmount).eq(createDepositParams.transferRequests.amounts[1]); // 50,000 USDC expect(deposit.numbers.minMarketTokens).eq(defaultParams.minMarketTokens); expect(deposit.numbers.executionFee).eq( - createDepositParams.feeParams.feeAmount.sub(createDepositParams.relayFeeAmount) + ethers.BigNumber.from(createDepositParams.feeParams.feeAmount).sub(createDepositParams.relayFeeAmount) ); // 0.005 - 0.002 = 0.003 ETH expect(deposit.numbers.callbackGasLimit).eq(defaultParams.callbackGasLimit); expect(deposit.flags.shouldUnwrapNativeToken).eq(defaultParams.shouldUnwrapNativeToken); expect(deposit._dataList).deep.eq(defaultParams.dataList); }); }); + + describe("createWithdrawal", () => { + let defaultWithdrawalParams; + let createWithdrawalParams: Parameters[0]; + beforeEach(async () => { + defaultWithdrawalParams = { + addresses: { + receiver: user1.address, + callbackContract: user2.address, + uiFeeReceiver: user2.address, + market: ethUsdMarket.marketToken, + longTokenSwapPath: [ethUsdMarket.marketToken], + shortTokenSwapPath: [ethUsdMarket.marketToken], + }, + minLongTokenAmount: 0, + minShortTokenAmount: 0, + shouldUnwrapNativeToken: false, + executionFee: 0, + callbackGasLimit: "200000", + dataList: [], + }; + + createWithdrawalParams = { + sender: relaySigner, + signer: user1, // user1 was the receiver of the deposit + feeParams: { + feeToken: wnt.address, + feeAmount: expandDecimals(5, 15), // 0.005 ETH + feeSwapPath: [], + }, + transferRequests: { + tokens: [ethUsdMarket.marketToken], + receivers: [withdrawalVault.address], + amounts: [expandDecimals(100_000, 18)], + }, + account: user1.address, // user1 was the receiver of the deposit + params: defaultWithdrawalParams, + deadline: 9999999999, + chainId, + srcChainId: chainId, + desChainId: chainId, + relayRouter: multichainGmRouter, + relayFeeToken: wnt.address, + relayFeeAmount: expandDecimals(2, 15), // 0.002 ETH + }; + }); + + it("creates withdrawal and sends relayer fee", async () => { + await sendCreateDeposit(createDepositParams); + + // const _initialLongToken = await contractAt("MintableToken", defaultParams.addresses.initialLongToken); + // await _initialLongToken.mint(depositVault.address, createDepositParams.transferRequests.amounts[0]); + // const _initialShortToken = await contractAt("MintableToken", defaultParams.addresses.initialShortToken); + // await _initialShortToken.mint(depositVault.address, createDepositParams.transferRequests.amounts[1]); + + expect(await wnt.balanceOf(user0.address)).eq(0); + expect(await usdc.balanceOf(user0.address)).eq(0); + expect(await getBalanceOf(ethUsdMarket.marketToken, user1.address)).eq(0); // GM + expect(await wnt.balanceOf(depositVault.address)).eq(expandDecimals(10, 18).add(expandDecimals(3, 15))); // 10.003 ETH + expect(await usdc.balanceOf(depositVault.address)).eq(expandDecimals(50_000, 6)); // 50,000 USDC + + // TODO: Deposit was cancelled: {"name":"UsdDeltaExceedsPoolValue","args":["-50000000000000000000000000000000000","0"]} + // if commenting out utils/deposit.ts/L140 => throw new Error(`Deposit was cancelled: ${getErrorString(cancellationReason)}`); + // then contracts execute the deposit, but funds are returned to user0 and no GM tokens are minted to user1 + expect(await getDepositCount(dataStore)).eq(1); + await executeDeposit(fixture, { gasUsageLabel: "executeDeposit" }); + expect(await getDepositCount(dataStore)).eq(0); + + // expect(await wnt.balanceOf(user0.address)).eq(0); // TODO: executeDeposit failed and 10 ETH was returned to user0 + // expect(await usdc.balanceOf(user0.address)).eq(0); // TODO: executeDeposit failed and 50,000 USDC was returned to user0 + // expect(await getBalanceOf(ethUsdMarket.marketToken, user1.address)).eq(expandDecimals(100_000, 18)); // TODO: executeDeposit failed and no GM tokens were minted to user1 + expect(await wnt.balanceOf(depositVault.address)).eq(0); + expect(await usdc.balanceOf(depositVault.address)).eq(0); + + // TODO: fix executeDeposit to mint GM tokens + + expect(await getWithdrawalCount(dataStore)).eq(0); + // await sendCreateWithdrawal(createWithdrawalParams); + // expect(await getWithdrawalCount(dataStore)).eq(1); + + // const withdrawalKeys = await getWithdrawalKeys(dataStore, 0, 1); + // const withdrawal = await reader.getWithdrawal(dataStore.address, withdrawalKeys[0]); + // expect(withdrawal.addresses.account).eq(user1.address); + // expect(withdrawal.addresses.receiver).eq(defaultWithdrawalParams.addresses.receiver); + // expect(withdrawal.addresses.callbackContract).eq(defaultWithdrawalParams.addresses.callbackContract); + // expect(withdrawal.addresses.market).eq(defaultWithdrawalParams.addresses.market); + // expect(withdrawal.numbers.marketTokenAmount).eq(createWithdrawalParams.transferRequests.amounts[0]); // 100,000 GM + // expect(withdrawal.numbers.minLongTokenAmount).eq(createWithdrawalParams.params.minLongTokenAmount); + // expect(withdrawal.numbers.minShortTokenAmount).eq(createWithdrawalParams.params.minShortTokenAmount); + // expect(withdrawal.numbers.executionFee).eq(createWithdrawalParams.params.executionFee); + // expect(withdrawal.numbers.callbackGasLimit).eq(createWithdrawalParams.params.callbackGasLimit); + // expect(withdrawal.flags.shouldUnwrapNativeToken).eq(createWithdrawalParams.params.shouldUnwrapNativeToken); + // expect(withdrawal._dataList).deep.eq(createWithdrawalParams.params.dataList); + }); + }); }); diff --git a/utils/relay/multichain.ts b/utils/relay/multichain.ts index ca12d113a..aa99fbff3 100644 --- a/utils/relay/multichain.ts +++ b/utils/relay/multichain.ts @@ -3,6 +3,7 @@ import { GELATO_RELAY_ADDRESS } from "./addresses"; import { hashRelayParams, signTypedData } from "./helpers"; import { getDomain } from "./helpers"; import { getRelayParams } from "./helpers"; +import { contractAt } from "../deploy"; export async function sendCreateDeposit(p: { signer: ethers.Signer; @@ -18,12 +19,6 @@ export async function sendCreateDeposit(p: { refundTokens: string[]; refundReceivers: string[]; }; - tokenPermits?: { - token: string; - spender: string; - value: BigNumberish; - deadline: BigNumberish; - }[]; feeParams: { feeToken: string; feeAmount: BigNumberish; @@ -70,6 +65,56 @@ export async function sendCreateDeposit(p: { }); } +export async function sendCreateWithdrawal(p: { + signer: ethers.Signer; + sender: ethers.Signer; + transferRequests: { + tokens: string[]; + receivers: string[]; + amounts: BigNumberish[]; + }; + account: string; + params: any; + signature?: string; + userNonce?: BigNumberish; + chainId: BigNumberish; + srcChainId: BigNumberish; + desChainId: BigNumberish; + feeParams: any; + deadline: BigNumberish; + relayRouter: ethers.Contract; + relayFeeToken: string; + relayFeeAmount: BigNumberish; +}) { + const relayParams = await getRelayParams(p); + let signature = p.signature; + if (!signature) { + signature = await getCreateWithdrawalSignature({ + signer: p.signer, + relayParams, + transferRequests: p.transferRequests, + verifyingContract: p.relayRouter.address, + params: p.params, + chainId: p.chainId, + }); + } + const createWithdrawalCalldata = p.relayRouter.interface.encodeFunctionData("createWithdrawal", [ + { ...relayParams, signature }, + p.account, + p.srcChainId, + p.transferRequests, + p.params, + ]); + const calldata = ethers.utils.solidityPack( + ["bytes", "address", "address", "uint256"], + [createWithdrawalCalldata, GELATO_RELAY_ADDRESS, p.relayFeeToken, p.relayFeeAmount] + ); + return p.sender.sendTransaction({ + to: p.relayRouter.address, + data: calldata, + }); +} + async function getCreateDepositSignature({ signer, relayParams, @@ -77,6 +122,13 @@ async function getCreateDepositSignature({ verifyingContract, params, chainId, +}: { + signer: ethers.Signer; + relayParams: any; + transferRequests: { tokens: string[]; receivers: string[]; amounts: BigNumberish[] }; + verifyingContract: string; + params: any; + chainId: BigNumberish; }) { if (relayParams.userNonce === undefined) { throw new Error("userNonce is required"); @@ -121,3 +173,61 @@ async function getCreateDepositSignature({ return signTypedData(signer, domain, types, typedData); } + +async function getCreateWithdrawalSignature({ + signer, + relayParams, + transferRequests, + verifyingContract, + params, + chainId, +}: { + signer: ethers.Signer; + relayParams: any; + transferRequests: { tokens: string[]; receivers: string[]; amounts: BigNumberish[] }; + verifyingContract: string; + params: any; + chainId: BigNumberish; +}) { + if (relayParams.userNonce === undefined) { + throw new Error("userNonce is required"); + } + const types = { + CreateWithdrawal: [ + { name: "transferTokens", type: "address[]" }, + { name: "transferReceivers", type: "address[]" }, + { name: "transferAmounts", type: "uint256[]" }, + { name: "addresses", type: "CreateWithdrawalAddresses" }, + { name: "minLongTokenAmount", type: "uint256" }, + { name: "minShortTokenAmount", type: "uint256" }, + { name: "shouldUnwrapNativeToken", type: "bool" }, + { name: "executionFee", type: "uint256" }, + { name: "callbackGasLimit", type: "uint256" }, + { name: "dataList", type: "bytes32[]" }, + { name: "relayParams", type: "bytes32" }, + ], + CreateWithdrawalAddresses: [ + { name: "receiver", type: "address" }, + { name: "callbackContract", type: "address" }, + { name: "uiFeeReceiver", type: "address" }, + { name: "market", type: "address" }, + { name: "longTokenSwapPath", type: "address[]" }, + { name: "shortTokenSwapPath", type: "address[]" }, + ], + }; + const typedData = { + transferTokens: transferRequests.tokens, + transferReceivers: transferRequests.receivers, + transferAmounts: transferRequests.amounts, + addresses: params.addresses, + minLongTokenAmount: params.minLongTokenAmount, + minShortTokenAmount: params.minShortTokenAmount, + shouldUnwrapNativeToken: params.shouldUnwrapNativeToken, + executionFee: params.executionFee, + callbackGasLimit: params.callbackGasLimit, + dataList: params.dataList, + relayParams: hashRelayParams(relayParams), + }; + const domain = getDomain(chainId, verifyingContract); + return signTypedData(signer, domain, types, typedData); +} From 0e839d02754b5e03321867df66cbd3d63a48095e Mon Sep 17 00:00:00 2001 From: Solar Date: Fri, 21 Feb 2025 10:50:00 +0300 Subject: [PATCH 089/205] squash - refactor callbacks to eventdata --- .gitignore | 2 + contracts/callback/CallbackUtils.sol | 91 +++++++++++++------ .../callback/IDepositCallbackReceiver.sol | 6 +- .../callback/IGlvDepositCallbackReceiver.sol | 5 +- .../IGlvWithdrawalCallbackReceiver.sol | 6 +- contracts/callback/IOrderCallbackReceiver.sol | 6 +- contracts/callback/IShiftCallbackReceiver.sol | 5 +- .../callback/IWithdrawalCallbackReceiver.sol | 5 +- contracts/deposit/DepositEventUtils.sol | 65 +++++++------ contracts/gas/GasUtils.sol | 32 +++---- .../glv/glvDeposit/GlvDepositEventUtils.sol | 68 +++++++------- .../glvWithdrawal/GlvWithdrawalEventUtils.sol | 60 ++++++------ contracts/market/MarketUtils.sol | 10 +- contracts/mock/MockCallbackReceiver.sol | 14 +-- contracts/mock/RevertingCallbackReceiver.sol | 4 +- contracts/order/OrderEventUtils.sol | 73 ++++++++------- contracts/pricing/SwapPricingUtils.sol | 4 +- contracts/shift/ShiftEventUtils.sol | 44 +++++---- contracts/swap/SwapUtils.sol | 4 +- contracts/withdrawal/WithdrawalEventUtils.sol | 61 +++++++------ deploy/deployAdlHandler.ts | 1 + deploy/deployAdlUtils.ts | 2 +- deploy/deployCallbackUtils.ts | 9 +- deploy/deployDepositUtils.ts | 3 + deploy/deployExecuteDepositUtils.ts | 1 + deploy/deployExecuteOrderUtils.ts | 1 + deploy/deployExecuteWithdrawalUtils.ts | 1 + deploy/deployGlvDepositEventUtils.ts | 3 + deploy/deployGlvDepositUtils.ts | 1 + deploy/deployGlvHandler.ts | 1 + deploy/deployGlvWithdrawalUtils.ts | 1 + deploy/deployLiquidationHandler.ts | 1 + deploy/deployLiquidationUtils.ts | 2 +- deploy/deployOrderHandler.ts | 10 +- deploy/deployOrderUtils.ts | 12 +-- deploy/deployReaderUtils.ts | 2 +- deploy/deployReaderWithdrawalUtils.ts | 2 +- deploy/deployShiftUtils.ts | 2 + deploy/deploySwapUtils.ts | 2 +- deploy/deployWithdrawalUtils.ts | 1 + test/migration/GlpMigrator.ts | 4 +- utils/fixture.ts | 2 + 42 files changed, 356 insertions(+), 273 deletions(-) diff --git a/.gitignore b/.gitignore index 6fdc08c5a..19d16f2ae 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ deployments/localhost deployments.txt *.log .cursorignore* + +.idea diff --git a/contracts/callback/CallbackUtils.sol b/contracts/callback/CallbackUtils.sol index aef333dc2..c66f1eda4 100644 --- a/contracts/callback/CallbackUtils.sol +++ b/contracts/callback/CallbackUtils.sol @@ -15,6 +15,13 @@ import "./IGasFeeCallbackReceiver.sol"; import "./IGlvDepositCallbackReceiver.sol"; import "./IGlvWithdrawalCallbackReceiver.sol"; +import "../order/OrderEventUtils.sol"; +import "../withdrawal/WithdrawalEventUtils.sol"; +import "../deposit/DepositEventUtils.sol"; +import "../shift/ShiftEventUtils.sol"; +import "../glv/glvDeposit/GlvDepositEventUtils.sol"; +import "../glv/glvWithdrawal/GlvWithdrawalEventUtils.sol"; + // @title CallbackUtils // @dev most features require a two step process to complete // the user first sends a request transaction, then a second transaction is sent @@ -64,7 +71,7 @@ library CallbackUtils { // executions to fail // @param dataStore DataStore // @param callbackGasLimit the callback gas limit - function validateCallbackGasLimit(DataStore dataStore, uint256 callbackGasLimit) internal view { + function validateCallbackGasLimit(DataStore dataStore, uint256 callbackGasLimit) external view { uint256 maxCallbackGasLimit = dataStore.getUint(Keys.MAX_CALLBACK_GAS_LIMIT); if (callbackGasLimit > maxCallbackGasLimit) { revert Errors.MaxCallbackGasLimitExceeded(callbackGasLimit, maxCallbackGasLimit); @@ -82,7 +89,7 @@ library CallbackUtils { dataStore.setAddress(Keys.savedCallbackContract(account, market), callbackContract); } - function getSavedCallbackContract(DataStore dataStore, address account, address market) internal view returns (address) { + function getSavedCallbackContract(DataStore dataStore, address account, address market) external view returns (address) { return dataStore.getAddress(Keys.savedCallbackContract(account, market)); } @@ -116,14 +123,16 @@ library CallbackUtils { bytes32 key, Deposit.Props memory deposit, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(deposit.callbackContract())) { return; } validateGasLeftForCallback(deposit.callbackGasLimit()); + EventUtils.EventLogData memory depositData = DepositEventUtils.createEventData(deposit, Deposit.DepositType.Normal); + try IDepositCallbackReceiver(deposit.callbackContract()).afterDepositExecution{ gas: deposit.callbackGasLimit() }( key, - deposit, + depositData, eventData ) { } catch { @@ -138,14 +147,16 @@ library CallbackUtils { bytes32 key, Deposit.Props memory deposit, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(deposit.callbackContract())) { return; } validateGasLeftForCallback(deposit.callbackGasLimit()); + EventUtils.EventLogData memory depositData = DepositEventUtils.createEventData(deposit, Deposit.DepositType.Normal); + try IDepositCallbackReceiver(deposit.callbackContract()).afterDepositCancellation{ gas: deposit.callbackGasLimit() }( key, - deposit, + depositData, eventData ) { } catch { @@ -160,14 +171,16 @@ library CallbackUtils { bytes32 key, Withdrawal.Props memory withdrawal, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(withdrawal.callbackContract())) { return; } validateGasLeftForCallback(withdrawal.callbackGasLimit()); + EventUtils.EventLogData memory withdrawalData = WithdrawalEventUtils.createEventData(withdrawal, Withdrawal.WithdrawalType.Normal); + try IWithdrawalCallbackReceiver(withdrawal.callbackContract()).afterWithdrawalExecution{ gas: withdrawal.callbackGasLimit() }( key, - withdrawal, + withdrawalData, eventData ) { } catch { @@ -182,14 +195,16 @@ library CallbackUtils { bytes32 key, Withdrawal.Props memory withdrawal, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(withdrawal.callbackContract())) { return; } validateGasLeftForCallback(withdrawal.callbackGasLimit()); + EventUtils.EventLogData memory withdrawalData = WithdrawalEventUtils.createEventData(withdrawal, Withdrawal.WithdrawalType.Normal); + try IWithdrawalCallbackReceiver(withdrawal.callbackContract()).afterWithdrawalCancellation{ gas: withdrawal.callbackGasLimit() }( key, - withdrawal, + withdrawalData, eventData ) { } catch { @@ -201,14 +216,16 @@ library CallbackUtils { bytes32 key, Shift.Props memory shift, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(shift.callbackContract())) { return; } validateGasLeftForCallback(shift.callbackGasLimit()); + EventUtils.EventLogData memory shiftData = ShiftEventUtils.createEventData(shift); + try IShiftCallbackReceiver(shift.callbackContract()).afterShiftExecution{ gas: shift.callbackGasLimit() }( key, - shift, + shiftData, eventData ) { } catch { @@ -219,14 +236,16 @@ library CallbackUtils { bytes32 key, Shift.Props memory shift, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(shift.callbackContract())) { return; } validateGasLeftForCallback(shift.callbackGasLimit()); + EventUtils.EventLogData memory shiftData = ShiftEventUtils.createEventData(shift); + try IShiftCallbackReceiver(shift.callbackContract()).afterShiftCancellation{ gas: shift.callbackGasLimit() }( key, - shift, + shiftData, eventData ) { } catch { @@ -244,14 +263,16 @@ library CallbackUtils { bytes32 key, Order.Props memory order, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(order.callbackContract())) { return; } validateGasLeftForCallback(order.callbackGasLimit()); + EventUtils.EventLogData memory orderData = OrderEventUtils.createEventData(order); + try IOrderCallbackReceiver(order.callbackContract()).afterOrderExecution{ gas: order.callbackGasLimit() }( key, - order, + orderData, eventData ) { } catch { @@ -266,14 +287,16 @@ library CallbackUtils { bytes32 key, Order.Props memory order, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(order.callbackContract())) { return; } validateGasLeftForCallback(order.callbackGasLimit()); + EventUtils.EventLogData memory orderData = OrderEventUtils.createEventData(order); + try IOrderCallbackReceiver(order.callbackContract()).afterOrderCancellation{ gas: order.callbackGasLimit() }( key, - order, + orderData, eventData ) { } catch { @@ -288,14 +311,16 @@ library CallbackUtils { bytes32 key, Order.Props memory order, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(order.callbackContract())) { return; } validateGasLeftForCallback(order.callbackGasLimit()); + EventUtils.EventLogData memory orderData = OrderEventUtils.createEventData(order); + try IOrderCallbackReceiver(order.callbackContract()).afterOrderFrozen{ gas: order.callbackGasLimit() }( key, - order, + orderData, eventData ) { } catch { @@ -310,16 +335,18 @@ library CallbackUtils { bytes32 key, GlvDeposit.Props memory glvDeposit, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(glvDeposit.callbackContract())) { return; } validateGasLeftForCallback(glvDeposit.callbackGasLimit()); + EventUtils.EventLogData memory glvData = GlvDepositEventUtils.createEventData(glvDeposit); + try IGlvDepositCallbackReceiver(glvDeposit.callbackContract()).afterGlvDepositExecution{ gas: glvDeposit.callbackGasLimit() }( key, - glvDeposit, + glvData, eventData ) { } catch { @@ -334,14 +361,16 @@ library CallbackUtils { bytes32 key, GlvDeposit.Props memory glvDeposit, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(glvDeposit.callbackContract())) { return; } validateGasLeftForCallback(glvDeposit.callbackGasLimit()); + EventUtils.EventLogData memory glvData = GlvDepositEventUtils.createEventData(glvDeposit); + try IGlvDepositCallbackReceiver(glvDeposit.callbackContract()).afterGlvDepositCancellation{ gas: glvDeposit.callbackGasLimit() }( key, - glvDeposit, + glvData, eventData ) { } catch { @@ -356,14 +385,16 @@ library CallbackUtils { bytes32 key, GlvWithdrawal.Props memory glvWithdrawal, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(glvWithdrawal.callbackContract())) { return; } validateGasLeftForCallback(glvWithdrawal.callbackGasLimit()); + EventUtils.EventLogData memory glvData = GlvWithdrawalEventUtils.createEventData(glvWithdrawal); + try IGlvWithdrawalCallbackReceiver(glvWithdrawal.callbackContract()).afterGlvWithdrawalExecution{ gas: glvWithdrawal.callbackGasLimit() }( key, - glvWithdrawal, + glvData, eventData ) { } catch { @@ -378,14 +409,16 @@ library CallbackUtils { bytes32 key, GlvWithdrawal.Props memory glvWithdrawal, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(glvWithdrawal.callbackContract())) { return; } validateGasLeftForCallback(glvWithdrawal.callbackGasLimit()); + EventUtils.EventLogData memory glvData = GlvWithdrawalEventUtils.createEventData(glvWithdrawal); + try IGlvWithdrawalCallbackReceiver(glvWithdrawal.callbackContract()).afterGlvWithdrawalCancellation{ gas: glvWithdrawal.callbackGasLimit() }( key, - glvWithdrawal, + glvData, eventData ) { } catch { @@ -395,7 +428,7 @@ library CallbackUtils { // @dev validates that the given address is a contract // @param callbackContract the contract to call - function isValidCallbackContract(address callbackContract) internal view returns (bool) { + function isValidCallbackContract(address callbackContract) public view returns (bool) { if (callbackContract == address(0)) { return false; } if (!callbackContract.isContract()) { return false; } diff --git a/contracts/callback/IDepositCallbackReceiver.sol b/contracts/callback/IDepositCallbackReceiver.sol index 8a3d4fbdb..4234591a7 100644 --- a/contracts/callback/IDepositCallbackReceiver.sol +++ b/contracts/callback/IDepositCallbackReceiver.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; import "../event/EventUtils.sol"; -import "../deposit/Deposit.sol"; + // @title IDepositCallbackReceiver // @dev interface for a deposit callback contract @@ -11,10 +11,10 @@ interface IDepositCallbackReceiver { // @dev called after a deposit execution // @param key the key of the deposit // @param deposit the deposit that was executed - function afterDepositExecution(bytes32 key, Deposit.Props memory deposit, EventUtils.EventLogData memory eventData) external; + function afterDepositExecution(bytes32 key, EventUtils.EventLogData memory depositData, EventUtils.EventLogData memory eventData) external; // @dev called after a deposit cancellation // @param key the key of the deposit // @param deposit the deposit that was cancelled - function afterDepositCancellation(bytes32 key, Deposit.Props memory deposit, EventUtils.EventLogData memory eventData) external; + function afterDepositCancellation(bytes32 key, EventUtils.EventLogData memory depositData, EventUtils.EventLogData memory eventData) external; } diff --git a/contracts/callback/IGlvDepositCallbackReceiver.sol b/contracts/callback/IGlvDepositCallbackReceiver.sol index ef8c37653..310a221dd 100644 --- a/contracts/callback/IGlvDepositCallbackReceiver.sol +++ b/contracts/callback/IGlvDepositCallbackReceiver.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.0; import "../event/EventUtils.sol"; -import "../glv/glvDeposit/GlvDeposit.sol"; // @title IGlvDepositCallbackReceiver // @dev interface for a glvDeposit callback contract @@ -13,7 +12,7 @@ interface IGlvDepositCallbackReceiver { // @param glvDeposit the glvDeposit that was executed function afterGlvDepositExecution( bytes32 key, - GlvDeposit.Props memory glvDeposit, + EventUtils.EventLogData memory glvDepositData, EventUtils.EventLogData memory eventData ) external; @@ -22,7 +21,7 @@ interface IGlvDepositCallbackReceiver { // @param glvDeposit the glvDeposit that was cancelled function afterGlvDepositCancellation( bytes32 key, - GlvDeposit.Props memory glvDeposit, + EventUtils.EventLogData memory glvDepositData, EventUtils.EventLogData memory eventData ) external; } diff --git a/contracts/callback/IGlvWithdrawalCallbackReceiver.sol b/contracts/callback/IGlvWithdrawalCallbackReceiver.sol index 0ea514790..576450b13 100644 --- a/contracts/callback/IGlvWithdrawalCallbackReceiver.sol +++ b/contracts/callback/IGlvWithdrawalCallbackReceiver.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; import "../event/EventUtils.sol"; -import "../glv/glvWithdrawal/GlvWithdrawal.sol"; + // @title IGlvWithdrawalCallbackReceiver // @dev interface for a glvWithdrawal callback contract @@ -13,7 +13,7 @@ interface IGlvWithdrawalCallbackReceiver { // @param glvWithdrawal the glvWithdrawal that was executed function afterGlvWithdrawalExecution( bytes32 key, - GlvWithdrawal.Props memory glvWithdrawal, + EventUtils.EventLogData memory glvWithdrawalData, EventUtils.EventLogData memory eventData ) external; @@ -22,7 +22,7 @@ interface IGlvWithdrawalCallbackReceiver { // @param glvWithdrawal the glvWithdrawal that was cancelled function afterGlvWithdrawalCancellation( bytes32 key, - GlvWithdrawal.Props memory glvWithdrawal, + EventUtils.EventLogData memory glvWithdrawalData, EventUtils.EventLogData memory eventData ) external; } diff --git a/contracts/callback/IOrderCallbackReceiver.sol b/contracts/callback/IOrderCallbackReceiver.sol index d1c331d79..b78727784 100644 --- a/contracts/callback/IOrderCallbackReceiver.sol +++ b/contracts/callback/IOrderCallbackReceiver.sol @@ -11,15 +11,15 @@ interface IOrderCallbackReceiver { // @dev called after an order execution // @param key the key of the order // @param order the order that was executed - function afterOrderExecution(bytes32 key, Order.Props memory order, EventUtils.EventLogData memory eventData) external; + function afterOrderExecution(bytes32 key, EventUtils.EventLogData memory orderData, EventUtils.EventLogData memory eventData) external; // @dev called after an order cancellation // @param key the key of the order // @param order the order that was cancelled - function afterOrderCancellation(bytes32 key, Order.Props memory order, EventUtils.EventLogData memory eventData) external; + function afterOrderCancellation(bytes32 key, EventUtils.EventLogData memory order, EventUtils.EventLogData memory eventData) external; // @dev called after an order has been frozen, see OrderUtils.freezeOrder in OrderHandler for more info // @param key the key of the order // @param order the order that was frozen - function afterOrderFrozen(bytes32 key, Order.Props memory order, EventUtils.EventLogData memory eventData) external; + function afterOrderFrozen(bytes32 key, EventUtils.EventLogData memory order, EventUtils.EventLogData memory eventData) external; } diff --git a/contracts/callback/IShiftCallbackReceiver.sol b/contracts/callback/IShiftCallbackReceiver.sol index 92b1acbc1..5b6f5a399 100644 --- a/contracts/callback/IShiftCallbackReceiver.sol +++ b/contracts/callback/IShiftCallbackReceiver.sol @@ -3,9 +3,8 @@ pragma solidity ^0.8.0; import "../event/EventUtils.sol"; -import "../shift/Shift.sol"; interface IShiftCallbackReceiver { - function afterShiftExecution(bytes32 key, Shift.Props memory shift, EventUtils.EventLogData memory eventData) external; - function afterShiftCancellation(bytes32 key, Shift.Props memory shift, EventUtils.EventLogData memory eventData) external; + function afterShiftExecution(bytes32 key, EventUtils.EventLogData memory shiftData, EventUtils.EventLogData memory eventData) external; + function afterShiftCancellation(bytes32 key, EventUtils.EventLogData memory shiftData, EventUtils.EventLogData memory eventData) external; } diff --git a/contracts/callback/IWithdrawalCallbackReceiver.sol b/contracts/callback/IWithdrawalCallbackReceiver.sol index c2250ebdb..2e9dd90e9 100644 --- a/contracts/callback/IWithdrawalCallbackReceiver.sol +++ b/contracts/callback/IWithdrawalCallbackReceiver.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.0; import "../event/EventUtils.sol"; -import "../withdrawal/Withdrawal.sol"; // @title IWithdrawalCallbackReceiver // @dev interface for a withdrawal callback contract @@ -11,10 +10,10 @@ interface IWithdrawalCallbackReceiver { // @dev called after a withdrawal execution // @param key the key of the withdrawal // @param withdrawal the withdrawal that was executed - function afterWithdrawalExecution(bytes32 key, Withdrawal.Props memory withdrawal, EventUtils.EventLogData memory eventData) external; + function afterWithdrawalExecution(bytes32 key, EventUtils.EventLogData memory withdrawal, EventUtils.EventLogData memory eventData) external; // @dev called after a withdrawal cancellation // @param key the key of the withdrawal // @param withdrawal the withdrawal that was cancelled - function afterWithdrawalCancellation(bytes32 key, Withdrawal.Props memory withdrawal, EventUtils.EventLogData memory eventData) external; + function afterWithdrawalCancellation(bytes32 key, EventUtils.EventLogData memory withdrawal, EventUtils.EventLogData memory eventData) external; } diff --git a/contracts/deposit/DepositEventUtils.sol b/contracts/deposit/DepositEventUtils.sol index 0d202af05..19d4ee4f8 100644 --- a/contracts/deposit/DepositEventUtils.sol +++ b/contracts/deposit/DepositEventUtils.sol @@ -26,40 +26,11 @@ library DepositEventUtils { Deposit.Props memory deposit, Deposit.DepositType depositType ) external { - EventUtils.EventLogData memory eventData; - - eventData.addressItems.initItems(7); - eventData.addressItems.setItem(0, "account", deposit.account()); - eventData.addressItems.setItem(1, "receiver", deposit.receiver()); - eventData.addressItems.setItem(2, "callbackContract", deposit.callbackContract()); - eventData.addressItems.setItem(3, "market", deposit.market()); - eventData.addressItems.setItem(4, "initialLongToken", deposit.initialLongToken()); - eventData.addressItems.setItem(5, "initialShortToken", deposit.initialShortToken()); - eventData.addressItems.setItem(6, "uiFeeReceiver", deposit.uiFeeReceiver()); - - eventData.addressItems.initArrayItems(2); - eventData.addressItems.setItem(0, "longTokenSwapPath", deposit.longTokenSwapPath()); - eventData.addressItems.setItem(1, "shortTokenSwapPath", deposit.shortTokenSwapPath()); - - eventData.uintItems.initItems(8); - eventData.uintItems.setItem(0, "initialLongTokenAmount", deposit.initialLongTokenAmount()); - eventData.uintItems.setItem(1, "initialShortTokenAmount", deposit.initialShortTokenAmount()); - eventData.uintItems.setItem(2, "minMarketTokens", deposit.minMarketTokens()); - eventData.uintItems.setItem(3, "updatedAtTime", deposit.updatedAtTime()); - eventData.uintItems.setItem(4, "executionFee", deposit.executionFee()); - eventData.uintItems.setItem(5, "callbackGasLimit", deposit.callbackGasLimit()); - eventData.uintItems.setItem(6, "depositType", uint256(depositType)); - eventData.uintItems.setItem(7, "srcChainId", deposit.srcChainId()); - - eventData.boolItems.initItems(1); - eventData.boolItems.setItem(0, "shouldUnwrapNativeToken", deposit.shouldUnwrapNativeToken()); + EventUtils.EventLogData memory eventData = createEventData(deposit, depositType); eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); - eventData.bytes32Items.initArrayItems(1); - eventData.bytes32Items.setItem(0, "dataList", deposit.dataList()); - eventEmitter.emitEventLog2( "DepositCreated", key, @@ -127,4 +98,38 @@ library DepositEventUtils { eventData ); } + + function createEventData(Deposit.Props memory deposit, Deposit.DepositType depositType) + public pure returns (EventUtils.EventLogData memory) { + EventUtils.EventLogData memory eventData; + + eventData.addressItems.initItems(7); + eventData.addressItems.setItem(0, "account", deposit.account()); + eventData.addressItems.setItem(1, "receiver", deposit.receiver()); + eventData.addressItems.setItem(2, "callbackContract", deposit.callbackContract()); + eventData.addressItems.setItem(3, "market", deposit.market()); + eventData.addressItems.setItem(4, "initialLongToken", deposit.initialLongToken()); + eventData.addressItems.setItem(5, "initialShortToken", deposit.initialShortToken()); + eventData.addressItems.setItem(6, "uiFeeReceiver", deposit.uiFeeReceiver()); + + eventData.addressItems.initArrayItems(2); + eventData.addressItems.setItem(0, "longTokenSwapPath", deposit.longTokenSwapPath()); + eventData.addressItems.setItem(1, "shortTokenSwapPath", deposit.shortTokenSwapPath()); + + eventData.uintItems.initItems(7); + eventData.uintItems.setItem(0, "initialLongTokenAmount", deposit.initialLongTokenAmount()); + eventData.uintItems.setItem(1, "initialShortTokenAmount", deposit.initialShortTokenAmount()); + eventData.uintItems.setItem(2, "minMarketTokens", deposit.minMarketTokens()); + eventData.uintItems.setItem(3, "updatedAtTime", deposit.updatedAtTime()); + eventData.uintItems.setItem(4, "executionFee", deposit.executionFee()); + eventData.uintItems.setItem(5, "callbackGasLimit", deposit.callbackGasLimit()); + eventData.uintItems.setItem(6, "depositType", uint256(depositType)); + + eventData.boolItems.initItems(1); + eventData.boolItems.setItem(0, "shouldUnwrapNativeToken", deposit.shouldUnwrapNativeToken()); + + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", deposit.dataList()); + return eventData; + } } diff --git a/contracts/gas/GasUtils.sol b/contracts/gas/GasUtils.sol index edab84630..f940df48a 100644 --- a/contracts/gas/GasUtils.sol +++ b/contracts/gas/GasUtils.sol @@ -54,8 +54,8 @@ library GasUtils { return dataStore.getUint(Keys.MIN_ADDITIONAL_GAS_FOR_EXECUTION); } - function getExecutionGas(DataStore dataStore, uint256 startingGas) internal view returns (uint256) { - uint256 minHandleExecutionErrorGasToForward = GasUtils.getMinHandleExecutionErrorGasToForward(dataStore); + function getExecutionGas(DataStore dataStore, uint256 startingGas) external view returns (uint256) { + uint256 minHandleExecutionErrorGasToForward = getMinHandleExecutionErrorGasToForward(dataStore); if (startingGas < minHandleExecutionErrorGasToForward) { revert Errors.InsufficientExecutionGasForErrorHandling(startingGas, minHandleExecutionErrorGasToForward); } @@ -63,7 +63,7 @@ library GasUtils { return startingGas - minHandleExecutionErrorGasToForward; } - function validateExecutionGas(DataStore dataStore, uint256 startingGas, uint256 estimatedGasLimit) internal view { + function validateExecutionGas(DataStore dataStore, uint256 startingGas, uint256 estimatedGasLimit) external view { uint256 minAdditionalGasForExecution = getMinAdditionalGasForExecution(dataStore); if (startingGas < estimatedGasLimit + minAdditionalGasForExecution) { revert Errors.InsufficientExecutionGas(startingGas, estimatedGasLimit, minAdditionalGasForExecution); @@ -81,7 +81,7 @@ library GasUtils { // // a malicious user could cause the estimateGas call of a keeper to fail, in which case the keeper could // still attempt to execute the transaction with a reasonable gas limit - function validateExecutionErrorGas(DataStore dataStore, bytes memory reasonBytes) internal view { + function validateExecutionErrorGas(DataStore dataStore, bytes memory reasonBytes) external view { // skip the validation if the execution did not fail due to an out of gas error // also skip the validation if this is not invoked in an estimateGas call (tx.origin != address(0)) if (reasonBytes.length != 0 || tx.origin != address(0)) { @@ -182,7 +182,7 @@ library GasUtils { uint256 estimatedGasLimit, uint256 executionFee, uint256 oraclePriceCount - ) internal view { + ) external view { validateExecutionFee(dataStore, estimatedGasLimit, executionFee, oraclePriceCount, false); } @@ -273,18 +273,18 @@ library GasUtils { // @dev get estimated number of oracle prices for withdrawal // @param swapsCount number of swaps in the withdrawal - function estimateWithdrawalOraclePriceCount(uint256 swapsCount) internal pure returns (uint256) { + function estimateWithdrawalOraclePriceCount(uint256 swapsCount) external pure returns (uint256) { return 3 + swapsCount; } // @dev get estimated number of oracle prices for order // @param swapsCount number of swaps in the order - function estimateOrderOraclePriceCount(uint256 swapsCount) internal pure returns (uint256) { + function estimateOrderOraclePriceCount(uint256 swapsCount) external pure returns (uint256) { return 3 + swapsCount; } // @dev get estimated number of oracle prices for shift - function estimateShiftOraclePriceCount() internal pure returns (uint256) { + function estimateShiftOraclePriceCount() external pure returns (uint256) { // for single asset markets only 3 prices will be required // and keeper will slightly overpay // it should not be an issue because execution fee goes back to keeper @@ -294,7 +294,7 @@ library GasUtils { function estimateGlvDepositOraclePriceCount( uint256 marketCount, uint256 swapsCount - ) internal pure returns (uint256) { + ) external pure returns (uint256) { // for single asset markets oracle price count will be overestimated by 1 // it should not be an issue for GLV with multiple markets // because relative difference would be insignificant @@ -317,7 +317,7 @@ library GasUtils { function estimateExecuteDepositGasLimit( DataStore dataStore, Deposit.Props memory deposit - ) internal view returns (uint256) { + ) external view returns (uint256) { uint256 gasPerSwap = dataStore.getUint(Keys.singleSwapGasLimitKey()); uint256 swapCount = deposit.longTokenSwapPath().length + deposit.shortTokenSwapPath().length; uint256 gasForSwaps = swapCount * gasPerSwap; @@ -331,7 +331,7 @@ library GasUtils { function estimateExecuteWithdrawalGasLimit( DataStore dataStore, Withdrawal.Props memory withdrawal - ) internal view returns (uint256) { + ) external view returns (uint256) { uint256 gasPerSwap = dataStore.getUint(Keys.singleSwapGasLimitKey()); uint256 swapCount = withdrawal.longTokenSwapPath().length + withdrawal.shortTokenSwapPath().length; uint256 gasForSwaps = swapCount * gasPerSwap; @@ -345,7 +345,7 @@ library GasUtils { function estimateExecuteShiftGasLimit( DataStore dataStore, Shift.Props memory shift - ) internal view returns (uint256) { + ) external view returns (uint256) { return dataStore.getUint(Keys.shiftGasLimitKey()) + shift.callbackGasLimit(); } @@ -355,7 +355,7 @@ library GasUtils { function estimateExecuteOrderGasLimit( DataStore dataStore, Order.Props memory order - ) internal view returns (uint256) { + ) external view returns (uint256) { if (BaseOrderUtils.isIncreaseOrder(order.orderType())) { return estimateExecuteIncreaseOrderGasLimit(dataStore, order); } @@ -424,7 +424,7 @@ library GasUtils { DataStore dataStore, GlvDeposit.Props memory glvDeposit, uint256 marketCount - ) internal view returns (uint256) { + ) external view returns (uint256) { // glv deposit execution gas consumption depends on the amount of markets uint256 gasPerGlvPerMarket = dataStore.getUint(Keys.glvPerMarketGasLimitKey()); uint256 gasForGlvMarkets = gasPerGlvPerMarket * marketCount; @@ -451,7 +451,7 @@ library GasUtils { DataStore dataStore, GlvWithdrawal.Props memory glvWithdrawal, uint256 marketCount - ) internal view returns (uint256) { + ) external view returns (uint256) { // glv withdrawal execution gas consumption depends on the amount of markets uint256 gasPerGlvPerMarket = dataStore.getUint(Keys.glvPerMarketGasLimitKey()); uint256 gasForGlvMarkets = gasPerGlvPerMarket * marketCount; @@ -466,7 +466,7 @@ library GasUtils { return gasLimit + dataStore.getUint(Keys.withdrawalGasLimitKey()) + gasForSwaps; } - function estimateExecuteGlvShiftGasLimit(DataStore dataStore) internal view returns (uint256) { + function estimateExecuteGlvShiftGasLimit(DataStore dataStore) external view returns (uint256) { return dataStore.getUint(Keys.glvShiftGasLimitKey()); } diff --git a/contracts/glv/glvDeposit/GlvDepositEventUtils.sol b/contracts/glv/glvDeposit/GlvDepositEventUtils.sol index 78483fa78..eb97405d9 100644 --- a/contracts/glv/glvDeposit/GlvDepositEventUtils.sol +++ b/contracts/glv/glvDeposit/GlvDepositEventUtils.sol @@ -24,42 +24,11 @@ library GlvDepositEventUtils { bytes32 key, GlvDeposit.Props memory glvDeposit ) external { - EventUtils.EventLogData memory eventData; - - eventData.addressItems.initItems(8); - eventData.addressItems.setItem(0, "account", glvDeposit.account()); - eventData.addressItems.setItem(1, "receiver", glvDeposit.receiver()); - eventData.addressItems.setItem(2, "callbackContract", glvDeposit.callbackContract()); - eventData.addressItems.setItem(3, "market", glvDeposit.market()); - eventData.addressItems.setItem(4, "glv", glvDeposit.glv()); - eventData.addressItems.setItem(5, "initialLongToken", glvDeposit.initialLongToken()); - eventData.addressItems.setItem(6, "initialShortToken", glvDeposit.initialShortToken()); - eventData.addressItems.setItem(7, "uiFeeReceiver", glvDeposit.uiFeeReceiver()); - - eventData.addressItems.initArrayItems(2); - eventData.addressItems.setItem(0, "longTokenSwapPath", glvDeposit.longTokenSwapPath()); - eventData.addressItems.setItem(1, "shortTokenSwapPath", glvDeposit.shortTokenSwapPath()); - - eventData.uintItems.initItems(8); - eventData.uintItems.setItem(0, "initialLongTokenAmount", glvDeposit.initialLongTokenAmount()); - eventData.uintItems.setItem(1, "initialShortTokenAmount", glvDeposit.initialShortTokenAmount()); - eventData.uintItems.setItem(2, "minGlvTokens", glvDeposit.minGlvTokens()); - eventData.uintItems.setItem(3, "updatedAtTime", glvDeposit.updatedAtTime()); - eventData.uintItems.setItem(4, "executionFee", glvDeposit.executionFee()); - eventData.uintItems.setItem(5, "callbackGasLimit", glvDeposit.callbackGasLimit()); - eventData.uintItems.setItem(6, "marketTokenAmount", glvDeposit.marketTokenAmount()); - eventData.uintItems.setItem(7, "srcChainId", glvDeposit.srcChainId()); - - eventData.boolItems.initItems(2); - eventData.boolItems.setItem(0, "shouldUnwrapNativeToken", glvDeposit.shouldUnwrapNativeToken()); - eventData.boolItems.setItem(1, "isMarketTokenDeposit", glvDeposit.isMarketTokenDeposit()); + EventUtils.EventLogData memory eventData = createEventData(glvDeposit); eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); - eventData.bytes32Items.initArrayItems(1); - eventData.bytes32Items.setItem(0, "dataList", glvDeposit.dataList()); - eventEmitter.emitEventLog2( "GlvDepositCreated", key, @@ -121,4 +90,39 @@ library GlvDepositEventUtils { eventData ); } + + function createEventData(GlvDeposit.Props memory glvDeposit) public pure + returns (EventUtils.EventLogData memory) { + EventUtils.EventLogData memory eventData; + eventData.addressItems.initItems(8); + eventData.addressItems.setItem(0, "account", glvDeposit.account()); + eventData.addressItems.setItem(1, "receiver", glvDeposit.receiver()); + eventData.addressItems.setItem(2, "callbackContract", glvDeposit.callbackContract()); + eventData.addressItems.setItem(3, "market", glvDeposit.market()); + eventData.addressItems.setItem(4, "glv", glvDeposit.glv()); + eventData.addressItems.setItem(5, "initialLongToken", glvDeposit.initialLongToken()); + eventData.addressItems.setItem(6, "initialShortToken", glvDeposit.initialShortToken()); + eventData.addressItems.setItem(7, "uiFeeReceiver", glvDeposit.uiFeeReceiver()); + + eventData.addressItems.initArrayItems(2); + eventData.addressItems.setItem(0, "longTokenSwapPath", glvDeposit.longTokenSwapPath()); + eventData.addressItems.setItem(1, "shortTokenSwapPath", glvDeposit.shortTokenSwapPath()); + + eventData.uintItems.initItems(7); + eventData.uintItems.setItem(0, "initialLongTokenAmount", glvDeposit.initialLongTokenAmount()); + eventData.uintItems.setItem(1, "initialShortTokenAmount", glvDeposit.initialShortTokenAmount()); + eventData.uintItems.setItem(2, "minGlvTokens", glvDeposit.minGlvTokens()); + eventData.uintItems.setItem(3, "updatedAtTime", glvDeposit.updatedAtTime()); + eventData.uintItems.setItem(4, "executionFee", glvDeposit.executionFee()); + eventData.uintItems.setItem(5, "callbackGasLimit", glvDeposit.callbackGasLimit()); + eventData.uintItems.setItem(6, "marketTokenAmount", glvDeposit.marketTokenAmount()); + + eventData.boolItems.initItems(2); + eventData.boolItems.setItem(0, "shouldUnwrapNativeToken", glvDeposit.shouldUnwrapNativeToken()); + eventData.boolItems.setItem(1, "isMarketTokenDeposit", glvDeposit.isMarketTokenDeposit()); + + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", glvDeposit.dataList()); + return eventData; + } } diff --git a/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol b/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol index dc94afb1b..1f15e22c6 100644 --- a/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol +++ b/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol @@ -24,38 +24,11 @@ library GlvWithdrawalEventUtils { bytes32 key, GlvWithdrawal.Props memory glvWithdrawal ) external { - EventUtils.EventLogData memory eventData; - - eventData.addressItems.initItems(6); - eventData.addressItems.setItem(0, "account", glvWithdrawal.account()); - eventData.addressItems.setItem(1, "receiver", glvWithdrawal.receiver()); - eventData.addressItems.setItem(2, "callbackContract", glvWithdrawal.callbackContract()); - eventData.addressItems.setItem(3, "market", glvWithdrawal.market()); - eventData.addressItems.setItem(4, "glv", glvWithdrawal.glv()); - eventData.addressItems.setItem(5, "uiFeeReceiver", glvWithdrawal.uiFeeReceiver()); - - eventData.addressItems.initArrayItems(2); - eventData.addressItems.setItem(0, "longTokenSwapPath", glvWithdrawal.longTokenSwapPath()); - eventData.addressItems.setItem(1, "shortTokenSwapPath", glvWithdrawal.shortTokenSwapPath()); - - eventData.uintItems.initItems(7); - eventData.uintItems.setItem(0, "glvTokenAmount", glvWithdrawal.glvTokenAmount()); - eventData.uintItems.setItem(1, "minLongTokenAmount", glvWithdrawal.minLongTokenAmount()); - eventData.uintItems.setItem(2, "minShortTokenAmount", glvWithdrawal.minShortTokenAmount()); - eventData.uintItems.setItem(3, "updatedAtTime", glvWithdrawal.updatedAtTime()); - eventData.uintItems.setItem(4, "executionFee", glvWithdrawal.executionFee()); - eventData.uintItems.setItem(5, "callbackGasLimit", glvWithdrawal.callbackGasLimit()); - eventData.uintItems.setItem(6, "srcChainId", glvWithdrawal.srcChainId()); - - eventData.boolItems.initItems(1); - eventData.boolItems.setItem(0, "shouldUnwrapNativeToken", glvWithdrawal.shouldUnwrapNativeToken()); + EventUtils.EventLogData memory eventData = createEventData(glvWithdrawal); eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); - eventData.bytes32Items.initArrayItems(1); - eventData.bytes32Items.setItem(0, "dataList", glvWithdrawal.dataList()); - eventEmitter.emitEventLog2("GlvWithdrawalCreated", key, Cast.toBytes32(glvWithdrawal.account()), eventData); } @@ -94,4 +67,35 @@ library GlvWithdrawalEventUtils { eventEmitter.emitEventLog2("GlvWithdrawalCancelled", key, Cast.toBytes32(account), eventData); } + + function createEventData(GlvWithdrawal.Props memory glvWithdrawal) public pure returns (EventUtils.EventLogData memory) { + EventUtils.EventLogData memory eventData; + + eventData.addressItems.initItems(6); + eventData.addressItems.setItem(0, "account", glvWithdrawal.account()); + eventData.addressItems.setItem(1, "receiver", glvWithdrawal.receiver()); + eventData.addressItems.setItem(2, "callbackContract", glvWithdrawal.callbackContract()); + eventData.addressItems.setItem(3, "market", glvWithdrawal.market()); + eventData.addressItems.setItem(4, "glv", glvWithdrawal.glv()); + eventData.addressItems.setItem(5, "uiFeeReceiver", glvWithdrawal.uiFeeReceiver()); + + eventData.addressItems.initArrayItems(2); + eventData.addressItems.setItem(0, "longTokenSwapPath", glvWithdrawal.longTokenSwapPath()); + eventData.addressItems.setItem(1, "shortTokenSwapPath", glvWithdrawal.shortTokenSwapPath()); + + eventData.uintItems.initItems(6); + eventData.uintItems.setItem(0, "glvTokenAmount", glvWithdrawal.glvTokenAmount()); + eventData.uintItems.setItem(1, "minLongTokenAmount", glvWithdrawal.minLongTokenAmount()); + eventData.uintItems.setItem(2, "minShortTokenAmount", glvWithdrawal.minShortTokenAmount()); + eventData.uintItems.setItem(3, "updatedAtTime", glvWithdrawal.updatedAtTime()); + eventData.uintItems.setItem(4, "executionFee", glvWithdrawal.executionFee()); + eventData.uintItems.setItem(5, "callbackGasLimit", glvWithdrawal.callbackGasLimit()); + + eventData.boolItems.initItems(1); + eventData.boolItems.setItem(0, "shouldUnwrapNativeToken", glvWithdrawal.shouldUnwrapNativeToken()); + + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", glvWithdrawal.dataList()); + return eventData; + } } diff --git a/contracts/market/MarketUtils.sol b/contracts/market/MarketUtils.sol index 8897c7834..74928be39 100644 --- a/contracts/market/MarketUtils.sol +++ b/contracts/market/MarketUtils.sol @@ -169,7 +169,7 @@ library MarketUtils { // @dev get the total supply of the marketToken // @param marketToken the marketToken // @return the total supply of the marketToken - function getMarketTokenSupply(MarketToken marketToken) internal view returns (uint256) { + function getMarketTokenSupply(MarketToken marketToken) public view returns (uint256) { return marketToken.totalSupply(); } @@ -2724,7 +2724,7 @@ library MarketUtils { // @dev validate that the specified market exists and is enabled // @param dataStore DataStore // @param marketAddress the address of the market - function validateEnabledMarket(DataStore dataStore, address marketAddress) internal view { + function validateEnabledMarket(DataStore dataStore, address marketAddress) external view { Market.Props memory market = MarketStoreUtils.get(dataStore, marketAddress); validateEnabledMarket(dataStore, market); } @@ -2732,7 +2732,7 @@ library MarketUtils { // @dev validate that the specified market exists and is enabled // @param dataStore DataStore // @param market the market to check - function validateEnabledMarket(DataStore dataStore, Market.Props memory market) internal view { + function validateEnabledMarket(DataStore dataStore, Market.Props memory market) public view { if (market.marketToken == address(0)) { revert Errors.EmptyMarket(); } @@ -2783,7 +2783,7 @@ library MarketUtils { // @dev get the enabled market, revert if the market does not exist or is not enabled // @param dataStore DataStore // @param marketAddress the address of the market - function getEnabledMarket(DataStore dataStore, address marketAddress) internal view returns (Market.Props memory) { + function getEnabledMarket(DataStore dataStore, address marketAddress) public view returns (Market.Props memory) { Market.Props memory market = MarketStoreUtils.get(dataStore, marketAddress); validateEnabledMarket(dataStore, market); return market; @@ -2808,7 +2808,7 @@ library MarketUtils { return markets; } - function validateSwapPath(DataStore dataStore, address[] memory swapPath) internal view { + function validateSwapPath(DataStore dataStore, address[] memory swapPath) external view { uint256 maxSwapPathLength = dataStore.getUint(Keys.MAX_SWAP_PATH_LENGTH); if (swapPath.length > maxSwapPathLength) { revert Errors.MaxSwapPathLengthExceeded(swapPath.length, maxSwapPathLength); diff --git a/contracts/mock/MockCallbackReceiver.sol b/contracts/mock/MockCallbackReceiver.sol index b34dacdb8..5cd272cc6 100644 --- a/contracts/mock/MockCallbackReceiver.sol +++ b/contracts/mock/MockCallbackReceiver.sol @@ -15,15 +15,15 @@ contract MockCallbackReceiver is IOrderCallbackReceiver, IGasFeeCallbackReceiver uint public glvWithdrawalExecutionCalled; uint public glvWithdrawalCancellationCalled; - function afterOrderExecution(bytes32 /* key */, Order.Props memory /* order */, EventUtils.EventLogData memory /* eventData */) external { + function afterOrderExecution(bytes32 /* key */, EventUtils.EventLogData memory /* order */, EventUtils.EventLogData memory /* eventData */) external { ++called; } - function afterOrderCancellation(bytes32 /* key */, Order.Props memory /* order */, EventUtils.EventLogData memory /* eventData */) external { + function afterOrderCancellation(bytes32 /* key */, EventUtils.EventLogData memory /* order */, EventUtils.EventLogData memory /* eventData */) external { ++called; } - function afterOrderFrozen(bytes32 /* key */, Order.Props memory /* order */, EventUtils.EventLogData memory /* eventData */) external { + function afterOrderFrozen(bytes32 /* key */, EventUtils.EventLogData memory /* order */, EventUtils.EventLogData memory /* eventData */) external { ++called; } @@ -31,19 +31,19 @@ contract MockCallbackReceiver is IOrderCallbackReceiver, IGasFeeCallbackReceiver ++called; } - function afterGlvDepositExecution(bytes32 /* key */, GlvDeposit.Props memory /* glv deposit */, EventUtils.EventLogData memory /* eventData */) external { + function afterGlvDepositExecution(bytes32 /* key */, EventUtils.EventLogData memory /* glv deposit */, EventUtils.EventLogData memory /* eventData */) external { ++glvDepositExecutionCalled; } - function afterGlvDepositCancellation(bytes32 /* key */, GlvDeposit.Props memory /* glv deposit */, EventUtils.EventLogData memory /* eventData */) external { + function afterGlvDepositCancellation(bytes32 /* key */, EventUtils.EventLogData memory /* glv deposit */, EventUtils.EventLogData memory /* eventData */) external { ++glvDepositCancellationCalled; } - function afterGlvWithdrawalExecution(bytes32 /* key */, GlvWithdrawal.Props memory /* glv withdrawal */, EventUtils.EventLogData memory /* eventData */) external { + function afterGlvWithdrawalExecution(bytes32 /* key */, EventUtils.EventLogData memory /* glv withdrawal */, EventUtils.EventLogData memory /* eventData */) external { ++glvWithdrawalExecutionCalled; } - function afterGlvWithdrawalCancellation(bytes32 /* key */, GlvWithdrawal.Props memory /* glv withdrawal */, EventUtils.EventLogData memory /* eventData */) external { + function afterGlvWithdrawalCancellation(bytes32 /* key */, EventUtils.EventLogData memory /* glv withdrawal */, EventUtils.EventLogData memory /* eventData */) external { ++glvWithdrawalCancellationCalled; } } diff --git a/contracts/mock/RevertingCallbackReceiver.sol b/contracts/mock/RevertingCallbackReceiver.sol index 414bd15b1..7978c958b 100644 --- a/contracts/mock/RevertingCallbackReceiver.sol +++ b/contracts/mock/RevertingCallbackReceiver.sol @@ -5,11 +5,11 @@ pragma solidity ^0.8.0; import "../callback/IDepositCallbackReceiver.sol"; contract RevertingCallbackReceiver is IDepositCallbackReceiver { - function afterDepositExecution(bytes32 /* key */, Deposit.Props memory /* deposit */, EventUtils.EventLogData memory /* eventData */) external pure { + function afterDepositExecution(bytes32 /* key */, EventUtils.EventLogData memory /* deposit */, EventUtils.EventLogData memory /* eventData */) external pure { revert("error"); } - function afterDepositCancellation(bytes32 /* key */, Deposit.Props memory /* deposit */, EventUtils.EventLogData memory /* eventData */) external pure { + function afterDepositCancellation(bytes32 /* key */, EventUtils.EventLogData memory /* deposit */, EventUtils.EventLogData memory /* eventData */) external pure { revert("error"); } } diff --git a/contracts/order/OrderEventUtils.sol b/contracts/order/OrderEventUtils.sol index 0e0297262..6eb649dbb 100644 --- a/contracts/order/OrderEventUtils.sol +++ b/contracts/order/OrderEventUtils.sol @@ -23,45 +23,11 @@ library OrderEventUtils { bytes32 key, Order.Props memory order ) external { - EventUtils.EventLogData memory eventData; - - eventData.addressItems.initItems(7); - eventData.addressItems.setItem(0, "account", order.account()); - eventData.addressItems.setItem(1, "receiver", order.receiver()); - eventData.addressItems.setItem(2, "callbackContract", order.callbackContract()); - eventData.addressItems.setItem(3, "uiFeeReceiver", order.uiFeeReceiver()); - eventData.addressItems.setItem(4, "market", order.market()); - eventData.addressItems.setItem(5, "initialCollateralToken", order.initialCollateralToken()); - eventData.addressItems.setItem(6, "cancellationReceiver", order.cancellationReceiver()); - - eventData.addressItems.initArrayItems(1); - eventData.addressItems.setItem(0, "swapPath", order.swapPath()); - - eventData.uintItems.initItems(12); - eventData.uintItems.setItem(0, "orderType", uint256(order.orderType())); - eventData.uintItems.setItem(1, "decreasePositionSwapType", uint256(order.decreasePositionSwapType())); - eventData.uintItems.setItem(2, "sizeDeltaUsd", order.sizeDeltaUsd()); - eventData.uintItems.setItem(3, "initialCollateralDeltaAmount", order.initialCollateralDeltaAmount()); - eventData.uintItems.setItem(4, "triggerPrice", order.triggerPrice()); - eventData.uintItems.setItem(5, "acceptablePrice", order.acceptablePrice()); - eventData.uintItems.setItem(6, "executionFee", order.executionFee()); - eventData.uintItems.setItem(7, "callbackGasLimit", order.callbackGasLimit()); - eventData.uintItems.setItem(8, "minOutputAmount", order.minOutputAmount()); - eventData.uintItems.setItem(9, "updatedAtTime", order.updatedAtTime()); - eventData.uintItems.setItem(10, "validFromTime", order.validFromTime()); - eventData.uintItems.setItem(11, "srcChainId", order.srcChainId()); - - eventData.boolItems.initItems(3); - eventData.boolItems.setItem(0, "isLong", order.isLong()); - eventData.boolItems.setItem(1, "shouldUnwrapNativeToken", order.shouldUnwrapNativeToken()); - eventData.boolItems.setItem(2, "autoCancel", order.autoCancel()); + EventUtils.EventLogData memory eventData = createEventData(order); eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); - eventData.bytes32Items.initArrayItems(1); - eventData.bytes32Items.setItem(0, "dataList", order.dataList()); - eventEmitter.emitEventLog2( "OrderCreated", key, @@ -232,4 +198,41 @@ library OrderEventUtils { eventData ); } + + function createEventData(Order.Props memory order) public pure returns (EventUtils.EventLogData memory) { + EventUtils.EventLogData memory eventData; + eventData.addressItems.initItems(7); + eventData.addressItems.setItem(0, "account", order.account()); + eventData.addressItems.setItem(1, "receiver", order.receiver()); + eventData.addressItems.setItem(2, "callbackContract", order.callbackContract()); + eventData.addressItems.setItem(3, "uiFeeReceiver", order.uiFeeReceiver()); + eventData.addressItems.setItem(4, "market", order.market()); + eventData.addressItems.setItem(5, "initialCollateralToken", order.initialCollateralToken()); + eventData.addressItems.setItem(6, "cancellationReceiver", order.cancellationReceiver()); + + eventData.addressItems.initArrayItems(1); + eventData.addressItems.setItem(0, "swapPath", order.swapPath()); + + eventData.uintItems.initItems(11); + eventData.uintItems.setItem(0, "orderType", uint256(order.orderType())); + eventData.uintItems.setItem(1, "decreasePositionSwapType", uint256(order.decreasePositionSwapType())); + eventData.uintItems.setItem(2, "sizeDeltaUsd", order.sizeDeltaUsd()); + eventData.uintItems.setItem(3, "initialCollateralDeltaAmount", order.initialCollateralDeltaAmount()); + eventData.uintItems.setItem(4, "triggerPrice", order.triggerPrice()); + eventData.uintItems.setItem(5, "acceptablePrice", order.acceptablePrice()); + eventData.uintItems.setItem(6, "executionFee", order.executionFee()); + eventData.uintItems.setItem(7, "callbackGasLimit", order.callbackGasLimit()); + eventData.uintItems.setItem(8, "minOutputAmount", order.minOutputAmount()); + eventData.uintItems.setItem(9, "updatedAtTime", order.updatedAtTime()); + eventData.uintItems.setItem(10, "validFromTime", order.validFromTime()); + + eventData.boolItems.initItems(3); + eventData.boolItems.setItem(0, "isLong", order.isLong()); + eventData.boolItems.setItem(1, "shouldUnwrapNativeToken", order.shouldUnwrapNativeToken()); + eventData.boolItems.setItem(2, "autoCancel", order.autoCancel()); + + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", order.dataList()); + return eventData; + } } diff --git a/contracts/pricing/SwapPricingUtils.sol b/contracts/pricing/SwapPricingUtils.sol index 4f3b335fc..6d94639ed 100644 --- a/contracts/pricing/SwapPricingUtils.sol +++ b/contracts/pricing/SwapPricingUtils.sol @@ -266,7 +266,7 @@ library SwapPricingUtils { bool balanceWasImproved, address uiFeeReceiver, ISwapPricingUtils.SwapPricingType swapPricingType - ) internal view returns (SwapFees memory) { + ) external view returns (SwapFees memory) { SwapFees memory fees; // note that since it is possible to incur both positive and negative price impact values @@ -310,7 +310,7 @@ library SwapPricingUtils { function emitSwapInfo( EventEmitter eventEmitter, EmitSwapInfoParams memory params - ) internal { + ) external { EventUtils.EventLogData memory eventData; eventData.bytes32Items.initItems(1); diff --git a/contracts/shift/ShiftEventUtils.sol b/contracts/shift/ShiftEventUtils.sol index 7f99c4294..3e6ce453e 100644 --- a/contracts/shift/ShiftEventUtils.sol +++ b/contracts/shift/ShiftEventUtils.sol @@ -24,30 +24,11 @@ library ShiftEventUtils { bytes32 key, Shift.Props memory shift ) external { - EventUtils.EventLogData memory eventData; - - eventData.addressItems.initItems(6); - eventData.addressItems.setItem(0, "account", shift.account()); - eventData.addressItems.setItem(1, "receiver", shift.receiver()); - eventData.addressItems.setItem(2, "callbackContract", shift.callbackContract()); - eventData.addressItems.setItem(3, "fromMarket", shift.fromMarket()); - eventData.addressItems.setItem(4, "toMarket", shift.toMarket()); - eventData.addressItems.setItem(5, "uiFeeReceiver", shift.uiFeeReceiver()); - - eventData.uintItems.initItems(6); - eventData.uintItems.setItem(0, "marketTokenAmount", shift.marketTokenAmount()); - eventData.uintItems.setItem(1, "minMarketTokens", shift.minMarketTokens()); - eventData.uintItems.setItem(2, "updatedAtTime", shift.updatedAtTime()); - eventData.uintItems.setItem(3, "executionFee", shift.executionFee()); - eventData.uintItems.setItem(4, "callbackGasLimit", shift.callbackGasLimit()); - eventData.uintItems.setItem(5, "srcChainId", shift.srcChainId()); + EventUtils.EventLogData memory eventData = createEventData(shift); eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); - eventData.bytes32Items.initArrayItems(1); - eventData.bytes32Items.setItem(0, "dataList", shift.dataList()); - eventEmitter.emitEventLog2( "ShiftCreated", key, @@ -109,4 +90,27 @@ library ShiftEventUtils { eventData ); } + + function createEventData(Shift.Props memory shift) public pure returns(EventUtils.EventLogData memory) { + EventUtils.EventLogData memory eventData; + + eventData.addressItems.initItems(6); + eventData.addressItems.setItem(0, "account", shift.account()); + eventData.addressItems.setItem(1, "receiver", shift.receiver()); + eventData.addressItems.setItem(2, "callbackContract", shift.callbackContract()); + eventData.addressItems.setItem(3, "fromMarket", shift.fromMarket()); + eventData.addressItems.setItem(4, "toMarket", shift.toMarket()); + eventData.addressItems.setItem(5, "uiFeeReceiver", shift.uiFeeReceiver()); + + eventData.uintItems.initItems(5); + eventData.uintItems.setItem(0, "marketTokenAmount", shift.marketTokenAmount()); + eventData.uintItems.setItem(1, "minMarketTokens", shift.minMarketTokens()); + eventData.uintItems.setItem(2, "updatedAtTime", shift.updatedAtTime()); + eventData.uintItems.setItem(3, "executionFee", shift.executionFee()); + eventData.uintItems.setItem(4, "callbackGasLimit", shift.callbackGasLimit()); + + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", shift.dataList()); + return eventData; + } } diff --git a/contracts/swap/SwapUtils.sol b/contracts/swap/SwapUtils.sol index b91398da9..2e8b681f4 100644 --- a/contracts/swap/SwapUtils.sol +++ b/contracts/swap/SwapUtils.sol @@ -177,7 +177,7 @@ library SwapUtils { address[] memory swapPath, address inputToken, address expectedOutputToken - ) internal view { + ) external view { address outputToken = getOutputToken(dataStore, swapPath, inputToken); if (outputToken != expectedOutputToken) { revert Errors.InvalidSwapOutputToken(outputToken, expectedOutputToken); @@ -188,7 +188,7 @@ library SwapUtils { DataStore dataStore, address[] memory swapPath, address inputToken - ) internal view returns (address) { + ) public view returns (address) { address outputToken = inputToken; Market.Props[] memory markets = MarketUtils.getSwapPathMarkets(dataStore, swapPath); uint256 marketCount = markets.length; diff --git a/contracts/withdrawal/WithdrawalEventUtils.sol b/contracts/withdrawal/WithdrawalEventUtils.sol index 99b0eb2d5..2d5cf3403 100644 --- a/contracts/withdrawal/WithdrawalEventUtils.sol +++ b/contracts/withdrawal/WithdrawalEventUtils.sol @@ -26,38 +26,11 @@ library WithdrawalEventUtils { Withdrawal.Props memory withdrawal, Withdrawal.WithdrawalType withdrawalType ) external { - EventUtils.EventLogData memory eventData; - - eventData.addressItems.initItems(5); - eventData.addressItems.setItem(0, "account", withdrawal.account()); - eventData.addressItems.setItem(1, "receiver", withdrawal.receiver()); - eventData.addressItems.setItem(2, "callbackContract", withdrawal.callbackContract()); - eventData.addressItems.setItem(3, "market", withdrawal.market()); - eventData.addressItems.setItem(4, "uiFeeReceiver", withdrawal.uiFeeReceiver()); - - eventData.addressItems.initArrayItems(2); - eventData.addressItems.setItem(0, "longTokenSwapPath", withdrawal.longTokenSwapPath()); - eventData.addressItems.setItem(1, "shortTokenSwapPath", withdrawal.shortTokenSwapPath()); - - eventData.uintItems.initItems(8); - eventData.uintItems.setItem(0, "marketTokenAmount", withdrawal.marketTokenAmount()); - eventData.uintItems.setItem(1, "minLongTokenAmount", withdrawal.minLongTokenAmount()); - eventData.uintItems.setItem(2, "minShortTokenAmount", withdrawal.minShortTokenAmount()); - eventData.uintItems.setItem(3, "updatedAtTime", withdrawal.updatedAtTime()); - eventData.uintItems.setItem(4, "executionFee", withdrawal.executionFee()); - eventData.uintItems.setItem(5, "callbackGasLimit", withdrawal.callbackGasLimit()); - eventData.uintItems.setItem(6, "withdrawalType", uint256(withdrawalType)); - eventData.uintItems.setItem(7, "srcChainId", withdrawal.srcChainId()); - - eventData.boolItems.initItems(1); - eventData.boolItems.setItem(0, "shouldUnwrapNativeToken", withdrawal.shouldUnwrapNativeToken()); + EventUtils.EventLogData memory eventData = createEventData(withdrawal, withdrawalType); eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); - eventData.bytes32Items.initArrayItems(1); - eventData.bytes32Items.setItem(0, "dataList", withdrawal.dataList()); - eventEmitter.emitEventLog2( "WithdrawalCreated", key, @@ -119,4 +92,36 @@ library WithdrawalEventUtils { eventData ); } + + function createEventData(Withdrawal.Props memory withdrawal, Withdrawal.WithdrawalType withdrawalType) + public pure returns(EventUtils.EventLogData memory) { + EventUtils.EventLogData memory eventData; + + eventData.addressItems.initItems(5); + eventData.addressItems.setItem(0, "account", withdrawal.account()); + eventData.addressItems.setItem(1, "receiver", withdrawal.receiver()); + eventData.addressItems.setItem(2, "callbackContract", withdrawal.callbackContract()); + eventData.addressItems.setItem(3, "market", withdrawal.market()); + eventData.addressItems.setItem(4, "uiFeeReceiver", withdrawal.uiFeeReceiver()); + + eventData.addressItems.initArrayItems(2); + eventData.addressItems.setItem(0, "longTokenSwapPath", withdrawal.longTokenSwapPath()); + eventData.addressItems.setItem(1, "shortTokenSwapPath", withdrawal.shortTokenSwapPath()); + + eventData.uintItems.initItems(7); + eventData.uintItems.setItem(0, "marketTokenAmount", withdrawal.marketTokenAmount()); + eventData.uintItems.setItem(1, "minLongTokenAmount", withdrawal.minLongTokenAmount()); + eventData.uintItems.setItem(2, "minShortTokenAmount", withdrawal.minShortTokenAmount()); + eventData.uintItems.setItem(3, "updatedAtTime", withdrawal.updatedAtTime()); + eventData.uintItems.setItem(4, "executionFee", withdrawal.executionFee()); + eventData.uintItems.setItem(5, "callbackGasLimit", withdrawal.callbackGasLimit()); + eventData.uintItems.setItem(6, "withdrawalType", uint256(withdrawalType)); + + eventData.boolItems.initItems(1); + eventData.boolItems.setItem(0, "shouldUnwrapNativeToken", withdrawal.shouldUnwrapNativeToken()); + + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", withdrawal.dataList()); + return eventData; + } } diff --git a/deploy/deployAdlHandler.ts b/deploy/deployAdlHandler.ts index d0f0603d5..f9d1f6507 100644 --- a/deploy/deployAdlHandler.ts +++ b/deploy/deployAdlHandler.ts @@ -25,6 +25,7 @@ const func = createDeployFunction({ "MarketStoreUtils", "PositionStoreUtils", "OrderStoreUtils", + "MarketUtils", ], afterDeploy: async ({ deployedContract }) => { await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); diff --git a/deploy/deployAdlUtils.ts b/deploy/deployAdlUtils.ts index 02ea41498..e16551e51 100644 --- a/deploy/deployAdlUtils.ts +++ b/deploy/deployAdlUtils.ts @@ -2,7 +2,7 @@ import { createDeployFunction } from "../utils/deploy"; const func = createDeployFunction({ contractName: "AdlUtils", - libraryNames: ["MarketStoreUtils", "PositionStoreUtils", "OrderStoreUtils", "OrderEventUtils"], + libraryNames: ["PositionStoreUtils", "OrderStoreUtils", "OrderEventUtils", "CallbackUtils", "MarketUtils"], }); export default func; diff --git a/deploy/deployCallbackUtils.ts b/deploy/deployCallbackUtils.ts index fbd601f5f..ee92b2e0f 100644 --- a/deploy/deployCallbackUtils.ts +++ b/deploy/deployCallbackUtils.ts @@ -2,7 +2,14 @@ import { createDeployFunction } from "../utils/deploy"; const func = createDeployFunction({ contractName: "CallbackUtils", - libraryNames: [], + libraryNames: [ + "GlvDepositEventUtils", + "GlvWithdrawalEventUtils", + "OrderEventUtils", + "WithdrawalEventUtils", + "DepositEventUtils", + "ShiftEventUtils", + ], }); export default func; diff --git a/deploy/deployDepositUtils.ts b/deploy/deployDepositUtils.ts index 2c05277c5..e08ec7d04 100644 --- a/deploy/deployDepositUtils.ts +++ b/deploy/deployDepositUtils.ts @@ -9,6 +9,9 @@ const func = createDeployFunction({ "MarketEventUtils", "DepositStoreUtils", "DepositEventUtils", + "ExecuteDepositUtils", + "CallbackUtils", + "MarketUtils", ], }); diff --git a/deploy/deployExecuteDepositUtils.ts b/deploy/deployExecuteDepositUtils.ts index 3b428b188..528504888 100644 --- a/deploy/deployExecuteDepositUtils.ts +++ b/deploy/deployExecuteDepositUtils.ts @@ -14,6 +14,7 @@ const func = createDeployFunction({ "SwapPricingUtils", "PositionUtils", "MultichainUtils", + "CallbackUtils", ], }); diff --git a/deploy/deployExecuteOrderUtils.ts b/deploy/deployExecuteOrderUtils.ts index aa0b7ae68..061db7f45 100644 --- a/deploy/deployExecuteOrderUtils.ts +++ b/deploy/deployExecuteOrderUtils.ts @@ -12,6 +12,7 @@ const func = createDeployFunction({ "SwapOrderUtils", "GasUtils", "PositionUtils", + "CallbackUtils", ], }); diff --git a/deploy/deployExecuteWithdrawalUtils.ts b/deploy/deployExecuteWithdrawalUtils.ts index b54360865..dbbbe678c 100644 --- a/deploy/deployExecuteWithdrawalUtils.ts +++ b/deploy/deployExecuteWithdrawalUtils.ts @@ -14,6 +14,7 @@ const func = createDeployFunction({ "SwapPricingUtils", "PositionUtils", "MultichainUtils", + "CallbackUtils", ], }); diff --git a/deploy/deployGlvDepositEventUtils.ts b/deploy/deployGlvDepositEventUtils.ts index 3304a0329..c0af4c568 100644 --- a/deploy/deployGlvDepositEventUtils.ts +++ b/deploy/deployGlvDepositEventUtils.ts @@ -2,6 +2,9 @@ import { createDeployFunction } from "../utils/deploy"; const func = createDeployFunction({ contractName: "GlvDepositEventUtils", + libraryNames: [ + // "GlvDepositMappingUtils", + ], }); export default func; diff --git a/deploy/deployGlvDepositUtils.ts b/deploy/deployGlvDepositUtils.ts index b1214e0a7..6d101d6f4 100644 --- a/deploy/deployGlvDepositUtils.ts +++ b/deploy/deployGlvDepositUtils.ts @@ -13,6 +13,7 @@ const func = createDeployFunction({ "GlvDepositCalc", "MarketStoreUtils", "MultichainUtils", + "CallbackUtils", ], }); diff --git a/deploy/deployGlvHandler.ts b/deploy/deployGlvHandler.ts index db9e93e86..747f3da0a 100644 --- a/deploy/deployGlvHandler.ts +++ b/deploy/deployGlvHandler.ts @@ -26,6 +26,7 @@ const func = createDeployFunction({ "GlvUtils", "GlvWithdrawalStoreUtils", "GlvWithdrawalUtils", + "GasUtils", ], afterDeploy: async ({ deployedContract }) => { await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); diff --git a/deploy/deployGlvWithdrawalUtils.ts b/deploy/deployGlvWithdrawalUtils.ts index f3635fea5..33eb0711f 100644 --- a/deploy/deployGlvWithdrawalUtils.ts +++ b/deploy/deployGlvWithdrawalUtils.ts @@ -12,6 +12,7 @@ const func = createDeployFunction({ "ExecuteWithdrawalUtils", "WithdrawalEventUtils", "MultichainUtils", + "CallbackUtils", ], }); diff --git a/deploy/deployLiquidationHandler.ts b/deploy/deployLiquidationHandler.ts index bb594680b..e4f7729cc 100644 --- a/deploy/deployLiquidationHandler.ts +++ b/deploy/deployLiquidationHandler.ts @@ -24,6 +24,7 @@ const func = createDeployFunction({ "MarketStoreUtils", "PositionStoreUtils", "OrderStoreUtils", + "MarketUtils", ], afterDeploy: async ({ deployedContract }) => { await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); diff --git a/deploy/deployLiquidationUtils.ts b/deploy/deployLiquidationUtils.ts index ab7a17dc8..c525a4132 100644 --- a/deploy/deployLiquidationUtils.ts +++ b/deploy/deployLiquidationUtils.ts @@ -2,7 +2,7 @@ import { createDeployFunction } from "../utils/deploy"; const func = createDeployFunction({ contractName: "LiquidationUtils", - libraryNames: ["PositionStoreUtils", "OrderStoreUtils", "OrderEventUtils"], + libraryNames: ["PositionStoreUtils", "OrderStoreUtils", "OrderEventUtils", "CallbackUtils"], }); export default func; diff --git a/deploy/deployOrderHandler.ts b/deploy/deployOrderHandler.ts index ecd13266b..f796b1dc7 100644 --- a/deploy/deployOrderHandler.ts +++ b/deploy/deployOrderHandler.ts @@ -17,7 +17,15 @@ const func = createDeployFunction({ getDeployArgs: async ({ dependencyContracts }) => { return constructorContracts.map((dependencyName) => dependencyContracts[dependencyName].address); }, - libraryNames: ["MarketStoreUtils", "OrderUtils", "ExecuteOrderUtils", "OrderStoreUtils", "OrderEventUtils"], + libraryNames: [ + "MarketStoreUtils", + "OrderUtils", + "ExecuteOrderUtils", + "OrderStoreUtils", + "OrderEventUtils", + "GasUtils", + "MarketUtils", + ], afterDeploy: async ({ deployedContract, getNamedAccounts, deployments, network }) => { const { deployer } = await getNamedAccounts(); const { execute } = deployments; diff --git a/deploy/deployOrderUtils.ts b/deploy/deployOrderUtils.ts index 6d94e62b6..c0362b3aa 100644 --- a/deploy/deployOrderUtils.ts +++ b/deploy/deployOrderUtils.ts @@ -2,17 +2,7 @@ import { createDeployFunction } from "../utils/deploy"; const func = createDeployFunction({ contractName: "OrderUtils", - libraryNames: [ - "Printer", - "MarketStoreUtils", - "MarketUtils", - "OrderStoreUtils", - "OrderEventUtils", - "IncreaseOrderUtils", - "DecreaseOrderUtils", - "SwapOrderUtils", - "GasUtils", - ], + libraryNames: ["MarketStoreUtils", "OrderStoreUtils", "OrderEventUtils", "GasUtils", "CallbackUtils", "MarketUtils"], }); export default func; diff --git a/deploy/deployReaderUtils.ts b/deploy/deployReaderUtils.ts index be2e9c139..6c1a63d1f 100644 --- a/deploy/deployReaderUtils.ts +++ b/deploy/deployReaderUtils.ts @@ -2,7 +2,7 @@ import { createDeployFunction } from "../utils/deploy"; const func = createDeployFunction({ contractName: "ReaderUtils", - libraryNames: ["MarketStoreUtils", "OrderStoreUtils", "ReaderPositionUtils"], + libraryNames: ["MarketStoreUtils", "OrderStoreUtils", "ReaderPositionUtils", "MarketUtils"], }); export default func; diff --git a/deploy/deployReaderWithdrawalUtils.ts b/deploy/deployReaderWithdrawalUtils.ts index f4c880181..6d4cb292e 100644 --- a/deploy/deployReaderWithdrawalUtils.ts +++ b/deploy/deployReaderWithdrawalUtils.ts @@ -2,7 +2,7 @@ import { createDeployFunction } from "../utils/deploy"; const func = createDeployFunction({ contractName: "ReaderWithdrawalUtils", - libraryNames: ["MarketUtils", "MarketStoreUtils", "PositionStoreUtils", "PositionUtils"], + libraryNames: ["MarketUtils", "MarketStoreUtils", "PositionStoreUtils", "PositionUtils", "SwapPricingUtils"], }); export default func; diff --git a/deploy/deployShiftUtils.ts b/deploy/deployShiftUtils.ts index 63bd440cc..cbc7e093e 100644 --- a/deploy/deployShiftUtils.ts +++ b/deploy/deployShiftUtils.ts @@ -12,6 +12,8 @@ const func = createDeployFunction({ "ExecuteDepositUtils", "ExecuteWithdrawalUtils", "MultichainUtils", + "CallbackUtils", + "MarketUtils", ], }); diff --git a/deploy/deploySwapUtils.ts b/deploy/deploySwapUtils.ts index 74aeaff69..ed88f7d8e 100644 --- a/deploy/deploySwapUtils.ts +++ b/deploy/deploySwapUtils.ts @@ -2,7 +2,7 @@ import { createDeployFunction } from "../utils/deploy"; const func = createDeployFunction({ contractName: "SwapUtils", - libraryNames: ["FeeUtils", "MarketEventUtils", "SwapPricingUtils"], + libraryNames: ["FeeUtils", "MarketEventUtils", "SwapPricingUtils", "MarketStoreUtils"], }); export default func; diff --git a/deploy/deployWithdrawalUtils.ts b/deploy/deployWithdrawalUtils.ts index 4f5e88c34..c60291e27 100644 --- a/deploy/deployWithdrawalUtils.ts +++ b/deploy/deployWithdrawalUtils.ts @@ -11,6 +11,7 @@ const func = createDeployFunction({ "WithdrawalStoreUtils", "WithdrawalEventUtils", "SwapUtils", + "CallbackUtils", ], }); diff --git a/test/migration/GlpMigrator.ts b/test/migration/GlpMigrator.ts index 35a2b6678..b7f72e8d4 100644 --- a/test/migration/GlpMigrator.ts +++ b/test/migration/GlpMigrator.ts @@ -22,6 +22,7 @@ describe("GlpMigrator", () => { depositHandler, externalHandler, marketStoreUtils, + marketUtils, stakedGlp, glpVault, glpTimelock, @@ -71,6 +72,7 @@ describe("GlpMigrator", () => { depositVault, depositHandler, marketStoreUtils, + marketUtils, ethUsdMarket, btcUsdMarket, wnt, @@ -100,7 +102,7 @@ describe("GlpMigrator", () => { ], { libraries: { - MarketStoreUtils: marketStoreUtils.address, + MarketUtils: marketUtils.address, }, } ); diff --git a/utils/fixture.ts b/utils/fixture.ts index a08855a16..082e1f46a 100644 --- a/utils/fixture.ts +++ b/utils/fixture.ts @@ -78,6 +78,7 @@ export async function deployFixture() { const glvFactory = await hre.ethers.getContract("GlvFactory"); const glvHandler = await hre.ethers.getContract("GlvHandler"); const glvRouter = await hre.ethers.getContract("GlvRouter"); + const callbackUtils = await hre.ethers.getContract("CallbackUtils"); const glvDepositStoreUtils = await hre.ethers.getContract("GlvDepositStoreUtils"); const GlvDepositCalc = await hre.ethers.getContract("GlvDepositCalc"); const glvWithdrawalStoreUtils = await hre.ethers.getContract("GlvWithdrawalStoreUtils"); @@ -335,6 +336,7 @@ export async function deployFixture() { multichainUtils, layerZeroProvider, mockStargatePool, + callbackUtils, }, props: { oracleSalt, signerIndexes: [0, 1, 2, 3, 4, 5, 6], executionFee: "1000000000000000" }, }; From 9b51da4e5987fd762a3d655f19fa627e5373941b Mon Sep 17 00:00:00 2001 From: Solar Date: Fri, 21 Feb 2025 10:50:00 +0300 Subject: [PATCH 090/205] squash - refactor callbacks to eventdata --- .gitignore | 2 + contracts/callback/CallbackUtils.sol | 91 +++++++++++++------ .../callback/IDepositCallbackReceiver.sol | 6 +- .../callback/IGlvDepositCallbackReceiver.sol | 5 +- .../IGlvWithdrawalCallbackReceiver.sol | 6 +- contracts/callback/IOrderCallbackReceiver.sol | 6 +- contracts/callback/IShiftCallbackReceiver.sol | 5 +- .../callback/IWithdrawalCallbackReceiver.sol | 5 +- contracts/deposit/DepositEventUtils.sol | 65 +++++++------ contracts/gas/GasUtils.sol | 28 +++--- .../glv/glvDeposit/GlvDepositEventUtils.sol | 68 +++++++------- .../glvWithdrawal/GlvWithdrawalEventUtils.sol | 60 ++++++------ contracts/market/MarketUtils.sol | 10 +- contracts/mock/MockCallbackReceiver.sol | 14 +-- contracts/mock/RevertingCallbackReceiver.sol | 4 +- contracts/order/OrderEventUtils.sol | 73 ++++++++------- contracts/pricing/SwapPricingUtils.sol | 4 +- contracts/shift/ShiftEventUtils.sol | 44 +++++---- contracts/swap/SwapUtils.sol | 4 +- contracts/withdrawal/WithdrawalEventUtils.sol | 61 +++++++------ deploy/deployAdlHandler.ts | 1 + deploy/deployAdlUtils.ts | 2 +- deploy/deployCallbackUtils.ts | 9 +- deploy/deployDepositUtils.ts | 3 + deploy/deployExecuteDepositUtils.ts | 1 + deploy/deployExecuteGlvDepositUtils.ts | 1 + deploy/deployExecuteOrderUtils.ts | 1 + deploy/deployExecuteWithdrawalUtils.ts | 1 + deploy/deployGlvDepositEventUtils.ts | 3 + deploy/deployGlvDepositUtils.ts | 1 + deploy/deployGlvHandler.ts | 1 + deploy/deployGlvWithdrawalUtils.ts | 1 + deploy/deployLiquidationHandler.ts | 1 + deploy/deployLiquidationUtils.ts | 2 +- deploy/deployMultichainGmRouter.ts | 11 ++- deploy/deployOrderHandler.ts | 10 +- deploy/deployOrderUtils.ts | 12 +-- deploy/deployReaderUtils.ts | 2 +- deploy/deployReaderWithdrawalUtils.ts | 2 +- deploy/deployShiftUtils.ts | 2 + deploy/deploySwapUtils.ts | 2 +- deploy/deployWithdrawalUtils.ts | 1 + test/migration/GlpMigrator.ts | 4 +- utils/fixture.ts | 2 + 44 files changed, 365 insertions(+), 272 deletions(-) diff --git a/.gitignore b/.gitignore index 6fdc08c5a..19d16f2ae 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ deployments/localhost deployments.txt *.log .cursorignore* + +.idea diff --git a/contracts/callback/CallbackUtils.sol b/contracts/callback/CallbackUtils.sol index aef333dc2..c66f1eda4 100644 --- a/contracts/callback/CallbackUtils.sol +++ b/contracts/callback/CallbackUtils.sol @@ -15,6 +15,13 @@ import "./IGasFeeCallbackReceiver.sol"; import "./IGlvDepositCallbackReceiver.sol"; import "./IGlvWithdrawalCallbackReceiver.sol"; +import "../order/OrderEventUtils.sol"; +import "../withdrawal/WithdrawalEventUtils.sol"; +import "../deposit/DepositEventUtils.sol"; +import "../shift/ShiftEventUtils.sol"; +import "../glv/glvDeposit/GlvDepositEventUtils.sol"; +import "../glv/glvWithdrawal/GlvWithdrawalEventUtils.sol"; + // @title CallbackUtils // @dev most features require a two step process to complete // the user first sends a request transaction, then a second transaction is sent @@ -64,7 +71,7 @@ library CallbackUtils { // executions to fail // @param dataStore DataStore // @param callbackGasLimit the callback gas limit - function validateCallbackGasLimit(DataStore dataStore, uint256 callbackGasLimit) internal view { + function validateCallbackGasLimit(DataStore dataStore, uint256 callbackGasLimit) external view { uint256 maxCallbackGasLimit = dataStore.getUint(Keys.MAX_CALLBACK_GAS_LIMIT); if (callbackGasLimit > maxCallbackGasLimit) { revert Errors.MaxCallbackGasLimitExceeded(callbackGasLimit, maxCallbackGasLimit); @@ -82,7 +89,7 @@ library CallbackUtils { dataStore.setAddress(Keys.savedCallbackContract(account, market), callbackContract); } - function getSavedCallbackContract(DataStore dataStore, address account, address market) internal view returns (address) { + function getSavedCallbackContract(DataStore dataStore, address account, address market) external view returns (address) { return dataStore.getAddress(Keys.savedCallbackContract(account, market)); } @@ -116,14 +123,16 @@ library CallbackUtils { bytes32 key, Deposit.Props memory deposit, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(deposit.callbackContract())) { return; } validateGasLeftForCallback(deposit.callbackGasLimit()); + EventUtils.EventLogData memory depositData = DepositEventUtils.createEventData(deposit, Deposit.DepositType.Normal); + try IDepositCallbackReceiver(deposit.callbackContract()).afterDepositExecution{ gas: deposit.callbackGasLimit() }( key, - deposit, + depositData, eventData ) { } catch { @@ -138,14 +147,16 @@ library CallbackUtils { bytes32 key, Deposit.Props memory deposit, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(deposit.callbackContract())) { return; } validateGasLeftForCallback(deposit.callbackGasLimit()); + EventUtils.EventLogData memory depositData = DepositEventUtils.createEventData(deposit, Deposit.DepositType.Normal); + try IDepositCallbackReceiver(deposit.callbackContract()).afterDepositCancellation{ gas: deposit.callbackGasLimit() }( key, - deposit, + depositData, eventData ) { } catch { @@ -160,14 +171,16 @@ library CallbackUtils { bytes32 key, Withdrawal.Props memory withdrawal, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(withdrawal.callbackContract())) { return; } validateGasLeftForCallback(withdrawal.callbackGasLimit()); + EventUtils.EventLogData memory withdrawalData = WithdrawalEventUtils.createEventData(withdrawal, Withdrawal.WithdrawalType.Normal); + try IWithdrawalCallbackReceiver(withdrawal.callbackContract()).afterWithdrawalExecution{ gas: withdrawal.callbackGasLimit() }( key, - withdrawal, + withdrawalData, eventData ) { } catch { @@ -182,14 +195,16 @@ library CallbackUtils { bytes32 key, Withdrawal.Props memory withdrawal, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(withdrawal.callbackContract())) { return; } validateGasLeftForCallback(withdrawal.callbackGasLimit()); + EventUtils.EventLogData memory withdrawalData = WithdrawalEventUtils.createEventData(withdrawal, Withdrawal.WithdrawalType.Normal); + try IWithdrawalCallbackReceiver(withdrawal.callbackContract()).afterWithdrawalCancellation{ gas: withdrawal.callbackGasLimit() }( key, - withdrawal, + withdrawalData, eventData ) { } catch { @@ -201,14 +216,16 @@ library CallbackUtils { bytes32 key, Shift.Props memory shift, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(shift.callbackContract())) { return; } validateGasLeftForCallback(shift.callbackGasLimit()); + EventUtils.EventLogData memory shiftData = ShiftEventUtils.createEventData(shift); + try IShiftCallbackReceiver(shift.callbackContract()).afterShiftExecution{ gas: shift.callbackGasLimit() }( key, - shift, + shiftData, eventData ) { } catch { @@ -219,14 +236,16 @@ library CallbackUtils { bytes32 key, Shift.Props memory shift, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(shift.callbackContract())) { return; } validateGasLeftForCallback(shift.callbackGasLimit()); + EventUtils.EventLogData memory shiftData = ShiftEventUtils.createEventData(shift); + try IShiftCallbackReceiver(shift.callbackContract()).afterShiftCancellation{ gas: shift.callbackGasLimit() }( key, - shift, + shiftData, eventData ) { } catch { @@ -244,14 +263,16 @@ library CallbackUtils { bytes32 key, Order.Props memory order, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(order.callbackContract())) { return; } validateGasLeftForCallback(order.callbackGasLimit()); + EventUtils.EventLogData memory orderData = OrderEventUtils.createEventData(order); + try IOrderCallbackReceiver(order.callbackContract()).afterOrderExecution{ gas: order.callbackGasLimit() }( key, - order, + orderData, eventData ) { } catch { @@ -266,14 +287,16 @@ library CallbackUtils { bytes32 key, Order.Props memory order, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(order.callbackContract())) { return; } validateGasLeftForCallback(order.callbackGasLimit()); + EventUtils.EventLogData memory orderData = OrderEventUtils.createEventData(order); + try IOrderCallbackReceiver(order.callbackContract()).afterOrderCancellation{ gas: order.callbackGasLimit() }( key, - order, + orderData, eventData ) { } catch { @@ -288,14 +311,16 @@ library CallbackUtils { bytes32 key, Order.Props memory order, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(order.callbackContract())) { return; } validateGasLeftForCallback(order.callbackGasLimit()); + EventUtils.EventLogData memory orderData = OrderEventUtils.createEventData(order); + try IOrderCallbackReceiver(order.callbackContract()).afterOrderFrozen{ gas: order.callbackGasLimit() }( key, - order, + orderData, eventData ) { } catch { @@ -310,16 +335,18 @@ library CallbackUtils { bytes32 key, GlvDeposit.Props memory glvDeposit, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(glvDeposit.callbackContract())) { return; } validateGasLeftForCallback(glvDeposit.callbackGasLimit()); + EventUtils.EventLogData memory glvData = GlvDepositEventUtils.createEventData(glvDeposit); + try IGlvDepositCallbackReceiver(glvDeposit.callbackContract()).afterGlvDepositExecution{ gas: glvDeposit.callbackGasLimit() }( key, - glvDeposit, + glvData, eventData ) { } catch { @@ -334,14 +361,16 @@ library CallbackUtils { bytes32 key, GlvDeposit.Props memory glvDeposit, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(glvDeposit.callbackContract())) { return; } validateGasLeftForCallback(glvDeposit.callbackGasLimit()); + EventUtils.EventLogData memory glvData = GlvDepositEventUtils.createEventData(glvDeposit); + try IGlvDepositCallbackReceiver(glvDeposit.callbackContract()).afterGlvDepositCancellation{ gas: glvDeposit.callbackGasLimit() }( key, - glvDeposit, + glvData, eventData ) { } catch { @@ -356,14 +385,16 @@ library CallbackUtils { bytes32 key, GlvWithdrawal.Props memory glvWithdrawal, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(glvWithdrawal.callbackContract())) { return; } validateGasLeftForCallback(glvWithdrawal.callbackGasLimit()); + EventUtils.EventLogData memory glvData = GlvWithdrawalEventUtils.createEventData(glvWithdrawal); + try IGlvWithdrawalCallbackReceiver(glvWithdrawal.callbackContract()).afterGlvWithdrawalExecution{ gas: glvWithdrawal.callbackGasLimit() }( key, - glvWithdrawal, + glvData, eventData ) { } catch { @@ -378,14 +409,16 @@ library CallbackUtils { bytes32 key, GlvWithdrawal.Props memory glvWithdrawal, EventUtils.EventLogData memory eventData - ) internal { + ) external { if (!isValidCallbackContract(glvWithdrawal.callbackContract())) { return; } validateGasLeftForCallback(glvWithdrawal.callbackGasLimit()); + EventUtils.EventLogData memory glvData = GlvWithdrawalEventUtils.createEventData(glvWithdrawal); + try IGlvWithdrawalCallbackReceiver(glvWithdrawal.callbackContract()).afterGlvWithdrawalCancellation{ gas: glvWithdrawal.callbackGasLimit() }( key, - glvWithdrawal, + glvData, eventData ) { } catch { @@ -395,7 +428,7 @@ library CallbackUtils { // @dev validates that the given address is a contract // @param callbackContract the contract to call - function isValidCallbackContract(address callbackContract) internal view returns (bool) { + function isValidCallbackContract(address callbackContract) public view returns (bool) { if (callbackContract == address(0)) { return false; } if (!callbackContract.isContract()) { return false; } diff --git a/contracts/callback/IDepositCallbackReceiver.sol b/contracts/callback/IDepositCallbackReceiver.sol index 8a3d4fbdb..4234591a7 100644 --- a/contracts/callback/IDepositCallbackReceiver.sol +++ b/contracts/callback/IDepositCallbackReceiver.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; import "../event/EventUtils.sol"; -import "../deposit/Deposit.sol"; + // @title IDepositCallbackReceiver // @dev interface for a deposit callback contract @@ -11,10 +11,10 @@ interface IDepositCallbackReceiver { // @dev called after a deposit execution // @param key the key of the deposit // @param deposit the deposit that was executed - function afterDepositExecution(bytes32 key, Deposit.Props memory deposit, EventUtils.EventLogData memory eventData) external; + function afterDepositExecution(bytes32 key, EventUtils.EventLogData memory depositData, EventUtils.EventLogData memory eventData) external; // @dev called after a deposit cancellation // @param key the key of the deposit // @param deposit the deposit that was cancelled - function afterDepositCancellation(bytes32 key, Deposit.Props memory deposit, EventUtils.EventLogData memory eventData) external; + function afterDepositCancellation(bytes32 key, EventUtils.EventLogData memory depositData, EventUtils.EventLogData memory eventData) external; } diff --git a/contracts/callback/IGlvDepositCallbackReceiver.sol b/contracts/callback/IGlvDepositCallbackReceiver.sol index ef8c37653..310a221dd 100644 --- a/contracts/callback/IGlvDepositCallbackReceiver.sol +++ b/contracts/callback/IGlvDepositCallbackReceiver.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.0; import "../event/EventUtils.sol"; -import "../glv/glvDeposit/GlvDeposit.sol"; // @title IGlvDepositCallbackReceiver // @dev interface for a glvDeposit callback contract @@ -13,7 +12,7 @@ interface IGlvDepositCallbackReceiver { // @param glvDeposit the glvDeposit that was executed function afterGlvDepositExecution( bytes32 key, - GlvDeposit.Props memory glvDeposit, + EventUtils.EventLogData memory glvDepositData, EventUtils.EventLogData memory eventData ) external; @@ -22,7 +21,7 @@ interface IGlvDepositCallbackReceiver { // @param glvDeposit the glvDeposit that was cancelled function afterGlvDepositCancellation( bytes32 key, - GlvDeposit.Props memory glvDeposit, + EventUtils.EventLogData memory glvDepositData, EventUtils.EventLogData memory eventData ) external; } diff --git a/contracts/callback/IGlvWithdrawalCallbackReceiver.sol b/contracts/callback/IGlvWithdrawalCallbackReceiver.sol index 0ea514790..576450b13 100644 --- a/contracts/callback/IGlvWithdrawalCallbackReceiver.sol +++ b/contracts/callback/IGlvWithdrawalCallbackReceiver.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; import "../event/EventUtils.sol"; -import "../glv/glvWithdrawal/GlvWithdrawal.sol"; + // @title IGlvWithdrawalCallbackReceiver // @dev interface for a glvWithdrawal callback contract @@ -13,7 +13,7 @@ interface IGlvWithdrawalCallbackReceiver { // @param glvWithdrawal the glvWithdrawal that was executed function afterGlvWithdrawalExecution( bytes32 key, - GlvWithdrawal.Props memory glvWithdrawal, + EventUtils.EventLogData memory glvWithdrawalData, EventUtils.EventLogData memory eventData ) external; @@ -22,7 +22,7 @@ interface IGlvWithdrawalCallbackReceiver { // @param glvWithdrawal the glvWithdrawal that was cancelled function afterGlvWithdrawalCancellation( bytes32 key, - GlvWithdrawal.Props memory glvWithdrawal, + EventUtils.EventLogData memory glvWithdrawalData, EventUtils.EventLogData memory eventData ) external; } diff --git a/contracts/callback/IOrderCallbackReceiver.sol b/contracts/callback/IOrderCallbackReceiver.sol index d1c331d79..b78727784 100644 --- a/contracts/callback/IOrderCallbackReceiver.sol +++ b/contracts/callback/IOrderCallbackReceiver.sol @@ -11,15 +11,15 @@ interface IOrderCallbackReceiver { // @dev called after an order execution // @param key the key of the order // @param order the order that was executed - function afterOrderExecution(bytes32 key, Order.Props memory order, EventUtils.EventLogData memory eventData) external; + function afterOrderExecution(bytes32 key, EventUtils.EventLogData memory orderData, EventUtils.EventLogData memory eventData) external; // @dev called after an order cancellation // @param key the key of the order // @param order the order that was cancelled - function afterOrderCancellation(bytes32 key, Order.Props memory order, EventUtils.EventLogData memory eventData) external; + function afterOrderCancellation(bytes32 key, EventUtils.EventLogData memory order, EventUtils.EventLogData memory eventData) external; // @dev called after an order has been frozen, see OrderUtils.freezeOrder in OrderHandler for more info // @param key the key of the order // @param order the order that was frozen - function afterOrderFrozen(bytes32 key, Order.Props memory order, EventUtils.EventLogData memory eventData) external; + function afterOrderFrozen(bytes32 key, EventUtils.EventLogData memory order, EventUtils.EventLogData memory eventData) external; } diff --git a/contracts/callback/IShiftCallbackReceiver.sol b/contracts/callback/IShiftCallbackReceiver.sol index 92b1acbc1..5b6f5a399 100644 --- a/contracts/callback/IShiftCallbackReceiver.sol +++ b/contracts/callback/IShiftCallbackReceiver.sol @@ -3,9 +3,8 @@ pragma solidity ^0.8.0; import "../event/EventUtils.sol"; -import "../shift/Shift.sol"; interface IShiftCallbackReceiver { - function afterShiftExecution(bytes32 key, Shift.Props memory shift, EventUtils.EventLogData memory eventData) external; - function afterShiftCancellation(bytes32 key, Shift.Props memory shift, EventUtils.EventLogData memory eventData) external; + function afterShiftExecution(bytes32 key, EventUtils.EventLogData memory shiftData, EventUtils.EventLogData memory eventData) external; + function afterShiftCancellation(bytes32 key, EventUtils.EventLogData memory shiftData, EventUtils.EventLogData memory eventData) external; } diff --git a/contracts/callback/IWithdrawalCallbackReceiver.sol b/contracts/callback/IWithdrawalCallbackReceiver.sol index c2250ebdb..2e9dd90e9 100644 --- a/contracts/callback/IWithdrawalCallbackReceiver.sol +++ b/contracts/callback/IWithdrawalCallbackReceiver.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.0; import "../event/EventUtils.sol"; -import "../withdrawal/Withdrawal.sol"; // @title IWithdrawalCallbackReceiver // @dev interface for a withdrawal callback contract @@ -11,10 +10,10 @@ interface IWithdrawalCallbackReceiver { // @dev called after a withdrawal execution // @param key the key of the withdrawal // @param withdrawal the withdrawal that was executed - function afterWithdrawalExecution(bytes32 key, Withdrawal.Props memory withdrawal, EventUtils.EventLogData memory eventData) external; + function afterWithdrawalExecution(bytes32 key, EventUtils.EventLogData memory withdrawal, EventUtils.EventLogData memory eventData) external; // @dev called after a withdrawal cancellation // @param key the key of the withdrawal // @param withdrawal the withdrawal that was cancelled - function afterWithdrawalCancellation(bytes32 key, Withdrawal.Props memory withdrawal, EventUtils.EventLogData memory eventData) external; + function afterWithdrawalCancellation(bytes32 key, EventUtils.EventLogData memory withdrawal, EventUtils.EventLogData memory eventData) external; } diff --git a/contracts/deposit/DepositEventUtils.sol b/contracts/deposit/DepositEventUtils.sol index 0d202af05..19d4ee4f8 100644 --- a/contracts/deposit/DepositEventUtils.sol +++ b/contracts/deposit/DepositEventUtils.sol @@ -26,40 +26,11 @@ library DepositEventUtils { Deposit.Props memory deposit, Deposit.DepositType depositType ) external { - EventUtils.EventLogData memory eventData; - - eventData.addressItems.initItems(7); - eventData.addressItems.setItem(0, "account", deposit.account()); - eventData.addressItems.setItem(1, "receiver", deposit.receiver()); - eventData.addressItems.setItem(2, "callbackContract", deposit.callbackContract()); - eventData.addressItems.setItem(3, "market", deposit.market()); - eventData.addressItems.setItem(4, "initialLongToken", deposit.initialLongToken()); - eventData.addressItems.setItem(5, "initialShortToken", deposit.initialShortToken()); - eventData.addressItems.setItem(6, "uiFeeReceiver", deposit.uiFeeReceiver()); - - eventData.addressItems.initArrayItems(2); - eventData.addressItems.setItem(0, "longTokenSwapPath", deposit.longTokenSwapPath()); - eventData.addressItems.setItem(1, "shortTokenSwapPath", deposit.shortTokenSwapPath()); - - eventData.uintItems.initItems(8); - eventData.uintItems.setItem(0, "initialLongTokenAmount", deposit.initialLongTokenAmount()); - eventData.uintItems.setItem(1, "initialShortTokenAmount", deposit.initialShortTokenAmount()); - eventData.uintItems.setItem(2, "minMarketTokens", deposit.minMarketTokens()); - eventData.uintItems.setItem(3, "updatedAtTime", deposit.updatedAtTime()); - eventData.uintItems.setItem(4, "executionFee", deposit.executionFee()); - eventData.uintItems.setItem(5, "callbackGasLimit", deposit.callbackGasLimit()); - eventData.uintItems.setItem(6, "depositType", uint256(depositType)); - eventData.uintItems.setItem(7, "srcChainId", deposit.srcChainId()); - - eventData.boolItems.initItems(1); - eventData.boolItems.setItem(0, "shouldUnwrapNativeToken", deposit.shouldUnwrapNativeToken()); + EventUtils.EventLogData memory eventData = createEventData(deposit, depositType); eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); - eventData.bytes32Items.initArrayItems(1); - eventData.bytes32Items.setItem(0, "dataList", deposit.dataList()); - eventEmitter.emitEventLog2( "DepositCreated", key, @@ -127,4 +98,38 @@ library DepositEventUtils { eventData ); } + + function createEventData(Deposit.Props memory deposit, Deposit.DepositType depositType) + public pure returns (EventUtils.EventLogData memory) { + EventUtils.EventLogData memory eventData; + + eventData.addressItems.initItems(7); + eventData.addressItems.setItem(0, "account", deposit.account()); + eventData.addressItems.setItem(1, "receiver", deposit.receiver()); + eventData.addressItems.setItem(2, "callbackContract", deposit.callbackContract()); + eventData.addressItems.setItem(3, "market", deposit.market()); + eventData.addressItems.setItem(4, "initialLongToken", deposit.initialLongToken()); + eventData.addressItems.setItem(5, "initialShortToken", deposit.initialShortToken()); + eventData.addressItems.setItem(6, "uiFeeReceiver", deposit.uiFeeReceiver()); + + eventData.addressItems.initArrayItems(2); + eventData.addressItems.setItem(0, "longTokenSwapPath", deposit.longTokenSwapPath()); + eventData.addressItems.setItem(1, "shortTokenSwapPath", deposit.shortTokenSwapPath()); + + eventData.uintItems.initItems(7); + eventData.uintItems.setItem(0, "initialLongTokenAmount", deposit.initialLongTokenAmount()); + eventData.uintItems.setItem(1, "initialShortTokenAmount", deposit.initialShortTokenAmount()); + eventData.uintItems.setItem(2, "minMarketTokens", deposit.minMarketTokens()); + eventData.uintItems.setItem(3, "updatedAtTime", deposit.updatedAtTime()); + eventData.uintItems.setItem(4, "executionFee", deposit.executionFee()); + eventData.uintItems.setItem(5, "callbackGasLimit", deposit.callbackGasLimit()); + eventData.uintItems.setItem(6, "depositType", uint256(depositType)); + + eventData.boolItems.initItems(1); + eventData.boolItems.setItem(0, "shouldUnwrapNativeToken", deposit.shouldUnwrapNativeToken()); + + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", deposit.dataList()); + return eventData; + } } diff --git a/contracts/gas/GasUtils.sol b/contracts/gas/GasUtils.sol index edab84630..6253ffb44 100644 --- a/contracts/gas/GasUtils.sol +++ b/contracts/gas/GasUtils.sol @@ -54,8 +54,8 @@ library GasUtils { return dataStore.getUint(Keys.MIN_ADDITIONAL_GAS_FOR_EXECUTION); } - function getExecutionGas(DataStore dataStore, uint256 startingGas) internal view returns (uint256) { - uint256 minHandleExecutionErrorGasToForward = GasUtils.getMinHandleExecutionErrorGasToForward(dataStore); + function getExecutionGas(DataStore dataStore, uint256 startingGas) external view returns (uint256) { + uint256 minHandleExecutionErrorGasToForward = getMinHandleExecutionErrorGasToForward(dataStore); if (startingGas < minHandleExecutionErrorGasToForward) { revert Errors.InsufficientExecutionGasForErrorHandling(startingGas, minHandleExecutionErrorGasToForward); } @@ -63,7 +63,7 @@ library GasUtils { return startingGas - minHandleExecutionErrorGasToForward; } - function validateExecutionGas(DataStore dataStore, uint256 startingGas, uint256 estimatedGasLimit) internal view { + function validateExecutionGas(DataStore dataStore, uint256 startingGas, uint256 estimatedGasLimit) external view { uint256 minAdditionalGasForExecution = getMinAdditionalGasForExecution(dataStore); if (startingGas < estimatedGasLimit + minAdditionalGasForExecution) { revert Errors.InsufficientExecutionGas(startingGas, estimatedGasLimit, minAdditionalGasForExecution); @@ -81,7 +81,7 @@ library GasUtils { // // a malicious user could cause the estimateGas call of a keeper to fail, in which case the keeper could // still attempt to execute the transaction with a reasonable gas limit - function validateExecutionErrorGas(DataStore dataStore, bytes memory reasonBytes) internal view { + function validateExecutionErrorGas(DataStore dataStore, bytes memory reasonBytes) external view { // skip the validation if the execution did not fail due to an out of gas error // also skip the validation if this is not invoked in an estimateGas call (tx.origin != address(0)) if (reasonBytes.length != 0 || tx.origin != address(0)) { @@ -182,7 +182,7 @@ library GasUtils { uint256 estimatedGasLimit, uint256 executionFee, uint256 oraclePriceCount - ) internal view { + ) external view { validateExecutionFee(dataStore, estimatedGasLimit, executionFee, oraclePriceCount, false); } @@ -273,18 +273,18 @@ library GasUtils { // @dev get estimated number of oracle prices for withdrawal // @param swapsCount number of swaps in the withdrawal - function estimateWithdrawalOraclePriceCount(uint256 swapsCount) internal pure returns (uint256) { + function estimateWithdrawalOraclePriceCount(uint256 swapsCount) external pure returns (uint256) { return 3 + swapsCount; } // @dev get estimated number of oracle prices for order // @param swapsCount number of swaps in the order - function estimateOrderOraclePriceCount(uint256 swapsCount) internal pure returns (uint256) { + function estimateOrderOraclePriceCount(uint256 swapsCount) external pure returns (uint256) { return 3 + swapsCount; } // @dev get estimated number of oracle prices for shift - function estimateShiftOraclePriceCount() internal pure returns (uint256) { + function estimateShiftOraclePriceCount() external pure returns (uint256) { // for single asset markets only 3 prices will be required // and keeper will slightly overpay // it should not be an issue because execution fee goes back to keeper @@ -294,7 +294,7 @@ library GasUtils { function estimateGlvDepositOraclePriceCount( uint256 marketCount, uint256 swapsCount - ) internal pure returns (uint256) { + ) external pure returns (uint256) { // for single asset markets oracle price count will be overestimated by 1 // it should not be an issue for GLV with multiple markets // because relative difference would be insignificant @@ -331,7 +331,7 @@ library GasUtils { function estimateExecuteWithdrawalGasLimit( DataStore dataStore, Withdrawal.Props memory withdrawal - ) internal view returns (uint256) { + ) external view returns (uint256) { uint256 gasPerSwap = dataStore.getUint(Keys.singleSwapGasLimitKey()); uint256 swapCount = withdrawal.longTokenSwapPath().length + withdrawal.shortTokenSwapPath().length; uint256 gasForSwaps = swapCount * gasPerSwap; @@ -345,7 +345,7 @@ library GasUtils { function estimateExecuteShiftGasLimit( DataStore dataStore, Shift.Props memory shift - ) internal view returns (uint256) { + ) external view returns (uint256) { return dataStore.getUint(Keys.shiftGasLimitKey()) + shift.callbackGasLimit(); } @@ -355,7 +355,7 @@ library GasUtils { function estimateExecuteOrderGasLimit( DataStore dataStore, Order.Props memory order - ) internal view returns (uint256) { + ) external view returns (uint256) { if (BaseOrderUtils.isIncreaseOrder(order.orderType())) { return estimateExecuteIncreaseOrderGasLimit(dataStore, order); } @@ -424,7 +424,7 @@ library GasUtils { DataStore dataStore, GlvDeposit.Props memory glvDeposit, uint256 marketCount - ) internal view returns (uint256) { + ) external view returns (uint256) { // glv deposit execution gas consumption depends on the amount of markets uint256 gasPerGlvPerMarket = dataStore.getUint(Keys.glvPerMarketGasLimitKey()); uint256 gasForGlvMarkets = gasPerGlvPerMarket * marketCount; @@ -466,7 +466,7 @@ library GasUtils { return gasLimit + dataStore.getUint(Keys.withdrawalGasLimitKey()) + gasForSwaps; } - function estimateExecuteGlvShiftGasLimit(DataStore dataStore) internal view returns (uint256) { + function estimateExecuteGlvShiftGasLimit(DataStore dataStore) external view returns (uint256) { return dataStore.getUint(Keys.glvShiftGasLimitKey()); } diff --git a/contracts/glv/glvDeposit/GlvDepositEventUtils.sol b/contracts/glv/glvDeposit/GlvDepositEventUtils.sol index 78483fa78..eb97405d9 100644 --- a/contracts/glv/glvDeposit/GlvDepositEventUtils.sol +++ b/contracts/glv/glvDeposit/GlvDepositEventUtils.sol @@ -24,42 +24,11 @@ library GlvDepositEventUtils { bytes32 key, GlvDeposit.Props memory glvDeposit ) external { - EventUtils.EventLogData memory eventData; - - eventData.addressItems.initItems(8); - eventData.addressItems.setItem(0, "account", glvDeposit.account()); - eventData.addressItems.setItem(1, "receiver", glvDeposit.receiver()); - eventData.addressItems.setItem(2, "callbackContract", glvDeposit.callbackContract()); - eventData.addressItems.setItem(3, "market", glvDeposit.market()); - eventData.addressItems.setItem(4, "glv", glvDeposit.glv()); - eventData.addressItems.setItem(5, "initialLongToken", glvDeposit.initialLongToken()); - eventData.addressItems.setItem(6, "initialShortToken", glvDeposit.initialShortToken()); - eventData.addressItems.setItem(7, "uiFeeReceiver", glvDeposit.uiFeeReceiver()); - - eventData.addressItems.initArrayItems(2); - eventData.addressItems.setItem(0, "longTokenSwapPath", glvDeposit.longTokenSwapPath()); - eventData.addressItems.setItem(1, "shortTokenSwapPath", glvDeposit.shortTokenSwapPath()); - - eventData.uintItems.initItems(8); - eventData.uintItems.setItem(0, "initialLongTokenAmount", glvDeposit.initialLongTokenAmount()); - eventData.uintItems.setItem(1, "initialShortTokenAmount", glvDeposit.initialShortTokenAmount()); - eventData.uintItems.setItem(2, "minGlvTokens", glvDeposit.minGlvTokens()); - eventData.uintItems.setItem(3, "updatedAtTime", glvDeposit.updatedAtTime()); - eventData.uintItems.setItem(4, "executionFee", glvDeposit.executionFee()); - eventData.uintItems.setItem(5, "callbackGasLimit", glvDeposit.callbackGasLimit()); - eventData.uintItems.setItem(6, "marketTokenAmount", glvDeposit.marketTokenAmount()); - eventData.uintItems.setItem(7, "srcChainId", glvDeposit.srcChainId()); - - eventData.boolItems.initItems(2); - eventData.boolItems.setItem(0, "shouldUnwrapNativeToken", glvDeposit.shouldUnwrapNativeToken()); - eventData.boolItems.setItem(1, "isMarketTokenDeposit", glvDeposit.isMarketTokenDeposit()); + EventUtils.EventLogData memory eventData = createEventData(glvDeposit); eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); - eventData.bytes32Items.initArrayItems(1); - eventData.bytes32Items.setItem(0, "dataList", glvDeposit.dataList()); - eventEmitter.emitEventLog2( "GlvDepositCreated", key, @@ -121,4 +90,39 @@ library GlvDepositEventUtils { eventData ); } + + function createEventData(GlvDeposit.Props memory glvDeposit) public pure + returns (EventUtils.EventLogData memory) { + EventUtils.EventLogData memory eventData; + eventData.addressItems.initItems(8); + eventData.addressItems.setItem(0, "account", glvDeposit.account()); + eventData.addressItems.setItem(1, "receiver", glvDeposit.receiver()); + eventData.addressItems.setItem(2, "callbackContract", glvDeposit.callbackContract()); + eventData.addressItems.setItem(3, "market", glvDeposit.market()); + eventData.addressItems.setItem(4, "glv", glvDeposit.glv()); + eventData.addressItems.setItem(5, "initialLongToken", glvDeposit.initialLongToken()); + eventData.addressItems.setItem(6, "initialShortToken", glvDeposit.initialShortToken()); + eventData.addressItems.setItem(7, "uiFeeReceiver", glvDeposit.uiFeeReceiver()); + + eventData.addressItems.initArrayItems(2); + eventData.addressItems.setItem(0, "longTokenSwapPath", glvDeposit.longTokenSwapPath()); + eventData.addressItems.setItem(1, "shortTokenSwapPath", glvDeposit.shortTokenSwapPath()); + + eventData.uintItems.initItems(7); + eventData.uintItems.setItem(0, "initialLongTokenAmount", glvDeposit.initialLongTokenAmount()); + eventData.uintItems.setItem(1, "initialShortTokenAmount", glvDeposit.initialShortTokenAmount()); + eventData.uintItems.setItem(2, "minGlvTokens", glvDeposit.minGlvTokens()); + eventData.uintItems.setItem(3, "updatedAtTime", glvDeposit.updatedAtTime()); + eventData.uintItems.setItem(4, "executionFee", glvDeposit.executionFee()); + eventData.uintItems.setItem(5, "callbackGasLimit", glvDeposit.callbackGasLimit()); + eventData.uintItems.setItem(6, "marketTokenAmount", glvDeposit.marketTokenAmount()); + + eventData.boolItems.initItems(2); + eventData.boolItems.setItem(0, "shouldUnwrapNativeToken", glvDeposit.shouldUnwrapNativeToken()); + eventData.boolItems.setItem(1, "isMarketTokenDeposit", glvDeposit.isMarketTokenDeposit()); + + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", glvDeposit.dataList()); + return eventData; + } } diff --git a/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol b/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol index dc94afb1b..1f15e22c6 100644 --- a/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol +++ b/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol @@ -24,38 +24,11 @@ library GlvWithdrawalEventUtils { bytes32 key, GlvWithdrawal.Props memory glvWithdrawal ) external { - EventUtils.EventLogData memory eventData; - - eventData.addressItems.initItems(6); - eventData.addressItems.setItem(0, "account", glvWithdrawal.account()); - eventData.addressItems.setItem(1, "receiver", glvWithdrawal.receiver()); - eventData.addressItems.setItem(2, "callbackContract", glvWithdrawal.callbackContract()); - eventData.addressItems.setItem(3, "market", glvWithdrawal.market()); - eventData.addressItems.setItem(4, "glv", glvWithdrawal.glv()); - eventData.addressItems.setItem(5, "uiFeeReceiver", glvWithdrawal.uiFeeReceiver()); - - eventData.addressItems.initArrayItems(2); - eventData.addressItems.setItem(0, "longTokenSwapPath", glvWithdrawal.longTokenSwapPath()); - eventData.addressItems.setItem(1, "shortTokenSwapPath", glvWithdrawal.shortTokenSwapPath()); - - eventData.uintItems.initItems(7); - eventData.uintItems.setItem(0, "glvTokenAmount", glvWithdrawal.glvTokenAmount()); - eventData.uintItems.setItem(1, "minLongTokenAmount", glvWithdrawal.minLongTokenAmount()); - eventData.uintItems.setItem(2, "minShortTokenAmount", glvWithdrawal.minShortTokenAmount()); - eventData.uintItems.setItem(3, "updatedAtTime", glvWithdrawal.updatedAtTime()); - eventData.uintItems.setItem(4, "executionFee", glvWithdrawal.executionFee()); - eventData.uintItems.setItem(5, "callbackGasLimit", glvWithdrawal.callbackGasLimit()); - eventData.uintItems.setItem(6, "srcChainId", glvWithdrawal.srcChainId()); - - eventData.boolItems.initItems(1); - eventData.boolItems.setItem(0, "shouldUnwrapNativeToken", glvWithdrawal.shouldUnwrapNativeToken()); + EventUtils.EventLogData memory eventData = createEventData(glvWithdrawal); eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); - eventData.bytes32Items.initArrayItems(1); - eventData.bytes32Items.setItem(0, "dataList", glvWithdrawal.dataList()); - eventEmitter.emitEventLog2("GlvWithdrawalCreated", key, Cast.toBytes32(glvWithdrawal.account()), eventData); } @@ -94,4 +67,35 @@ library GlvWithdrawalEventUtils { eventEmitter.emitEventLog2("GlvWithdrawalCancelled", key, Cast.toBytes32(account), eventData); } + + function createEventData(GlvWithdrawal.Props memory glvWithdrawal) public pure returns (EventUtils.EventLogData memory) { + EventUtils.EventLogData memory eventData; + + eventData.addressItems.initItems(6); + eventData.addressItems.setItem(0, "account", glvWithdrawal.account()); + eventData.addressItems.setItem(1, "receiver", glvWithdrawal.receiver()); + eventData.addressItems.setItem(2, "callbackContract", glvWithdrawal.callbackContract()); + eventData.addressItems.setItem(3, "market", glvWithdrawal.market()); + eventData.addressItems.setItem(4, "glv", glvWithdrawal.glv()); + eventData.addressItems.setItem(5, "uiFeeReceiver", glvWithdrawal.uiFeeReceiver()); + + eventData.addressItems.initArrayItems(2); + eventData.addressItems.setItem(0, "longTokenSwapPath", glvWithdrawal.longTokenSwapPath()); + eventData.addressItems.setItem(1, "shortTokenSwapPath", glvWithdrawal.shortTokenSwapPath()); + + eventData.uintItems.initItems(6); + eventData.uintItems.setItem(0, "glvTokenAmount", glvWithdrawal.glvTokenAmount()); + eventData.uintItems.setItem(1, "minLongTokenAmount", glvWithdrawal.minLongTokenAmount()); + eventData.uintItems.setItem(2, "minShortTokenAmount", glvWithdrawal.minShortTokenAmount()); + eventData.uintItems.setItem(3, "updatedAtTime", glvWithdrawal.updatedAtTime()); + eventData.uintItems.setItem(4, "executionFee", glvWithdrawal.executionFee()); + eventData.uintItems.setItem(5, "callbackGasLimit", glvWithdrawal.callbackGasLimit()); + + eventData.boolItems.initItems(1); + eventData.boolItems.setItem(0, "shouldUnwrapNativeToken", glvWithdrawal.shouldUnwrapNativeToken()); + + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", glvWithdrawal.dataList()); + return eventData; + } } diff --git a/contracts/market/MarketUtils.sol b/contracts/market/MarketUtils.sol index 8897c7834..74928be39 100644 --- a/contracts/market/MarketUtils.sol +++ b/contracts/market/MarketUtils.sol @@ -169,7 +169,7 @@ library MarketUtils { // @dev get the total supply of the marketToken // @param marketToken the marketToken // @return the total supply of the marketToken - function getMarketTokenSupply(MarketToken marketToken) internal view returns (uint256) { + function getMarketTokenSupply(MarketToken marketToken) public view returns (uint256) { return marketToken.totalSupply(); } @@ -2724,7 +2724,7 @@ library MarketUtils { // @dev validate that the specified market exists and is enabled // @param dataStore DataStore // @param marketAddress the address of the market - function validateEnabledMarket(DataStore dataStore, address marketAddress) internal view { + function validateEnabledMarket(DataStore dataStore, address marketAddress) external view { Market.Props memory market = MarketStoreUtils.get(dataStore, marketAddress); validateEnabledMarket(dataStore, market); } @@ -2732,7 +2732,7 @@ library MarketUtils { // @dev validate that the specified market exists and is enabled // @param dataStore DataStore // @param market the market to check - function validateEnabledMarket(DataStore dataStore, Market.Props memory market) internal view { + function validateEnabledMarket(DataStore dataStore, Market.Props memory market) public view { if (market.marketToken == address(0)) { revert Errors.EmptyMarket(); } @@ -2783,7 +2783,7 @@ library MarketUtils { // @dev get the enabled market, revert if the market does not exist or is not enabled // @param dataStore DataStore // @param marketAddress the address of the market - function getEnabledMarket(DataStore dataStore, address marketAddress) internal view returns (Market.Props memory) { + function getEnabledMarket(DataStore dataStore, address marketAddress) public view returns (Market.Props memory) { Market.Props memory market = MarketStoreUtils.get(dataStore, marketAddress); validateEnabledMarket(dataStore, market); return market; @@ -2808,7 +2808,7 @@ library MarketUtils { return markets; } - function validateSwapPath(DataStore dataStore, address[] memory swapPath) internal view { + function validateSwapPath(DataStore dataStore, address[] memory swapPath) external view { uint256 maxSwapPathLength = dataStore.getUint(Keys.MAX_SWAP_PATH_LENGTH); if (swapPath.length > maxSwapPathLength) { revert Errors.MaxSwapPathLengthExceeded(swapPath.length, maxSwapPathLength); diff --git a/contracts/mock/MockCallbackReceiver.sol b/contracts/mock/MockCallbackReceiver.sol index b34dacdb8..5cd272cc6 100644 --- a/contracts/mock/MockCallbackReceiver.sol +++ b/contracts/mock/MockCallbackReceiver.sol @@ -15,15 +15,15 @@ contract MockCallbackReceiver is IOrderCallbackReceiver, IGasFeeCallbackReceiver uint public glvWithdrawalExecutionCalled; uint public glvWithdrawalCancellationCalled; - function afterOrderExecution(bytes32 /* key */, Order.Props memory /* order */, EventUtils.EventLogData memory /* eventData */) external { + function afterOrderExecution(bytes32 /* key */, EventUtils.EventLogData memory /* order */, EventUtils.EventLogData memory /* eventData */) external { ++called; } - function afterOrderCancellation(bytes32 /* key */, Order.Props memory /* order */, EventUtils.EventLogData memory /* eventData */) external { + function afterOrderCancellation(bytes32 /* key */, EventUtils.EventLogData memory /* order */, EventUtils.EventLogData memory /* eventData */) external { ++called; } - function afterOrderFrozen(bytes32 /* key */, Order.Props memory /* order */, EventUtils.EventLogData memory /* eventData */) external { + function afterOrderFrozen(bytes32 /* key */, EventUtils.EventLogData memory /* order */, EventUtils.EventLogData memory /* eventData */) external { ++called; } @@ -31,19 +31,19 @@ contract MockCallbackReceiver is IOrderCallbackReceiver, IGasFeeCallbackReceiver ++called; } - function afterGlvDepositExecution(bytes32 /* key */, GlvDeposit.Props memory /* glv deposit */, EventUtils.EventLogData memory /* eventData */) external { + function afterGlvDepositExecution(bytes32 /* key */, EventUtils.EventLogData memory /* glv deposit */, EventUtils.EventLogData memory /* eventData */) external { ++glvDepositExecutionCalled; } - function afterGlvDepositCancellation(bytes32 /* key */, GlvDeposit.Props memory /* glv deposit */, EventUtils.EventLogData memory /* eventData */) external { + function afterGlvDepositCancellation(bytes32 /* key */, EventUtils.EventLogData memory /* glv deposit */, EventUtils.EventLogData memory /* eventData */) external { ++glvDepositCancellationCalled; } - function afterGlvWithdrawalExecution(bytes32 /* key */, GlvWithdrawal.Props memory /* glv withdrawal */, EventUtils.EventLogData memory /* eventData */) external { + function afterGlvWithdrawalExecution(bytes32 /* key */, EventUtils.EventLogData memory /* glv withdrawal */, EventUtils.EventLogData memory /* eventData */) external { ++glvWithdrawalExecutionCalled; } - function afterGlvWithdrawalCancellation(bytes32 /* key */, GlvWithdrawal.Props memory /* glv withdrawal */, EventUtils.EventLogData memory /* eventData */) external { + function afterGlvWithdrawalCancellation(bytes32 /* key */, EventUtils.EventLogData memory /* glv withdrawal */, EventUtils.EventLogData memory /* eventData */) external { ++glvWithdrawalCancellationCalled; } } diff --git a/contracts/mock/RevertingCallbackReceiver.sol b/contracts/mock/RevertingCallbackReceiver.sol index 414bd15b1..7978c958b 100644 --- a/contracts/mock/RevertingCallbackReceiver.sol +++ b/contracts/mock/RevertingCallbackReceiver.sol @@ -5,11 +5,11 @@ pragma solidity ^0.8.0; import "../callback/IDepositCallbackReceiver.sol"; contract RevertingCallbackReceiver is IDepositCallbackReceiver { - function afterDepositExecution(bytes32 /* key */, Deposit.Props memory /* deposit */, EventUtils.EventLogData memory /* eventData */) external pure { + function afterDepositExecution(bytes32 /* key */, EventUtils.EventLogData memory /* deposit */, EventUtils.EventLogData memory /* eventData */) external pure { revert("error"); } - function afterDepositCancellation(bytes32 /* key */, Deposit.Props memory /* deposit */, EventUtils.EventLogData memory /* eventData */) external pure { + function afterDepositCancellation(bytes32 /* key */, EventUtils.EventLogData memory /* deposit */, EventUtils.EventLogData memory /* eventData */) external pure { revert("error"); } } diff --git a/contracts/order/OrderEventUtils.sol b/contracts/order/OrderEventUtils.sol index 0e0297262..6eb649dbb 100644 --- a/contracts/order/OrderEventUtils.sol +++ b/contracts/order/OrderEventUtils.sol @@ -23,45 +23,11 @@ library OrderEventUtils { bytes32 key, Order.Props memory order ) external { - EventUtils.EventLogData memory eventData; - - eventData.addressItems.initItems(7); - eventData.addressItems.setItem(0, "account", order.account()); - eventData.addressItems.setItem(1, "receiver", order.receiver()); - eventData.addressItems.setItem(2, "callbackContract", order.callbackContract()); - eventData.addressItems.setItem(3, "uiFeeReceiver", order.uiFeeReceiver()); - eventData.addressItems.setItem(4, "market", order.market()); - eventData.addressItems.setItem(5, "initialCollateralToken", order.initialCollateralToken()); - eventData.addressItems.setItem(6, "cancellationReceiver", order.cancellationReceiver()); - - eventData.addressItems.initArrayItems(1); - eventData.addressItems.setItem(0, "swapPath", order.swapPath()); - - eventData.uintItems.initItems(12); - eventData.uintItems.setItem(0, "orderType", uint256(order.orderType())); - eventData.uintItems.setItem(1, "decreasePositionSwapType", uint256(order.decreasePositionSwapType())); - eventData.uintItems.setItem(2, "sizeDeltaUsd", order.sizeDeltaUsd()); - eventData.uintItems.setItem(3, "initialCollateralDeltaAmount", order.initialCollateralDeltaAmount()); - eventData.uintItems.setItem(4, "triggerPrice", order.triggerPrice()); - eventData.uintItems.setItem(5, "acceptablePrice", order.acceptablePrice()); - eventData.uintItems.setItem(6, "executionFee", order.executionFee()); - eventData.uintItems.setItem(7, "callbackGasLimit", order.callbackGasLimit()); - eventData.uintItems.setItem(8, "minOutputAmount", order.minOutputAmount()); - eventData.uintItems.setItem(9, "updatedAtTime", order.updatedAtTime()); - eventData.uintItems.setItem(10, "validFromTime", order.validFromTime()); - eventData.uintItems.setItem(11, "srcChainId", order.srcChainId()); - - eventData.boolItems.initItems(3); - eventData.boolItems.setItem(0, "isLong", order.isLong()); - eventData.boolItems.setItem(1, "shouldUnwrapNativeToken", order.shouldUnwrapNativeToken()); - eventData.boolItems.setItem(2, "autoCancel", order.autoCancel()); + EventUtils.EventLogData memory eventData = createEventData(order); eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); - eventData.bytes32Items.initArrayItems(1); - eventData.bytes32Items.setItem(0, "dataList", order.dataList()); - eventEmitter.emitEventLog2( "OrderCreated", key, @@ -232,4 +198,41 @@ library OrderEventUtils { eventData ); } + + function createEventData(Order.Props memory order) public pure returns (EventUtils.EventLogData memory) { + EventUtils.EventLogData memory eventData; + eventData.addressItems.initItems(7); + eventData.addressItems.setItem(0, "account", order.account()); + eventData.addressItems.setItem(1, "receiver", order.receiver()); + eventData.addressItems.setItem(2, "callbackContract", order.callbackContract()); + eventData.addressItems.setItem(3, "uiFeeReceiver", order.uiFeeReceiver()); + eventData.addressItems.setItem(4, "market", order.market()); + eventData.addressItems.setItem(5, "initialCollateralToken", order.initialCollateralToken()); + eventData.addressItems.setItem(6, "cancellationReceiver", order.cancellationReceiver()); + + eventData.addressItems.initArrayItems(1); + eventData.addressItems.setItem(0, "swapPath", order.swapPath()); + + eventData.uintItems.initItems(11); + eventData.uintItems.setItem(0, "orderType", uint256(order.orderType())); + eventData.uintItems.setItem(1, "decreasePositionSwapType", uint256(order.decreasePositionSwapType())); + eventData.uintItems.setItem(2, "sizeDeltaUsd", order.sizeDeltaUsd()); + eventData.uintItems.setItem(3, "initialCollateralDeltaAmount", order.initialCollateralDeltaAmount()); + eventData.uintItems.setItem(4, "triggerPrice", order.triggerPrice()); + eventData.uintItems.setItem(5, "acceptablePrice", order.acceptablePrice()); + eventData.uintItems.setItem(6, "executionFee", order.executionFee()); + eventData.uintItems.setItem(7, "callbackGasLimit", order.callbackGasLimit()); + eventData.uintItems.setItem(8, "minOutputAmount", order.minOutputAmount()); + eventData.uintItems.setItem(9, "updatedAtTime", order.updatedAtTime()); + eventData.uintItems.setItem(10, "validFromTime", order.validFromTime()); + + eventData.boolItems.initItems(3); + eventData.boolItems.setItem(0, "isLong", order.isLong()); + eventData.boolItems.setItem(1, "shouldUnwrapNativeToken", order.shouldUnwrapNativeToken()); + eventData.boolItems.setItem(2, "autoCancel", order.autoCancel()); + + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", order.dataList()); + return eventData; + } } diff --git a/contracts/pricing/SwapPricingUtils.sol b/contracts/pricing/SwapPricingUtils.sol index 4f3b335fc..6d94639ed 100644 --- a/contracts/pricing/SwapPricingUtils.sol +++ b/contracts/pricing/SwapPricingUtils.sol @@ -266,7 +266,7 @@ library SwapPricingUtils { bool balanceWasImproved, address uiFeeReceiver, ISwapPricingUtils.SwapPricingType swapPricingType - ) internal view returns (SwapFees memory) { + ) external view returns (SwapFees memory) { SwapFees memory fees; // note that since it is possible to incur both positive and negative price impact values @@ -310,7 +310,7 @@ library SwapPricingUtils { function emitSwapInfo( EventEmitter eventEmitter, EmitSwapInfoParams memory params - ) internal { + ) external { EventUtils.EventLogData memory eventData; eventData.bytes32Items.initItems(1); diff --git a/contracts/shift/ShiftEventUtils.sol b/contracts/shift/ShiftEventUtils.sol index 7f99c4294..3e6ce453e 100644 --- a/contracts/shift/ShiftEventUtils.sol +++ b/contracts/shift/ShiftEventUtils.sol @@ -24,30 +24,11 @@ library ShiftEventUtils { bytes32 key, Shift.Props memory shift ) external { - EventUtils.EventLogData memory eventData; - - eventData.addressItems.initItems(6); - eventData.addressItems.setItem(0, "account", shift.account()); - eventData.addressItems.setItem(1, "receiver", shift.receiver()); - eventData.addressItems.setItem(2, "callbackContract", shift.callbackContract()); - eventData.addressItems.setItem(3, "fromMarket", shift.fromMarket()); - eventData.addressItems.setItem(4, "toMarket", shift.toMarket()); - eventData.addressItems.setItem(5, "uiFeeReceiver", shift.uiFeeReceiver()); - - eventData.uintItems.initItems(6); - eventData.uintItems.setItem(0, "marketTokenAmount", shift.marketTokenAmount()); - eventData.uintItems.setItem(1, "minMarketTokens", shift.minMarketTokens()); - eventData.uintItems.setItem(2, "updatedAtTime", shift.updatedAtTime()); - eventData.uintItems.setItem(3, "executionFee", shift.executionFee()); - eventData.uintItems.setItem(4, "callbackGasLimit", shift.callbackGasLimit()); - eventData.uintItems.setItem(5, "srcChainId", shift.srcChainId()); + EventUtils.EventLogData memory eventData = createEventData(shift); eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); - eventData.bytes32Items.initArrayItems(1); - eventData.bytes32Items.setItem(0, "dataList", shift.dataList()); - eventEmitter.emitEventLog2( "ShiftCreated", key, @@ -109,4 +90,27 @@ library ShiftEventUtils { eventData ); } + + function createEventData(Shift.Props memory shift) public pure returns(EventUtils.EventLogData memory) { + EventUtils.EventLogData memory eventData; + + eventData.addressItems.initItems(6); + eventData.addressItems.setItem(0, "account", shift.account()); + eventData.addressItems.setItem(1, "receiver", shift.receiver()); + eventData.addressItems.setItem(2, "callbackContract", shift.callbackContract()); + eventData.addressItems.setItem(3, "fromMarket", shift.fromMarket()); + eventData.addressItems.setItem(4, "toMarket", shift.toMarket()); + eventData.addressItems.setItem(5, "uiFeeReceiver", shift.uiFeeReceiver()); + + eventData.uintItems.initItems(5); + eventData.uintItems.setItem(0, "marketTokenAmount", shift.marketTokenAmount()); + eventData.uintItems.setItem(1, "minMarketTokens", shift.minMarketTokens()); + eventData.uintItems.setItem(2, "updatedAtTime", shift.updatedAtTime()); + eventData.uintItems.setItem(3, "executionFee", shift.executionFee()); + eventData.uintItems.setItem(4, "callbackGasLimit", shift.callbackGasLimit()); + + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", shift.dataList()); + return eventData; + } } diff --git a/contracts/swap/SwapUtils.sol b/contracts/swap/SwapUtils.sol index b91398da9..2e8b681f4 100644 --- a/contracts/swap/SwapUtils.sol +++ b/contracts/swap/SwapUtils.sol @@ -177,7 +177,7 @@ library SwapUtils { address[] memory swapPath, address inputToken, address expectedOutputToken - ) internal view { + ) external view { address outputToken = getOutputToken(dataStore, swapPath, inputToken); if (outputToken != expectedOutputToken) { revert Errors.InvalidSwapOutputToken(outputToken, expectedOutputToken); @@ -188,7 +188,7 @@ library SwapUtils { DataStore dataStore, address[] memory swapPath, address inputToken - ) internal view returns (address) { + ) public view returns (address) { address outputToken = inputToken; Market.Props[] memory markets = MarketUtils.getSwapPathMarkets(dataStore, swapPath); uint256 marketCount = markets.length; diff --git a/contracts/withdrawal/WithdrawalEventUtils.sol b/contracts/withdrawal/WithdrawalEventUtils.sol index 99b0eb2d5..2d5cf3403 100644 --- a/contracts/withdrawal/WithdrawalEventUtils.sol +++ b/contracts/withdrawal/WithdrawalEventUtils.sol @@ -26,38 +26,11 @@ library WithdrawalEventUtils { Withdrawal.Props memory withdrawal, Withdrawal.WithdrawalType withdrawalType ) external { - EventUtils.EventLogData memory eventData; - - eventData.addressItems.initItems(5); - eventData.addressItems.setItem(0, "account", withdrawal.account()); - eventData.addressItems.setItem(1, "receiver", withdrawal.receiver()); - eventData.addressItems.setItem(2, "callbackContract", withdrawal.callbackContract()); - eventData.addressItems.setItem(3, "market", withdrawal.market()); - eventData.addressItems.setItem(4, "uiFeeReceiver", withdrawal.uiFeeReceiver()); - - eventData.addressItems.initArrayItems(2); - eventData.addressItems.setItem(0, "longTokenSwapPath", withdrawal.longTokenSwapPath()); - eventData.addressItems.setItem(1, "shortTokenSwapPath", withdrawal.shortTokenSwapPath()); - - eventData.uintItems.initItems(8); - eventData.uintItems.setItem(0, "marketTokenAmount", withdrawal.marketTokenAmount()); - eventData.uintItems.setItem(1, "minLongTokenAmount", withdrawal.minLongTokenAmount()); - eventData.uintItems.setItem(2, "minShortTokenAmount", withdrawal.minShortTokenAmount()); - eventData.uintItems.setItem(3, "updatedAtTime", withdrawal.updatedAtTime()); - eventData.uintItems.setItem(4, "executionFee", withdrawal.executionFee()); - eventData.uintItems.setItem(5, "callbackGasLimit", withdrawal.callbackGasLimit()); - eventData.uintItems.setItem(6, "withdrawalType", uint256(withdrawalType)); - eventData.uintItems.setItem(7, "srcChainId", withdrawal.srcChainId()); - - eventData.boolItems.initItems(1); - eventData.boolItems.setItem(0, "shouldUnwrapNativeToken", withdrawal.shouldUnwrapNativeToken()); + EventUtils.EventLogData memory eventData = createEventData(withdrawal, withdrawalType); eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); - eventData.bytes32Items.initArrayItems(1); - eventData.bytes32Items.setItem(0, "dataList", withdrawal.dataList()); - eventEmitter.emitEventLog2( "WithdrawalCreated", key, @@ -119,4 +92,36 @@ library WithdrawalEventUtils { eventData ); } + + function createEventData(Withdrawal.Props memory withdrawal, Withdrawal.WithdrawalType withdrawalType) + public pure returns(EventUtils.EventLogData memory) { + EventUtils.EventLogData memory eventData; + + eventData.addressItems.initItems(5); + eventData.addressItems.setItem(0, "account", withdrawal.account()); + eventData.addressItems.setItem(1, "receiver", withdrawal.receiver()); + eventData.addressItems.setItem(2, "callbackContract", withdrawal.callbackContract()); + eventData.addressItems.setItem(3, "market", withdrawal.market()); + eventData.addressItems.setItem(4, "uiFeeReceiver", withdrawal.uiFeeReceiver()); + + eventData.addressItems.initArrayItems(2); + eventData.addressItems.setItem(0, "longTokenSwapPath", withdrawal.longTokenSwapPath()); + eventData.addressItems.setItem(1, "shortTokenSwapPath", withdrawal.shortTokenSwapPath()); + + eventData.uintItems.initItems(7); + eventData.uintItems.setItem(0, "marketTokenAmount", withdrawal.marketTokenAmount()); + eventData.uintItems.setItem(1, "minLongTokenAmount", withdrawal.minLongTokenAmount()); + eventData.uintItems.setItem(2, "minShortTokenAmount", withdrawal.minShortTokenAmount()); + eventData.uintItems.setItem(3, "updatedAtTime", withdrawal.updatedAtTime()); + eventData.uintItems.setItem(4, "executionFee", withdrawal.executionFee()); + eventData.uintItems.setItem(5, "callbackGasLimit", withdrawal.callbackGasLimit()); + eventData.uintItems.setItem(6, "withdrawalType", uint256(withdrawalType)); + + eventData.boolItems.initItems(1); + eventData.boolItems.setItem(0, "shouldUnwrapNativeToken", withdrawal.shouldUnwrapNativeToken()); + + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", withdrawal.dataList()); + return eventData; + } } diff --git a/deploy/deployAdlHandler.ts b/deploy/deployAdlHandler.ts index d0f0603d5..f9d1f6507 100644 --- a/deploy/deployAdlHandler.ts +++ b/deploy/deployAdlHandler.ts @@ -25,6 +25,7 @@ const func = createDeployFunction({ "MarketStoreUtils", "PositionStoreUtils", "OrderStoreUtils", + "MarketUtils", ], afterDeploy: async ({ deployedContract }) => { await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); diff --git a/deploy/deployAdlUtils.ts b/deploy/deployAdlUtils.ts index 02ea41498..e16551e51 100644 --- a/deploy/deployAdlUtils.ts +++ b/deploy/deployAdlUtils.ts @@ -2,7 +2,7 @@ import { createDeployFunction } from "../utils/deploy"; const func = createDeployFunction({ contractName: "AdlUtils", - libraryNames: ["MarketStoreUtils", "PositionStoreUtils", "OrderStoreUtils", "OrderEventUtils"], + libraryNames: ["PositionStoreUtils", "OrderStoreUtils", "OrderEventUtils", "CallbackUtils", "MarketUtils"], }); export default func; diff --git a/deploy/deployCallbackUtils.ts b/deploy/deployCallbackUtils.ts index fbd601f5f..ee92b2e0f 100644 --- a/deploy/deployCallbackUtils.ts +++ b/deploy/deployCallbackUtils.ts @@ -2,7 +2,14 @@ import { createDeployFunction } from "../utils/deploy"; const func = createDeployFunction({ contractName: "CallbackUtils", - libraryNames: [], + libraryNames: [ + "GlvDepositEventUtils", + "GlvWithdrawalEventUtils", + "OrderEventUtils", + "WithdrawalEventUtils", + "DepositEventUtils", + "ShiftEventUtils", + ], }); export default func; diff --git a/deploy/deployDepositUtils.ts b/deploy/deployDepositUtils.ts index 2c05277c5..e08ec7d04 100644 --- a/deploy/deployDepositUtils.ts +++ b/deploy/deployDepositUtils.ts @@ -9,6 +9,9 @@ const func = createDeployFunction({ "MarketEventUtils", "DepositStoreUtils", "DepositEventUtils", + "ExecuteDepositUtils", + "CallbackUtils", + "MarketUtils", ], }); diff --git a/deploy/deployExecuteDepositUtils.ts b/deploy/deployExecuteDepositUtils.ts index 3b428b188..528504888 100644 --- a/deploy/deployExecuteDepositUtils.ts +++ b/deploy/deployExecuteDepositUtils.ts @@ -14,6 +14,7 @@ const func = createDeployFunction({ "SwapPricingUtils", "PositionUtils", "MultichainUtils", + "CallbackUtils", ], }); diff --git a/deploy/deployExecuteGlvDepositUtils.ts b/deploy/deployExecuteGlvDepositUtils.ts index 7b141840a..549590cac 100644 --- a/deploy/deployExecuteGlvDepositUtils.ts +++ b/deploy/deployExecuteGlvDepositUtils.ts @@ -13,6 +13,7 @@ const func = createDeployFunction({ "GlvDepositCalc", "MarketStoreUtils", "MultichainUtils", + "CallbackUtils", ], }); diff --git a/deploy/deployExecuteOrderUtils.ts b/deploy/deployExecuteOrderUtils.ts index aa0b7ae68..061db7f45 100644 --- a/deploy/deployExecuteOrderUtils.ts +++ b/deploy/deployExecuteOrderUtils.ts @@ -12,6 +12,7 @@ const func = createDeployFunction({ "SwapOrderUtils", "GasUtils", "PositionUtils", + "CallbackUtils", ], }); diff --git a/deploy/deployExecuteWithdrawalUtils.ts b/deploy/deployExecuteWithdrawalUtils.ts index b54360865..dbbbe678c 100644 --- a/deploy/deployExecuteWithdrawalUtils.ts +++ b/deploy/deployExecuteWithdrawalUtils.ts @@ -14,6 +14,7 @@ const func = createDeployFunction({ "SwapPricingUtils", "PositionUtils", "MultichainUtils", + "CallbackUtils", ], }); diff --git a/deploy/deployGlvDepositEventUtils.ts b/deploy/deployGlvDepositEventUtils.ts index 3304a0329..c0af4c568 100644 --- a/deploy/deployGlvDepositEventUtils.ts +++ b/deploy/deployGlvDepositEventUtils.ts @@ -2,6 +2,9 @@ import { createDeployFunction } from "../utils/deploy"; const func = createDeployFunction({ contractName: "GlvDepositEventUtils", + libraryNames: [ + // "GlvDepositMappingUtils", + ], }); export default func; diff --git a/deploy/deployGlvDepositUtils.ts b/deploy/deployGlvDepositUtils.ts index b1214e0a7..6d101d6f4 100644 --- a/deploy/deployGlvDepositUtils.ts +++ b/deploy/deployGlvDepositUtils.ts @@ -13,6 +13,7 @@ const func = createDeployFunction({ "GlvDepositCalc", "MarketStoreUtils", "MultichainUtils", + "CallbackUtils", ], }); diff --git a/deploy/deployGlvHandler.ts b/deploy/deployGlvHandler.ts index db9e93e86..747f3da0a 100644 --- a/deploy/deployGlvHandler.ts +++ b/deploy/deployGlvHandler.ts @@ -26,6 +26,7 @@ const func = createDeployFunction({ "GlvUtils", "GlvWithdrawalStoreUtils", "GlvWithdrawalUtils", + "GasUtils", ], afterDeploy: async ({ deployedContract }) => { await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); diff --git a/deploy/deployGlvWithdrawalUtils.ts b/deploy/deployGlvWithdrawalUtils.ts index f3635fea5..33eb0711f 100644 --- a/deploy/deployGlvWithdrawalUtils.ts +++ b/deploy/deployGlvWithdrawalUtils.ts @@ -12,6 +12,7 @@ const func = createDeployFunction({ "ExecuteWithdrawalUtils", "WithdrawalEventUtils", "MultichainUtils", + "CallbackUtils", ], }); diff --git a/deploy/deployLiquidationHandler.ts b/deploy/deployLiquidationHandler.ts index bb594680b..e4f7729cc 100644 --- a/deploy/deployLiquidationHandler.ts +++ b/deploy/deployLiquidationHandler.ts @@ -24,6 +24,7 @@ const func = createDeployFunction({ "MarketStoreUtils", "PositionStoreUtils", "OrderStoreUtils", + "MarketUtils", ], afterDeploy: async ({ deployedContract }) => { await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); diff --git a/deploy/deployLiquidationUtils.ts b/deploy/deployLiquidationUtils.ts index ab7a17dc8..c525a4132 100644 --- a/deploy/deployLiquidationUtils.ts +++ b/deploy/deployLiquidationUtils.ts @@ -2,7 +2,7 @@ import { createDeployFunction } from "../utils/deploy"; const func = createDeployFunction({ contractName: "LiquidationUtils", - libraryNames: ["PositionStoreUtils", "OrderStoreUtils", "OrderEventUtils"], + libraryNames: ["PositionStoreUtils", "OrderStoreUtils", "OrderEventUtils", "CallbackUtils"], }); export default func; diff --git a/deploy/deployMultichainGmRouter.ts b/deploy/deployMultichainGmRouter.ts index 40a13964b..c0e6ec52f 100644 --- a/deploy/deployMultichainGmRouter.ts +++ b/deploy/deployMultichainGmRouter.ts @@ -38,7 +38,16 @@ const func = createDeployFunction({ dependencyContracts.ShiftVault.address, ]; }, - libraryNames: ["MarketStoreUtils", "MultichainUtils", "OrderStoreUtils", "RelayUtils", "ShiftUtils", "SwapUtils"], + libraryNames: [ + "MarketStoreUtils", + "MultichainUtils", + "OrderStoreUtils", + "RelayUtils", + "ShiftUtils", + "SwapUtils", + "MarketUtils", + ], + afterDeploy: async ({ deployedContract }) => { await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); await grantRoleIfNotGranted(deployedContract.address, "ROUTER_PLUGIN"); diff --git a/deploy/deployOrderHandler.ts b/deploy/deployOrderHandler.ts index ecd13266b..f796b1dc7 100644 --- a/deploy/deployOrderHandler.ts +++ b/deploy/deployOrderHandler.ts @@ -17,7 +17,15 @@ const func = createDeployFunction({ getDeployArgs: async ({ dependencyContracts }) => { return constructorContracts.map((dependencyName) => dependencyContracts[dependencyName].address); }, - libraryNames: ["MarketStoreUtils", "OrderUtils", "ExecuteOrderUtils", "OrderStoreUtils", "OrderEventUtils"], + libraryNames: [ + "MarketStoreUtils", + "OrderUtils", + "ExecuteOrderUtils", + "OrderStoreUtils", + "OrderEventUtils", + "GasUtils", + "MarketUtils", + ], afterDeploy: async ({ deployedContract, getNamedAccounts, deployments, network }) => { const { deployer } = await getNamedAccounts(); const { execute } = deployments; diff --git a/deploy/deployOrderUtils.ts b/deploy/deployOrderUtils.ts index 6d94e62b6..c0362b3aa 100644 --- a/deploy/deployOrderUtils.ts +++ b/deploy/deployOrderUtils.ts @@ -2,17 +2,7 @@ import { createDeployFunction } from "../utils/deploy"; const func = createDeployFunction({ contractName: "OrderUtils", - libraryNames: [ - "Printer", - "MarketStoreUtils", - "MarketUtils", - "OrderStoreUtils", - "OrderEventUtils", - "IncreaseOrderUtils", - "DecreaseOrderUtils", - "SwapOrderUtils", - "GasUtils", - ], + libraryNames: ["MarketStoreUtils", "OrderStoreUtils", "OrderEventUtils", "GasUtils", "CallbackUtils", "MarketUtils"], }); export default func; diff --git a/deploy/deployReaderUtils.ts b/deploy/deployReaderUtils.ts index be2e9c139..6c1a63d1f 100644 --- a/deploy/deployReaderUtils.ts +++ b/deploy/deployReaderUtils.ts @@ -2,7 +2,7 @@ import { createDeployFunction } from "../utils/deploy"; const func = createDeployFunction({ contractName: "ReaderUtils", - libraryNames: ["MarketStoreUtils", "OrderStoreUtils", "ReaderPositionUtils"], + libraryNames: ["MarketStoreUtils", "OrderStoreUtils", "ReaderPositionUtils", "MarketUtils"], }); export default func; diff --git a/deploy/deployReaderWithdrawalUtils.ts b/deploy/deployReaderWithdrawalUtils.ts index f4c880181..6d4cb292e 100644 --- a/deploy/deployReaderWithdrawalUtils.ts +++ b/deploy/deployReaderWithdrawalUtils.ts @@ -2,7 +2,7 @@ import { createDeployFunction } from "../utils/deploy"; const func = createDeployFunction({ contractName: "ReaderWithdrawalUtils", - libraryNames: ["MarketUtils", "MarketStoreUtils", "PositionStoreUtils", "PositionUtils"], + libraryNames: ["MarketUtils", "MarketStoreUtils", "PositionStoreUtils", "PositionUtils", "SwapPricingUtils"], }); export default func; diff --git a/deploy/deployShiftUtils.ts b/deploy/deployShiftUtils.ts index 63bd440cc..cbc7e093e 100644 --- a/deploy/deployShiftUtils.ts +++ b/deploy/deployShiftUtils.ts @@ -12,6 +12,8 @@ const func = createDeployFunction({ "ExecuteDepositUtils", "ExecuteWithdrawalUtils", "MultichainUtils", + "CallbackUtils", + "MarketUtils", ], }); diff --git a/deploy/deploySwapUtils.ts b/deploy/deploySwapUtils.ts index 74aeaff69..ed88f7d8e 100644 --- a/deploy/deploySwapUtils.ts +++ b/deploy/deploySwapUtils.ts @@ -2,7 +2,7 @@ import { createDeployFunction } from "../utils/deploy"; const func = createDeployFunction({ contractName: "SwapUtils", - libraryNames: ["FeeUtils", "MarketEventUtils", "SwapPricingUtils"], + libraryNames: ["FeeUtils", "MarketEventUtils", "SwapPricingUtils", "MarketStoreUtils"], }); export default func; diff --git a/deploy/deployWithdrawalUtils.ts b/deploy/deployWithdrawalUtils.ts index 4f5e88c34..c60291e27 100644 --- a/deploy/deployWithdrawalUtils.ts +++ b/deploy/deployWithdrawalUtils.ts @@ -11,6 +11,7 @@ const func = createDeployFunction({ "WithdrawalStoreUtils", "WithdrawalEventUtils", "SwapUtils", + "CallbackUtils", ], }); diff --git a/test/migration/GlpMigrator.ts b/test/migration/GlpMigrator.ts index 35a2b6678..b7f72e8d4 100644 --- a/test/migration/GlpMigrator.ts +++ b/test/migration/GlpMigrator.ts @@ -22,6 +22,7 @@ describe("GlpMigrator", () => { depositHandler, externalHandler, marketStoreUtils, + marketUtils, stakedGlp, glpVault, glpTimelock, @@ -71,6 +72,7 @@ describe("GlpMigrator", () => { depositVault, depositHandler, marketStoreUtils, + marketUtils, ethUsdMarket, btcUsdMarket, wnt, @@ -100,7 +102,7 @@ describe("GlpMigrator", () => { ], { libraries: { - MarketStoreUtils: marketStoreUtils.address, + MarketUtils: marketUtils.address, }, } ); diff --git a/utils/fixture.ts b/utils/fixture.ts index a08855a16..082e1f46a 100644 --- a/utils/fixture.ts +++ b/utils/fixture.ts @@ -78,6 +78,7 @@ export async function deployFixture() { const glvFactory = await hre.ethers.getContract("GlvFactory"); const glvHandler = await hre.ethers.getContract("GlvHandler"); const glvRouter = await hre.ethers.getContract("GlvRouter"); + const callbackUtils = await hre.ethers.getContract("CallbackUtils"); const glvDepositStoreUtils = await hre.ethers.getContract("GlvDepositStoreUtils"); const GlvDepositCalc = await hre.ethers.getContract("GlvDepositCalc"); const glvWithdrawalStoreUtils = await hre.ethers.getContract("GlvWithdrawalStoreUtils"); @@ -335,6 +336,7 @@ export async function deployFixture() { multichainUtils, layerZeroProvider, mockStargatePool, + callbackUtils, }, props: { oracleSalt, signerIndexes: [0, 1, 2, 3, 4, 5, 6], executionFee: "1000000000000000" }, }; From d52370b1e924b274b0b89a22123a8e4fe73618e0 Mon Sep 17 00:00:00 2001 From: Solar Date: Fri, 21 Feb 2025 13:31:29 +0300 Subject: [PATCH 091/205] fix tests --- contracts/gas/GasUtils.sol | 4 ++-- contracts/market/MarketUtils.sol | 2 +- deploy/deploySubaccountGelatoRelayRouter.ts | 2 +- deploy/deploySwapUtils.ts | 2 +- test/exchange/MarketIncreaseOrder.ts | 4 ++-- test/router/relay/signatures.ts | 6 ++++-- 6 files changed, 11 insertions(+), 9 deletions(-) diff --git a/contracts/gas/GasUtils.sol b/contracts/gas/GasUtils.sol index f940df48a..6253ffb44 100644 --- a/contracts/gas/GasUtils.sol +++ b/contracts/gas/GasUtils.sol @@ -317,7 +317,7 @@ library GasUtils { function estimateExecuteDepositGasLimit( DataStore dataStore, Deposit.Props memory deposit - ) external view returns (uint256) { + ) internal view returns (uint256) { uint256 gasPerSwap = dataStore.getUint(Keys.singleSwapGasLimitKey()); uint256 swapCount = deposit.longTokenSwapPath().length + deposit.shortTokenSwapPath().length; uint256 gasForSwaps = swapCount * gasPerSwap; @@ -451,7 +451,7 @@ library GasUtils { DataStore dataStore, GlvWithdrawal.Props memory glvWithdrawal, uint256 marketCount - ) external view returns (uint256) { + ) internal view returns (uint256) { // glv withdrawal execution gas consumption depends on the amount of markets uint256 gasPerGlvPerMarket = dataStore.getUint(Keys.glvPerMarketGasLimitKey()); uint256 gasForGlvMarkets = gasPerGlvPerMarket * marketCount; diff --git a/contracts/market/MarketUtils.sol b/contracts/market/MarketUtils.sol index 74928be39..91c65e456 100644 --- a/contracts/market/MarketUtils.sol +++ b/contracts/market/MarketUtils.sol @@ -2797,7 +2797,7 @@ library MarketUtils { // @dev get a list of market values based on an input array of market addresses // @param swapPath list of market addresses - function getSwapPathMarkets(DataStore dataStore, address[] memory swapPath) internal view returns (Market.Props[] memory) { + function getSwapPathMarkets(DataStore dataStore, address[] memory swapPath) external view returns (Market.Props[] memory) { Market.Props[] memory markets = new Market.Props[](swapPath.length); for (uint256 i; i < swapPath.length; i++) { diff --git a/deploy/deploySubaccountGelatoRelayRouter.ts b/deploy/deploySubaccountGelatoRelayRouter.ts index 065d029c8..9616821d5 100644 --- a/deploy/deploySubaccountGelatoRelayRouter.ts +++ b/deploy/deploySubaccountGelatoRelayRouter.ts @@ -17,7 +17,7 @@ const func = createDeployFunction({ getDeployArgs: async ({ dependencyContracts }) => { return constructorContracts.map((dependencyName) => dependencyContracts[dependencyName].address); }, - libraryNames: ["MarketStoreUtils", "OrderStoreUtils", "SwapUtils", "SubaccountUtils"], + libraryNames: ["MarketStoreUtils", "OrderStoreUtils", "SwapUtils", "SubaccountUtils", "MarketUtils"], afterDeploy: async ({ deployedContract }) => { await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); await grantRoleIfNotGranted(deployedContract.address, "ROUTER_PLUGIN"); diff --git a/deploy/deploySwapUtils.ts b/deploy/deploySwapUtils.ts index ed88f7d8e..d7e1abf71 100644 --- a/deploy/deploySwapUtils.ts +++ b/deploy/deploySwapUtils.ts @@ -2,7 +2,7 @@ import { createDeployFunction } from "../utils/deploy"; const func = createDeployFunction({ contractName: "SwapUtils", - libraryNames: ["FeeUtils", "MarketEventUtils", "SwapPricingUtils", "MarketStoreUtils"], + libraryNames: ["FeeUtils", "MarketEventUtils", "SwapPricingUtils", "MarketStoreUtils", "MarketUtils"], }); export default func; diff --git a/test/exchange/MarketIncreaseOrder.ts b/test/exchange/MarketIncreaseOrder.ts index 15e9471cc..3693b68c3 100644 --- a/test/exchange/MarketIncreaseOrder.ts +++ b/test/exchange/MarketIncreaseOrder.ts @@ -262,7 +262,7 @@ describe("Exchange.MarketIncreaseOrder", () => { await handleOrder(fixture, { create: params }); - expect((await provider.getBalance(user1.address)).sub(initialBalance)).closeTo("129042985032344", "10000000000000"); + expect((await provider.getBalance(user1.address)).sub(initialBalance)).closeTo("96068984768552", "10000000000000"); }); it("refund execution fee callback", async () => { @@ -294,7 +294,7 @@ describe("Exchange.MarketIncreaseOrder", () => { expect((await provider.getBalance(user1.address)).sub(initialBalance)).eq(0); - expect(await provider.getBalance(mockCallbackReceiver.address)).closeTo("98560984788488", "10000000000000"); + expect(await provider.getBalance(mockCallbackReceiver.address)).closeTo("2240984017928", "10000000000000"); }); it("validates reserve", async () => { diff --git a/test/router/relay/signatures.ts b/test/router/relay/signatures.ts index 180dd14fa..8abc101fa 100644 --- a/test/router/relay/signatures.ts +++ b/test/router/relay/signatures.ts @@ -22,7 +22,8 @@ describe("Relay signatures", () => { orderStoreUtils, swapUtils, relayUtils, - mockContract; + mockContract, + marketUtils; beforeEach(async () => { fixture = await deployFixture(); @@ -38,6 +39,7 @@ describe("Relay signatures", () => { orderStoreUtils, swapUtils, relayUtils, + marketUtils, } = fixture.contracts); }); @@ -55,10 +57,10 @@ describe("Relay signatures", () => { ], { libraries: { - MarketStoreUtils: marketStoreUtils.address, OrderStoreUtils: orderStoreUtils.address, SwapUtils: swapUtils.address, RelayUtils: relayUtils.address, + MarketUtils: marketUtils.address, }, } ); From 583cbabd7a4ebcb3deb5ba06e783f32ae37f4e1b Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 22 Feb 2025 17:14:10 +0200 Subject: [PATCH 092/205] Fix tests after gasless merge --- test/router/SubaccountRouter.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/router/SubaccountRouter.ts b/test/router/SubaccountRouter.ts index 99c51973c..3a6694cea 100644 --- a/test/router/SubaccountRouter.ts +++ b/test/router/SubaccountRouter.ts @@ -267,8 +267,8 @@ describe("SubaccountRouter", () => { expect(order._dataList).deep.eq(dataList); // 0.1 WETH in total - expect(order.numbers.executionFee).eq("2411100480000000"); - await expectBalance(wnt.address, user2.address, "97588899520000000"); + expect(order.numbers.executionFee).eq("2111032140000000"); + await expectBalance(wnt.address, user2.address, "97888967860000000"); expect( await dataStore.getUint( @@ -677,7 +677,7 @@ describe("SubaccountRouter", () => { await subaccountRouter.connect(subaccount).cancelOrder(orderKey); - expect(initialWntBalance0.sub(await wnt.balanceOf(user0.address))).closeTo("1598971241887446", "10000000000000"); // 0.001598971241887446 ETH + expect(initialWntBalance0.sub(await wnt.balanceOf(user0.address))).closeTo("1635869004900372", "10000000000000"); // 0.001635869004900372 ETH expect(await usdc.balanceOf(user0.address)).eq(expandDecimals(101, 6)); From 9a7ca9f0f8f977c5503c25e58fe80dbf23f6b27a Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 23 Feb 2025 11:18:11 +0200 Subject: [PATCH 093/205] test multichain withdrawal --- contracts/error/Errors.sol | 2 +- contracts/multichain/MultichainUtils.sol | 2 +- test/multichain/MultichainGmRouter.ts | 300 ++++++++++++++--------- utils/relay/multichain.ts | 29 +-- 4 files changed, 187 insertions(+), 146 deletions(-) diff --git a/contracts/error/Errors.sol b/contracts/error/Errors.sol index 800cf9d7a..4736484d9 100644 --- a/contracts/error/Errors.sol +++ b/contracts/error/Errors.sol @@ -433,7 +433,7 @@ library Errors { error InvalidTransferRequestsLength(); error EmptyMultichainTransferInAmount(); error EmptyMultichainTransferOutAmount(); - error InsufficientMultichainBalance(address token, uint256 balance, uint256 amount); + error InsufficientMultichainBalance(address account, address token, uint256 balance, uint256 amount); error InvalidDestinationChainId(uint256 desChainId); error InvalidMultichainProvider(address provider); diff --git a/contracts/multichain/MultichainUtils.sol b/contracts/multichain/MultichainUtils.sol index 86b498da8..eebf1e02a 100644 --- a/contracts/multichain/MultichainUtils.sol +++ b/contracts/multichain/MultichainUtils.sol @@ -68,7 +68,7 @@ library MultichainUtils { uint256 balance = dataStore.getUint(balanceKey); if (balance < amount) { - revert Errors.InsufficientMultichainBalance(token, balance, amount); + revert Errors.InsufficientMultichainBalance(account, token, balance, amount); } multichainVault.transferOut(token, receiver, amount); diff --git a/test/multichain/MultichainGmRouter.ts b/test/multichain/MultichainGmRouter.ts index 7161f3a29..12c2d90ed 100644 --- a/test/multichain/MultichainGmRouter.ts +++ b/test/multichain/MultichainGmRouter.ts @@ -7,28 +7,51 @@ import { GELATO_RELAY_ADDRESS } from "../../utils/relay/addresses"; import { sendCreateDeposit, sendCreateWithdrawal } from "../../utils/relay/multichain"; import * as keys from "../../utils/keys"; import { executeDeposit, getDepositCount, getDepositKeys } from "../../utils/deposit"; -import { getWithdrawalCount, getWithdrawalKeys } from "../../utils/withdrawal"; +import { executeWithdrawal, getWithdrawalCount, getWithdrawalKeys } from "../../utils/withdrawal"; import { getBalanceOf } from "../../utils/token"; +import { BigNumberish, Contract } from "ethers"; describe("MultichainGmRouter", () => { let fixture; let user0, user1, user2, user3; - let reader, - dataStore, - multichainGmRouter, - depositVault, - withdrawalVault, - ethUsdMarket, - wnt, - usdc, - layerZeroProvider, - mockStargatePool; + let reader, dataStore, multichainGmRouter, multichainVault, depositVault, withdrawalVault, ethUsdMarket, wnt, usdc; let relaySigner; let chainId; - let defaultParams; + let defaultDepositParams; let createDepositParams: Parameters[0]; + async function mintAndBridge( + fixture, + overrides: { + account?: string; + token: Contract; + tokenAmount: BigNumberish; + srcChainId?: BigNumberish; + } + ) { + const { mockStargatePool, layerZeroProvider } = fixture.contracts; + const { user0 } = fixture.accounts; + + const account = overrides.account || user0; + const token = overrides.token; + const tokenAmount = overrides.tokenAmount; + const srcChainId = + overrides.srcChainId || (await hre.ethers.provider.getNetwork().then((network) => network.chainId)); + + await token.mint(account.address, tokenAmount); + + // mock token bridging (increase user's multichain balance) + const encodedMessageEth = ethers.utils.defaultAbiCoder.encode( + ["address", "address", "uint256"], + [account.address, token.address, srcChainId] + ); + await token.connect(account).approve(mockStargatePool.address, tokenAmount); + await mockStargatePool + .connect(account) + .sendToken(token.address, layerZeroProvider.address, tokenAmount, encodedMessageEth); + } + beforeEach(async () => { fixture = await deployFixture(); ({ user0, user1, user2, user3 } = fixture.accounts); @@ -36,135 +59,148 @@ describe("MultichainGmRouter", () => { reader, dataStore, multichainGmRouter, + multichainVault, depositVault, withdrawalVault, ethUsdMarket, wnt, usdc, - layerZeroProvider, - mockStargatePool, } = fixture.contracts); - defaultParams = { + defaultDepositParams = { addresses: { receiver: user1.address, - callbackContract: user1.address, + callbackContract: user2.address, uiFeeReceiver: user2.address, market: ethUsdMarket.marketToken, initialLongToken: ethUsdMarket.longToken, initialShortToken: ethUsdMarket.shortToken, - longTokenSwapPath: [ethUsdMarket.marketToken], - shortTokenSwapPath: [ethUsdMarket.marketToken], + longTokenSwapPath: [], + shortTokenSwapPath: [], }, minMarketTokens: 100, shouldUnwrapNativeToken: false, - executionFee: 0, + executionFee: expandDecimals(123, 15), // TODO: Why executionFee is not deducted and sent to FEE_RECEIVER when depsoit is executed? callbackGasLimit: "200000", dataList: [], }; await impersonateAccount(GELATO_RELAY_ADDRESS); - await setBalance(GELATO_RELAY_ADDRESS, expandDecimals(1, 16)); // 0.01 ETH to pay tx fees + await setBalance(GELATO_RELAY_ADDRESS, expandDecimals(1, 16)); // ETH to pay tx fees relaySigner = await hre.ethers.getSigner(GELATO_RELAY_ADDRESS); chainId = await hre.ethers.provider.getNetwork().then((network) => network.chainId); + const wntAmount = expandDecimals(10, 18); + const usdcAmount = expandDecimals(45_000, 6); + const feeAmount = expandDecimals(6, 15); + createDepositParams = { sender: relaySigner, signer: user0, feeParams: { feeToken: wnt.address, - feeAmount: expandDecimals(5, 15), // 0.005 ETH + feeAmount: feeAmount, // 0.006 ETH feeSwapPath: [], }, transferRequests: { tokens: [wnt.address, usdc.address], receivers: [depositVault.address, depositVault.address], - amounts: [expandDecimals(10, 18), expandDecimals(50_000, 6)], + amounts: [wntAmount, usdcAmount], }, account: user0.address, - params: defaultParams, + params: defaultDepositParams, deadline: 9999999999, chainId, srcChainId: chainId, // 0 would mean same chain action - desChainId: chainId, // for non-multichain actions, desChainId and srcChainId are the same + desChainId: chainId, relayRouter: multichainGmRouter, relayFeeToken: wnt.address, relayFeeAmount: expandDecimals(2, 15), // 0.002 ETH }; await dataStore.setAddress(keys.FEE_RECEIVER, user3.address); - - const wntAmount = expandDecimals(15, 18); // 15 ETH - const usdcAmount = expandDecimals(75_000, 6); // 75,000 USDC - await wnt.mint(user0.address, wntAmount); - await usdc.mint(user0.address, usdcAmount); - - // mock wnt bridging (increase user's wnt multichain balance) - const encodedMessageEth = ethers.utils.defaultAbiCoder.encode( - ["address", "address", "uint256"], - [user0.address, wnt.address, createDepositParams.srcChainId] - ); - await wnt.connect(user0).approve(mockStargatePool.address, wntAmount); - await mockStargatePool - .connect(user0) - .sendToken(wnt.address, layerZeroProvider.address, wntAmount, encodedMessageEth); - // mock usdc bridging (increase user's usdc multichain balance) - const encodedMessageUsdc = ethers.utils.defaultAbiCoder.encode( - ["address", "address", "uint256"], - [user0.address, usdc.address, createDepositParams.srcChainId] - ); - await usdc.connect(user0).approve(mockStargatePool.address, usdcAmount); - await mockStargatePool - .connect(user0) - .sendToken(usdc.address, layerZeroProvider.address, usdcAmount, encodedMessageUsdc); + await mintAndBridge(fixture, { token: wnt, tokenAmount: wntAmount.add(feeAmount) }); + await mintAndBridge(fixture, { token: usdc, tokenAmount: usdcAmount }); }); describe("createDeposit", () => { it("creates deposit and sends relayer fee", async () => { + // funds have already been bridged to multichainVault and recorder under user's multichain balance + expect(await wnt.balanceOf(multichainVault.address)).eq(expandDecimals(10_006, 15)); // 10 + 0.006 = 10.006 ETH + expect(await usdc.balanceOf(multichainVault.address)).eq(expandDecimals(45_000, 6)); expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, wnt.address))).to.eq( - expandDecimals(15, 18) - ); // 15 ETH + expandDecimals(10_006, 15) + ); expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, usdc.address))).to.eq( - expandDecimals(75_000, 6) - ); // 75,000 USDC + expandDecimals(45_000, 6) + ); + expect(await wnt.balanceOf(depositVault.address)).eq(0); + expect(await usdc.balanceOf(depositVault.address)).eq(0); expect(await wnt.balanceOf(GELATO_RELAY_ADDRESS)).to.eq(0); expect(await wnt.balanceOf(user3.address)).eq(0); // FEE_RECEIVER await sendCreateDeposit(createDepositParams); - // user's multichain balance was decreased by the deposit amounts + 0.005 ETH fee - expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, wnt.address))).to.eq( - // fee is paid first, transfers are proccessed afterwards => user must bridge deposit + fee - // TODO: should the 0.005 fee be taken from deposit instead of user's multichain balance - // e.g. if there are exactly 10 WNT in user's multichain balance and does a 10 WNT deposit, tx fails because there are no additional funds to pay the fee - expandDecimals(4_995, 15) - ); // 15 - 10 - 0.005 = 4.995 ETH - expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, usdc.address))).to.eq( - expandDecimals(25_000, 6) - ); // 75,000 - 50,000 = 25,000 USDC - expect(await wnt.balanceOf(GELATO_RELAY_ADDRESS)).to.eq(createDepositParams.relayFeeAmount); // 0.002 ETH + // createDeposit moves the funds from multichainVault to depositVault and decreases user's multichain balance + // fee is paid first, transfers are proccessed afterwards => user must bridge deposit + fee + // e.g. if there are exactly 10 WNT in user's multichain balance and does a 10 WNT deposit, tx fails because there are no additional funds to pay the fee + expect(await wnt.balanceOf(multichainVault.address)).eq(0); + expect(await usdc.balanceOf(multichainVault.address)).eq(0); + expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, wnt.address))).to.eq(0); + expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, usdc.address))).to.eq(0); + expect(await wnt.balanceOf(depositVault.address)).eq(expandDecimals(10_004, 15)); // deposit + residualFee + expect(await usdc.balanceOf(depositVault.address)).eq(expandDecimals(45_000, 6)); + expect(await wnt.balanceOf(GELATO_RELAY_ADDRESS)).to.eq(expandDecimals(2, 15)); // createDepositParams.relayFeeAmount expect(await wnt.balanceOf(user3.address)).eq(0); // FEE_RECEIVER + // check deposit was created correctly const depositKeys = await getDepositKeys(dataStore, 0, 1); const deposit = await reader.getDeposit(dataStore.address, depositKeys[0]); expect(deposit.addresses.account).eq(user0.address); - expect(deposit.addresses.receiver).eq(defaultParams.addresses.receiver); - expect(deposit.addresses.callbackContract).eq(defaultParams.addresses.callbackContract); - expect(deposit.addresses.market).eq(defaultParams.addresses.market); + expect(deposit.addresses.receiver).eq(defaultDepositParams.addresses.receiver); + expect(deposit.addresses.callbackContract).eq(defaultDepositParams.addresses.callbackContract); + expect(deposit.addresses.market).eq(defaultDepositParams.addresses.market); expect(deposit.addresses.initialLongToken).eq(createDepositParams.transferRequests.tokens[0]); expect(deposit.addresses.initialShortToken).eq(createDepositParams.transferRequests.tokens[1]); - expect(deposit.addresses.longTokenSwapPath).deep.eq(defaultParams.addresses.longTokenSwapPath); - expect(deposit.addresses.shortTokenSwapPath).deep.eq(defaultParams.addresses.shortTokenSwapPath); - expect(deposit.numbers.initialLongTokenAmount).eq(createDepositParams.transferRequests.amounts[0]); // 10 ETH - expect(deposit.numbers.initialShortTokenAmount).eq(createDepositParams.transferRequests.amounts[1]); // 50,000 USDC - expect(deposit.numbers.minMarketTokens).eq(defaultParams.minMarketTokens); - expect(deposit.numbers.executionFee).eq( - ethers.BigNumber.from(createDepositParams.feeParams.feeAmount).sub(createDepositParams.relayFeeAmount) - ); // 0.005 - 0.002 = 0.003 ETH - expect(deposit.numbers.callbackGasLimit).eq(defaultParams.callbackGasLimit); - expect(deposit.flags.shouldUnwrapNativeToken).eq(defaultParams.shouldUnwrapNativeToken); - expect(deposit._dataList).deep.eq(defaultParams.dataList); + expect(deposit.addresses.longTokenSwapPath).deep.eq(defaultDepositParams.addresses.longTokenSwapPath); + expect(deposit.addresses.shortTokenSwapPath).deep.eq(defaultDepositParams.addresses.shortTokenSwapPath); + expect(deposit.numbers.initialLongTokenAmount).eq(createDepositParams.transferRequests.amounts[0]); // 10.006 ETH + expect(deposit.numbers.initialShortTokenAmount).eq(createDepositParams.transferRequests.amounts[1]); // 45,000.00 USDC + expect(deposit.numbers.minMarketTokens).eq(defaultDepositParams.minMarketTokens); + expect(deposit.numbers.executionFee).eq(expandDecimals(4, 15)); // 0.006 - 0.002 = 0.004 ETH + expect(deposit.numbers.callbackGasLimit).eq(defaultDepositParams.callbackGasLimit); + expect(deposit.flags.shouldUnwrapNativeToken).eq(defaultDepositParams.shouldUnwrapNativeToken); + expect(deposit._dataList).deep.eq(defaultDepositParams.dataList); + + // state before executing deposit + expect(await getDepositCount(dataStore)).eq(1); + expect(await wnt.balanceOf(depositVault.address)).eq(expandDecimals(10_004, 15)); // 10 + 0.006 - 0.002 = 10.004 ETH + expect(await usdc.balanceOf(depositVault.address)).eq(expandDecimals(45_000, 6)); // 45,000 USDC + expect(await wnt.balanceOf(ethUsdMarket.marketToken)).eq(0); + expect(await usdc.balanceOf(ethUsdMarket.marketToken)).eq(0); + expect(await getBalanceOf(ethUsdMarket.marketToken, multichainVault.address)).eq(0); // 0 GM + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, ethUsdMarket.marketToken))).to.eq(0); // 0 GM + + // moves funds from depositVault to market and mints GM tokens + // GM tkens are minted to multichainVault and user's multichain balance is increased + await executeDeposit(fixture, { gasUsageLabel: "executeDeposit" }); + + // state after executing deposit + expect(await getDepositCount(dataStore)).eq(0); + expect(await wnt.balanceOf(multichainVault.address)).eq(expandDecimals(4, 15)); + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, wnt.address))).to.eq( + expandDecimals(4, 15) + ); // TODO: Why was executionFee returned to multichainVault/user1's multichain balance? + expect(await usdc.balanceOf(multichainVault.address)).eq(0); + expect(await wnt.balanceOf(depositVault.address)).eq(0); + expect(await usdc.balanceOf(depositVault.address)).eq(0); + expect(await wnt.balanceOf(ethUsdMarket.marketToken)).eq(expandDecimals(10, 18)); + expect(await usdc.balanceOf(ethUsdMarket.marketToken)).eq(expandDecimals(45_000, 6)); + expect(await getBalanceOf(ethUsdMarket.marketToken, multichainVault.address)).eq(expandDecimals(95_000, 18)); // 95,000 GM + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, ethUsdMarket.marketToken))).to.eq( + expandDecimals(95_000, 18) + ); // 95,000 GM }); }); @@ -178,8 +214,8 @@ describe("MultichainGmRouter", () => { callbackContract: user2.address, uiFeeReceiver: user2.address, market: ethUsdMarket.marketToken, - longTokenSwapPath: [ethUsdMarket.marketToken], - shortTokenSwapPath: [ethUsdMarket.marketToken], + longTokenSwapPath: [], + shortTokenSwapPath: [], }, minLongTokenAmount: 0, minShortTokenAmount: 0, @@ -194,13 +230,13 @@ describe("MultichainGmRouter", () => { signer: user1, // user1 was the receiver of the deposit feeParams: { feeToken: wnt.address, - feeAmount: expandDecimals(5, 15), // 0.005 ETH + feeAmount: expandDecimals(7, 15), // 0.007 ETH feeSwapPath: [], }, transferRequests: { tokens: [ethUsdMarket.marketToken], receivers: [withdrawalVault.address], - amounts: [expandDecimals(100_000, 18)], + amounts: [expandDecimals(95_000, 18)], }, account: user1.address, // user1 was the receiver of the deposit params: defaultWithdrawalParams, @@ -210,56 +246,80 @@ describe("MultichainGmRouter", () => { desChainId: chainId, relayRouter: multichainGmRouter, relayFeeToken: wnt.address, - relayFeeAmount: expandDecimals(2, 15), // 0.002 ETH + relayFeeAmount: expandDecimals(3, 15), // 0.003 ETH }; }); it("creates withdrawal and sends relayer fee", async () => { - await sendCreateDeposit(createDepositParams); + await sendCreateDeposit(createDepositParams); // leaves the residualFee (i.e. executionfee) of 0.004 ETH fee in multichainVault/user's multichain balance + await mintAndBridge(fixture, { account: user1, token: wnt, tokenAmount: expandDecimals(3, 15) }); // add additional fee to user1's multichain balance + await executeDeposit(fixture, { gasUsageLabel: "executeDeposit" }); - // const _initialLongToken = await contractAt("MintableToken", defaultParams.addresses.initialLongToken); - // await _initialLongToken.mint(depositVault.address, createDepositParams.transferRequests.amounts[0]); - // const _initialShortToken = await contractAt("MintableToken", defaultParams.addresses.initialShortToken); - // await _initialShortToken.mint(depositVault.address, createDepositParams.transferRequests.amounts[1]); + expect(await getWithdrawalCount(dataStore)).eq(0); + expect(await wnt.balanceOf(multichainVault.address)).eq(expandDecimals(7, 15)); + expect(await usdc.balanceOf(multichainVault.address)).eq(0); + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, wnt.address))).to.eq( + expandDecimals(7, 15) + ); + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, usdc.address))).to.eq(0); + expect(await wnt.balanceOf(withdrawalVault.address)).eq(0); + expect(await usdc.balanceOf(withdrawalVault.address)).eq(0); + expect(await getBalanceOf(ethUsdMarket.marketToken, multichainVault.address)).eq(expandDecimals(95_000, 18)); // 95,000 GM + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, ethUsdMarket.marketToken))).to.eq( + expandDecimals(95_000, 18) + ); // 95,000 GM - expect(await wnt.balanceOf(user0.address)).eq(0); - expect(await usdc.balanceOf(user0.address)).eq(0); - expect(await getBalanceOf(ethUsdMarket.marketToken, user1.address)).eq(0); // GM - expect(await wnt.balanceOf(depositVault.address)).eq(expandDecimals(10, 18).add(expandDecimals(3, 15))); // 10.003 ETH - expect(await usdc.balanceOf(depositVault.address)).eq(expandDecimals(50_000, 6)); // 50,000 USDC + // moves the GM from multichainVault to withdrawalVault and decreases user's GM multichain balance + // GM tokens are burned + // wnt/usdc are sent to multichainVault and user's multichain balance is increased + await sendCreateWithdrawal(createWithdrawalParams); + expect(await getWithdrawalCount(dataStore)).eq(1); - // TODO: Deposit was cancelled: {"name":"UsdDeltaExceedsPoolValue","args":["-50000000000000000000000000000000000","0"]} - // if commenting out utils/deposit.ts/L140 => throw new Error(`Deposit was cancelled: ${getErrorString(cancellationReason)}`); - // then contracts execute the deposit, but funds are returned to user0 and no GM tokens are minted to user1 - expect(await getDepositCount(dataStore)).eq(1); - await executeDeposit(fixture, { gasUsageLabel: "executeDeposit" }); - expect(await getDepositCount(dataStore)).eq(0); + const withdrawalKeys = await getWithdrawalKeys(dataStore, 0, 1); + const withdrawal = await reader.getWithdrawal(dataStore.address, withdrawalKeys[0]); + expect(withdrawal.addresses.account).eq(user1.address); + expect(withdrawal.addresses.receiver).eq(defaultWithdrawalParams.addresses.receiver); + expect(withdrawal.addresses.callbackContract).eq(defaultWithdrawalParams.addresses.callbackContract); + expect(withdrawal.addresses.market).eq(defaultWithdrawalParams.addresses.market); + expect(withdrawal.numbers.marketTokenAmount).eq(createWithdrawalParams.transferRequests.amounts[0]); // 95,000 GM + expect(withdrawal.numbers.minLongTokenAmount).eq(createWithdrawalParams.params.minLongTokenAmount); + expect(withdrawal.numbers.minShortTokenAmount).eq(createWithdrawalParams.params.minShortTokenAmount); + expect(withdrawal.numbers.executionFee).eq(expandDecimals(4, 15)); // 0.007 - 0.003 = 0.004 ETH (feeAmount - relayFeeAmount) + expect(withdrawal.numbers.callbackGasLimit).eq(createWithdrawalParams.params.callbackGasLimit); + expect(withdrawal.flags.shouldUnwrapNativeToken).eq(createWithdrawalParams.params.shouldUnwrapNativeToken); + expect(withdrawal._dataList).deep.eq(createWithdrawalParams.params.dataList); - // expect(await wnt.balanceOf(user0.address)).eq(0); // TODO: executeDeposit failed and 10 ETH was returned to user0 - // expect(await usdc.balanceOf(user0.address)).eq(0); // TODO: executeDeposit failed and 50,000 USDC was returned to user0 - // expect(await getBalanceOf(ethUsdMarket.marketToken, user1.address)).eq(expandDecimals(100_000, 18)); // TODO: executeDeposit failed and no GM tokens were minted to user1 - expect(await wnt.balanceOf(depositVault.address)).eq(0); - expect(await usdc.balanceOf(depositVault.address)).eq(0); + // check gm tokens have been burned and wnt/usdc have been sent to withdrawalVault and user's multichain balance has been increased + expect(await getWithdrawalCount(dataStore)).eq(1); + expect(await wnt.balanceOf(multichainVault.address)).eq(0); // fee was sent to withdrawalVault + expect(await usdc.balanceOf(multichainVault.address)).eq(0); + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, wnt.address))).to.eq(0); // user's fee was sent to withdrawalVault + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, usdc.address))).to.eq(0); + expect(await wnt.balanceOf(withdrawalVault.address)).eq(expandDecimals(4, 15)); // 0.004 ETH --> executionFee is sent to withdrawalVault + expect(await usdc.balanceOf(withdrawalVault.address)).eq(0); + expect(await getBalanceOf(ethUsdMarket.marketToken, multichainVault.address)).eq(0); // GM tokens were transferred out from multichainVault + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, ethUsdMarket.marketToken))).to.eq(0); // user's multichain balance was decreased + expect(await getBalanceOf(ethUsdMarket.marketToken, withdrawalVault.address)).eq(expandDecimals(95_000, 18)); // GM tokens were transferred into withdrawalVault - // TODO: fix executeDeposit to mint GM tokens + // executeWithdrawal + // moves funds from withdrawalVault to market and burns GM tokens + // GM tokens are burned and wnt/usdc are sent to multichainVault and user's multichain balance is increased + await executeWithdrawal(fixture, { gasUsageLabel: "executeWithdrawal" }); + // state after execute withdrawal expect(await getWithdrawalCount(dataStore)).eq(0); - // await sendCreateWithdrawal(createWithdrawalParams); - // expect(await getWithdrawalCount(dataStore)).eq(1); - - // const withdrawalKeys = await getWithdrawalKeys(dataStore, 0, 1); - // const withdrawal = await reader.getWithdrawal(dataStore.address, withdrawalKeys[0]); - // expect(withdrawal.addresses.account).eq(user1.address); - // expect(withdrawal.addresses.receiver).eq(defaultWithdrawalParams.addresses.receiver); - // expect(withdrawal.addresses.callbackContract).eq(defaultWithdrawalParams.addresses.callbackContract); - // expect(withdrawal.addresses.market).eq(defaultWithdrawalParams.addresses.market); - // expect(withdrawal.numbers.marketTokenAmount).eq(createWithdrawalParams.transferRequests.amounts[0]); // 100,000 GM - // expect(withdrawal.numbers.minLongTokenAmount).eq(createWithdrawalParams.params.minLongTokenAmount); - // expect(withdrawal.numbers.minShortTokenAmount).eq(createWithdrawalParams.params.minShortTokenAmount); - // expect(withdrawal.numbers.executionFee).eq(createWithdrawalParams.params.executionFee); - // expect(withdrawal.numbers.callbackGasLimit).eq(createWithdrawalParams.params.callbackGasLimit); - // expect(withdrawal.flags.shouldUnwrapNativeToken).eq(createWithdrawalParams.params.shouldUnwrapNativeToken); - // expect(withdrawal._dataList).deep.eq(createWithdrawalParams.params.dataList); + expect(await wnt.balanceOf(multichainVault.address)).eq(expandDecimals(10_004, 15)); + expect(await usdc.balanceOf(multichainVault.address)).eq(expandDecimals(45_000, 6)); + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, wnt.address))).to.eq( + expandDecimals(10_004, 15) + ); + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, usdc.address))).to.eq( + expandDecimals(45_000, 6) + ); + expect(await wnt.balanceOf(withdrawalVault.address)).eq(0); // all wnt was sent to multichainVault + expect(await usdc.balanceOf(withdrawalVault.address)).eq(0); // all usdc was sent to multichainVault + expect(await getBalanceOf(ethUsdMarket.marketToken, multichainVault.address)).eq(0); // all GM tokens were burned + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, ethUsdMarket.marketToken))).to.eq(0); // all user's GM tokens were burned }); }); }); diff --git a/utils/relay/multichain.ts b/utils/relay/multichain.ts index aa99fbff3..0ea5c25c3 100644 --- a/utils/relay/multichain.ts +++ b/utils/relay/multichain.ts @@ -3,9 +3,8 @@ import { GELATO_RELAY_ADDRESS } from "./addresses"; import { hashRelayParams, signTypedData } from "./helpers"; import { getDomain } from "./helpers"; import { getRelayParams } from "./helpers"; -import { contractAt } from "../deploy"; -export async function sendCreateDeposit(p: { +interface SendCreate { signer: ethers.Signer; sender: ethers.Signer; oracleParams?: { @@ -40,7 +39,9 @@ export async function sendCreateDeposit(p: { relayRouter: ethers.Contract; relayFeeToken: string; relayFeeAmount: BigNumberish; -}) { +} + +export async function sendCreateDeposit(p: SendCreate) { const relayParams = await getRelayParams(p); let signature = p.signature; @@ -65,27 +66,7 @@ export async function sendCreateDeposit(p: { }); } -export async function sendCreateWithdrawal(p: { - signer: ethers.Signer; - sender: ethers.Signer; - transferRequests: { - tokens: string[]; - receivers: string[]; - amounts: BigNumberish[]; - }; - account: string; - params: any; - signature?: string; - userNonce?: BigNumberish; - chainId: BigNumberish; - srcChainId: BigNumberish; - desChainId: BigNumberish; - feeParams: any; - deadline: BigNumberish; - relayRouter: ethers.Contract; - relayFeeToken: string; - relayFeeAmount: BigNumberish; -}) { +export async function sendCreateWithdrawal(p: SendCreate) { const relayParams = await getRelayParams(p); let signature = p.signature; if (!signature) { From f1645a50960642a463b696fa508b642c220c3920 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 24 Feb 2025 08:12:37 +0200 Subject: [PATCH 094/205] Revert "immutable" to state variable. It was removed as a temporary fix to reduce the contract size and it's not needed anymore. Different refactoring was implemented --- contracts/router/relay/BaseGelatoRelayRouter.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/router/relay/BaseGelatoRelayRouter.sol b/contracts/router/relay/BaseGelatoRelayRouter.sol index 23297b7ab..e87dcccc9 100644 --- a/contracts/router/relay/BaseGelatoRelayRouter.sol +++ b/contracts/router/relay/BaseGelatoRelayRouter.sol @@ -36,7 +36,7 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, Router public immutable router; DataStore public immutable dataStore; EventEmitter public immutable eventEmitter; - IExternalHandler public externalHandler; // TODO: immutable removed as temporary fix for contract size + IExternalHandler public immutable externalHandler; bytes32 public constant DOMAIN_SEPARATOR_TYPEHASH = keccak256(bytes("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")); From f85b9f02cc315751b63c77dabfff56bf69f646e5 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 24 Feb 2025 11:25:48 +0200 Subject: [PATCH 095/205] Split CreateShiftParams into subgroups to solve relay typehash error --- contracts/router/relay/RelayUtils.sol | 101 +++++++++++++------------- contracts/shift/ShiftUtils.sol | 34 +++++---- utils/shift.ts | 12 +-- 3 files changed, 77 insertions(+), 70 deletions(-) diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index 2a375da5d..13fd0117c 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -133,6 +133,19 @@ library RelayUtils { ) ); + bytes32 public constant CREATE_SHIFT_TYPEHASH = + keccak256( + bytes( + "CreateShift(address[] transferTokens,address[] transferReceivers,uint256[] transferAmounts,CreateShiftAddresses addresses,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList,bytes32 relayParams)CreateShiftAddresses(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket)" + ) + ); + bytes32 public constant CREATE_SHIFT_ADDRESSES_TYPEHASH = + keccak256( + bytes( + "CreateShiftAddresses(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket)" + ) + ); + bytes32 public constant CREATE_GLV_DEPOSIT_TYPEHASH = keccak256( bytes( @@ -171,18 +184,6 @@ library RelayUtils { ) ); - bytes32 public constant CREATE_SHIFT_TYPEHASH = - keccak256( - bytes( - "CreateShift(address[] transferTokens,address[] transferReceivers,uint256[] transferAmounts,CreateShiftParams params,bytes32 relayParams)CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" - ) - ); - bytes32 public constant CREATE_SHIFT_PARAMS_TYPEHASH = - keccak256( - bytes( - "CreateShiftParams(address receiver,address callbackContract,address uiFeeReceiver,address fromMarket,address toMarket,uint256 minMarketTokens,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" - ) - ); bytes32 public constant TRANSFER_REQUESTS_TYPEHASH = keccak256(bytes("TransferRequests(address[] tokens,address[] receivers,uint256[] amounts)")); @@ -401,6 +402,44 @@ library RelayUtils { ); } + function getCreateShiftStructHash( + RelayParams calldata relayParams, + TransferRequests calldata transferRequests, + ShiftUtils.CreateShiftParams memory params + ) external pure returns (bytes32) { + return + keccak256( + abi.encode( + CREATE_SHIFT_TYPEHASH, + keccak256(abi.encodePacked(transferRequests.tokens)), + keccak256(abi.encodePacked(transferRequests.receivers)), + keccak256(abi.encodePacked(transferRequests.amounts)), + _getCreateShiftAddressesStructHash(params.addresses), + params.minMarketTokens, + params.executionFee, + params.callbackGasLimit, + keccak256(abi.encodePacked(params.dataList)), + _getRelayParamsHash(relayParams) + ) + ); + } + + function _getCreateShiftAddressesStructHash( + ShiftUtils.CreateShiftParamsAddresses memory addresses + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + CREATE_SHIFT_ADDRESSES_TYPEHASH, + addresses.receiver, + addresses.callbackContract, + addresses.uiFeeReceiver, + addresses.fromMarket, + addresses.toMarket + ) + ); + } + function getCreateGlvDepositStructHash( RelayParams calldata relayParams, TransferRequests calldata transferRequests, @@ -511,44 +550,6 @@ library RelayUtils { ); } - function getCreateShiftStructHash( - RelayParams calldata relayParams, - TransferRequests calldata transferRequests, - ShiftUtils.CreateShiftParams memory params - ) external pure returns (bytes32) { - return - keccak256( - abi.encode( - CREATE_SHIFT_TYPEHASH, - keccak256(abi.encodePacked(transferRequests.tokens)), - keccak256(abi.encodePacked(transferRequests.receivers)), - keccak256(abi.encodePacked(transferRequests.amounts)), - _getCreateShiftParamsStructHash(params), - _getRelayParamsHash(relayParams) - ) - ); - } - - function _getCreateShiftParamsStructHash( - ShiftUtils.CreateShiftParams memory params - ) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - CREATE_SHIFT_PARAMS_TYPEHASH, - params.receiver, - params.callbackContract, - params.uiFeeReceiver, - params.fromMarket, - params.toMarket, - params.minMarketTokens, - params.executionFee, - params.callbackGasLimit, - keccak256(abi.encodePacked(params.dataList)) - ) - ); - } - function getBridgeOutStructHash( RelayParams calldata relayParams, BridgeOutParams memory params diff --git a/contracts/shift/ShiftUtils.sol b/contracts/shift/ShiftUtils.sol index fcb479efe..26a6389fa 100644 --- a/contracts/shift/ShiftUtils.sol +++ b/contracts/shift/ShiftUtils.sol @@ -32,15 +32,19 @@ library ShiftUtils { using EventUtils for EventUtils.StringItems; struct CreateShiftParams { + CreateShiftParamsAddresses addresses; + uint256 minMarketTokens; + uint256 executionFee; + uint256 callbackGasLimit; + bytes32[] dataList; + } + + struct CreateShiftParamsAddresses { address receiver; address callbackContract; address uiFeeReceiver; address fromMarket; address toMarket; - uint256 minMarketTokens; - uint256 executionFee; - uint256 callbackGasLimit; - bytes32[] dataList; } struct CreateShiftCache { @@ -83,8 +87,8 @@ library ShiftUtils { ) external returns (bytes32) { AccountUtils.validateAccount(account); - if (params.fromMarket == params.toMarket) { - revert Errors.ShiftFromAndToMarketAreEqual(params.fromMarket); + if (params.addresses.fromMarket == params.addresses.toMarket) { + revert Errors.ShiftFromAndToMarketAreEqual(params.addresses.fromMarket); } address wnt = TokenUtils.wnt(dataStore); @@ -94,9 +98,9 @@ library ShiftUtils { revert Errors.InsufficientWntAmountForExecutionFee(wntAmount, params.executionFee); } - AccountUtils.validateReceiver(params.receiver); + AccountUtils.validateReceiver(params.addresses.receiver); - uint256 marketTokenAmount = shiftVault.recordTransferIn(params.fromMarket); + uint256 marketTokenAmount = shiftVault.recordTransferIn(params.addresses.fromMarket); if (marketTokenAmount == 0) { revert Errors.EmptyShiftAmount(); @@ -104,8 +108,8 @@ library ShiftUtils { params.executionFee = wntAmount; - Market.Props memory fromMarket = MarketUtils.getEnabledMarket(dataStore, params.fromMarket); - Market.Props memory toMarket = MarketUtils.getEnabledMarket(dataStore, params.toMarket); + Market.Props memory fromMarket = MarketUtils.getEnabledMarket(dataStore, params.addresses.fromMarket); + Market.Props memory toMarket = MarketUtils.getEnabledMarket(dataStore, params.addresses.toMarket); if (fromMarket.longToken != toMarket.longToken) { revert Errors.LongTokensAreNotEqual(fromMarket.longToken, toMarket.longToken); @@ -118,11 +122,11 @@ library ShiftUtils { Shift.Props memory shift = Shift.Props( Shift.Addresses( account, - params.receiver, - params.callbackContract, - params.uiFeeReceiver, - params.fromMarket, - params.toMarket + params.addresses.receiver, + params.addresses.callbackContract, + params.addresses.uiFeeReceiver, + params.addresses.fromMarket, + params.addresses.toMarket ), Shift.Numbers( marketTokenAmount, diff --git a/utils/shift.ts b/utils/shift.ts index b1c92579f..fa33a5494 100644 --- a/utils/shift.ts +++ b/utils/shift.ts @@ -48,11 +48,13 @@ export async function createShift(fixture, overrides: any = {}) { await marketToken.connect(account).transfer(shiftVault.address, marketTokenAmount); const params = { - receiver: receiver.address, - callbackContract: callbackContract.address, - uiFeeReceiver: uiFeeReceiver.address, - fromMarket: fromMarket.marketToken, - toMarket: toMarket.marketToken, + addresses: { + receiver: receiver.address, + callbackContract: callbackContract.address, + uiFeeReceiver: uiFeeReceiver.address, + fromMarket: fromMarket.marketToken, + toMarket: toMarket.marketToken, + }, minMarketTokens, executionFee, callbackGasLimit, From 11d29f05677b3ae8fd647b2650c02d5350944b6f Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 24 Feb 2025 18:21:18 +0200 Subject: [PATCH 096/205] Add bridgeIn and move bridgeOut from MultichainOrderRouter into MultichainTransferRouter --- .../multichain/MultichainOrderRouter.sol | 36 +--------- .../multichain/MultichainTransferRouter.sol | 71 +++++++++++++++++++ contracts/router/relay/RelayUtils.sol | 38 +++++++--- 3 files changed, 100 insertions(+), 45 deletions(-) create mode 100644 contracts/multichain/MultichainTransferRouter.sol diff --git a/contracts/multichain/MultichainOrderRouter.sol b/contracts/multichain/MultichainOrderRouter.sol index ad9121eb7..ac21d11aa 100644 --- a/contracts/multichain/MultichainOrderRouter.sol +++ b/contracts/multichain/MultichainOrderRouter.sol @@ -3,14 +3,10 @@ pragma solidity ^0.8.0; import "./MultichainRouter.sol"; -import "./IMultichainProvider.sol"; contract MultichainOrderRouter is MultichainRouter { - IMultichainProvider multichainProvider; - constructor(BaseConstructorParams memory params, IMultichainProvider _multichainProvider) MultichainRouter(params) { - multichainProvider = _multichainProvider; - } + constructor(BaseConstructorParams memory params) MultichainRouter(params) {} // TODO: handle partial fee payment @@ -60,34 +56,4 @@ contract MultichainOrderRouter is MultichainRouter { _cancelOrder(relayParams, account, key, false /* isSubaccount */); } - - function bridgeOut( - RelayUtils.RelayParams calldata relayParams, - address provider, - address receiver, - uint256 srcChainId, - bytes calldata data, // encoded provider specific data e.g. dstEid - RelayUtils.BridgeOutParams calldata params - ) external nonReentrant onlyGelatoRelay { - _validateDesChainId(relayParams.desChainId); - _validateMultichainProvider(dataStore, provider); - - bytes32 structHash = RelayUtils.getBridgeOutStructHash(relayParams, params); - _validateCall(relayParams, receiver, structHash, srcChainId); - - multichainProvider.bridgeOut( - provider, - receiver, - params.token, - params.amount, - data - ); - } - - function _validateMultichainProvider(DataStore dataStore, address provider) internal view { - bytes32 providerKey = Keys.isMultichainProviderEnabledKey(provider); - if (!dataStore.getBool(providerKey)) { - revert Errors.InvalidMultichainProvider(provider); - } - } } diff --git a/contracts/multichain/MultichainTransferRouter.sol b/contracts/multichain/MultichainTransferRouter.sol new file mode 100644 index 000000000..a7fdf8d69 --- /dev/null +++ b/contracts/multichain/MultichainTransferRouter.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; + +import "./MultichainRouter.sol"; +import "./MultichainUtils.sol"; +import "./IMultichainProvider.sol"; + +contract MultichainTransferRouter is MultichainRouter { // TODO: inherit BaseRouter for bridgeIn multicall + IMultichainProvider multichainProvider; + + constructor(BaseConstructorParams memory params, IMultichainProvider _multichainProvider) MultichainRouter(params) { + multichainProvider = _multichainProvider; + } + + /** + * payable function so that it can be called as a multicall + * if a user is liquidated or ADLed, the funds would be sent to the user's account on Arbitrum + * this would be used to move those funds into their multichain balance + */ + function bridgeIn( + RelayUtils.RelayParams calldata relayParams, + address account, + uint256 srcChainId, + RelayUtils.BridgeInParams calldata params + ) external payable nonReentrant onlyGelatoRelay { + _validateDesChainId(relayParams.desChainId); + + bytes32 structHash = RelayUtils.getBridgeInStructHash(relayParams, params); + _validateCall(relayParams, account, structHash, srcChainId); + + MultichainUtils.recordTransferIn( + dataStore, + eventEmitter, + multichainVault, + params.token, + account, + srcChainId + ); + } + + function bridgeOut( + RelayUtils.RelayParams calldata relayParams, + address provider, + address receiver, + uint256 srcChainId, + bytes calldata data, // encoded provider specific data e.g. dstEid + RelayUtils.BridgeOutParams calldata params + ) external nonReentrant onlyGelatoRelay { + _validateDesChainId(relayParams.desChainId); + _validateMultichainProvider(dataStore, provider); + + bytes32 structHash = RelayUtils.getBridgeOutStructHash(relayParams, params); + _validateCall(relayParams, receiver, structHash, srcChainId); + + multichainProvider.bridgeOut( + provider, + receiver, + params.token, + params.amount, + data + ); + } + + function _validateMultichainProvider(DataStore dataStore, address provider) internal view { + bytes32 providerKey = Keys.isMultichainProviderEnabledKey(provider); + if (!dataStore.getBool(providerKey)) { + revert Errors.InvalidMultichainProvider(provider); + } + } +} diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index 13fd0117c..f868be0d4 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -63,6 +63,10 @@ library RelayUtils { uint256[] amounts; } + struct BridgeInParams { + address token; + } + struct BridgeOutParams { address token; address receiver; @@ -188,17 +192,16 @@ library RelayUtils { bytes32 public constant TRANSFER_REQUESTS_TYPEHASH = keccak256(bytes("TransferRequests(address[] tokens,address[] receivers,uint256[] amounts)")); - bytes32 public constant BRIDGE_OUT_TYPEHASH = + bytes32 public constant BRIDGE_IN_TYPEHASH = keccak256( bytes( - "BridgeOut(BridgeOutParams params,bytes32 relayParams)BridgeOutParams(address token,uint256 amount)" + "BridgeIn(address token,bytes32 relayParams)" ) ); - - bytes32 public constant BRIDGE_OUT_PARAMS_TYPEHASH = + bytes32 public constant BRIDGE_OUT_TYPEHASH = keccak256( bytes( - "BridgeOutParams(address token,uint256 amount)" + "BridgeOut(address token,uint256 amount,bytes32 relayParams)" ) ); @@ -550,17 +553,32 @@ library RelayUtils { ); } - function getBridgeOutStructHash( + function getBridgeInStructHash( RelayParams calldata relayParams, - BridgeOutParams memory params + BridgeInParams memory params ) external pure returns (bytes32) { return keccak256( - abi.encode(BRIDGE_OUT_TYPEHASH, _getBridgeOutParamsStructHash(params), _getRelayParamsHash(relayParams)) + abi.encode( + BRIDGE_IN_TYPEHASH, + params.token, + _getRelayParamsHash(relayParams) + ) ); } - function _getBridgeOutParamsStructHash(BridgeOutParams memory params) internal pure returns (bytes32) { - return keccak256(abi.encode(BRIDGE_OUT_PARAMS_TYPEHASH, params.token, params.amount)); + function getBridgeOutStructHash( + RelayParams calldata relayParams, + BridgeOutParams memory params + ) external pure returns (bytes32) { + return + keccak256( + abi.encode( + BRIDGE_OUT_TYPEHASH, + params.token, + params.amount, + _getRelayParamsHash(relayParams) + ) + ); } } From 380d97d991ba28ffedf5504bce9f62c3a8c3e38e Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 24 Feb 2025 21:04:18 +0200 Subject: [PATCH 097/205] Test multichain withdrawal (wip) --- contracts/error/Errors.sol | 4 +- contracts/multichain/MultichainUtils.sol | 4 +- deploy/deployMultichainGmRouter.ts | 10 +-- test/multichain/MultichainGmRouter.ts | 83 +++++++++++++++++++- utils/relay/multichain.ts | 98 ++++++++++++++++++++++++ 5 files changed, 184 insertions(+), 15 deletions(-) diff --git a/contracts/error/Errors.sol b/contracts/error/Errors.sol index 4736484d9..3357b7e1b 100644 --- a/contracts/error/Errors.sol +++ b/contracts/error/Errors.sol @@ -431,8 +431,8 @@ library Errors { // Multichain errors error InvalidTransferRequestsLength(); - error EmptyMultichainTransferInAmount(); - error EmptyMultichainTransferOutAmount(); + error EmptyMultichainTransferInAmount(address account, address token); + error EmptyMultichainTransferOutAmount(address account, address token); error InsufficientMultichainBalance(address account, address token, uint256 balance, uint256 amount); error InvalidDestinationChainId(uint256 desChainId); error InvalidMultichainProvider(address provider); diff --git a/contracts/multichain/MultichainUtils.sol b/contracts/multichain/MultichainUtils.sol index eebf1e02a..ab8a25c4b 100644 --- a/contracts/multichain/MultichainUtils.sol +++ b/contracts/multichain/MultichainUtils.sol @@ -35,7 +35,7 @@ library MultichainUtils { // token should have been transferred to multichainVault by IMultichainProvider uint256 amount = multichainVault.recordTransferIn(token); if (amount == 0) { - revert Errors.EmptyMultichainTransferInAmount(); + revert Errors.EmptyMultichainTransferInAmount(account, token); } dataStore.incrementUint(Keys.multichainBalanceKey(account, token), amount); @@ -61,7 +61,7 @@ library MultichainUtils { uint256 srcChainId ) external { if (amount == 0) { - revert Errors.EmptyMultichainTransferOutAmount(); + revert Errors.EmptyMultichainTransferOutAmount(account, token); } bytes32 balanceKey = Keys.multichainBalanceKey(account, token); diff --git a/deploy/deployMultichainGmRouter.ts b/deploy/deployMultichainGmRouter.ts index c0e6ec52f..18bed8d09 100644 --- a/deploy/deployMultichainGmRouter.ts +++ b/deploy/deployMultichainGmRouter.ts @@ -38,15 +38,7 @@ const func = createDeployFunction({ dependencyContracts.ShiftVault.address, ]; }, - libraryNames: [ - "MarketStoreUtils", - "MultichainUtils", - "OrderStoreUtils", - "RelayUtils", - "ShiftUtils", - "SwapUtils", - "MarketUtils", - ], + libraryNames: ["MultichainUtils", "OrderStoreUtils", "RelayUtils", "ShiftUtils", "SwapUtils", "MarketUtils"], afterDeploy: async ({ deployedContract }) => { await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); diff --git a/test/multichain/MultichainGmRouter.ts b/test/multichain/MultichainGmRouter.ts index 12c2d90ed..5037706cf 100644 --- a/test/multichain/MultichainGmRouter.ts +++ b/test/multichain/MultichainGmRouter.ts @@ -4,17 +4,28 @@ import { impersonateAccount, setBalance } from "@nomicfoundation/hardhat-network import { expandDecimals } from "../../utils/math"; import { deployFixture } from "../../utils/fixture"; import { GELATO_RELAY_ADDRESS } from "../../utils/relay/addresses"; -import { sendCreateDeposit, sendCreateWithdrawal } from "../../utils/relay/multichain"; +import { sendCreateDeposit, sendCreateWithdrawal, sendCreateShift } from "../../utils/relay/multichain"; import * as keys from "../../utils/keys"; import { executeDeposit, getDepositCount, getDepositKeys } from "../../utils/deposit"; import { executeWithdrawal, getWithdrawalCount, getWithdrawalKeys } from "../../utils/withdrawal"; import { getBalanceOf } from "../../utils/token"; import { BigNumberish, Contract } from "ethers"; +import { executeShift, getShiftCount, getShiftKeys } from "../../utils/shift"; describe("MultichainGmRouter", () => { let fixture; let user0, user1, user2, user3; - let reader, dataStore, multichainGmRouter, multichainVault, depositVault, withdrawalVault, ethUsdMarket, wnt, usdc; + let reader, + dataStore, + multichainGmRouter, + multichainVault, + depositVault, + withdrawalVault, + shiftVault, + ethUsdMarket, + solUsdMarket, + wnt, + usdc; let relaySigner; let chainId; @@ -62,7 +73,9 @@ describe("MultichainGmRouter", () => { multichainVault, depositVault, withdrawalVault, + shiftVault, ethUsdMarket, + solUsdMarket, wnt, usdc, } = fixture.contracts); @@ -322,4 +335,70 @@ describe("MultichainGmRouter", () => { expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, ethUsdMarket.marketToken))).to.eq(0); // all user's GM tokens were burned }); }); + + describe("createShift", () => { + let defaultShiftParams; + let createShiftParams: Parameters[0]; + const feeAmount = expandDecimals(4, 15); + + beforeEach(async () => { + defaultShiftParams = { + addresses: { + receiver: user1.address, + callbackContract: user2.address, + uiFeeReceiver: user3.address, + fromMarket: ethUsdMarket.marketToken, + toMarket: solUsdMarket.marketToken, + }, + minMarketTokens: 50, + executionFee: expandDecimals(1, 15), + callbackGasLimit: "200000", + dataList: [], + }; + + createShiftParams = { + sender: relaySigner, + signer: user1, + feeParams: { + feeToken: wnt.address, + feeAmount: feeAmount, + feeSwapPath: [], + }, + transferRequests: { + tokens: [ethUsdMarket.marketToken], + receivers: [shiftVault.address], + amounts: [expandDecimals(50_000, 18)], + }, + account: user1.address, + params: defaultShiftParams, + deadline: 9999999999, + chainId, + srcChainId: chainId, + desChainId: chainId, + relayRouter: multichainGmRouter, + relayFeeToken: wnt.address, + relayFeeAmount: expandDecimals(2, 15), + }; + }); + + it("creates shift and sends relayer fee", async () => { + await sendCreateDeposit(createDepositParams); + await executeDeposit(fixture, { gasUsageLabel: "executeDeposit" }); + + expect(await getBalanceOf(ethUsdMarket.marketToken, multichainVault.address)).eq(expandDecimals(95_000, 18)); + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, ethUsdMarket.marketToken))).to.eq( + expandDecimals(95_000, 18) + ); + await sendCreateShift(createShiftParams); + + const shiftKeys = await getShiftKeys(dataStore, 0, 1); + const shift = await reader.getShift(dataStore.address, shiftKeys[0]); + expect(shift.addresses.account).eq(user1.address); + expect(await getShiftCount(dataStore)).eq(1); + + // await executeShift(fixture, { gasUsageLabel: "executeShift" }); + // shift = await reader.getShift(dataStore.address, shiftKeys[0]); + // expect(shift.addresses.account).eq(ethers.constants.AddressZero); + }); + }); }); diff --git a/utils/relay/multichain.ts b/utils/relay/multichain.ts index 0ea5c25c3..2e56c255d 100644 --- a/utils/relay/multichain.ts +++ b/utils/relay/multichain.ts @@ -96,6 +96,36 @@ export async function sendCreateWithdrawal(p: SendCreate) { }); } +export async function sendCreateShift(p: SendCreate) { + const relayParams = await getRelayParams(p); + let signature = p.signature; + if (!signature) { + signature = await getCreateShiftSignature({ + signer: p.signer, + relayParams, + transferRequests: p.transferRequests, + verifyingContract: p.relayRouter.address, + params: p.params, + chainId: p.chainId, + }); + } + const createShiftCalldata = p.relayRouter.interface.encodeFunctionData("createShift", [ + { ...relayParams, signature }, + p.account, + p.srcChainId, + p.transferRequests, + p.params, + ]); + const calldata = ethers.utils.solidityPack( + ["bytes", "address", "address", "uint256"], + [createShiftCalldata, GELATO_RELAY_ADDRESS, p.relayFeeToken, p.relayFeeAmount] + ); + return p.sender.sendTransaction({ + to: p.relayRouter.address, + data: calldata, + }); +} + async function getCreateDepositSignature({ signer, relayParams, @@ -212,3 +242,71 @@ async function getCreateWithdrawalSignature({ const domain = getDomain(chainId, verifyingContract); return signTypedData(signer, domain, types, typedData); } + +async function getCreateShiftSignature({ + signer, + relayParams, + transferRequests, + verifyingContract, + params, + chainId, +}: { + signer: ethers.Signer; + relayParams: any; + transferRequests: { tokens: string[]; receivers: string[]; amounts: BigNumberish[] }; + verifyingContract: string; + params: { + addresses: { + receiver: string; + callbackContract: string; + uiFeeReceiver: string; + fromMarket: string; + toMarket: string; + }; + minMarketTokens: BigNumberish; + executionFee: BigNumberish; + callbackGasLimit: BigNumberish; + dataList: string[]; + }; + chainId: BigNumberish; +}) { + if (relayParams.userNonce === undefined) { + throw new Error("userNonce is required"); + } + + const types = { + CreateShift: [ + { name: "transferTokens", type: "address[]" }, + { name: "transferReceivers", type: "address[]" }, + { name: "transferAmounts", type: "uint256[]" }, + { name: "addresses", type: "CreateShiftAddresses" }, + { name: "minMarketTokens", type: "uint256" }, + { name: "executionFee", type: "uint256" }, + { name: "callbackGasLimit", type: "uint256" }, + { name: "dataList", type: "bytes32[]" }, + { name: "relayParams", type: "bytes32" }, + ], + CreateShiftAddresses: [ + { name: "receiver", type: "address" }, + { name: "callbackContract", type: "address" }, + { name: "uiFeeReceiver", type: "address" }, + { name: "fromMarket", type: "address" }, + { name: "toMarket", type: "address" }, + ], + }; + + const typedData = { + transferTokens: transferRequests.tokens, + transferReceivers: transferRequests.receivers, + transferAmounts: transferRequests.amounts, + addresses: params.addresses, + minMarketTokens: params.minMarketTokens, + executionFee: params.executionFee, + callbackGasLimit: params.callbackGasLimit, + dataList: params.dataList, + relayParams: hashRelayParams(relayParams), + }; + + const domain = getDomain(chainId, verifyingContract); + return signTypedData(signer, domain, types, typedData); +} From b0405e595777b22e7d21f38fe8bd666b1f85bc14 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 24 Feb 2025 22:40:23 +0200 Subject: [PATCH 098/205] Test multichain glv deposit, fix typehases, add deploy script --- contracts/router/relay/RelayUtils.sol | 38 +++--------- deploy/deployMultichainGlvRouter.ts | 42 +++++++++++++ test/multichain/MultichainGmRouter.ts | 77 +++++++++++++++++++++++- utils/fixture.ts | 2 + utils/relay/multichain.ts | 87 +++++++++++++++++++++++++++ 5 files changed, 215 insertions(+), 31 deletions(-) create mode 100644 deploy/deployMultichainGlvRouter.ts diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index f868be0d4..543642ced 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -152,21 +152,11 @@ library RelayUtils { bytes32 public constant CREATE_GLV_DEPOSIT_TYPEHASH = keccak256( - bytes( - "CreateGlvDeposit(address[] transferTokens,address[] transferReceivers,uint256[] transferAmounts,CreateGlvDepositParams params,bytes32 relayParams)CreateGlvDepositParams(CreateGlvDepositParamsAddresses addresses,uint256 minGlvTokens,uint256 executionFee,uint256 callbackGasLimit,bool shouldUnwrapNativeToken,bool isMarketTokenDeposit,bytes32[] dataList)" - ) + "CreateGlvDeposit(address[] transferTokens,address[] transferReceivers,uint256[] transferAmounts,CreateGlvDepositAddresses addresses,uint256 minGlvTokens,uint256 executionFee,uint256 callbackGasLimit,bool shouldUnwrapNativeToken,bool isMarketTokenDeposit,bytes32[] dataList,bytes32 relayParams)CreateGlvDepositAddresses(address glv,address market,address receiver,address callbackContract,address uiFeeReceiver,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath)" ); - bytes32 public constant CREATE_GLV_DEPOSIT_PARAMS_ADDRESSES_TYPEHASH = + bytes32 public constant CREATE_GLV_DEPOSIT_ADDRESSES_TYPEHASH = keccak256( - bytes( - "CreateGlvDepositParamsAddresses(address glv,address market,address receiver,address callbackContract,address uiFeeReceiver,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath)" - ) - ); - bytes32 public constant CREATE_GLV_DEPOSIT_PARAMS_TYPEHASH = - keccak256( - bytes( - "CreateGlvDepositParams(CreateGlvDepositParamsAddresses addresses,uint256 minGlvTokens,uint256 executionFee,uint256 callbackGasLimit,bool shouldUnwrapNativeToken,bool isMarketTokenDeposit,bytes32[] dataList)CreateGlvDepositParamsAddresses(address glv,address market,address receiver,address callbackContract,address uiFeeReceiver,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath)" - ) + "CreateGlvDepositAddresses(address glv,address market,address receiver,address callbackContract,address uiFeeReceiver,address initialLongToken,address initialShortToken,address[] longTokenSwapPath,address[] shortTokenSwapPath)" ); bytes32 public constant CREATE_GLV_WITHDRAWAL_TYPEHASH = @@ -455,37 +445,25 @@ library RelayUtils { keccak256(abi.encodePacked(transferRequests.tokens)), keccak256(abi.encodePacked(transferRequests.receivers)), keccak256(abi.encodePacked(transferRequests.amounts)), - _getCreateGlvDepositParamsStructHash(params), - _getRelayParamsHash(relayParams) - ) - ); - } - - function _getCreateGlvDepositParamsStructHash( - GlvDepositUtils.CreateGlvDepositParams memory params - ) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - CREATE_GLV_DEPOSIT_PARAMS_TYPEHASH, - _getCreateGlvDepositParamsAddressesStructHash(params.addresses), + _getCreateGlvDepositAddressesStructHash(params.addresses), params.minGlvTokens, params.executionFee, params.callbackGasLimit, params.shouldUnwrapNativeToken, params.isMarketTokenDeposit, - keccak256(abi.encodePacked(params.dataList)) + keccak256(abi.encodePacked(params.dataList)), + _getRelayParamsHash(relayParams) ) ); } - function _getCreateGlvDepositParamsAddressesStructHash( + function _getCreateGlvDepositAddressesStructHash( GlvDepositUtils.CreateGlvDepositParamsAddresses memory addresses ) internal pure returns (bytes32) { return keccak256( abi.encode( - CREATE_GLV_DEPOSIT_PARAMS_ADDRESSES_TYPEHASH, + CREATE_GLV_DEPOSIT_ADDRESSES_TYPEHASH, addresses.glv, addresses.market, addresses.receiver, diff --git a/deploy/deployMultichainGlvRouter.ts b/deploy/deployMultichainGlvRouter.ts new file mode 100644 index 000000000..b92e3e388 --- /dev/null +++ b/deploy/deployMultichainGlvRouter.ts @@ -0,0 +1,42 @@ +import { grantRoleIfNotGranted } from "../utils/role"; +import { createDeployFunction } from "../utils/deploy"; + +const baseConstructorContracts = [ + "Router", + "DataStore", + "EventEmitter", + "Oracle", + "OrderVault", + "OrderHandler", + "ExternalHandler", + "MultichainVault", +]; + +const glvConstructorContracts = ["GlvHandler", "GlvVault"]; + +const func = createDeployFunction({ + contractName: "MultichainGlvRouter", + dependencyNames: [...baseConstructorContracts, ...glvConstructorContracts], + getDeployArgs: async ({ dependencyContracts }) => { + const baseParams = { + router: dependencyContracts.Router.address, + dataStore: dependencyContracts.DataStore.address, + eventEmitter: dependencyContracts.EventEmitter.address, + oracle: dependencyContracts.Oracle.address, + orderVault: dependencyContracts.OrderVault.address, + orderHandler: dependencyContracts.OrderHandler.address, + externalHandler: dependencyContracts.ExternalHandler.address, + multichainVault: dependencyContracts.MultichainVault.address, + }; + + return [baseParams, dependencyContracts.GlvHandler.address, dependencyContracts.GlvVault.address]; + }, + libraryNames: ["MultichainUtils", "OrderStoreUtils", "RelayUtils", "SwapUtils", "MarketUtils", "GlvWithdrawalUtils"], + + afterDeploy: async ({ deployedContract }) => { + await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); + await grantRoleIfNotGranted(deployedContract.address, "ROUTER_PLUGIN"); + }, +}); + +export default func; diff --git a/test/multichain/MultichainGmRouter.ts b/test/multichain/MultichainGmRouter.ts index 5037706cf..dd9962dcc 100644 --- a/test/multichain/MultichainGmRouter.ts +++ b/test/multichain/MultichainGmRouter.ts @@ -4,13 +4,19 @@ import { impersonateAccount, setBalance } from "@nomicfoundation/hardhat-network import { expandDecimals } from "../../utils/math"; import { deployFixture } from "../../utils/fixture"; import { GELATO_RELAY_ADDRESS } from "../../utils/relay/addresses"; -import { sendCreateDeposit, sendCreateWithdrawal, sendCreateShift } from "../../utils/relay/multichain"; +import { + sendCreateDeposit, + sendCreateWithdrawal, + sendCreateShift, + sendCreateGlvDeposit, +} from "../../utils/relay/multichain"; import * as keys from "../../utils/keys"; import { executeDeposit, getDepositCount, getDepositKeys } from "../../utils/deposit"; import { executeWithdrawal, getWithdrawalCount, getWithdrawalKeys } from "../../utils/withdrawal"; import { getBalanceOf } from "../../utils/token"; import { BigNumberish, Contract } from "ethers"; import { executeShift, getShiftCount, getShiftKeys } from "../../utils/shift"; +import { executeGlvDeposit, getGlvDepositCount } from "../../utils/glv"; describe("MultichainGmRouter", () => { let fixture; @@ -18,12 +24,15 @@ describe("MultichainGmRouter", () => { let reader, dataStore, multichainGmRouter, + multichainGlvRouter, multichainVault, depositVault, withdrawalVault, shiftVault, + glvVault, ethUsdMarket, solUsdMarket, + ethUsdGlvAddress, wnt, usdc; let relaySigner; @@ -70,12 +79,15 @@ describe("MultichainGmRouter", () => { reader, dataStore, multichainGmRouter, + multichainGlvRouter, multichainVault, depositVault, withdrawalVault, shiftVault, + glvVault, ethUsdMarket, solUsdMarket, + ethUsdGlvAddress, wnt, usdc, } = fixture.contracts); @@ -401,4 +413,67 @@ describe("MultichainGmRouter", () => { // expect(shift.addresses.account).eq(ethers.constants.AddressZero); }); }); + + describe("createGlvDeposit", () => { + let defaultGlvDepositParams; + let createGlvDepositParams: Parameters[0]; + beforeEach(async () => { + defaultGlvDepositParams = { + addresses: { + glv: ethUsdGlvAddress, + receiver: user1.address, + callbackContract: user2.address, + uiFeeReceiver: user3.address, + market: ethUsdMarket.marketToken, + initialLongToken: ethers.constants.AddressZero, + initialShortToken: ethers.constants.AddressZero, + longTokenSwapPath: [], + shortTokenSwapPath: [], + }, + minGlvTokens: 100, + executionFee: 0, + callbackGasLimit: "200000", + shouldUnwrapNativeToken: true, + isMarketTokenDeposit: true, + dataList: [], + }; + + createGlvDepositParams = { + sender: relaySigner, + signer: user1, + feeParams: { + feeToken: wnt.address, + feeAmount: expandDecimals(7, 15), // 0.007 ETH + feeSwapPath: [], + }, + transferRequests: { + tokens: [ethUsdMarket.marketToken], + receivers: [glvVault.address], + amounts: [expandDecimals(95_000, 18)], + }, + account: user1.address, + params: defaultGlvDepositParams, + deadline: 9999999999, + chainId, + srcChainId: chainId, + desChainId: chainId, + relayRouter: multichainGlvRouter, + relayFeeToken: wnt.address, + relayFeeAmount: expandDecimals(3, 15), // 0.003 ETH + }; + }); + + it("creates glvDeposit and sends relayer fee", async () => { + await sendCreateDeposit(createDepositParams); // leaves the residualFee (i.e. executionfee) of 0.004 ETH fee in multichainVault/user's multichain balance + await mintAndBridge(fixture, { account: user1, token: wnt, tokenAmount: expandDecimals(3, 15) }); // add additional fee to user1's multichain balance + await executeDeposit(fixture, { gasUsageLabel: "executeDeposit" }); + + expect(await getGlvDepositCount(dataStore)).eq(0); + await sendCreateGlvDeposit(createGlvDepositParams); + expect(await getGlvDepositCount(dataStore)).eq(1); + + await executeGlvDeposit(fixture, { gasUsageLabel: "executeGlvDeposit" }); + expect(await getGlvDepositCount(dataStore)).eq(0); + }); + }); }); diff --git a/utils/fixture.ts b/utils/fixture.ts index 082e1f46a..2b954b63c 100644 --- a/utils/fixture.ts +++ b/utils/fixture.ts @@ -100,6 +100,7 @@ export async function deployFixture() { const subaccountGelatoRelayRouter = await hre.ethers.getContract("SubaccountGelatoRelayRouter"); const subaccountRouter = await hre.ethers.getContract("SubaccountRouter"); const multichainGmRouter = await hre.ethers.getContract("MultichainGmRouter"); + const multichainGlvRouter = await hre.ethers.getContract("MultichainGlvRouter"); const relayUtils = await hre.ethers.getContract("RelayUtils"); const oracle = await hre.ethers.getContract("Oracle"); const gmOracleProvider = await hre.ethers.getContract("GmOracleProvider"); @@ -284,6 +285,7 @@ export async function deployFixture() { subaccountGelatoRelayRouter, subaccountRouter, multichainGmRouter, + multichainGlvRouter, relayUtils, oracle, gmOracleProvider, diff --git a/utils/relay/multichain.ts b/utils/relay/multichain.ts index 2e56c255d..f877f2494 100644 --- a/utils/relay/multichain.ts +++ b/utils/relay/multichain.ts @@ -126,6 +126,31 @@ export async function sendCreateShift(p: SendCreate) { }); } +export async function sendCreateGlvDeposit(p: SendCreate) { + const relayParams = await getRelayParams(p); + + let signature = p.signature; + if (!signature) { + signature = await getCreateGlvDepositSignature({ ...p, relayParams, verifyingContract: p.relayRouter.address }); + } + + const createGlvDepositCalldata = p.relayRouter.interface.encodeFunctionData("createGlvDeposit", [ + { ...relayParams, signature }, + p.account, + p.srcChainId, + p.transferRequests, + p.params, + ]); + const calldata = ethers.utils.solidityPack( + ["bytes", "address", "address", "uint256"], + [createGlvDepositCalldata, GELATO_RELAY_ADDRESS, p.relayFeeToken, p.relayFeeAmount] + ); + return p.sender.sendTransaction({ + to: p.relayRouter.address, + data: calldata, + }); +} + async function getCreateDepositSignature({ signer, relayParams, @@ -310,3 +335,65 @@ async function getCreateShiftSignature({ const domain = getDomain(chainId, verifyingContract); return signTypedData(signer, domain, types, typedData); } + +async function getCreateGlvDepositSignature({ + signer, + relayParams, + transferRequests, + verifyingContract, + params, + chainId, +}: { + signer: ethers.Signer; + relayParams: any; + transferRequests: { tokens: string[]; receivers: string[]; amounts: BigNumberish[] }; + verifyingContract: string; + params: any; + chainId: BigNumberish; +}) { + if (relayParams.userNonce === undefined) { + throw new Error("userNonce is required"); + } + const types = { + CreateGlvDeposit: [ + { name: "transferTokens", type: "address[]" }, + { name: "transferReceivers", type: "address[]" }, + { name: "transferAmounts", type: "uint256[]" }, + { name: "addresses", type: "CreateGlvDepositAddresses" }, + { name: "minGlvTokens", type: "uint256" }, + { name: "executionFee", type: "uint256" }, + { name: "callbackGasLimit", type: "uint256" }, + { name: "shouldUnwrapNativeToken", type: "bool" }, + { name: "isMarketTokenDeposit", type: "bool" }, + { name: "dataList", type: "bytes32[]" }, + { name: "relayParams", type: "bytes32" }, + ], + CreateGlvDepositAddresses: [ + { name: "glv", type: "address" }, + { name: "market", type: "address" }, + { name: "receiver", type: "address" }, + { name: "callbackContract", type: "address" }, + { name: "uiFeeReceiver", type: "address" }, + { name: "initialLongToken", type: "address" }, + { name: "initialShortToken", type: "address" }, + { name: "longTokenSwapPath", type: "address[]" }, + { name: "shortTokenSwapPath", type: "address[]" }, + ], + }; + const typedData = { + transferTokens: transferRequests.tokens, + transferReceivers: transferRequests.receivers, + transferAmounts: transferRequests.amounts, + addresses: params.addresses, + minGlvTokens: params.minGlvTokens, + executionFee: params.executionFee, + callbackGasLimit: params.callbackGasLimit, + shouldUnwrapNativeToken: params.shouldUnwrapNativeToken, + isMarketTokenDeposit: params.isMarketTokenDeposit, + dataList: params.dataList, + relayParams: hashRelayParams(relayParams), + }; + const domain = getDomain(chainId, verifyingContract); + + return signTypedData(signer, domain, types, typedData); +} From cf9ca85833b879c307c941ccaccf998b44858644 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 4 Dec 2024 17:58:57 +0200 Subject: [PATCH 099/205] Add multichain handler contract --- contracts/multichain/MultichainHandler.sol | 74 ++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 contracts/multichain/MultichainHandler.sol diff --git a/contracts/multichain/MultichainHandler.sol b/contracts/multichain/MultichainHandler.sol new file mode 100644 index 000000000..24321c03b --- /dev/null +++ b/contracts/multichain/MultichainHandler.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; + +import "../oracle/OracleModule.sol"; +import "../role/RoleModule.sol"; +import "../utils/GlobalReentrancyGuard.sol"; + +/** + * @title MultichainHandler + */ +import { RoleStore } from "../role/RoleStore.sol"; +import { DataStore } from "../data/DataStore.sol"; +import { EventEmitter } from "../event/EventEmitter.sol"; +import { Oracle } from "../oracle/Oracle.sol"; +import { Keys } from "../data/Keys.sol"; +import { MultichainVault } from "./MultichainVault.sol"; +import { MultichainUtils } from "./MultichainUtils.sol"; +import { MultichainEventUtils } from "./MultichainEventUtils.sol"; + +import {PayableMulticall} from "../utils/PayableMulticall.sol"; // TODO: what contract is this? + +contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { + MultichainVault public multichainVault; + EventEmitter public eventEmitter; + PayableMulticall public payableMulticall; + + constructor( + RoleStore _roleStore, + DataStore _dataStore, + EventEmitter _eventEmitter, + Oracle _oracle, + MultichainVault _multichainVault, + PayableMulticall _payableMulticall + ) RoleModule(_roleStore) GlobalReentrancyGuard(_dataStore) OracleModule(_oracle) { + multichainVault = _multichainVault; + eventEmitter = _eventEmitter; + payableMulticall = _payableMulticall; + } + + /** + * Record a deposit from another chain + * @dev this contract needs controller role in order to record deposits + * @param account user address on the source chain + * @param token address of the token being deposited + * @param sourceChainId chain id of the source chain + */ + function recordDeposit(address account, address token, uint256 sourceChainId) external onlyController() { + // token should have been transferred to multichainVault by IMultichainProvider + uint256 amount = multichainVault.recordTransferIn(token); + if (amount == 0) { + revert Errors.EmptyMultichainAmount(); + } + + address virtualAccount = MultichainUtils.getVirtualAccount(account, sourceChainId); + + dataStore.incrementUint(Keys.multichainBalanceKey(virtualAccount, token), amount); + + MultichainEventUtils.emitMultichainDeposit(eventEmitter, token, virtualAccount, amount, sourceChainId); + } + + function executeMessage( + address account, + uint256 sourceChainId, + bytes[] calldata multicallArgs + ) external onlyController() { + address virtualAccount = MultichainUtils.getVirtualAccount(account, sourceChainId); + + // execute multicall + payableMulticall.multicall(multicallArgs); + + MultichainEventUtils.emitMultichainMessageReceived(eventEmitter, virtualAccount, sourceChainId); + } +} From 700e9269c721d0cfb2dcf375d89b1441b2bf50ba Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 5 Dec 2024 23:47:07 +0200 Subject: [PATCH 100/205] Add multichain withdrawal --- contracts/multichain/MultichainHandler.sol | 55 ++++++++++++++++++---- contracts/role/Role.sol | 6 +++ contracts/role/RoleModule.sol | 8 ++++ 3 files changed, 59 insertions(+), 10 deletions(-) diff --git a/contracts/multichain/MultichainHandler.sol b/contracts/multichain/MultichainHandler.sol index 24321c03b..b982f2cc7 100644 --- a/contracts/multichain/MultichainHandler.sol +++ b/contracts/multichain/MultichainHandler.sol @@ -6,9 +6,6 @@ import "../oracle/OracleModule.sol"; import "../role/RoleModule.sol"; import "../utils/GlobalReentrancyGuard.sol"; -/** - * @title MultichainHandler - */ import { RoleStore } from "../role/RoleStore.sol"; import { DataStore } from "../data/DataStore.sol"; import { EventEmitter } from "../event/EventEmitter.sol"; @@ -18,8 +15,11 @@ import { MultichainVault } from "./MultichainVault.sol"; import { MultichainUtils } from "./MultichainUtils.sol"; import { MultichainEventUtils } from "./MultichainEventUtils.sol"; -import {PayableMulticall} from "../utils/PayableMulticall.sol"; // TODO: what contract is this? +import { PayableMulticall } from "../utils/PayableMulticall.sol"; // TODO: what contract is this? +/** + * @title MultichainHandler + */ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { MultichainVault public multichainVault; EventEmitter public eventEmitter; @@ -40,16 +40,15 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { /** * Record a deposit from another chain - * @dev this contract needs controller role in order to record deposits * @param account user address on the source chain * @param token address of the token being deposited * @param sourceChainId chain id of the source chain */ - function recordDeposit(address account, address token, uint256 sourceChainId) external onlyController() { + function recordDeposit(address account, address token, uint256 sourceChainId) external onlyMultichainController { // token should have been transferred to multichainVault by IMultichainProvider uint256 amount = multichainVault.recordTransferIn(token); if (amount == 0) { - revert Errors.EmptyMultichainAmount(); + revert Errors.EmptyMultichainDepositAmount(); } address virtualAccount = MultichainUtils.getVirtualAccount(account, sourceChainId); @@ -59,16 +58,52 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { MultichainEventUtils.emitMultichainDeposit(eventEmitter, token, virtualAccount, amount, sourceChainId); } - function executeMessage( + /** + * Record a message from another chain. Executes a multicall. + * The multicall arguments contains the function calls to be executed (e.g. createDeposit, createOrder, createWithdrawal, etc) + * @param account user address on the source chain + * @param sourceChainId chain id of the source chain + * @param multicallArgs array of bytes containing the multicall arguments + */ + function executeMulticall( address account, uint256 sourceChainId, bytes[] calldata multicallArgs - ) external onlyController() { + ) external onlyMultichainController { address virtualAccount = MultichainUtils.getVirtualAccount(account, sourceChainId); // execute multicall payableMulticall.multicall(multicallArgs); - MultichainEventUtils.emitMultichainMessageReceived(eventEmitter, virtualAccount, sourceChainId); + MultichainEventUtils.emitMultichainMessage(eventEmitter, virtualAccount, sourceChainId); + } + + /** + * Record a withdrawal to another chain + * @param account user address on the source chain + * @param token address of the token being withdrawn + * @param amount amount of token being withdrawn + * @param sourceChainId chain id of the source chain + */ + function recordWithdrawal(address account, address token, uint256 amount, uint256 sourceChainId, bytes[] memory multicallArgs) external onlyMultichainController { + if (amount == 0) { + revert Errors.EmptyMultichainWithdrawalAmount(); + } + + address virtualAccount = MultichainUtils.getVirtualAccount(account, sourceChainId); + bytes32 balanceKey = Keys.multichainBalanceKey(virtualAccount, token); + + uint256 balance = dataStore.getUint(balanceKey); + if (balance < amount) { + revert Errors.InsufficientMultichainBalance(); + // should amount be capped instead of reverting? i.e. amount = balance; + } + + dataStore.decrementUint(balanceKey, amount); + + // transfer tokens to IMultichainProvider + multichainVault.transferOut(token, msg.sender, amount); + + MultichainEventUtils.emitMultichainWithdrawal(eventEmitter, token, virtualAccount, amount, sourceChainId); } } diff --git a/contracts/role/Role.sol b/contracts/role/Role.sol index 03e4b0546..eb7d90d91 100644 --- a/contracts/role/Role.sol +++ b/contracts/role/Role.sol @@ -43,6 +43,12 @@ library Role { */ bytes32 public constant CONTROLLER = keccak256(abi.encode("CONTROLLER")); + /** + * @dev The MULTICHAIN_CONTROLLER role. + * Hash: 0x8061565a700f909f44d6a4d48782e0ca745eec3662480ec6dba3f9dc8b4554d0 + */ + bytes32 public constant MULTICHAIN_CONTROLLER = keccak256(abi.encode("MULTICHAIN_CONTROLLER")); + /** * @dev The GOV_TOKEN_CONTROLLER role. * Hash: 0x16a157db08319d4eaf6b157a71f5d2e18c6500cab8a25bee0b4f9c753cb13690 diff --git a/contracts/role/RoleModule.sol b/contracts/role/RoleModule.sol index 4d29865f7..ea267304b 100644 --- a/contracts/role/RoleModule.sol +++ b/contracts/role/RoleModule.sol @@ -70,6 +70,14 @@ contract RoleModule { _; } + /** + * @dev Only allows addresses with the MULTICHAIN_CONTROLLER role to call the function. + */ + modifier onlyMultichainController() { + _validateRole(Role.MULTICHAIN_CONTROLLER, "MULTICHAIN_CONTROLLER"); + _; + } + /** * @dev Only allows addresses with the GOV_TOKEN_CONTROLLER role to call the function. */ From ba123a0d0dd88f8f84724c621510b5678f3f08ee Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 8 Dec 2024 21:43:42 +0200 Subject: [PATCH 101/205] Add multichain provider interface, multichain provider signature contract and multichain provider utils lib --- .../MultichainProviderSignature.sol | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 contracts/multichain/MultichainProviderSignature.sol diff --git a/contracts/multichain/MultichainProviderSignature.sol b/contracts/multichain/MultichainProviderSignature.sol new file mode 100644 index 000000000..e2e159503 --- /dev/null +++ b/contracts/multichain/MultichainProviderSignature.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; +import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; + +import { MultichainProviderUtils } from "./MultichainProviderUtils.sol"; + +contract MultichainProviderSignature is EIP712 { + using ECDSA for bytes32; + + string private constant SIGNING_DOMAIN = "MultichainProviderSignatureDomain"; + string private constant SIGNATURE_VERSION = "1"; + + // Define the EIP-712 struct type: + // Message(address token,uint256 amount,address account,uint256 sourceChainId,uint32 srcEid) + bytes32 private constant _MESSAGE_TYPEHASH = + keccak256("Message(address token,uint256 amount,address account,uint256 sourceChainId,uint32 srcEid)"); + + constructor() EIP712(SIGNING_DOMAIN, SIGNATURE_VERSION) {} + + /** + * Check the signature for a given message + * @param message The ABI encoded parameters (token, amount, account, sourceChainId, srcEid). + * @param signature The EIP-712 signature. + */ + function isSigner(bytes calldata message, bytes calldata signature) external view returns (bool) { + // Decode the message + (address token, uint256 amount, address account, uint256 sourceChainId, uint32 srcEid) = MultichainProviderUtils + .decodeWithdrawal(message); + + // Build the struct hash + bytes32 structHash = keccak256(abi.encode(_MESSAGE_TYPEHASH, token, amount, account, sourceChainId, srcEid)); + + // Get the typed data hash for EIP-712 + bytes32 hash = _hashTypedDataV4(structHash); + + // Recover the signer from the signature + address signer = ECDSA.recover(hash, signature); + + return signer == account; + } +} From 410982109855dbb4d3bd6e0638ba10c8da92d982 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 8 Dec 2024 21:55:49 +0200 Subject: [PATCH 102/205] Add multichain errors, small refactor --- contracts/multichain/MultichainHandler.sol | 34 ++++++++++++---------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/contracts/multichain/MultichainHandler.sol b/contracts/multichain/MultichainHandler.sol index b982f2cc7..437354b56 100644 --- a/contracts/multichain/MultichainHandler.sol +++ b/contracts/multichain/MultichainHandler.sol @@ -2,21 +2,21 @@ pragma solidity ^0.8.0; -import "../oracle/OracleModule.sol"; -import "../role/RoleModule.sol"; -import "../utils/GlobalReentrancyGuard.sol"; - import { RoleStore } from "../role/RoleStore.sol"; +import { RoleModule } from "../role/RoleModule.sol"; import { DataStore } from "../data/DataStore.sol"; -import { EventEmitter } from "../event/EventEmitter.sol"; -import { Oracle } from "../oracle/Oracle.sol"; import { Keys } from "../data/Keys.sol"; +import { Oracle } from "../oracle/Oracle.sol"; +import { OracleModule } from "../oracle/OracleModule.sol"; +import { EventEmitter } from "../event/EventEmitter.sol"; +import { Errors } from "../error/Errors.sol"; +import { GlobalReentrancyGuard } from "../utils/GlobalReentrancyGuard.sol"; +import { PayableMulticall } from "../utils/PayableMulticall.sol"; // TODO: what contract should this be? + import { MultichainVault } from "./MultichainVault.sol"; import { MultichainUtils } from "./MultichainUtils.sol"; import { MultichainEventUtils } from "./MultichainEventUtils.sol"; -import { PayableMulticall } from "../utils/PayableMulticall.sol"; // TODO: what contract is this? - /** * @title MultichainHandler */ @@ -39,7 +39,7 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { } /** - * Record a deposit from another chain + * Records a deposit from another chain. IMultichainProvider has MULTICHAIN_CONTROLLER role * @param account user address on the source chain * @param token address of the token being deposited * @param sourceChainId chain id of the source chain @@ -59,7 +59,7 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { } /** - * Record a message from another chain. Executes a multicall. + * Executes the multicall for the given args * The multicall arguments contains the function calls to be executed (e.g. createDeposit, createOrder, createWithdrawal, etc) * @param account user address on the source chain * @param sourceChainId chain id of the source chain @@ -70,22 +70,26 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { uint256 sourceChainId, bytes[] calldata multicallArgs ) external onlyMultichainController { - address virtualAccount = MultichainUtils.getVirtualAccount(account, sourceChainId); - // execute multicall payableMulticall.multicall(multicallArgs); + address virtualAccount = MultichainUtils.getVirtualAccount(account, sourceChainId); MultichainEventUtils.emitMultichainMessage(eventEmitter, virtualAccount, sourceChainId); } /** - * Record a withdrawal to another chain + * Record a withdrawal to another chain. IMultichainProvider has MULTICHAIN_CONTROLLER role * @param account user address on the source chain * @param token address of the token being withdrawn * @param amount amount of token being withdrawn * @param sourceChainId chain id of the source chain */ - function recordWithdrawal(address account, address token, uint256 amount, uint256 sourceChainId, bytes[] memory multicallArgs) external onlyMultichainController { + function recordWithdrawal( + address account, + address token, + uint256 amount, + uint256 sourceChainId + ) external onlyMultichainController { if (amount == 0) { revert Errors.EmptyMultichainWithdrawalAmount(); } @@ -96,7 +100,7 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { uint256 balance = dataStore.getUint(balanceKey); if (balance < amount) { revert Errors.InsufficientMultichainBalance(); - // should amount be capped instead of reverting? i.e. amount = balance; + // TODO: should amount be capped instead of reverting? i.e. amount = balance; } dataStore.decrementUint(balanceKey, amount); From 7cd83bc6f6b15c6309b76f3186fd7d7e8b1aa61f Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 9 Dec 2024 20:28:55 +0200 Subject: [PATCH 103/205] Simplify LayerZeroProvider contract (keep only lzCompose for now) --- contracts/multichain/MultichainHandler.sol | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/contracts/multichain/MultichainHandler.sol b/contracts/multichain/MultichainHandler.sol index 437354b56..0d2a91aea 100644 --- a/contracts/multichain/MultichainHandler.sol +++ b/contracts/multichain/MultichainHandler.sol @@ -11,7 +11,7 @@ import { OracleModule } from "../oracle/OracleModule.sol"; import { EventEmitter } from "../event/EventEmitter.sol"; import { Errors } from "../error/Errors.sol"; import { GlobalReentrancyGuard } from "../utils/GlobalReentrancyGuard.sol"; -import { PayableMulticall } from "../utils/PayableMulticall.sol"; // TODO: what contract should this be? +import { ExchangeRouter } from "../router/ExchangeRouter.sol"; import { MultichainVault } from "./MultichainVault.sol"; import { MultichainUtils } from "./MultichainUtils.sol"; @@ -23,7 +23,7 @@ import { MultichainEventUtils } from "./MultichainEventUtils.sol"; contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { MultichainVault public multichainVault; EventEmitter public eventEmitter; - PayableMulticall public payableMulticall; + ExchangeRouter public exchangeRouter; constructor( RoleStore _roleStore, @@ -31,11 +31,11 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { EventEmitter _eventEmitter, Oracle _oracle, MultichainVault _multichainVault, - PayableMulticall _payableMulticall + ExchangeRouter _exchangeRouter ) RoleModule(_roleStore) GlobalReentrancyGuard(_dataStore) OracleModule(_oracle) { multichainVault = _multichainVault; eventEmitter = _eventEmitter; - payableMulticall = _payableMulticall; + exchangeRouter = _exchangeRouter; } /** @@ -44,14 +44,18 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { * @param token address of the token being deposited * @param sourceChainId chain id of the source chain */ - function recordDeposit(address account, address token, uint256 sourceChainId) external onlyMultichainController { + function recordDeposit( + address account, + address token, + uint256 sourceChainId + ) external onlyMultichainController returns (address virtualAccount) { // token should have been transferred to multichainVault by IMultichainProvider uint256 amount = multichainVault.recordTransferIn(token); if (amount == 0) { revert Errors.EmptyMultichainDepositAmount(); } - address virtualAccount = MultichainUtils.getVirtualAccount(account, sourceChainId); + virtualAccount = MultichainUtils.getVirtualAccount(account, sourceChainId); dataStore.incrementUint(Keys.multichainBalanceKey(virtualAccount, token), amount); @@ -71,7 +75,7 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { bytes[] calldata multicallArgs ) external onlyMultichainController { // execute multicall - payableMulticall.multicall(multicallArgs); + exchangeRouter.multicall(multicallArgs); address virtualAccount = MultichainUtils.getVirtualAccount(account, sourceChainId); MultichainEventUtils.emitMultichainMessage(eventEmitter, virtualAccount, sourceChainId); From 89675038f8a43da63fb24f13f49865ac7917e39b Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 10 Dec 2024 13:01:36 +0200 Subject: [PATCH 104/205] Fix compilers error, add source chain balance key under the allowed base keys --- contracts/multichain/MultichainHandler.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/multichain/MultichainHandler.sol b/contracts/multichain/MultichainHandler.sol index 0d2a91aea..3ef9f3a2b 100644 --- a/contracts/multichain/MultichainHandler.sol +++ b/contracts/multichain/MultichainHandler.sol @@ -57,7 +57,7 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { virtualAccount = MultichainUtils.getVirtualAccount(account, sourceChainId); - dataStore.incrementUint(Keys.multichainBalanceKey(virtualAccount, token), amount); + dataStore.incrementUint(Keys.sourceChainBalanceKey(virtualAccount, token), amount); MultichainEventUtils.emitMultichainDeposit(eventEmitter, token, virtualAccount, amount, sourceChainId); } @@ -99,7 +99,7 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { } address virtualAccount = MultichainUtils.getVirtualAccount(account, sourceChainId); - bytes32 balanceKey = Keys.multichainBalanceKey(virtualAccount, token); + bytes32 balanceKey = Keys.sourceChainBalanceKey(virtualAccount, token); uint256 balance = dataStore.getUint(balanceKey); if (balance < amount) { From d9c0ec11936a960c5baa6154b128b279c21e8a73 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 11 Dec 2024 12:59:30 +0200 Subject: [PATCH 105/205] Remove multichain controller role, use controller role for provider contract --- contracts/multichain/MultichainHandler.sol | 6 +++--- contracts/role/Role.sol | 6 ------ contracts/role/RoleModule.sol | 8 -------- 3 files changed, 3 insertions(+), 17 deletions(-) diff --git a/contracts/multichain/MultichainHandler.sol b/contracts/multichain/MultichainHandler.sol index 3ef9f3a2b..9469e8a27 100644 --- a/contracts/multichain/MultichainHandler.sol +++ b/contracts/multichain/MultichainHandler.sol @@ -48,7 +48,7 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { address account, address token, uint256 sourceChainId - ) external onlyMultichainController returns (address virtualAccount) { + ) external onlyController returns (address virtualAccount) { // token should have been transferred to multichainVault by IMultichainProvider uint256 amount = multichainVault.recordTransferIn(token); if (amount == 0) { @@ -73,7 +73,7 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { address account, uint256 sourceChainId, bytes[] calldata multicallArgs - ) external onlyMultichainController { + ) external onlyController { // execute multicall exchangeRouter.multicall(multicallArgs); @@ -93,7 +93,7 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { address token, uint256 amount, uint256 sourceChainId - ) external onlyMultichainController { + ) external onlyController { if (amount == 0) { revert Errors.EmptyMultichainWithdrawalAmount(); } diff --git a/contracts/role/Role.sol b/contracts/role/Role.sol index eb7d90d91..03e4b0546 100644 --- a/contracts/role/Role.sol +++ b/contracts/role/Role.sol @@ -43,12 +43,6 @@ library Role { */ bytes32 public constant CONTROLLER = keccak256(abi.encode("CONTROLLER")); - /** - * @dev The MULTICHAIN_CONTROLLER role. - * Hash: 0x8061565a700f909f44d6a4d48782e0ca745eec3662480ec6dba3f9dc8b4554d0 - */ - bytes32 public constant MULTICHAIN_CONTROLLER = keccak256(abi.encode("MULTICHAIN_CONTROLLER")); - /** * @dev The GOV_TOKEN_CONTROLLER role. * Hash: 0x16a157db08319d4eaf6b157a71f5d2e18c6500cab8a25bee0b4f9c753cb13690 diff --git a/contracts/role/RoleModule.sol b/contracts/role/RoleModule.sol index ea267304b..4d29865f7 100644 --- a/contracts/role/RoleModule.sol +++ b/contracts/role/RoleModule.sol @@ -70,14 +70,6 @@ contract RoleModule { _; } - /** - * @dev Only allows addresses with the MULTICHAIN_CONTROLLER role to call the function. - */ - modifier onlyMultichainController() { - _validateRole(Role.MULTICHAIN_CONTROLLER, "MULTICHAIN_CONTROLLER"); - _; - } - /** * @dev Only allows addresses with the GOV_TOKEN_CONTROLLER role to call the function. */ From c7668c4e590c91d19afe46209910e043968e3147 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 11 Dec 2024 13:43:26 +0200 Subject: [PATCH 106/205] Add deploy scripts --- deploy/deployMultichainHandler.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 deploy/deployMultichainHandler.ts diff --git a/deploy/deployMultichainHandler.ts b/deploy/deployMultichainHandler.ts new file mode 100644 index 000000000..cf8559ed3 --- /dev/null +++ b/deploy/deployMultichainHandler.ts @@ -0,0 +1,17 @@ +import { grantRoleIfNotGranted } from "../utils/role"; +import { createDeployFunction } from "../utils/deploy"; + +const constructorContracts = ["RoleStore", "DataStore", "EventEmitter", "Oracle", "MultichainVault", "ExchangeRouter"]; + +const func = createDeployFunction({ + contractName: "MultichainHandler", + dependencyNames: constructorContracts, + getDeployArgs: async ({ dependencyContracts }) => { + return constructorContracts.map((dependencyName) => dependencyContracts[dependencyName].address); + }, + afterDeploy: async ({ deployedContract }) => { + await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); + }, +}); + +export default func; From 8dcc72a5c73d19a1e73118c1ecfc3f029323e595 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 11 Dec 2024 13:46:21 +0200 Subject: [PATCH 107/205] Add multichain utils --- utils/multichain.ts | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 utils/multichain.ts diff --git a/utils/multichain.ts b/utils/multichain.ts new file mode 100644 index 000000000..24a03459d --- /dev/null +++ b/utils/multichain.ts @@ -0,0 +1,8 @@ +import { hashData, getAddressFromHash } from "./hash"; + +export const GMX_MULTICHAIN = "GMX Multichain"; + +export function getVirtualAccount(account: string, sourceChainId: number): string { + const hash = hashData(["string", "address", "uint256"], [GMX_MULTICHAIN, account, sourceChainId]); + return getAddressFromHash(hash); +} From d6c1e3ebeb2b37bf462aef6b7bf807bedfd04df6 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 17 Dec 2024 00:22:45 +0200 Subject: [PATCH 108/205] Store open position price impact on position open instead of charging it directly, charge the total price impact (open + close) on position close This allows total price impact to be capped, with the excess being claimable --- contracts/data/Keys.sol | 9 ++++++++ contracts/market/MarketEventUtils.sol | 22 ++++++++++++++++++++ contracts/position/DecreasePositionUtils.sol | 9 ++++++++ contracts/position/PositionEventUtils.sol | 4 ++-- 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/contracts/data/Keys.sol b/contracts/data/Keys.sol index c1028999a..5bc0a4c6e 100644 --- a/contracts/data/Keys.sol +++ b/contracts/data/Keys.sol @@ -311,6 +311,8 @@ library Keys { bytes32 public constant MAX_POOL_USD_FOR_DEPOSIT = keccak256(abi.encode("MAX_POOL_USD_FOR_DEPOSIT")); // @dev key for max open interest bytes32 public constant MAX_OPEN_INTEREST = keccak256(abi.encode("MAX_OPEN_INTEREST")); + // @dev key for position impact pending amount + bytes32 public constant POSITION_IMPACT_PENDING_AMOUNT = keccak256(abi.encode("POSITION_IMPACT_PENDING_AMOUNT")); // @dev key for position impact pool amount bytes32 public constant POSITION_IMPACT_POOL_AMOUNT = keccak256(abi.encode("POSITION_IMPACT_POOL_AMOUNT")); // @dev key for min position impact pool amount @@ -1321,6 +1323,13 @@ library Keys { )); } + function positionImpactPendingAmountKey(bytes32 positionKey) internal pure returns (bytes32) { + return keccak256(abi.encode( + POSITION_IMPACT_PENDING_AMOUNT, + positionKey + )); + } + // @dev key for min amount of tokens in a market's position impact pool // @param market the market to check // @return key for min amount of tokens in a market's position impact pool diff --git a/contracts/market/MarketEventUtils.sol b/contracts/market/MarketEventUtils.sol index cba05b196..5aba4a0c0 100644 --- a/contracts/market/MarketEventUtils.sol +++ b/contracts/market/MarketEventUtils.sol @@ -201,6 +201,28 @@ library MarketEventUtils { ); } + function emitPositionImpactPendingAmountUpdated( + EventEmitter eventEmitter, + bytes32 positionKey, + int256 delta, + int256 nextValue + ) external { + EventUtils.EventLogData memory eventData; + + eventData.bytes32Items.initItems(1); + eventData.bytes32Items.setItem(0, "positionKey", positionKey); + + eventData.intItems.initItems(2); + eventData.intItems.setItem(0, "delta", delta); + eventData.intItems.setItem(1, "nextValue", nextValue); + + eventEmitter.emitEventLog1( + "PositionImpactPendingAmountUpdated", + positionKey, + eventData + ); + } + function emitOpenInterestUpdated( EventEmitter eventEmitter, address market, diff --git a/contracts/position/DecreasePositionUtils.sol b/contracts/position/DecreasePositionUtils.sol index cb5199bb3..e0c6c2346 100644 --- a/contracts/position/DecreasePositionUtils.sol +++ b/contracts/position/DecreasePositionUtils.sol @@ -120,6 +120,7 @@ library DecreasePositionUtils { params.position.sizeInUsd() ); + // TODO q: is price impact considered for pnl? cache.estimatedRealizedPnlUsd = Precision.mulDiv(cache.estimatedPositionPnlUsd, params.order.sizeDeltaUsd(), params.position.sizeInUsd()); cache.estimatedRemainingPnlUsd = cache.estimatedPositionPnlUsd - cache.estimatedRealizedPnlUsd; @@ -247,6 +248,11 @@ library DecreasePositionUtils { ); params.position.setSizeInUsd(cache.nextPositionSizeInUsd); + // TODO: values.sizeDeltaInTokens is calculated as position.sizeInTokens() * sizeDeltaUsd / position.sizeInUsd() AND sizeDeltaUsd is the change in position size + // we decrease the position size in tokens by the delta/order size in tokens + // TODO: params.position.sizeInTokens() doesn't include the price impact from increasePosition. Instead the impact was stored + // TODO: values.sizeDeltaInTokens also doesn't include the impact because it is calculated from position.sizeInTokens() + // it's a % of the position in tokens, proportional to the order size in usd, but using the position size in usd which did take into account the price impact params.position.setSizeInTokens(params.position.sizeInTokens() - values.sizeDeltaInTokens); params.position.setCollateralAmount(values.remainingCollateralAmount); params.position.setPendingImpactAmount(params.position.pendingImpactAmount() - values.proportionalPendingImpactAmount); @@ -282,6 +288,9 @@ library DecreasePositionUtils { -(cache.initialCollateralAmount - params.position.collateralAmount()).toInt256() ); + // TODO: sizeDeltaInTokens includes impact? (is taken from position) + // I guess not => params.position.setSizeInTokens(params.position.sizeInTokens() + cache.baseSizeDeltaInTokens); + // => no changes necessary here in DecreasePositionUtils PositionUtils.updateOpenInterest( params, -params.order.sizeDeltaUsd().toInt256(), diff --git a/contracts/position/PositionEventUtils.sol b/contracts/position/PositionEventUtils.sol index 38f3a6d3a..e362f55ef 100644 --- a/contracts/position/PositionEventUtils.sol +++ b/contracts/position/PositionEventUtils.sol @@ -30,7 +30,7 @@ library PositionEventUtils { Price.Props collateralTokenPrice; uint256 executionPrice; uint256 sizeDeltaUsd; - uint256 sizeDeltaInTokens; + uint256 baseSizeDeltaInTokens; int256 collateralDeltaAmount; int256 priceImpactUsd; int256 priceImpactAmount; @@ -59,7 +59,7 @@ library PositionEventUtils { eventData.uintItems.setItem(10, "collateralTokenPrice.max", params.collateralTokenPrice.max); eventData.uintItems.setItem(11, "collateralTokenPrice.min", params.collateralTokenPrice.min); eventData.uintItems.setItem(12, "sizeDeltaUsd", params.sizeDeltaUsd); - eventData.uintItems.setItem(13, "sizeDeltaInTokens", params.sizeDeltaInTokens); + eventData.uintItems.setItem(13, "baseSizeDeltaInTokens", params.baseSizeDeltaInTokens); eventData.uintItems.setItem(14, "orderType", uint256(params.orderType)); eventData.uintItems.setItem(15, "increasedAtTime", uint256(params.position.increasedAtTime())); From 7fb24884cf07a21458ccaadeb0082a536e6868d1 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 17 Dec 2024 09:42:19 +0200 Subject: [PATCH 109/205] Keep the original event key since it is used for e.g. analytics --- contracts/position/PositionEventUtils.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/position/PositionEventUtils.sol b/contracts/position/PositionEventUtils.sol index e362f55ef..38f3a6d3a 100644 --- a/contracts/position/PositionEventUtils.sol +++ b/contracts/position/PositionEventUtils.sol @@ -30,7 +30,7 @@ library PositionEventUtils { Price.Props collateralTokenPrice; uint256 executionPrice; uint256 sizeDeltaUsd; - uint256 baseSizeDeltaInTokens; + uint256 sizeDeltaInTokens; int256 collateralDeltaAmount; int256 priceImpactUsd; int256 priceImpactAmount; @@ -59,7 +59,7 @@ library PositionEventUtils { eventData.uintItems.setItem(10, "collateralTokenPrice.max", params.collateralTokenPrice.max); eventData.uintItems.setItem(11, "collateralTokenPrice.min", params.collateralTokenPrice.min); eventData.uintItems.setItem(12, "sizeDeltaUsd", params.sizeDeltaUsd); - eventData.uintItems.setItem(13, "baseSizeDeltaInTokens", params.baseSizeDeltaInTokens); + eventData.uintItems.setItem(13, "sizeDeltaInTokens", params.sizeDeltaInTokens); eventData.uintItems.setItem(14, "orderType", uint256(params.orderType)); eventData.uintItems.setItem(15, "increasedAtTime", uint256(params.position.increasedAtTime())); From f0a0c58533b87c7d78feae2aa70e132853ba03bc Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 17 Dec 2024 09:54:14 +0200 Subject: [PATCH 110/205] Remove comments --- contracts/position/DecreasePositionUtils.sol | 9 --------- 1 file changed, 9 deletions(-) diff --git a/contracts/position/DecreasePositionUtils.sol b/contracts/position/DecreasePositionUtils.sol index e0c6c2346..cb5199bb3 100644 --- a/contracts/position/DecreasePositionUtils.sol +++ b/contracts/position/DecreasePositionUtils.sol @@ -120,7 +120,6 @@ library DecreasePositionUtils { params.position.sizeInUsd() ); - // TODO q: is price impact considered for pnl? cache.estimatedRealizedPnlUsd = Precision.mulDiv(cache.estimatedPositionPnlUsd, params.order.sizeDeltaUsd(), params.position.sizeInUsd()); cache.estimatedRemainingPnlUsd = cache.estimatedPositionPnlUsd - cache.estimatedRealizedPnlUsd; @@ -248,11 +247,6 @@ library DecreasePositionUtils { ); params.position.setSizeInUsd(cache.nextPositionSizeInUsd); - // TODO: values.sizeDeltaInTokens is calculated as position.sizeInTokens() * sizeDeltaUsd / position.sizeInUsd() AND sizeDeltaUsd is the change in position size - // we decrease the position size in tokens by the delta/order size in tokens - // TODO: params.position.sizeInTokens() doesn't include the price impact from increasePosition. Instead the impact was stored - // TODO: values.sizeDeltaInTokens also doesn't include the impact because it is calculated from position.sizeInTokens() - // it's a % of the position in tokens, proportional to the order size in usd, but using the position size in usd which did take into account the price impact params.position.setSizeInTokens(params.position.sizeInTokens() - values.sizeDeltaInTokens); params.position.setCollateralAmount(values.remainingCollateralAmount); params.position.setPendingImpactAmount(params.position.pendingImpactAmount() - values.proportionalPendingImpactAmount); @@ -288,9 +282,6 @@ library DecreasePositionUtils { -(cache.initialCollateralAmount - params.position.collateralAmount()).toInt256() ); - // TODO: sizeDeltaInTokens includes impact? (is taken from position) - // I guess not => params.position.setSizeInTokens(params.position.sizeInTokens() + cache.baseSizeDeltaInTokens); - // => no changes necessary here in DecreasePositionUtils PositionUtils.updateOpenInterest( params, -params.order.sizeDeltaUsd().toInt256(), From 1656a776157cad56f33626588b5bf477df2dc9cb Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 17 Dec 2024 22:22:49 +0200 Subject: [PATCH 111/205] Track impact pending amount using a hash from account and market. Using the positionKey which is the hash of account, market, collateralToken, and isPositive would make it harder to track the impact for the same position on increase/decrease. --- contracts/position/Position.sol | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/contracts/position/Position.sol b/contracts/position/Position.sol index 2da0950c0..68b877815 100644 --- a/contracts/position/Position.sol +++ b/contracts/position/Position.sol @@ -203,4 +203,9 @@ library Position { return _key; } + + function getCombinedPositionKey(address _account, address _market) internal pure returns (bytes32) { + bytes32 _key = keccak256(abi.encode(_account, _market)); + return _key; + } } From 2bf9d81fd63fa984bdf160bf7fd18b4aa5cfe2ac Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 18 Dec 2024 10:49:34 +0200 Subject: [PATCH 112/205] Remove combined key logic for storing the pending impact --- contracts/position/Position.sol | 5 ----- 1 file changed, 5 deletions(-) diff --git a/contracts/position/Position.sol b/contracts/position/Position.sol index 68b877815..2da0950c0 100644 --- a/contracts/position/Position.sol +++ b/contracts/position/Position.sol @@ -203,9 +203,4 @@ library Position { return _key; } - - function getCombinedPositionKey(address _account, address _market) internal pure returns (bytes32) { - bytes32 _key = keccak256(abi.encode(_account, _market)); - return _key; - } } From f523941e82b73c0a8e7bf94a74f982f58bef7470 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 18 Dec 2024 17:41:23 +0200 Subject: [PATCH 113/205] Use position store instead of the data store for storing/retrieving position impact pending amount --- contracts/data/Keys.sol | 8 -------- contracts/market/MarketEventUtils.sol | 21 --------------------- 2 files changed, 29 deletions(-) diff --git a/contracts/data/Keys.sol b/contracts/data/Keys.sol index 5bc0a4c6e..db5dc1277 100644 --- a/contracts/data/Keys.sol +++ b/contracts/data/Keys.sol @@ -311,8 +311,6 @@ library Keys { bytes32 public constant MAX_POOL_USD_FOR_DEPOSIT = keccak256(abi.encode("MAX_POOL_USD_FOR_DEPOSIT")); // @dev key for max open interest bytes32 public constant MAX_OPEN_INTEREST = keccak256(abi.encode("MAX_OPEN_INTEREST")); - // @dev key for position impact pending amount - bytes32 public constant POSITION_IMPACT_PENDING_AMOUNT = keccak256(abi.encode("POSITION_IMPACT_PENDING_AMOUNT")); // @dev key for position impact pool amount bytes32 public constant POSITION_IMPACT_POOL_AMOUNT = keccak256(abi.encode("POSITION_IMPACT_POOL_AMOUNT")); // @dev key for min position impact pool amount @@ -1323,12 +1321,6 @@ library Keys { )); } - function positionImpactPendingAmountKey(bytes32 positionKey) internal pure returns (bytes32) { - return keccak256(abi.encode( - POSITION_IMPACT_PENDING_AMOUNT, - positionKey - )); - } // @dev key for min amount of tokens in a market's position impact pool // @param market the market to check diff --git a/contracts/market/MarketEventUtils.sol b/contracts/market/MarketEventUtils.sol index 5aba4a0c0..0d581e768 100644 --- a/contracts/market/MarketEventUtils.sol +++ b/contracts/market/MarketEventUtils.sol @@ -201,27 +201,6 @@ library MarketEventUtils { ); } - function emitPositionImpactPendingAmountUpdated( - EventEmitter eventEmitter, - bytes32 positionKey, - int256 delta, - int256 nextValue - ) external { - EventUtils.EventLogData memory eventData; - - eventData.bytes32Items.initItems(1); - eventData.bytes32Items.setItem(0, "positionKey", positionKey); - - eventData.intItems.initItems(2); - eventData.intItems.setItem(0, "delta", delta); - eventData.intItems.setItem(1, "nextValue", nextValue); - - eventEmitter.emitEventLog1( - "PositionImpactPendingAmountUpdated", - positionKey, - eventData - ); - } function emitOpenInterestUpdated( EventEmitter eventEmitter, From ece6a48f5b9b39ee944541b04e9b6e36df2f9c58 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 20 Dec 2024 08:23:36 +0200 Subject: [PATCH 114/205] Remove blank lines --- contracts/data/Keys.sol | 1 - contracts/market/MarketEventUtils.sol | 1 - 2 files changed, 2 deletions(-) diff --git a/contracts/data/Keys.sol b/contracts/data/Keys.sol index db5dc1277..c1028999a 100644 --- a/contracts/data/Keys.sol +++ b/contracts/data/Keys.sol @@ -1321,7 +1321,6 @@ library Keys { )); } - // @dev key for min amount of tokens in a market's position impact pool // @param market the market to check // @return key for min amount of tokens in a market's position impact pool diff --git a/contracts/market/MarketEventUtils.sol b/contracts/market/MarketEventUtils.sol index 0d581e768..cba05b196 100644 --- a/contracts/market/MarketEventUtils.sol +++ b/contracts/market/MarketEventUtils.sol @@ -201,7 +201,6 @@ library MarketEventUtils { ); } - function emitOpenInterestUpdated( EventEmitter eventEmitter, address market, From eb4c624900787988edc7fe20a1ad5caf70432c39 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 12 Jan 2025 23:06:17 +0200 Subject: [PATCH 115/205] Use balanceWasImproved instead of priceImpactUsd > 0 as forPositiveImpact --- test/guardian/testDPCU.ts | 41 +++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/test/guardian/testDPCU.ts b/test/guardian/testDPCU.ts index b0de4a88c..2e93f75bd 100644 --- a/test/guardian/testDPCU.ts +++ b/test/guardian/testDPCU.ts @@ -130,16 +130,16 @@ describe("Guardian.DecreasePositionCollateralUtils", () => { // 140 tokens with each token profiting $500 // 140 * $500 = $70,000 // (5/7) * $70,000 = $50,000 profit = 9.090909 ETH of profit - // Position Fee: $500,000 * 0.05 = $25,000 in fees = 20,000 USDC (entire collateral) + 0.90909 ETH + // TODO: it seems there are no more position fees. Why? - // ETH Pool Amount = 1,000 ETH - 9.090909 ETH + 0.90909 ETH = 991.818181 ETH - // USDC Pool Amount = 1,000,000 USDC + 20,000 USDC = 1,020,000 USDC - // Receiver gets sent: 9.090909 ETH - 0.90909 ETH = 8.181818 ETH - expect(await getBalanceOf(wnt.address, user1.address)).to.eq("8181818181818181819"); + // ETH Pool Amount = 1,000 ETH - 9.090909 ETH = 990.90909 ETH + // USDC Pool Amount = 1,000,000 USDC + // Receiver gets sent: 9.090909 ETH + expect(await getBalanceOf(wnt.address, user1.address)).to.eq("9090909090909090909"); // 9.090909 ETH expect(await getBalanceOf(usdc.address, user1.address)).to.eq("0"); // Verify Pool Amounts - expect(await getPoolAmount(dataStore, ethUsdMarket.marketToken, wnt.address)).eq("991818181818181818181"); // 991.818181 ETH - expect(await getPoolAmount(dataStore, ethUsdMarket.marketToken, usdc.address)).eq(expandDecimals(1_020_000, 6)); + expect(await getPoolAmount(dataStore, ethUsdMarket.marketToken, wnt.address)).eq("990909090909090909091"); // 990.909090909090909091 ETH + expect(await getPoolAmount(dataStore, ethUsdMarket.marketToken, usdc.address)).eq(expandDecimals(1_000_000, 6)); expect(await getPositionCount(dataStore)).eq(1); }); @@ -173,7 +173,7 @@ describe("Guardian.DecreasePositionCollateralUtils", () => { await dataStore.setUint(keys.positionFeeFactorKey(ethUsdMarket.marketToken, true), decimalToFloat(5, 2)); // 5% // Entire collateral used to pay fees, - // so initialCollateralDeltaAmount of 1 USDC will be enough to trigger auto-update + // so initialCollateralDeltaAmount of > 18k USDC will be needed to trigger auto-update await scenes.decreasePosition.long.positivePnl(fixture, { create: { receiver: user1, @@ -188,26 +188,29 @@ describe("Guardian.DecreasePositionCollateralUtils", () => { precisions: [8, 18], afterExecution: async ({ logs }) => { const autoUpdate = getEventData(logs, "OrderCollateralDeltaAmountAutoUpdated"); - expect(autoUpdate.collateralDeltaAmount).to.eq(expandDecimals(1, 6)); - expect(autoUpdate.nextCollateralDeltaAmount).to.eq(0); + // TODO: remainingCollateralAmount changed from 0 to 20k + // => params.order.initialCollateralDeltaAmount() > values.remainingCollateralAmount changed from true to false + // => OrderCollateralDeltaAmountAutoUpdated not emitted anymore + // expect(autoUpdate.collateralDeltaAmount).to.eq(expandDecimals(1, 6)); + // expect(autoUpdate.nextCollateralDeltaAmount).to.eq(0); }, }, }); - expect(await getBalanceOf(usdc.address, user1.address)).to.eq("0"); + expect(await getBalanceOf(usdc.address, user1.address)).to.eq(expandDecimals(1, 6)); // 140 tokens with each token profiting $500 // 140 * $500 = $70,000 // (5/7) * $70,000 = $50,000 profit = 9.090909 ETH of profit - // Position Fee: $500,000 * 0.05 = $25,000 in fees = 20,000 USDC (entire collateral) + 0.90909 ETH + // TODO: it seems there are no more position fees. Why? - // ETH Pool Amount = 1,000 ETH - 9.090909 ETH + 0.90909 ETH = 991.818181 ETH - // USDC Pool Amount = 1,000,000 USDC + 20,000 USDC = 1,020,000 USDC - // Receiver gets sent: 9.090909 ETH - 0.90909 ETH = 8.181818 ETH - expect(await getBalanceOf(wnt.address, user1.address)).to.eq("8181818181818181819"); - expect(await getBalanceOf(usdc.address, user1.address)).to.eq("0"); + // ETH Pool Amount = 1,000 ETH - 9.090909 ETH = 990.90909 ETH + // USDC Pool Amount = 1,000,000 USDC + // Receiver gets sent: 9.090909 ETH + expect(await getBalanceOf(wnt.address, user1.address)).to.eq("9090909090909090909"); + expect(await getBalanceOf(usdc.address, user1.address)).to.eq(expandDecimals(1, 6)); // Verify Pool Amounts - expect(await getPoolAmount(dataStore, ethUsdMarket.marketToken, wnt.address)).eq("991818181818181818181"); // 991.818181 ETH - expect(await getPoolAmount(dataStore, ethUsdMarket.marketToken, usdc.address)).eq(expandDecimals(1_020_000, 6)); + expect(await getPoolAmount(dataStore, ethUsdMarket.marketToken, wnt.address)).eq("990909090909090909091"); // 990.909 ETH + expect(await getPoolAmount(dataStore, ethUsdMarket.marketToken, usdc.address)).eq(expandDecimals(1_000_000, 6)); expect(await getPositionCount(dataStore)).eq(1); }); From f5b6e63049d323d03d43786e87f74bbfb901ce06 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 13 Jan 2025 18:25:56 +0200 Subject: [PATCH 116/205] Use balanceWasImproved for crossover rebalance --- test/guardian/testDPCU.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/guardian/testDPCU.ts b/test/guardian/testDPCU.ts index 2e93f75bd..de570c2ee 100644 --- a/test/guardian/testDPCU.ts +++ b/test/guardian/testDPCU.ts @@ -130,7 +130,7 @@ describe("Guardian.DecreasePositionCollateralUtils", () => { // 140 tokens with each token profiting $500 // 140 * $500 = $70,000 // (5/7) * $70,000 = $50,000 profit = 9.090909 ETH of profit - // TODO: it seems there are no more position fees. Why? + // TODO: it seems there are no position fees anymore. Why? // ETH Pool Amount = 1,000 ETH - 9.090909 ETH = 990.90909 ETH // USDC Pool Amount = 1,000,000 USDC @@ -173,7 +173,7 @@ describe("Guardian.DecreasePositionCollateralUtils", () => { await dataStore.setUint(keys.positionFeeFactorKey(ethUsdMarket.marketToken, true), decimalToFloat(5, 2)); // 5% // Entire collateral used to pay fees, - // so initialCollateralDeltaAmount of > 18k USDC will be needed to trigger auto-update + // so initialCollateralDeltaAmount of 1 USDC will be enough to trigger auto-update await scenes.decreasePosition.long.positivePnl(fixture, { create: { receiver: user1, @@ -188,11 +188,11 @@ describe("Guardian.DecreasePositionCollateralUtils", () => { precisions: [8, 18], afterExecution: async ({ logs }) => { const autoUpdate = getEventData(logs, "OrderCollateralDeltaAmountAutoUpdated"); - // TODO: remainingCollateralAmount changed from 0 to 20k + // TODO: remainingCollateralAmount changed from 0 to 20k. Debug why. Why auto-update is triggered only if initialCollateralDeltaAmount of > 18k USDC? // => params.order.initialCollateralDeltaAmount() > values.remainingCollateralAmount changed from true to false // => OrderCollateralDeltaAmountAutoUpdated not emitted anymore - // expect(autoUpdate.collateralDeltaAmount).to.eq(expandDecimals(1, 6)); - // expect(autoUpdate.nextCollateralDeltaAmount).to.eq(0); + expect(autoUpdate.collateralDeltaAmount).to.eq(expandDecimals(1, 6)); + expect(autoUpdate.nextCollateralDeltaAmount).to.eq(0); }, }, }); @@ -201,7 +201,7 @@ describe("Guardian.DecreasePositionCollateralUtils", () => { // 140 tokens with each token profiting $500 // 140 * $500 = $70,000 // (5/7) * $70,000 = $50,000 profit = 9.090909 ETH of profit - // TODO: it seems there are no more position fees. Why? + // TODO: it seems there are no position fees anymore. Why? // ETH Pool Amount = 1,000 ETH - 9.090909 ETH = 990.90909 ETH // USDC Pool Amount = 1,000,000 USDC From 11fe64b01ff9dffb325d6e2a7860868137687922 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 14 Jan 2025 14:03:01 +0200 Subject: [PATCH 117/205] Update affiliate reward tests when balance was improved --- test/guardian/testDPCU.ts | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/test/guardian/testDPCU.ts b/test/guardian/testDPCU.ts index de570c2ee..f595971c6 100644 --- a/test/guardian/testDPCU.ts +++ b/test/guardian/testDPCU.ts @@ -130,16 +130,16 @@ describe("Guardian.DecreasePositionCollateralUtils", () => { // 140 tokens with each token profiting $500 // 140 * $500 = $70,000 // (5/7) * $70,000 = $50,000 profit = 9.090909 ETH of profit - // TODO: it seems there are no position fees anymore. Why? + // Position Fee: $500,000 * 0.05 = $25,000 in fees = 20,000 USDC (entire collateral) + 0.90909 ETH - // ETH Pool Amount = 1,000 ETH - 9.090909 ETH = 990.90909 ETH - // USDC Pool Amount = 1,000,000 USDC - // Receiver gets sent: 9.090909 ETH - expect(await getBalanceOf(wnt.address, user1.address)).to.eq("9090909090909090909"); // 9.090909 ETH + // ETH Pool Amount = 1,000 ETH - 9.090909 ETH + 0.90909 ETH = 991.818181 ETH + // USDC Pool Amount = 1,000,000 USDC + 20,000 USDC = 1,020,000 USDC + // Receiver gets sent: 9.090909 ETH - 0.90909 ETH = 8.181818 ETH + expect(await getBalanceOf(wnt.address, user1.address)).to.eq("8181818181818181819"); expect(await getBalanceOf(usdc.address, user1.address)).to.eq("0"); // Verify Pool Amounts - expect(await getPoolAmount(dataStore, ethUsdMarket.marketToken, wnt.address)).eq("990909090909090909091"); // 990.909090909090909091 ETH - expect(await getPoolAmount(dataStore, ethUsdMarket.marketToken, usdc.address)).eq(expandDecimals(1_000_000, 6)); + expect(await getPoolAmount(dataStore, ethUsdMarket.marketToken, wnt.address)).eq("991818181818181818181"); // 991.818181 ETH + expect(await getPoolAmount(dataStore, ethUsdMarket.marketToken, usdc.address)).eq(expandDecimals(1_020_000, 6)); expect(await getPositionCount(dataStore)).eq(1); }); @@ -172,8 +172,8 @@ describe("Guardian.DecreasePositionCollateralUtils", () => { // Position fee factor set which will be emptied on getEmptyFees. Balance was improved, positive fee factor is used. await dataStore.setUint(keys.positionFeeFactorKey(ethUsdMarket.marketToken, true), decimalToFloat(5, 2)); // 5% - // Entire collateral used to pay fees, - // so initialCollateralDeltaAmount of 1 USDC will be enough to trigger auto-update + // Collateral used to pay fees, + // so initialCollateralDeltaAmount of 19,000 USDC will trigger auto-update await scenes.decreasePosition.long.positivePnl(fixture, { create: { receiver: user1, @@ -188,29 +188,26 @@ describe("Guardian.DecreasePositionCollateralUtils", () => { precisions: [8, 18], afterExecution: async ({ logs }) => { const autoUpdate = getEventData(logs, "OrderCollateralDeltaAmountAutoUpdated"); - // TODO: remainingCollateralAmount changed from 0 to 20k. Debug why. Why auto-update is triggered only if initialCollateralDeltaAmount of > 18k USDC? - // => params.order.initialCollateralDeltaAmount() > values.remainingCollateralAmount changed from true to false - // => OrderCollateralDeltaAmountAutoUpdated not emitted anymore expect(autoUpdate.collateralDeltaAmount).to.eq(expandDecimals(1, 6)); expect(autoUpdate.nextCollateralDeltaAmount).to.eq(0); }, }, }); - expect(await getBalanceOf(usdc.address, user1.address)).to.eq(expandDecimals(1, 6)); + expect(await getBalanceOf(usdc.address, user1.address)).to.eq(0); // 140 tokens with each token profiting $500 // 140 * $500 = $70,000 // (5/7) * $70,000 = $50,000 profit = 9.090909 ETH of profit - // TODO: it seems there are no position fees anymore. Why? + // Position Fee: $500,000 * 0.05 = $25,000 in fees = 20,000 USDC (entire collateral) + 0.90909 ETH - // ETH Pool Amount = 1,000 ETH - 9.090909 ETH = 990.90909 ETH - // USDC Pool Amount = 1,000,000 USDC - // Receiver gets sent: 9.090909 ETH - expect(await getBalanceOf(wnt.address, user1.address)).to.eq("9090909090909090909"); - expect(await getBalanceOf(usdc.address, user1.address)).to.eq(expandDecimals(1, 6)); + // ETH Pool Amount = 1,000 ETH - 9.090909 ETH + 0.90909 ETH = 991.818181 ETH + // USDC Pool Amount = 1,000,000 USDC + 20,000 USDC = 1,020,000 USDC + // Receiver gets sent: 9.090909 ETH - 0.90909 ETH = 8.181818 ETH + expect(await getBalanceOf(wnt.address, user1.address)).to.eq("8181818181818181819"); + expect(await getBalanceOf(usdc.address, user1.address)).to.eq("0"); // Verify Pool Amounts - expect(await getPoolAmount(dataStore, ethUsdMarket.marketToken, wnt.address)).eq("990909090909090909091"); // 990.909 ETH - expect(await getPoolAmount(dataStore, ethUsdMarket.marketToken, usdc.address)).eq(expandDecimals(1_000_000, 6)); + expect(await getPoolAmount(dataStore, ethUsdMarket.marketToken, wnt.address)).eq("991818181818181818181"); // 991.818181 ETH + expect(await getPoolAmount(dataStore, ethUsdMarket.marketToken, usdc.address)).eq(expandDecimals(1_020_000, 6)); expect(await getPositionCount(dataStore)).eq(1); }); From 76d9e819c91b8fa852e1c9b8422ae85b6412ec1e Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 14 Jan 2025 14:19:42 +0200 Subject: [PATCH 118/205] Update comments --- test/guardian/testDPCU.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/guardian/testDPCU.ts b/test/guardian/testDPCU.ts index f595971c6..b0de4a88c 100644 --- a/test/guardian/testDPCU.ts +++ b/test/guardian/testDPCU.ts @@ -172,8 +172,8 @@ describe("Guardian.DecreasePositionCollateralUtils", () => { // Position fee factor set which will be emptied on getEmptyFees. Balance was improved, positive fee factor is used. await dataStore.setUint(keys.positionFeeFactorKey(ethUsdMarket.marketToken, true), decimalToFloat(5, 2)); // 5% - // Collateral used to pay fees, - // so initialCollateralDeltaAmount of 19,000 USDC will trigger auto-update + // Entire collateral used to pay fees, + // so initialCollateralDeltaAmount of 1 USDC will be enough to trigger auto-update await scenes.decreasePosition.long.positivePnl(fixture, { create: { receiver: user1, @@ -194,7 +194,7 @@ describe("Guardian.DecreasePositionCollateralUtils", () => { }, }); - expect(await getBalanceOf(usdc.address, user1.address)).to.eq(0); + expect(await getBalanceOf(usdc.address, user1.address)).to.eq("0"); // 140 tokens with each token profiting $500 // 140 * $500 = $70,000 // (5/7) * $70,000 = $50,000 profit = 9.090909 ETH of profit From 7af0f758c29deeda259f6b6e4c356bf522cc19f2 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 13 Jan 2025 22:30:04 +0200 Subject: [PATCH 119/205] Add token address to OracleTimestampsAreSmallerThanRequired error --- contracts/adl/AdlUtils.sol | 4 ++-- contracts/deposit/ExecuteDepositUtils.sol | 1 + contracts/error/Errors.sol | 2 +- contracts/order/DecreaseOrderUtils.sol | 10 ++++++---- contracts/order/IncreaseOrderUtils.sol | 2 ++ contracts/order/SwapOrderUtils.sol | 2 ++ contracts/withdrawal/ExecuteWithdrawalUtils.sol | 1 + test/exchange/LimitIncreaseOrder.ts | 2 +- 8 files changed, 16 insertions(+), 8 deletions(-) diff --git a/contracts/adl/AdlUtils.sol b/contracts/adl/AdlUtils.sol index 3a9f43415..b0903ac27 100644 --- a/contracts/adl/AdlUtils.sol +++ b/contracts/adl/AdlUtils.sol @@ -93,7 +93,7 @@ library AdlUtils { uint256 latestAdlTime = getLatestAdlTime(dataStore, market, isLong); if (oracle.maxTimestamp() < latestAdlTime) { - revert Errors.OracleTimestampsAreSmallerThanRequired(oracle.maxTimestamp(), latestAdlTime); + revert Errors.OracleTimestampsAreSmallerThanRequired(market, oracle.maxTimestamp(), latestAdlTime); } Market.Props memory _market = MarketUtils.getEnabledMarket(dataStore, market); @@ -221,7 +221,7 @@ library AdlUtils { uint256 latestAdlTime = AdlUtils.getLatestAdlTime(dataStore, market, isLong); if (oracle.maxTimestamp() < latestAdlTime) { - revert Errors.OracleTimestampsAreSmallerThanRequired(oracle.maxTimestamp(), latestAdlTime); + revert Errors.OracleTimestampsAreSmallerThanRequired(market, oracle.maxTimestamp(), latestAdlTime); } } diff --git a/contracts/deposit/ExecuteDepositUtils.sol b/contracts/deposit/ExecuteDepositUtils.sol index 8cd83e650..6923acf48 100644 --- a/contracts/deposit/ExecuteDepositUtils.sol +++ b/contracts/deposit/ExecuteDepositUtils.sol @@ -116,6 +116,7 @@ library ExecuteDepositUtils { if (params.oracle.minTimestamp() < deposit.updatedAtTime()) { revert Errors.OracleTimestampsAreSmallerThanRequired( + deposit.market(), params.oracle.minTimestamp(), deposit.updatedAtTime() ); diff --git a/contracts/error/Errors.sol b/contracts/error/Errors.sol index 3357b7e1b..b0f2531ae 100644 --- a/contracts/error/Errors.sol +++ b/contracts/error/Errors.sol @@ -251,7 +251,7 @@ library Errors { error EmptyPrimaryPrice(address token); - error OracleTimestampsAreSmallerThanRequired(uint256 minOracleTimestamp, uint256 expectedTimestamp); + error OracleTimestampsAreSmallerThanRequired(address market, uint256 minOracleTimestamp, uint256 expectedTimestamp); error OracleTimestampsAreLargerThanRequestExpirationTime(uint256 maxOracleTimestamp, uint256 requestTimestamp, uint256 requestExpirationTime); // BaseOrderUtils errors diff --git a/contracts/order/DecreaseOrderUtils.sol b/contracts/order/DecreaseOrderUtils.sol index f47bd5105..a1eda38c0 100644 --- a/contracts/order/DecreaseOrderUtils.sol +++ b/contracts/order/DecreaseOrderUtils.sol @@ -36,6 +36,7 @@ library DecreaseOrderUtils { validateOracleTimestamp( params.contracts.dataStore, order.orderType(), + order.market(), order.updatedAtTime(), order.validFromTime(), position.increasedAtTime(), @@ -153,6 +154,7 @@ library DecreaseOrderUtils { function validateOracleTimestamp( DataStore dataStore, Order.OrderType orderType, + address market, uint256 orderUpdatedAtTime, uint256 orderValidFromTime, uint256 positionIncreasedAtTime, @@ -162,7 +164,7 @@ library DecreaseOrderUtils { ) internal view { if (orderType == Order.OrderType.MarketDecrease) { if (minOracleTimestamp < orderUpdatedAtTime) { - revert Errors.OracleTimestampsAreSmallerThanRequired(minOracleTimestamp, orderUpdatedAtTime); + revert Errors.OracleTimestampsAreSmallerThanRequired(market, minOracleTimestamp, orderUpdatedAtTime); } uint256 requestExpirationTime = dataStore.getUint(Keys.REQUEST_EXPIRATION_TIME); @@ -181,7 +183,7 @@ library DecreaseOrderUtils { !BaseOrderUtils.isMarketOrder(orderType) && minOracleTimestamp < orderValidFromTime ) { - revert Errors.OracleTimestampsAreSmallerThanRequired(minOracleTimestamp, orderValidFromTime); + revert Errors.OracleTimestampsAreSmallerThanRequired(market, minOracleTimestamp, orderValidFromTime); } // a user could attempt to frontrun prices by creating a limit decrease @@ -206,7 +208,7 @@ library DecreaseOrderUtils { ) { uint256 latestUpdatedAtTime = orderUpdatedAtTime > positionIncreasedAtTime ? orderUpdatedAtTime : positionIncreasedAtTime; if (minOracleTimestamp < latestUpdatedAtTime) { - revert Errors.OracleTimestampsAreSmallerThanRequired(minOracleTimestamp, latestUpdatedAtTime); + revert Errors.OracleTimestampsAreSmallerThanRequired(market, minOracleTimestamp, latestUpdatedAtTime); } return; } @@ -214,7 +216,7 @@ library DecreaseOrderUtils { if (orderType == Order.OrderType.Liquidation) { uint256 latestUpdatedAtTime = positionIncreasedAtTime > positionDecreasedAtTime ? positionIncreasedAtTime : positionDecreasedAtTime; if (minOracleTimestamp < latestUpdatedAtTime) { - revert Errors.OracleTimestampsAreSmallerThanRequired(minOracleTimestamp, latestUpdatedAtTime); + revert Errors.OracleTimestampsAreSmallerThanRequired(market, minOracleTimestamp, latestUpdatedAtTime); } return; } diff --git a/contracts/order/IncreaseOrderUtils.sol b/contracts/order/IncreaseOrderUtils.sol index 598a28863..cbe3ffc22 100644 --- a/contracts/order/IncreaseOrderUtils.sol +++ b/contracts/order/IncreaseOrderUtils.sol @@ -53,6 +53,7 @@ library IncreaseOrderUtils { if (params.minOracleTimestamp < params.order.updatedAtTime()) { revert Errors.OracleTimestampsAreSmallerThanRequired( + params.order.market(), params.minOracleTimestamp, params.order.updatedAtTime() ); @@ -63,6 +64,7 @@ library IncreaseOrderUtils { params.minOracleTimestamp < params.order.validFromTime() ) { revert Errors.OracleTimestampsAreSmallerThanRequired( + params.order.market(), params.minOracleTimestamp, params.order.validFromTime() ); diff --git a/contracts/order/SwapOrderUtils.sol b/contracts/order/SwapOrderUtils.sol index 95ed0dde1..85d47e99f 100644 --- a/contracts/order/SwapOrderUtils.sol +++ b/contracts/order/SwapOrderUtils.sol @@ -28,6 +28,7 @@ library SwapOrderUtils { if (params.minOracleTimestamp < params.order.updatedAtTime()) { revert Errors.OracleTimestampsAreSmallerThanRequired( + params.order.market(), params.minOracleTimestamp, params.order.updatedAtTime() ); @@ -38,6 +39,7 @@ library SwapOrderUtils { params.minOracleTimestamp < params.order.validFromTime() ) { revert Errors.OracleTimestampsAreSmallerThanRequired( + params.order.market(), params.minOracleTimestamp, params.order.validFromTime() ); diff --git a/contracts/withdrawal/ExecuteWithdrawalUtils.sol b/contracts/withdrawal/ExecuteWithdrawalUtils.sol index 85a00638c..f1f035217 100644 --- a/contracts/withdrawal/ExecuteWithdrawalUtils.sol +++ b/contracts/withdrawal/ExecuteWithdrawalUtils.sol @@ -102,6 +102,7 @@ library ExecuteWithdrawalUtils { if (params.oracle.minTimestamp() < withdrawal.updatedAtTime()) { revert Errors.OracleTimestampsAreSmallerThanRequired( + withdrawal.market(), params.oracle.minTimestamp(), withdrawal.updatedAtTime() ); diff --git a/test/exchange/LimitIncreaseOrder.ts b/test/exchange/LimitIncreaseOrder.ts index 8667a72f4..f7a76fdc8 100644 --- a/test/exchange/LimitIncreaseOrder.ts +++ b/test/exchange/LimitIncreaseOrder.ts @@ -195,7 +195,7 @@ describe("Exchange.LimitIncreaseOrder", () => { }) ) .to.be.revertedWithCustomError(errorsContract, "OracleTimestampsAreSmallerThanRequired") - .withArgs(validFromTime - 1, validFromTime); + .withArgs(ethUsdMarket.marketToken, validFromTime - 1, validFromTime); await executeOrder(fixture, { oracleTimestamps: [validFromTime, validFromTime], From 92bcbb95fcf82196afd4eede665e60f4d692a870 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 16 Jan 2025 10:08:27 +0200 Subject: [PATCH 120/205] Revert "Add token address to OracleTimestampsAreSmallerThanRequired error" This reverts commit 77832ffd1735815d551b896255a389efef7c2e2b. --- contracts/adl/AdlUtils.sol | 4 ++-- contracts/deposit/ExecuteDepositUtils.sol | 1 - contracts/error/Errors.sol | 2 +- contracts/order/DecreaseOrderUtils.sol | 10 ++++------ contracts/order/IncreaseOrderUtils.sol | 2 -- contracts/order/SwapOrderUtils.sol | 2 -- contracts/withdrawal/ExecuteWithdrawalUtils.sol | 1 - test/exchange/LimitIncreaseOrder.ts | 2 +- 8 files changed, 8 insertions(+), 16 deletions(-) diff --git a/contracts/adl/AdlUtils.sol b/contracts/adl/AdlUtils.sol index b0903ac27..3a9f43415 100644 --- a/contracts/adl/AdlUtils.sol +++ b/contracts/adl/AdlUtils.sol @@ -93,7 +93,7 @@ library AdlUtils { uint256 latestAdlTime = getLatestAdlTime(dataStore, market, isLong); if (oracle.maxTimestamp() < latestAdlTime) { - revert Errors.OracleTimestampsAreSmallerThanRequired(market, oracle.maxTimestamp(), latestAdlTime); + revert Errors.OracleTimestampsAreSmallerThanRequired(oracle.maxTimestamp(), latestAdlTime); } Market.Props memory _market = MarketUtils.getEnabledMarket(dataStore, market); @@ -221,7 +221,7 @@ library AdlUtils { uint256 latestAdlTime = AdlUtils.getLatestAdlTime(dataStore, market, isLong); if (oracle.maxTimestamp() < latestAdlTime) { - revert Errors.OracleTimestampsAreSmallerThanRequired(market, oracle.maxTimestamp(), latestAdlTime); + revert Errors.OracleTimestampsAreSmallerThanRequired(oracle.maxTimestamp(), latestAdlTime); } } diff --git a/contracts/deposit/ExecuteDepositUtils.sol b/contracts/deposit/ExecuteDepositUtils.sol index 6923acf48..8cd83e650 100644 --- a/contracts/deposit/ExecuteDepositUtils.sol +++ b/contracts/deposit/ExecuteDepositUtils.sol @@ -116,7 +116,6 @@ library ExecuteDepositUtils { if (params.oracle.minTimestamp() < deposit.updatedAtTime()) { revert Errors.OracleTimestampsAreSmallerThanRequired( - deposit.market(), params.oracle.minTimestamp(), deposit.updatedAtTime() ); diff --git a/contracts/error/Errors.sol b/contracts/error/Errors.sol index b0f2531ae..3357b7e1b 100644 --- a/contracts/error/Errors.sol +++ b/contracts/error/Errors.sol @@ -251,7 +251,7 @@ library Errors { error EmptyPrimaryPrice(address token); - error OracleTimestampsAreSmallerThanRequired(address market, uint256 minOracleTimestamp, uint256 expectedTimestamp); + error OracleTimestampsAreSmallerThanRequired(uint256 minOracleTimestamp, uint256 expectedTimestamp); error OracleTimestampsAreLargerThanRequestExpirationTime(uint256 maxOracleTimestamp, uint256 requestTimestamp, uint256 requestExpirationTime); // BaseOrderUtils errors diff --git a/contracts/order/DecreaseOrderUtils.sol b/contracts/order/DecreaseOrderUtils.sol index a1eda38c0..f47bd5105 100644 --- a/contracts/order/DecreaseOrderUtils.sol +++ b/contracts/order/DecreaseOrderUtils.sol @@ -36,7 +36,6 @@ library DecreaseOrderUtils { validateOracleTimestamp( params.contracts.dataStore, order.orderType(), - order.market(), order.updatedAtTime(), order.validFromTime(), position.increasedAtTime(), @@ -154,7 +153,6 @@ library DecreaseOrderUtils { function validateOracleTimestamp( DataStore dataStore, Order.OrderType orderType, - address market, uint256 orderUpdatedAtTime, uint256 orderValidFromTime, uint256 positionIncreasedAtTime, @@ -164,7 +162,7 @@ library DecreaseOrderUtils { ) internal view { if (orderType == Order.OrderType.MarketDecrease) { if (minOracleTimestamp < orderUpdatedAtTime) { - revert Errors.OracleTimestampsAreSmallerThanRequired(market, minOracleTimestamp, orderUpdatedAtTime); + revert Errors.OracleTimestampsAreSmallerThanRequired(minOracleTimestamp, orderUpdatedAtTime); } uint256 requestExpirationTime = dataStore.getUint(Keys.REQUEST_EXPIRATION_TIME); @@ -183,7 +181,7 @@ library DecreaseOrderUtils { !BaseOrderUtils.isMarketOrder(orderType) && minOracleTimestamp < orderValidFromTime ) { - revert Errors.OracleTimestampsAreSmallerThanRequired(market, minOracleTimestamp, orderValidFromTime); + revert Errors.OracleTimestampsAreSmallerThanRequired(minOracleTimestamp, orderValidFromTime); } // a user could attempt to frontrun prices by creating a limit decrease @@ -208,7 +206,7 @@ library DecreaseOrderUtils { ) { uint256 latestUpdatedAtTime = orderUpdatedAtTime > positionIncreasedAtTime ? orderUpdatedAtTime : positionIncreasedAtTime; if (minOracleTimestamp < latestUpdatedAtTime) { - revert Errors.OracleTimestampsAreSmallerThanRequired(market, minOracleTimestamp, latestUpdatedAtTime); + revert Errors.OracleTimestampsAreSmallerThanRequired(minOracleTimestamp, latestUpdatedAtTime); } return; } @@ -216,7 +214,7 @@ library DecreaseOrderUtils { if (orderType == Order.OrderType.Liquidation) { uint256 latestUpdatedAtTime = positionIncreasedAtTime > positionDecreasedAtTime ? positionIncreasedAtTime : positionDecreasedAtTime; if (minOracleTimestamp < latestUpdatedAtTime) { - revert Errors.OracleTimestampsAreSmallerThanRequired(market, minOracleTimestamp, latestUpdatedAtTime); + revert Errors.OracleTimestampsAreSmallerThanRequired(minOracleTimestamp, latestUpdatedAtTime); } return; } diff --git a/contracts/order/IncreaseOrderUtils.sol b/contracts/order/IncreaseOrderUtils.sol index cbe3ffc22..598a28863 100644 --- a/contracts/order/IncreaseOrderUtils.sol +++ b/contracts/order/IncreaseOrderUtils.sol @@ -53,7 +53,6 @@ library IncreaseOrderUtils { if (params.minOracleTimestamp < params.order.updatedAtTime()) { revert Errors.OracleTimestampsAreSmallerThanRequired( - params.order.market(), params.minOracleTimestamp, params.order.updatedAtTime() ); @@ -64,7 +63,6 @@ library IncreaseOrderUtils { params.minOracleTimestamp < params.order.validFromTime() ) { revert Errors.OracleTimestampsAreSmallerThanRequired( - params.order.market(), params.minOracleTimestamp, params.order.validFromTime() ); diff --git a/contracts/order/SwapOrderUtils.sol b/contracts/order/SwapOrderUtils.sol index 85d47e99f..95ed0dde1 100644 --- a/contracts/order/SwapOrderUtils.sol +++ b/contracts/order/SwapOrderUtils.sol @@ -28,7 +28,6 @@ library SwapOrderUtils { if (params.minOracleTimestamp < params.order.updatedAtTime()) { revert Errors.OracleTimestampsAreSmallerThanRequired( - params.order.market(), params.minOracleTimestamp, params.order.updatedAtTime() ); @@ -39,7 +38,6 @@ library SwapOrderUtils { params.minOracleTimestamp < params.order.validFromTime() ) { revert Errors.OracleTimestampsAreSmallerThanRequired( - params.order.market(), params.minOracleTimestamp, params.order.validFromTime() ); diff --git a/contracts/withdrawal/ExecuteWithdrawalUtils.sol b/contracts/withdrawal/ExecuteWithdrawalUtils.sol index f1f035217..85a00638c 100644 --- a/contracts/withdrawal/ExecuteWithdrawalUtils.sol +++ b/contracts/withdrawal/ExecuteWithdrawalUtils.sol @@ -102,7 +102,6 @@ library ExecuteWithdrawalUtils { if (params.oracle.minTimestamp() < withdrawal.updatedAtTime()) { revert Errors.OracleTimestampsAreSmallerThanRequired( - withdrawal.market(), params.oracle.minTimestamp(), withdrawal.updatedAtTime() ); diff --git a/test/exchange/LimitIncreaseOrder.ts b/test/exchange/LimitIncreaseOrder.ts index f7a76fdc8..8667a72f4 100644 --- a/test/exchange/LimitIncreaseOrder.ts +++ b/test/exchange/LimitIncreaseOrder.ts @@ -195,7 +195,7 @@ describe("Exchange.LimitIncreaseOrder", () => { }) ) .to.be.revertedWithCustomError(errorsContract, "OracleTimestampsAreSmallerThanRequired") - .withArgs(ethUsdMarket.marketToken, validFromTime - 1, validFromTime); + .withArgs(validFromTime - 1, validFromTime); await executeOrder(fixture, { oracleTimestamps: [validFromTime, validFromTime], From cdb6a711d4d56e60d6ff3a7640694deda8bd100c Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 16 Jan 2025 17:13:33 +0200 Subject: [PATCH 121/205] Order.market should be validated for swap orders --- contracts/error/Errors.sol | 3 --- contracts/order/SwapOrderUtils.sol | 4 ---- test/exchange/SwapOrder.ts | 8 ++++++++ test/exchange/VirtualSwapPriceImpact.ts | 5 +++++ test/guardian/testGasEstimation.ts | 2 ++ test/guardian/testLifeCycle.ts | 8 +++++--- test/guardian/testMarketSwap.ts | 7 +++++++ test/guardian/testSpotOnly.ts | 2 ++ test/guardian/testSwap.ts | 2 ++ 9 files changed, 31 insertions(+), 10 deletions(-) diff --git a/contracts/error/Errors.sol b/contracts/error/Errors.sol index 3357b7e1b..fce5ca58d 100644 --- a/contracts/error/Errors.sol +++ b/contracts/error/Errors.sol @@ -283,9 +283,6 @@ library Errors { // OrderStoreUtils errors error OrderNotFound(bytes32 key); - // SwapOrderUtils errors - error UnexpectedMarket(); - // DecreasePositionCollateralUtils errors error InsufficientFundsToPayForCosts(uint256 remainingCostUsd, string step); error InvalidOutputToken(address tokenOut, address expectedTokenOut); diff --git a/contracts/order/SwapOrderUtils.sol b/contracts/order/SwapOrderUtils.sol index 95ed0dde1..244b1067f 100644 --- a/contracts/order/SwapOrderUtils.sol +++ b/contracts/order/SwapOrderUtils.sol @@ -22,10 +22,6 @@ library SwapOrderUtils { // @dev process a swap order // @param params BaseOrderUtils.ExecuteOrderParams function processOrder(BaseOrderUtils.ExecuteOrderParams memory params) external returns (EventUtils.EventLogData memory) { - if (params.order.market() != address(0)) { - revert Errors.UnexpectedMarket(); - } - if (params.minOracleTimestamp < params.order.updatedAtTime()) { revert Errors.OracleTimestampsAreSmallerThanRequired( params.minOracleTimestamp, diff --git a/test/exchange/SwapOrder.ts b/test/exchange/SwapOrder.ts index 1aa1c6513..3d7390e42 100644 --- a/test/exchange/SwapOrder.ts +++ b/test/exchange/SwapOrder.ts @@ -33,6 +33,7 @@ describe("Exchange.SwapOrder", () => { await handleOrder(fixture, { create: { + market: ethUsdMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(10, 18), acceptablePrice: 0, @@ -63,6 +64,7 @@ describe("Exchange.SwapOrder", () => { await handleOrder(fixture, { create: { + market: ethUsdSpotOnlyMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(10, 18), acceptablePrice: 0, @@ -100,6 +102,7 @@ describe("Exchange.SwapOrder", () => { // should be empty and the priceImpactAmount should be zero await handleOrder(fixture, { create: { + market: ethUsdMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(5, 18), acceptablePrice: 0, @@ -130,6 +133,7 @@ describe("Exchange.SwapOrder", () => { // since the pool is mostly balanced, this order should have a negative price impact await handleOrder(fixture, { create: { + market: ethUsdMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(1, 18), acceptablePrice: 0, @@ -159,6 +163,7 @@ describe("Exchange.SwapOrder", () => { // this order should have a positive price impact await handleOrder(fixture, { create: { + market: ethUsdMarket, initialCollateralToken: usdc, initialCollateralDeltaAmount: expandDecimals(5000, 6), acceptablePrice: 0, @@ -203,6 +208,7 @@ describe("Exchange.SwapOrder", () => { // should be empty and the priceImpactAmount should be zero await handleOrder(fixture, { create: { + market: ethUsdMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(5, 18), acceptablePrice: 0, @@ -235,6 +241,7 @@ describe("Exchange.SwapOrder", () => { // since the pool is mostly balanced, this order should have a negative price impact await handleOrder(fixture, { create: { + market: ethUsdMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(1, 18), acceptablePrice: 0, @@ -266,6 +273,7 @@ describe("Exchange.SwapOrder", () => { // this order should have a positive price impact await handleOrder(fixture, { create: { + market: ethUsdMarket, initialCollateralToken: usdc, initialCollateralDeltaAmount: expandDecimals(5000, 6), acceptablePrice: 0, diff --git a/test/exchange/VirtualSwapPriceImpact.ts b/test/exchange/VirtualSwapPriceImpact.ts index 4d01ac36b..b3b64ff74 100644 --- a/test/exchange/VirtualSwapPriceImpact.ts +++ b/test/exchange/VirtualSwapPriceImpact.ts @@ -64,6 +64,7 @@ describe("Exchange.VirtualSwapPriceImpact", () => { await usingResult( handleOrder(fixture, { create: { + market: ethUsdcMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(10, 18), acceptablePrice: 0, @@ -87,6 +88,7 @@ describe("Exchange.VirtualSwapPriceImpact", () => { await usingResult( handleOrder(fixture, { create: { + market: ethUsdtMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(10, 18), acceptablePrice: 0, @@ -127,6 +129,7 @@ describe("Exchange.VirtualSwapPriceImpact", () => { await usingResult( handleOrder(fixture, { create: { + market: ethUsdcMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(10, 18), acceptablePrice: 0, @@ -150,6 +153,7 @@ describe("Exchange.VirtualSwapPriceImpact", () => { await usingResult( handleOrder(fixture, { create: { + market: ethUsdtMarket, initialCollateralToken: usdt, initialCollateralDeltaAmount: expandDecimals(50_000, 6), acceptablePrice: 0, @@ -174,6 +178,7 @@ describe("Exchange.VirtualSwapPriceImpact", () => { await usingResult( handleOrder(fixture, { create: { + market: ethUsdtMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(10, 18), acceptablePrice: 0, diff --git a/test/guardian/testGasEstimation.ts b/test/guardian/testGasEstimation.ts index f6146f09d..639ea0db0 100644 --- a/test/guardian/testGasEstimation.ts +++ b/test/guardian/testGasEstimation.ts @@ -297,6 +297,7 @@ describe("Guardian.GasEstimation", () => { // Gas required is around 50_000 + 7_000 * 7 prices + (4 swaps * 25_000 + 300_000) * 1.5 = 0.0007 ETH, create fails await expect( createOrder(fixture, { + market: ethUsdMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(1, 18), swapPath: [ @@ -315,6 +316,7 @@ describe("Guardian.GasEstimation", () => { // Sufficient executionFee passes await createOrder(fixture, { + market: ethUsdMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(1, 18), swapPath: [ diff --git a/test/guardian/testLifeCycle.ts b/test/guardian/testLifeCycle.ts index 3b6100376..07ebbcdf7 100644 --- a/test/guardian/testLifeCycle.ts +++ b/test/guardian/testLifeCycle.ts @@ -1369,6 +1369,7 @@ describe("Guardian.Lifecycle", () => { // #1 Swap 5,000 await handleOrder(fixture, { create: { + market: ethUsdMarket, account: user1, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(1, 18), @@ -1425,7 +1426,7 @@ describe("Guardian.Lifecycle", () => { const positionFeesCollectedEvent = getEventData(logs, "PositionFeesCollected"); expect(positionFeesCollectedEvent.fundingFeeAmount).closeTo("225782", "10000"); // 0.225782 USDC - expect(positionFeesCollectedEvent.borrowingFeeAmount).closeTo("60904483", "100000"); // 60.904483 USDC + expect(positionFeesCollectedEvent.borrowingFeeAmount).closeTo("62832539", "100000"); // 62.832539 USDC }, }, }); @@ -1433,6 +1434,7 @@ describe("Guardian.Lifecycle", () => { // #2 Swap 4,000 await handleOrder(fixture, { create: { + market: ethUsdMarket, account: user3, initialCollateralToken: usdc, initialCollateralDeltaAmount: expandDecimals(4000, 6), @@ -1522,7 +1524,7 @@ describe("Guardian.Lifecycle", () => { const positionFeesCollectedEvent = getEventData(logs, "PositionFeesCollected"); expect(positionFeesCollectedEvent.fundingFeeAmount).closeTo("20738", "20000"); // 0.020738 USDC - expect(positionFeesCollectedEvent.borrowingFeeAmount).closeTo("30677911", "20000"); // 30.67 USDC + expect(positionFeesCollectedEvent.borrowingFeeAmount).closeTo("30099876", "20000"); // 30.10 USDC }, }, }); @@ -1582,7 +1584,7 @@ describe("Guardian.Lifecycle", () => { const positionFeesCollectedEvent = getEventData(logs, "PositionFeesCollected"); expect(positionFeesCollectedEvent.fundingFeeAmount).closeTo("31107", "20000"); // 0.031107 USDC - expect(positionFeesCollectedEvent.borrowingFeeAmount).closeTo("42921864", "20000"); // 42.921864 USDC + expect(positionFeesCollectedEvent.borrowingFeeAmount).closeTo("42054832", "20000"); // 42.054832 USDC }, }, }); diff --git a/test/guardian/testMarketSwap.ts b/test/guardian/testMarketSwap.ts index d69695b4b..e4102f722 100644 --- a/test/guardian/testMarketSwap.ts +++ b/test/guardian/testMarketSwap.ts @@ -35,6 +35,7 @@ describe("Guardian.MktSwapOrder", () => { await handleOrder(fixture, { create: { + market: ethUsdMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(10, 18), acceptablePrice: 0, @@ -79,6 +80,7 @@ describe("Guardian.MktSwapOrder", () => { // ETH -> USDC, USDC -> ETH, ETH -> USDC await handleOrder(fixture, { create: { + market: ethUsdMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(10, 18), acceptablePrice: 0, @@ -115,6 +117,7 @@ describe("Guardian.MktSwapOrder", () => { // Revert with InvalidTokenIn await handleOrder(fixture, { create: { + market: ethUsdMarket, initialCollateralToken: wbtc, initialCollateralDeltaAmount: expandDecimals(10, 8), acceptablePrice: 0, @@ -139,6 +142,7 @@ describe("Guardian.MktSwapOrder", () => { // Revert with UsdDeltaExceedsPoolValue await handleOrder(fixture, { create: { + market: ethUsdMarket, initialCollateralToken: wnt, receiver: user0, initialCollateralDeltaAmount: expandDecimals(50, 18), // Swap for $250,000 USDC but pool only has $50,000 @@ -175,6 +179,7 @@ describe("Guardian.MktSwapOrder", () => { await expect( handleOrder(fixture, { create: { + market: ethUsdMarket, initialCollateralToken: wnt, receiver: user0, initialCollateralDeltaAmount: 0, @@ -210,6 +215,7 @@ describe("Guardian.MktSwapOrder", () => { // Reverted with InsufficientSwapOutputAmount await handleOrder(fixture, { create: { + market: ethUsdMarket, initialCollateralToken: wnt, receiver: user0, initialCollateralDeltaAmount: expandDecimals(10, 18), // $50,000 @@ -263,6 +269,7 @@ describe("Guardian.MktSwapOrder", () => { // Decrease imbalance from $99,925 to $49,925 (increase WNT by $25,000 and decrease USDC by $25,000) await handleOrder(fixture, { create: { + market: ethUsdMarket, initialCollateralToken: wnt, receiver: user0, initialCollateralDeltaAmount: expandDecimals(5, 18), // $25,000 diff --git a/test/guardian/testSpotOnly.ts b/test/guardian/testSpotOnly.ts index 6b0315eab..b35822190 100644 --- a/test/guardian/testSpotOnly.ts +++ b/test/guardian/testSpotOnly.ts @@ -42,6 +42,7 @@ describe("Guardian.SpotOnlyMarkets", () => { await handleOrder(fixture, { create: { + market: ethUsdSpotOnlyMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(10, 18), acceptablePrice: 0, @@ -61,6 +62,7 @@ describe("Guardian.SpotOnlyMarkets", () => { await handleOrder(fixture, { create: { + market: ethUsdSpotOnlyMarket, initialCollateralToken: usdc, initialCollateralDeltaAmount: expandDecimals(50_000, 6), acceptablePrice: 0, diff --git a/test/guardian/testSwap.ts b/test/guardian/testSwap.ts index 413175569..ffb5225f9 100644 --- a/test/guardian/testSwap.ts +++ b/test/guardian/testSwap.ts @@ -244,6 +244,7 @@ describe("Guardian.Swap", () => { // duplicated swap order await expect( createOrder(fixture, { + market: ethUsdSingleTokenMarket, initialCollateralToken: usdc, initialCollateralDeltaAmount: expandDecimals(4000, 6), acceptablePrice: 0, @@ -260,6 +261,7 @@ describe("Guardian.Swap", () => { // duplicated swap order await handleOrder(fixture, { create: { + market: ethUsdMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(1, 18), acceptablePrice: 0, From 255da58c67762cae35f71212fc9502c8b4d2aa34 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 17 Jan 2025 10:24:11 +0200 Subject: [PATCH 122/205] Revert "Order.market should be validated for swap orders" This reverts commit a09709a98ce804c6d557f609908a89f9c382882d. --- contracts/error/Errors.sol | 3 +++ contracts/order/SwapOrderUtils.sol | 4 ++++ test/exchange/SwapOrder.ts | 8 -------- test/exchange/VirtualSwapPriceImpact.ts | 5 ----- test/guardian/testGasEstimation.ts | 2 -- test/guardian/testLifeCycle.ts | 8 +++----- test/guardian/testMarketSwap.ts | 7 ------- test/guardian/testSpotOnly.ts | 2 -- test/guardian/testSwap.ts | 2 -- 9 files changed, 10 insertions(+), 31 deletions(-) diff --git a/contracts/error/Errors.sol b/contracts/error/Errors.sol index fce5ca58d..3357b7e1b 100644 --- a/contracts/error/Errors.sol +++ b/contracts/error/Errors.sol @@ -283,6 +283,9 @@ library Errors { // OrderStoreUtils errors error OrderNotFound(bytes32 key); + // SwapOrderUtils errors + error UnexpectedMarket(); + // DecreasePositionCollateralUtils errors error InsufficientFundsToPayForCosts(uint256 remainingCostUsd, string step); error InvalidOutputToken(address tokenOut, address expectedTokenOut); diff --git a/contracts/order/SwapOrderUtils.sol b/contracts/order/SwapOrderUtils.sol index 244b1067f..95ed0dde1 100644 --- a/contracts/order/SwapOrderUtils.sol +++ b/contracts/order/SwapOrderUtils.sol @@ -22,6 +22,10 @@ library SwapOrderUtils { // @dev process a swap order // @param params BaseOrderUtils.ExecuteOrderParams function processOrder(BaseOrderUtils.ExecuteOrderParams memory params) external returns (EventUtils.EventLogData memory) { + if (params.order.market() != address(0)) { + revert Errors.UnexpectedMarket(); + } + if (params.minOracleTimestamp < params.order.updatedAtTime()) { revert Errors.OracleTimestampsAreSmallerThanRequired( params.minOracleTimestamp, diff --git a/test/exchange/SwapOrder.ts b/test/exchange/SwapOrder.ts index 3d7390e42..1aa1c6513 100644 --- a/test/exchange/SwapOrder.ts +++ b/test/exchange/SwapOrder.ts @@ -33,7 +33,6 @@ describe("Exchange.SwapOrder", () => { await handleOrder(fixture, { create: { - market: ethUsdMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(10, 18), acceptablePrice: 0, @@ -64,7 +63,6 @@ describe("Exchange.SwapOrder", () => { await handleOrder(fixture, { create: { - market: ethUsdSpotOnlyMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(10, 18), acceptablePrice: 0, @@ -102,7 +100,6 @@ describe("Exchange.SwapOrder", () => { // should be empty and the priceImpactAmount should be zero await handleOrder(fixture, { create: { - market: ethUsdMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(5, 18), acceptablePrice: 0, @@ -133,7 +130,6 @@ describe("Exchange.SwapOrder", () => { // since the pool is mostly balanced, this order should have a negative price impact await handleOrder(fixture, { create: { - market: ethUsdMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(1, 18), acceptablePrice: 0, @@ -163,7 +159,6 @@ describe("Exchange.SwapOrder", () => { // this order should have a positive price impact await handleOrder(fixture, { create: { - market: ethUsdMarket, initialCollateralToken: usdc, initialCollateralDeltaAmount: expandDecimals(5000, 6), acceptablePrice: 0, @@ -208,7 +203,6 @@ describe("Exchange.SwapOrder", () => { // should be empty and the priceImpactAmount should be zero await handleOrder(fixture, { create: { - market: ethUsdMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(5, 18), acceptablePrice: 0, @@ -241,7 +235,6 @@ describe("Exchange.SwapOrder", () => { // since the pool is mostly balanced, this order should have a negative price impact await handleOrder(fixture, { create: { - market: ethUsdMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(1, 18), acceptablePrice: 0, @@ -273,7 +266,6 @@ describe("Exchange.SwapOrder", () => { // this order should have a positive price impact await handleOrder(fixture, { create: { - market: ethUsdMarket, initialCollateralToken: usdc, initialCollateralDeltaAmount: expandDecimals(5000, 6), acceptablePrice: 0, diff --git a/test/exchange/VirtualSwapPriceImpact.ts b/test/exchange/VirtualSwapPriceImpact.ts index b3b64ff74..4d01ac36b 100644 --- a/test/exchange/VirtualSwapPriceImpact.ts +++ b/test/exchange/VirtualSwapPriceImpact.ts @@ -64,7 +64,6 @@ describe("Exchange.VirtualSwapPriceImpact", () => { await usingResult( handleOrder(fixture, { create: { - market: ethUsdcMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(10, 18), acceptablePrice: 0, @@ -88,7 +87,6 @@ describe("Exchange.VirtualSwapPriceImpact", () => { await usingResult( handleOrder(fixture, { create: { - market: ethUsdtMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(10, 18), acceptablePrice: 0, @@ -129,7 +127,6 @@ describe("Exchange.VirtualSwapPriceImpact", () => { await usingResult( handleOrder(fixture, { create: { - market: ethUsdcMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(10, 18), acceptablePrice: 0, @@ -153,7 +150,6 @@ describe("Exchange.VirtualSwapPriceImpact", () => { await usingResult( handleOrder(fixture, { create: { - market: ethUsdtMarket, initialCollateralToken: usdt, initialCollateralDeltaAmount: expandDecimals(50_000, 6), acceptablePrice: 0, @@ -178,7 +174,6 @@ describe("Exchange.VirtualSwapPriceImpact", () => { await usingResult( handleOrder(fixture, { create: { - market: ethUsdtMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(10, 18), acceptablePrice: 0, diff --git a/test/guardian/testGasEstimation.ts b/test/guardian/testGasEstimation.ts index 639ea0db0..f6146f09d 100644 --- a/test/guardian/testGasEstimation.ts +++ b/test/guardian/testGasEstimation.ts @@ -297,7 +297,6 @@ describe("Guardian.GasEstimation", () => { // Gas required is around 50_000 + 7_000 * 7 prices + (4 swaps * 25_000 + 300_000) * 1.5 = 0.0007 ETH, create fails await expect( createOrder(fixture, { - market: ethUsdMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(1, 18), swapPath: [ @@ -316,7 +315,6 @@ describe("Guardian.GasEstimation", () => { // Sufficient executionFee passes await createOrder(fixture, { - market: ethUsdMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(1, 18), swapPath: [ diff --git a/test/guardian/testLifeCycle.ts b/test/guardian/testLifeCycle.ts index 07ebbcdf7..3b6100376 100644 --- a/test/guardian/testLifeCycle.ts +++ b/test/guardian/testLifeCycle.ts @@ -1369,7 +1369,6 @@ describe("Guardian.Lifecycle", () => { // #1 Swap 5,000 await handleOrder(fixture, { create: { - market: ethUsdMarket, account: user1, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(1, 18), @@ -1426,7 +1425,7 @@ describe("Guardian.Lifecycle", () => { const positionFeesCollectedEvent = getEventData(logs, "PositionFeesCollected"); expect(positionFeesCollectedEvent.fundingFeeAmount).closeTo("225782", "10000"); // 0.225782 USDC - expect(positionFeesCollectedEvent.borrowingFeeAmount).closeTo("62832539", "100000"); // 62.832539 USDC + expect(positionFeesCollectedEvent.borrowingFeeAmount).closeTo("60904483", "100000"); // 60.904483 USDC }, }, }); @@ -1434,7 +1433,6 @@ describe("Guardian.Lifecycle", () => { // #2 Swap 4,000 await handleOrder(fixture, { create: { - market: ethUsdMarket, account: user3, initialCollateralToken: usdc, initialCollateralDeltaAmount: expandDecimals(4000, 6), @@ -1524,7 +1522,7 @@ describe("Guardian.Lifecycle", () => { const positionFeesCollectedEvent = getEventData(logs, "PositionFeesCollected"); expect(positionFeesCollectedEvent.fundingFeeAmount).closeTo("20738", "20000"); // 0.020738 USDC - expect(positionFeesCollectedEvent.borrowingFeeAmount).closeTo("30099876", "20000"); // 30.10 USDC + expect(positionFeesCollectedEvent.borrowingFeeAmount).closeTo("30677911", "20000"); // 30.67 USDC }, }, }); @@ -1584,7 +1582,7 @@ describe("Guardian.Lifecycle", () => { const positionFeesCollectedEvent = getEventData(logs, "PositionFeesCollected"); expect(positionFeesCollectedEvent.fundingFeeAmount).closeTo("31107", "20000"); // 0.031107 USDC - expect(positionFeesCollectedEvent.borrowingFeeAmount).closeTo("42054832", "20000"); // 42.054832 USDC + expect(positionFeesCollectedEvent.borrowingFeeAmount).closeTo("42921864", "20000"); // 42.921864 USDC }, }, }); diff --git a/test/guardian/testMarketSwap.ts b/test/guardian/testMarketSwap.ts index e4102f722..d69695b4b 100644 --- a/test/guardian/testMarketSwap.ts +++ b/test/guardian/testMarketSwap.ts @@ -35,7 +35,6 @@ describe("Guardian.MktSwapOrder", () => { await handleOrder(fixture, { create: { - market: ethUsdMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(10, 18), acceptablePrice: 0, @@ -80,7 +79,6 @@ describe("Guardian.MktSwapOrder", () => { // ETH -> USDC, USDC -> ETH, ETH -> USDC await handleOrder(fixture, { create: { - market: ethUsdMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(10, 18), acceptablePrice: 0, @@ -117,7 +115,6 @@ describe("Guardian.MktSwapOrder", () => { // Revert with InvalidTokenIn await handleOrder(fixture, { create: { - market: ethUsdMarket, initialCollateralToken: wbtc, initialCollateralDeltaAmount: expandDecimals(10, 8), acceptablePrice: 0, @@ -142,7 +139,6 @@ describe("Guardian.MktSwapOrder", () => { // Revert with UsdDeltaExceedsPoolValue await handleOrder(fixture, { create: { - market: ethUsdMarket, initialCollateralToken: wnt, receiver: user0, initialCollateralDeltaAmount: expandDecimals(50, 18), // Swap for $250,000 USDC but pool only has $50,000 @@ -179,7 +175,6 @@ describe("Guardian.MktSwapOrder", () => { await expect( handleOrder(fixture, { create: { - market: ethUsdMarket, initialCollateralToken: wnt, receiver: user0, initialCollateralDeltaAmount: 0, @@ -215,7 +210,6 @@ describe("Guardian.MktSwapOrder", () => { // Reverted with InsufficientSwapOutputAmount await handleOrder(fixture, { create: { - market: ethUsdMarket, initialCollateralToken: wnt, receiver: user0, initialCollateralDeltaAmount: expandDecimals(10, 18), // $50,000 @@ -269,7 +263,6 @@ describe("Guardian.MktSwapOrder", () => { // Decrease imbalance from $99,925 to $49,925 (increase WNT by $25,000 and decrease USDC by $25,000) await handleOrder(fixture, { create: { - market: ethUsdMarket, initialCollateralToken: wnt, receiver: user0, initialCollateralDeltaAmount: expandDecimals(5, 18), // $25,000 diff --git a/test/guardian/testSpotOnly.ts b/test/guardian/testSpotOnly.ts index b35822190..6b0315eab 100644 --- a/test/guardian/testSpotOnly.ts +++ b/test/guardian/testSpotOnly.ts @@ -42,7 +42,6 @@ describe("Guardian.SpotOnlyMarkets", () => { await handleOrder(fixture, { create: { - market: ethUsdSpotOnlyMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(10, 18), acceptablePrice: 0, @@ -62,7 +61,6 @@ describe("Guardian.SpotOnlyMarkets", () => { await handleOrder(fixture, { create: { - market: ethUsdSpotOnlyMarket, initialCollateralToken: usdc, initialCollateralDeltaAmount: expandDecimals(50_000, 6), acceptablePrice: 0, diff --git a/test/guardian/testSwap.ts b/test/guardian/testSwap.ts index ffb5225f9..413175569 100644 --- a/test/guardian/testSwap.ts +++ b/test/guardian/testSwap.ts @@ -244,7 +244,6 @@ describe("Guardian.Swap", () => { // duplicated swap order await expect( createOrder(fixture, { - market: ethUsdSingleTokenMarket, initialCollateralToken: usdc, initialCollateralDeltaAmount: expandDecimals(4000, 6), acceptablePrice: 0, @@ -261,7 +260,6 @@ describe("Guardian.Swap", () => { // duplicated swap order await handleOrder(fixture, { create: { - market: ethUsdMarket, initialCollateralToken: wnt, initialCollateralDeltaAmount: expandDecimals(1, 18), acceptablePrice: 0, From 2101d4ea979aa517bd7236802565bbb08dbdecba Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 22 Jan 2025 21:38:24 +0200 Subject: [PATCH 123/205] Add ConfigUtils lib and move logic from the Config to reduce the contract size --- contracts/config/IConfigUtils.sol | 68 +++++++++++++++++++++++++++++ deploy/deployConfig.ts | 2 +- scripts/updateGeneralConfigUtils.ts | 4 +- 3 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 contracts/config/IConfigUtils.sol diff --git a/contracts/config/IConfigUtils.sol b/contracts/config/IConfigUtils.sol new file mode 100644 index 000000000..4ec8888c5 --- /dev/null +++ b/contracts/config/IConfigUtils.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; + +import "../data/DataStore.sol"; +import "../event/EventEmitter.sol"; + +interface IConfigUtils { + function setPriceFeed( + DataStore dataStore, + EventEmitter eventEmitter, + address token, + address priceFeed, + uint256 priceFeedMultiplier, + uint256 priceFeedHeartbeatDuration, + uint256 stablePrice + ) external; + + function setDataStream( + DataStore dataStore, + EventEmitter eventEmitter, + address token, + bytes32 feedId, + uint256 dataStreamMultiplier, + uint256 dataStreamSpreadReductionFactor, + uint256 maxAllowedMaxFundingFactorPerSecond, + uint256 maxAllowedFundingIncreaseFactorPerSecond, + uint256 maxAllowedFundingDecreaseFactorPerSecond + ) external; + + function setClaimableCollateralFactorForTime( + DataStore dataStore, + EventEmitter eventEmitter, + address market, + address token, + uint256 timeKey, + uint256 factor + ) external; + + function setClaimableCollateralFactorForAccount( + DataStore dataStore, + EventEmitter eventEmitter, + address market, + address token, + uint256 timeKey, + address account, + uint256 factor + ) external; + + function setPositionImpactDistributionRate( + DataStore dataStore, + EventEmitter eventEmitter, + address market, + uint256 minPositionImpactPoolAmount, + uint256 positionImpactPoolDistributionRate, + uint256 minPositionImpactPoolDistributionTime + ) external; + + function validateRange( + DataStore dataStore, + bytes32 baseKey, + bytes memory data, + uint256 value, + uint256 maxAllowedMaxFundingFactorPerSecond, + uint256 maxAllowedFundingIncreaseFactorPerSecond, + uint256 maxAllowedFundingDecreaseFactorPerSecond + ) external; +} diff --git a/deploy/deployConfig.ts b/deploy/deployConfig.ts index 456d19705..b5d3d02d2 100644 --- a/deploy/deployConfig.ts +++ b/deploy/deployConfig.ts @@ -1,7 +1,7 @@ import { grantRoleIfNotGranted } from "../utils/role"; import { createDeployFunction } from "../utils/deploy"; -const constructorContracts = ["RoleStore", "DataStore", "EventEmitter"]; +const constructorContracts = ["RoleStore", "DataStore", "EventEmitter", "ConfigUtils"]; const func = createDeployFunction({ contractName: "Config", diff --git a/scripts/updateGeneralConfigUtils.ts b/scripts/updateGeneralConfigUtils.ts index d9e55bfb5..983383a5e 100644 --- a/scripts/updateGeneralConfigUtils.ts +++ b/scripts/updateGeneralConfigUtils.ts @@ -382,7 +382,9 @@ export async function updateGeneralConfig({ write }) { } if (write) { - const tx = await config.multicall(multicallWriteParams); + console.log(" TODO: FAILS HERE !!!"); + const tx = await config.multicall(multicallWriteParams); // TODO: this seems to fails !!! + console.log(" TODO: FAILED !!!"); console.log(`tx sent: ${tx.hash}`); } else { await config.callStatic.multicall(multicallWriteParams, { From 57e8abc0ac6093a87a91e5b007acab94300641f2 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 23 Jan 2025 07:41:28 +0200 Subject: [PATCH 124/205] Remove IConfigUtils interface and import ConfigUtils directly. Since the lib methods are external, Config contract size is still reduced. No need to use an interface. --- contracts/config/IConfigUtils.sol | 68 ------------------------------- 1 file changed, 68 deletions(-) delete mode 100644 contracts/config/IConfigUtils.sol diff --git a/contracts/config/IConfigUtils.sol b/contracts/config/IConfigUtils.sol deleted file mode 100644 index 4ec8888c5..000000000 --- a/contracts/config/IConfigUtils.sol +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.0; - -import "../data/DataStore.sol"; -import "../event/EventEmitter.sol"; - -interface IConfigUtils { - function setPriceFeed( - DataStore dataStore, - EventEmitter eventEmitter, - address token, - address priceFeed, - uint256 priceFeedMultiplier, - uint256 priceFeedHeartbeatDuration, - uint256 stablePrice - ) external; - - function setDataStream( - DataStore dataStore, - EventEmitter eventEmitter, - address token, - bytes32 feedId, - uint256 dataStreamMultiplier, - uint256 dataStreamSpreadReductionFactor, - uint256 maxAllowedMaxFundingFactorPerSecond, - uint256 maxAllowedFundingIncreaseFactorPerSecond, - uint256 maxAllowedFundingDecreaseFactorPerSecond - ) external; - - function setClaimableCollateralFactorForTime( - DataStore dataStore, - EventEmitter eventEmitter, - address market, - address token, - uint256 timeKey, - uint256 factor - ) external; - - function setClaimableCollateralFactorForAccount( - DataStore dataStore, - EventEmitter eventEmitter, - address market, - address token, - uint256 timeKey, - address account, - uint256 factor - ) external; - - function setPositionImpactDistributionRate( - DataStore dataStore, - EventEmitter eventEmitter, - address market, - uint256 minPositionImpactPoolAmount, - uint256 positionImpactPoolDistributionRate, - uint256 minPositionImpactPoolDistributionTime - ) external; - - function validateRange( - DataStore dataStore, - bytes32 baseKey, - bytes memory data, - uint256 value, - uint256 maxAllowedMaxFundingFactorPerSecond, - uint256 maxAllowedFundingIncreaseFactorPerSecond, - uint256 maxAllowedFundingDecreaseFactorPerSecond - ) external; -} From 3258480d48dde6099f5cdb853136ba571cb45642 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 23 Jan 2025 07:56:20 +0200 Subject: [PATCH 125/205] Remove contructor param from Config --- deploy/deployConfig.ts | 2 +- scripts/updateGeneralConfigUtils.ts | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/deploy/deployConfig.ts b/deploy/deployConfig.ts index b5d3d02d2..456d19705 100644 --- a/deploy/deployConfig.ts +++ b/deploy/deployConfig.ts @@ -1,7 +1,7 @@ import { grantRoleIfNotGranted } from "../utils/role"; import { createDeployFunction } from "../utils/deploy"; -const constructorContracts = ["RoleStore", "DataStore", "EventEmitter", "ConfigUtils"]; +const constructorContracts = ["RoleStore", "DataStore", "EventEmitter"]; const func = createDeployFunction({ contractName: "Config", diff --git a/scripts/updateGeneralConfigUtils.ts b/scripts/updateGeneralConfigUtils.ts index 983383a5e..d9e55bfb5 100644 --- a/scripts/updateGeneralConfigUtils.ts +++ b/scripts/updateGeneralConfigUtils.ts @@ -382,9 +382,7 @@ export async function updateGeneralConfig({ write }) { } if (write) { - console.log(" TODO: FAILS HERE !!!"); - const tx = await config.multicall(multicallWriteParams); // TODO: this seems to fails !!! - console.log(" TODO: FAILED !!!"); + const tx = await config.multicall(multicallWriteParams); console.log(`tx sent: ${tx.hash}`); } else { await config.callStatic.multicall(multicallWriteParams, { From d0b570a120f8042532f9fc1c3053c15c14a67976 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 28 Jan 2025 00:01:04 +0200 Subject: [PATCH 126/205] WIP multichain --- contracts/multichain/MultichainHandler.sol | 37 +++++++++++++++------- contracts/position/Position.sol | 11 ++++++- utils/multichain.ts | 8 ----- 3 files changed, 36 insertions(+), 20 deletions(-) diff --git a/contracts/multichain/MultichainHandler.sol b/contracts/multichain/MultichainHandler.sol index 9469e8a27..876f47a56 100644 --- a/contracts/multichain/MultichainHandler.sol +++ b/contracts/multichain/MultichainHandler.sol @@ -2,6 +2,9 @@ pragma solidity ^0.8.0; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + import { RoleStore } from "../role/RoleStore.sol"; import { RoleModule } from "../role/RoleModule.sol"; import { DataStore } from "../data/DataStore.sol"; @@ -21,6 +24,8 @@ import { MultichainEventUtils } from "./MultichainEventUtils.sol"; * @title MultichainHandler */ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { + using SafeERC20 for IERC20; + MultichainVault public multichainVault; EventEmitter public eventEmitter; ExchangeRouter public exchangeRouter; @@ -39,7 +44,7 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { } /** - * Records a deposit from another chain. IMultichainProvider has MULTICHAIN_CONTROLLER role + * Records a deposit from another chain. IMultichainProvider has CONTROLLER role * @param account user address on the source chain * @param token address of the token being deposited * @param sourceChainId chain id of the source chain @@ -48,18 +53,30 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { address account, address token, uint256 sourceChainId - ) external onlyController returns (address virtualAccount) { + ) external onlyController { // token should have been transferred to multichainVault by IMultichainProvider uint256 amount = multichainVault.recordTransferIn(token); if (amount == 0) { revert Errors.EmptyMultichainDepositAmount(); } - virtualAccount = MultichainUtils.getVirtualAccount(account, sourceChainId); + dataStore.incrementUint(Keys.sourceChainBalanceKey(sourceChainId, account, token), amount); - dataStore.incrementUint(Keys.sourceChainBalanceKey(virtualAccount, token), amount); + MultichainEventUtils.emitMultichainDeposit(eventEmitter, token, account, amount, sourceChainId); + } + + /** + * @dev transfer the specified amount of tokens from account to receiver + * @param account the account for which the tokens are subtracted + * @param token the token to transfer + * @param receiver the account to transfer to + * @param amount the amount of tokens to transfer + */ + function pluginTransfer(address token, address account, address receiver, uint256 amount) external onlyRouterPlugin { // TODO: confirm access control + // TODO: tokens should come from MultichainVault and the user's multichain balance should be decreased + // dataStore.decrementUint(Keys.sourceChainBalanceKey(sourceChainId, account, token), amount); - MultichainEventUtils.emitMultichainDeposit(eventEmitter, token, virtualAccount, amount, sourceChainId); + IERC20(token).safeTransferFrom(account, receiver, amount); } /** @@ -77,12 +94,11 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { // execute multicall exchangeRouter.multicall(multicallArgs); - address virtualAccount = MultichainUtils.getVirtualAccount(account, sourceChainId); - MultichainEventUtils.emitMultichainMessage(eventEmitter, virtualAccount, sourceChainId); + MultichainEventUtils.emitMultichainMessage(eventEmitter, account, sourceChainId); } /** - * Record a withdrawal to another chain. IMultichainProvider has MULTICHAIN_CONTROLLER role + * Record a withdrawal to another chain. IMultichainProvider has CONTROLLER role * @param account user address on the source chain * @param token address of the token being withdrawn * @param amount amount of token being withdrawn @@ -98,8 +114,7 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { revert Errors.EmptyMultichainWithdrawalAmount(); } - address virtualAccount = MultichainUtils.getVirtualAccount(account, sourceChainId); - bytes32 balanceKey = Keys.sourceChainBalanceKey(virtualAccount, token); + bytes32 balanceKey = Keys.sourceChainBalanceKey(sourceChainId, account, token); uint256 balance = dataStore.getUint(balanceKey); if (balance < amount) { @@ -112,6 +127,6 @@ contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { // transfer tokens to IMultichainProvider multichainVault.transferOut(token, msg.sender, amount); - MultichainEventUtils.emitMultichainWithdrawal(eventEmitter, token, virtualAccount, amount, sourceChainId); + MultichainEventUtils.emitMultichainWithdrawal(eventEmitter, token, account, amount, sourceChainId); } } diff --git a/contracts/position/Position.sol b/contracts/position/Position.sol index 2da0950c0..94caef0de 100644 --- a/contracts/position/Position.sol +++ b/contracts/position/Position.sol @@ -197,8 +197,17 @@ library Position { // @param market the position's market // @param collateralToken the position's collateralToken // @param isLong whether the position is long or short + // @param chainId the source chain id // @return the position key - function getPositionKey(address _account, address _market, address _collateralToken, bool _isLong) internal pure returns (bytes32) { + function getPositionKey(address _account, address _market, address _collateralToken, bool _isLong/*, uint256 _chainId*/) internal pure returns (bytes32) { + // TODO: confirm orders should have a chainId as well + // bytes32 _key; + // if (_chainId == 0) { + // _key = keccak256(abi.encode(_account, _market, _collateralToken, _isLong)); + // } else { + // _key = keccak256(abi.encode(_account, _chainId, _market, _collateralToken, _isLong)); + // } + bytes32 _key = keccak256(abi.encode(_account, _market, _collateralToken, _isLong)); return _key; diff --git a/utils/multichain.ts b/utils/multichain.ts index 24a03459d..e69de29bb 100644 --- a/utils/multichain.ts +++ b/utils/multichain.ts @@ -1,8 +0,0 @@ -import { hashData, getAddressFromHash } from "./hash"; - -export const GMX_MULTICHAIN = "GMX Multichain"; - -export function getVirtualAccount(account: string, sourceChainId: number): string { - const hash = hashData(["string", "address", "uint256"], [GMX_MULTICHAIN, account, sourceChainId]); - return getAddressFromHash(hash); -} From 14de976dd4b171bdd38861bc1e70b28015c61705 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 21 Jan 2025 15:00:15 +0200 Subject: [PATCH 127/205] Add support for bytes type in DataStore (bytesValues, getBytes, setBytes, removeBytes) --- contracts/data/DataStore.sol | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/contracts/data/DataStore.sol b/contracts/data/DataStore.sol index 36279a5f3..a86b2da11 100644 --- a/contracts/data/DataStore.sol +++ b/contracts/data/DataStore.sol @@ -29,6 +29,8 @@ contract DataStore is RoleModule { mapping(bytes32 => string) public stringValues; // store for bytes32 values mapping(bytes32 => bytes32) public bytes32Values; + // store for bytes values + mapping(bytes32 => bytes) public bytesValues; // store for uint[] values mapping(bytes32 => uint256[]) public uintArrayValues; @@ -274,6 +276,28 @@ contract DataStore is RoleModule { delete bytes32Values[key]; } + // @dev get the bytes value for the given key + // @param key the key of the value + // @return the bytes value for the key + function getBytes(bytes32 key) external view returns (bytes memory) { + return bytesValues[key]; + } + + // @dev set the bytes value for the given key + // @param key the key of the value + // @param value the value to set + // @return the bytes value for the key + function setBytes(bytes32 key, bytes memory value) external onlyController returns (bytes memory) { + bytesValues[key] = value; + return value; + } + + // @dev delete the bytes value for the given key + // @param key the key of the value + function removeBytes(bytes32 key) external onlyController { + delete bytesValues[key]; + } + // @dev get the uint array for the given key // @param key the key of the uint array // @return the uint array for the key From 62bfd6a26f652314c3ddd8da92ed3e5ebf4fadc3 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 21 Jan 2025 15:08:08 +0200 Subject: [PATCH 128/205] WIP add data field for deposits, orders, withdrawals, shifts etc --- contracts/glv/glvShift/GlvShift.sol | 9 +++++++++ contracts/glv/glvShift/GlvShiftStoreUtils.sol | 15 +++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/contracts/glv/glvShift/GlvShift.sol b/contracts/glv/glvShift/GlvShift.sol index 63d8de826..6384f7218 100644 --- a/contracts/glv/glvShift/GlvShift.sol +++ b/contracts/glv/glvShift/GlvShift.sol @@ -6,6 +6,7 @@ library GlvShift { struct Props { Addresses addresses; Numbers numbers; + bytes dataField; } struct Addresses { @@ -67,4 +68,12 @@ library GlvShift { function setUpdatedAtTime(Props memory props, uint256 value) internal pure { props.numbers.updatedAtTime = value; } + + function data(Props memory props) internal pure returns (bytes memory) { + return props.dataField; + } + + function setData(Props memory props, bytes memory value) internal pure { + props.dataField = value; + } } diff --git a/contracts/glv/glvShift/GlvShiftStoreUtils.sol b/contracts/glv/glvShift/GlvShiftStoreUtils.sol index 06aea8fbd..187138287 100644 --- a/contracts/glv/glvShift/GlvShiftStoreUtils.sol +++ b/contracts/glv/glvShift/GlvShiftStoreUtils.sol @@ -18,6 +18,8 @@ library GlvShiftStoreUtils { bytes32 public constant MIN_MARKET_TOKENS = keccak256(abi.encode("MIN_MARKET_TOKENS")); bytes32 public constant UPDATED_AT_TIME = keccak256(abi.encode("UPDATED_AT_TIME")); + bytes32 public constant DATA = keccak256(abi.encode("DATA")); + function get(DataStore dataStore, bytes32 key) external view returns (GlvShift.Props memory) { GlvShift.Props memory glvShift; if (!dataStore.containsBytes32(Keys.GLV_SHIFT_LIST, key)) { @@ -48,6 +50,10 @@ library GlvShiftStoreUtils { keccak256(abi.encode(key, UPDATED_AT_TIME)) )); + glvShift.setData(dataStore.getBytes( + keccak256(abi.encode(key, DATA)) + )); + return glvShift; } @@ -86,6 +92,11 @@ library GlvShiftStoreUtils { keccak256(abi.encode(key, UPDATED_AT_TIME)), glvShift.updatedAtTime() ); + + dataStore.setBytes( + keccak256(abi.encode(key, DATA)), + glvShift.data() + ); } function remove(DataStore dataStore, bytes32 key) external { @@ -121,6 +132,10 @@ library GlvShiftStoreUtils { dataStore.removeUint( keccak256(abi.encode(key, UPDATED_AT_TIME)) ); + + dataStore.removeBytes( + keccak256(abi.encode(key, DATA)) + ); } function getGlvShiftCount(DataStore dataStore) internal view returns (uint256) { From 1e5ef9f0a5c1b4490cb6afdd706dcd3f04414021 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 22 Jan 2025 21:47:58 +0200 Subject: [PATCH 129/205] Revert changes for the DataStore --- contracts/data/DataStore.sol | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/contracts/data/DataStore.sol b/contracts/data/DataStore.sol index a86b2da11..36279a5f3 100644 --- a/contracts/data/DataStore.sol +++ b/contracts/data/DataStore.sol @@ -29,8 +29,6 @@ contract DataStore is RoleModule { mapping(bytes32 => string) public stringValues; // store for bytes32 values mapping(bytes32 => bytes32) public bytes32Values; - // store for bytes values - mapping(bytes32 => bytes) public bytesValues; // store for uint[] values mapping(bytes32 => uint256[]) public uintArrayValues; @@ -276,28 +274,6 @@ contract DataStore is RoleModule { delete bytes32Values[key]; } - // @dev get the bytes value for the given key - // @param key the key of the value - // @return the bytes value for the key - function getBytes(bytes32 key) external view returns (bytes memory) { - return bytesValues[key]; - } - - // @dev set the bytes value for the given key - // @param key the key of the value - // @param value the value to set - // @return the bytes value for the key - function setBytes(bytes32 key, bytes memory value) external onlyController returns (bytes memory) { - bytesValues[key] = value; - return value; - } - - // @dev delete the bytes value for the given key - // @param key the key of the value - function removeBytes(bytes32 key) external onlyController { - delete bytesValues[key]; - } - // @dev get the uint array for the given key // @param key the key of the uint array // @return the uint array for the key From 11e0d03cc7e6575e57c6a4704fd1a700e2dc4589 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 22 Jan 2025 22:35:00 +0200 Subject: [PATCH 130/205] Replace bytes data field with bytes32[] dataList --- contracts/glv/glvShift/GlvShift.sol | 10 +++++----- contracts/glv/glvShift/GlvShiftStoreUtils.sol | 16 ++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/contracts/glv/glvShift/GlvShift.sol b/contracts/glv/glvShift/GlvShift.sol index 6384f7218..3a405f934 100644 --- a/contracts/glv/glvShift/GlvShift.sol +++ b/contracts/glv/glvShift/GlvShift.sol @@ -6,7 +6,7 @@ library GlvShift { struct Props { Addresses addresses; Numbers numbers; - bytes dataField; + bytes32[] _dataList; } struct Addresses { @@ -69,11 +69,11 @@ library GlvShift { props.numbers.updatedAtTime = value; } - function data(Props memory props) internal pure returns (bytes memory) { - return props.dataField; + function dataList(Props memory props) internal pure returns (bytes32[] memory) { + return props._dataList; } - function setData(Props memory props, bytes memory value) internal pure { - props.dataField = value; + function setDataList(Props memory props, bytes32[] memory value) internal pure { + props._dataList = value; } } diff --git a/contracts/glv/glvShift/GlvShiftStoreUtils.sol b/contracts/glv/glvShift/GlvShiftStoreUtils.sol index 187138287..1163b8812 100644 --- a/contracts/glv/glvShift/GlvShiftStoreUtils.sol +++ b/contracts/glv/glvShift/GlvShiftStoreUtils.sol @@ -18,7 +18,7 @@ library GlvShiftStoreUtils { bytes32 public constant MIN_MARKET_TOKENS = keccak256(abi.encode("MIN_MARKET_TOKENS")); bytes32 public constant UPDATED_AT_TIME = keccak256(abi.encode("UPDATED_AT_TIME")); - bytes32 public constant DATA = keccak256(abi.encode("DATA")); + bytes32 public constant DATA_LIST = keccak256(abi.encode("DATA_LIST")); function get(DataStore dataStore, bytes32 key) external view returns (GlvShift.Props memory) { GlvShift.Props memory glvShift; @@ -50,8 +50,8 @@ library GlvShiftStoreUtils { keccak256(abi.encode(key, UPDATED_AT_TIME)) )); - glvShift.setData(dataStore.getBytes( - keccak256(abi.encode(key, DATA)) + glvShift.setDataList(dataStore.getBytes32Array( + keccak256(abi.encode(key, DATA_LIST)) )); return glvShift; @@ -93,9 +93,9 @@ library GlvShiftStoreUtils { glvShift.updatedAtTime() ); - dataStore.setBytes( - keccak256(abi.encode(key, DATA)), - glvShift.data() + dataStore.setBytes32Array( + keccak256(abi.encode(key, DATA_LIST)), + glvShift.dataList() ); } @@ -133,8 +133,8 @@ library GlvShiftStoreUtils { keccak256(abi.encode(key, UPDATED_AT_TIME)) ); - dataStore.removeBytes( - keccak256(abi.encode(key, DATA)) + dataStore.removeBytes32Array( + keccak256(abi.encode(key, DATA_LIST)) ); } From 4c60ef7aa7ae65cc48562b469478f09ab929f288 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 22 Jan 2025 22:46:27 +0200 Subject: [PATCH 131/205] Fix tests: default dataList to empty array --- utils/glv/glvShift.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/glv/glvShift.ts b/utils/glv/glvShift.ts index f996a0cd8..9d2640eca 100644 --- a/utils/glv/glvShift.ts +++ b/utils/glv/glvShift.ts @@ -26,6 +26,7 @@ export async function createGlvShift(fixture, overrides: any = {}) { toMarket: toMarket.marketToken, marketTokenAmount, minMarketTokens, + dataList: [], }; for (const [key, value] of Object.entries(params)) { From 6a45be017db517e4fb9f6d4a0656d1e23f4fc50a Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 22 Jan 2025 23:03:41 +0200 Subject: [PATCH 132/205] Fix tests: increase expectedPropsLength by one for the structs where the dataList field was added --- test/glv/GlvShiftStoreUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/glv/GlvShiftStoreUtils.ts b/test/glv/GlvShiftStoreUtils.ts index 8b04eaeb0..dc1873c55 100644 --- a/test/glv/GlvShiftStoreUtils.ts +++ b/test/glv/GlvShiftStoreUtils.ts @@ -37,7 +37,7 @@ describe("GlvShiftStoreUtils", () => { }, getItemCount: getGlvShiftCount, getItemKeys: getGlvShiftKeys, - expectedPropsLength: 2, + expectedPropsLength: 3, }); }); }); From 7e7f4833af85350b1f79fd33c1899b0fdc990ec7 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 23 Jan 2025 14:55:17 +0200 Subject: [PATCH 133/205] Add data length validation --- contracts/data/Keys.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contracts/data/Keys.sol b/contracts/data/Keys.sol index c1028999a..b9f9b8739 100644 --- a/contracts/data/Keys.sol +++ b/contracts/data/Keys.sol @@ -485,6 +485,9 @@ library Keys { // @dev key for the maximum length for data list array of bytes32 bytes32 public constant MAX_DATA_LENGTH = keccak256(abi.encode("MAX_DATA_LENGTH")); + // @dev key for the maximum length for data list array of bytes32 + bytes32 public constant MAX_DATA_LENGTH = keccak256(abi.encode("MAX_DATA_LENGTH")); + // @dev constant for user initiated cancel reason string public constant USER_INITIATED_CANCEL = "USER_INITIATED_CANCEL"; From 84a58cbaa2580a3703c8ea361c0708bd7184e016 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 23 Jan 2025 15:41:35 +0200 Subject: [PATCH 134/205] Add dataList to Orders --- contracts/exchange/AdlHandler.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/exchange/AdlHandler.sol b/contracts/exchange/AdlHandler.sol index 7ce889aae..54dcfb2e5 100644 --- a/contracts/exchange/AdlHandler.sol +++ b/contracts/exchange/AdlHandler.sol @@ -124,7 +124,8 @@ contract AdlHandler is BaseOrderHandler { collateralToken, isLong, sizeDeltaUsd, - oracle.minTimestamp() // updatedAtTime + oracle.minTimestamp(), // updatedAtTime + new bytes32[](0) // TODO: Should this be added as executeAdl param ? ) ); From e178969ac3104bff390f90540246ce647f0327ee Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 23 Jan 2025 21:21:41 +0200 Subject: [PATCH 135/205] Revert "Refactor ExchangeRouter to reduce it's size" This reverts commit 2bc7efab8d0f6046c0f55050a89e7b952200bb41. --- contracts/fee/FeeUtils.sol | 34 +------------------- contracts/referral/ReferralUtils.sol | 41 +------------------------ contracts/router/ExchangeRouter.sol | 46 +++++++++++++++++++++++++--- 3 files changed, 44 insertions(+), 77 deletions(-) diff --git a/contracts/fee/FeeUtils.sol b/contracts/fee/FeeUtils.sol index db000e6ca..a815050ad 100644 --- a/contracts/fee/FeeUtils.sol +++ b/contracts/fee/FeeUtils.sol @@ -13,8 +13,6 @@ import "../market/MarketUtils.sol"; import "../market/MarketToken.sol"; -import "../feature/FeatureUtils.sol"; - // @title FeeUtils // @dev Library for fee actions library FeeUtils { @@ -167,36 +165,6 @@ library FeeUtils { return feeAmount; } - function batchClaimUiFees( - DataStore dataStore, - EventEmitter eventEmitter, - address[] memory markets, - address[] memory tokens, - address receiver, - address uiFeeReceiver - ) external returns (uint256[] memory) { - if (markets.length != tokens.length) { - revert Errors.InvalidClaimUiFeesInput(markets.length, tokens.length); - } - - FeatureUtils.validateFeature(dataStore, Keys.claimUiFeesFeatureDisabledKey(address(this))); - - uint256[] memory claimedAmounts = new uint256[](markets.length); - - for (uint256 i; i < markets.length; i++) { - claimedAmounts[i] = claimUiFees( - dataStore, - eventEmitter, - uiFeeReceiver, - markets[i], - tokens[i], - receiver - ); - } - - return claimedAmounts; - } - function claimUiFees( DataStore dataStore, EventEmitter eventEmitter, @@ -204,7 +172,7 @@ library FeeUtils { address market, address token, address receiver - ) internal returns (uint256) { + ) external returns (uint256) { AccountUtils.validateReceiver(receiver); bytes32 key = Keys.claimableUiFeeAmountKey(market, token, uiFeeReceiver); diff --git a/contracts/referral/ReferralUtils.sol b/contracts/referral/ReferralUtils.sol index 6fd119104..2f5c435b8 100644 --- a/contracts/referral/ReferralUtils.sol +++ b/contracts/referral/ReferralUtils.sol @@ -14,8 +14,6 @@ import "./ReferralEventUtils.sol"; import "../utils/Precision.sol"; -import "../feature/FeatureUtils.sol"; - // @title ReferralUtils // @dev Library for referral functions library ReferralUtils { @@ -108,43 +106,6 @@ library ReferralUtils { ); } - // @dev Claims affiliate rewards for the given markets and tokens and sends the rewards to the specified receiver. - // @param dataStore The data store instance to use. - // @param eventEmitter The event emitter instance to use. - // @param markets An array of market addresses - // @param tokens An array of token addresses, corresponding to the given markets - // @param receiver The address to which the claimed rewards should be sent - // @param account The affiliate's address. - function batchClaimAffiliateRewards( - DataStore dataStore, - EventEmitter eventEmitter, - address[] memory markets, - address[] memory tokens, - address receiver, - address account - ) external returns (uint256[] memory) { - if (markets.length != tokens.length) { - revert Errors.InvalidClaimAffiliateRewardsInput(markets.length, tokens.length); - } - - FeatureUtils.validateFeature(dataStore, Keys.claimAffiliateRewardsFeatureDisabledKey(address(this))); - - uint256[] memory claimedAmounts = new uint256[](markets.length); - - for (uint256 i; i < markets.length; i++) { - claimedAmounts[i] = claimAffiliateReward( - dataStore, - eventEmitter, - markets[i], - tokens[i], - account, - receiver - ); - } - - return claimedAmounts; - } - // @dev Claims the affiliate's reward balance and transfers it to the specified receiver. // @param dataStore The data store instance to use. // @param eventEmitter The event emitter instance to use. @@ -159,7 +120,7 @@ library ReferralUtils { address token, address account, address receiver - ) internal returns (uint256) { + ) external returns (uint256) { bytes32 key = Keys.affiliateRewardKey(market, token, account); uint256 rewardAmount = dataStore.getUint(key); diff --git a/contracts/router/ExchangeRouter.sol b/contracts/router/ExchangeRouter.sol index f2257d1e7..474a00160 100644 --- a/contracts/router/ExchangeRouter.sol +++ b/contracts/router/ExchangeRouter.sol @@ -398,10 +398,28 @@ contract ExchangeRouter is IExchangeRouter, BaseRouter { address[] memory tokens, address receiver ) external payable nonReentrant returns (uint256[] memory) { + if (markets.length != tokens.length) { + revert Errors.InvalidClaimAffiliateRewardsInput(markets.length, tokens.length); + } + + FeatureUtils.validateFeature(dataStore, Keys.claimAffiliateRewardsFeatureDisabledKey(address(this))); + address account = msg.sender; - return ReferralUtils.batchClaimAffiliateRewards( - dataStore, eventEmitter, markets, tokens, receiver, account - ); + + uint256[] memory claimedAmounts = new uint256[](markets.length); + + for (uint256 i; i < markets.length; i++) { + claimedAmounts[i] = ReferralUtils.claimAffiliateReward( + dataStore, + eventEmitter, + markets[i], + tokens[i], + account, + receiver + ); + } + + return claimedAmounts; } function setUiFeeFactor(uint256 uiFeeFactor) external payable nonReentrant { @@ -414,7 +432,27 @@ contract ExchangeRouter is IExchangeRouter, BaseRouter { address[] memory tokens, address receiver ) external payable nonReentrant returns (uint256[] memory) { + if (markets.length != tokens.length) { + revert Errors.InvalidClaimUiFeesInput(markets.length, tokens.length); + } + + FeatureUtils.validateFeature(dataStore, Keys.claimUiFeesFeatureDisabledKey(address(this))); + address uiFeeReceiver = msg.sender; - return FeeUtils.batchClaimUiFees(dataStore, eventEmitter, markets, tokens, receiver, uiFeeReceiver); + + uint256[] memory claimedAmounts = new uint256[](markets.length); + + for (uint256 i; i < markets.length; i++) { + claimedAmounts[i] = FeeUtils.claimUiFees( + dataStore, + eventEmitter, + uiFeeReceiver, + markets[i], + tokens[i], + receiver + ); + } + + return claimedAmounts; } } From 6d5398685a5356efa85dd6e02bc08375730d9c45 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 24 Jan 2025 10:26:12 +0200 Subject: [PATCH 136/205] Comments update --- contracts/exchange/AdlHandler.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/exchange/AdlHandler.sol b/contracts/exchange/AdlHandler.sol index 54dcfb2e5..fcd16ed15 100644 --- a/contracts/exchange/AdlHandler.sol +++ b/contracts/exchange/AdlHandler.sol @@ -125,7 +125,7 @@ contract AdlHandler is BaseOrderHandler { isLong, sizeDeltaUsd, oracle.minTimestamp(), // updatedAtTime - new bytes32[](0) // TODO: Should this be added as executeAdl param ? + new bytes32[](0) ) ); From 099931c141d288882c9d83336048d416fff64611 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 24 Jan 2025 10:41:45 +0200 Subject: [PATCH 137/205] Revert dataList for GlvShift GlvShift doesn't need dataList since createGlvShift is only used internally by keepers, so integrations can't use it --- contracts/glv/glvShift/GlvShift.sol | 9 --------- contracts/glv/glvShift/GlvShiftStoreUtils.sol | 15 --------------- test/glv/GlvShiftStoreUtils.ts | 2 +- utils/glv/glvShift.ts | 1 - 4 files changed, 1 insertion(+), 26 deletions(-) diff --git a/contracts/glv/glvShift/GlvShift.sol b/contracts/glv/glvShift/GlvShift.sol index 3a405f934..63d8de826 100644 --- a/contracts/glv/glvShift/GlvShift.sol +++ b/contracts/glv/glvShift/GlvShift.sol @@ -6,7 +6,6 @@ library GlvShift { struct Props { Addresses addresses; Numbers numbers; - bytes32[] _dataList; } struct Addresses { @@ -68,12 +67,4 @@ library GlvShift { function setUpdatedAtTime(Props memory props, uint256 value) internal pure { props.numbers.updatedAtTime = value; } - - function dataList(Props memory props) internal pure returns (bytes32[] memory) { - return props._dataList; - } - - function setDataList(Props memory props, bytes32[] memory value) internal pure { - props._dataList = value; - } } diff --git a/contracts/glv/glvShift/GlvShiftStoreUtils.sol b/contracts/glv/glvShift/GlvShiftStoreUtils.sol index 1163b8812..06aea8fbd 100644 --- a/contracts/glv/glvShift/GlvShiftStoreUtils.sol +++ b/contracts/glv/glvShift/GlvShiftStoreUtils.sol @@ -18,8 +18,6 @@ library GlvShiftStoreUtils { bytes32 public constant MIN_MARKET_TOKENS = keccak256(abi.encode("MIN_MARKET_TOKENS")); bytes32 public constant UPDATED_AT_TIME = keccak256(abi.encode("UPDATED_AT_TIME")); - bytes32 public constant DATA_LIST = keccak256(abi.encode("DATA_LIST")); - function get(DataStore dataStore, bytes32 key) external view returns (GlvShift.Props memory) { GlvShift.Props memory glvShift; if (!dataStore.containsBytes32(Keys.GLV_SHIFT_LIST, key)) { @@ -50,10 +48,6 @@ library GlvShiftStoreUtils { keccak256(abi.encode(key, UPDATED_AT_TIME)) )); - glvShift.setDataList(dataStore.getBytes32Array( - keccak256(abi.encode(key, DATA_LIST)) - )); - return glvShift; } @@ -92,11 +86,6 @@ library GlvShiftStoreUtils { keccak256(abi.encode(key, UPDATED_AT_TIME)), glvShift.updatedAtTime() ); - - dataStore.setBytes32Array( - keccak256(abi.encode(key, DATA_LIST)), - glvShift.dataList() - ); } function remove(DataStore dataStore, bytes32 key) external { @@ -132,10 +121,6 @@ library GlvShiftStoreUtils { dataStore.removeUint( keccak256(abi.encode(key, UPDATED_AT_TIME)) ); - - dataStore.removeBytes32Array( - keccak256(abi.encode(key, DATA_LIST)) - ); } function getGlvShiftCount(DataStore dataStore) internal view returns (uint256) { diff --git a/test/glv/GlvShiftStoreUtils.ts b/test/glv/GlvShiftStoreUtils.ts index dc1873c55..8b04eaeb0 100644 --- a/test/glv/GlvShiftStoreUtils.ts +++ b/test/glv/GlvShiftStoreUtils.ts @@ -37,7 +37,7 @@ describe("GlvShiftStoreUtils", () => { }, getItemCount: getGlvShiftCount, getItemKeys: getGlvShiftKeys, - expectedPropsLength: 3, + expectedPropsLength: 2, }); }); }); diff --git a/utils/glv/glvShift.ts b/utils/glv/glvShift.ts index 9d2640eca..f996a0cd8 100644 --- a/utils/glv/glvShift.ts +++ b/utils/glv/glvShift.ts @@ -26,7 +26,6 @@ export async function createGlvShift(fixture, overrides: any = {}) { toMarket: toMarket.marketToken, marketTokenAmount, minMarketTokens, - dataList: [], }; for (const [key, value] of Object.entries(params)) { From d3237b5f47b5319005b8f94815fed83ba82da8e9 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 27 Jan 2025 09:31:51 +0200 Subject: [PATCH 138/205] Remove dataList from ADL orders --- contracts/exchange/AdlHandler.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contracts/exchange/AdlHandler.sol b/contracts/exchange/AdlHandler.sol index fcd16ed15..7ce889aae 100644 --- a/contracts/exchange/AdlHandler.sol +++ b/contracts/exchange/AdlHandler.sol @@ -124,8 +124,7 @@ contract AdlHandler is BaseOrderHandler { collateralToken, isLong, sizeDeltaUsd, - oracle.minTimestamp(), // updatedAtTime - new bytes32[](0) + oracle.minTimestamp() // updatedAtTime ) ); From ff05d57abbea400838e86233c68706de0c0ad511 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 22 Jan 2025 10:31:46 +0200 Subject: [PATCH 139/205] WIP Allow setting a CLAIMABLE_COLLATERAL_REDUCTION_FACTOR for (account + time + market) key for price impact rebates --- contracts/config/Config.sol | 2 ++ contracts/config/ConfigSyncer.sol | 34 +++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/contracts/config/Config.sol b/contracts/config/Config.sol index 1f5952764..a2a83255d 100644 --- a/contracts/config/Config.sol +++ b/contracts/config/Config.sol @@ -548,6 +548,8 @@ contract Config is ReentrancyGuard, RoleModule, BasicMulticall { allowedLimitedBaseKeys[Keys.GLV_MAX_MARKET_TOKEN_BALANCE_AMOUNT] = true; allowedLimitedBaseKeys[Keys.PRO_TRADER_TIER] = true; + + // allowedBaseKeys[Keys.CLAIMABLE_COLLATERAL_REDUCTION_FACTOR] = true; // TODO: confirm allowing this key here instead of ConfigSyncer.sol + config.ts } // @dev validate that the baseKey is allowed to be used diff --git a/contracts/config/ConfigSyncer.sol b/contracts/config/ConfigSyncer.sol index b5a98dc14..7df1e4d31 100644 --- a/contracts/config/ConfigSyncer.sol +++ b/contracts/config/ConfigSyncer.sol @@ -11,6 +11,7 @@ import "../feature/FeatureUtils.sol"; // @title ConfigSyncer // @dev Contract to handle market parameter updates contract ConfigSyncer is ReentrancyGuard, RoleModule { + using EventUtils for EventUtils.AddressItems; using EventUtils for EventUtils.UintItems; using EventUtils for EventUtils.BoolItems; @@ -119,6 +120,37 @@ contract ConfigSyncer is ReentrancyGuard, RoleModule { } } + function setClaimableCollateralReductionFactorForAccount( + address market, + address token, + uint256 timeKey, + address account, + uint256 factor + ) external onlyLimitedConfigKeeper nonReentrant { // TODO: confirm modifier + if (factor > Precision.FLOAT_PRECISION) { revert Errors.InvalidClaimableReductionFactor(factor); } + + bytes32 key = Keys.claimableCollateralReductionFactorKey(market, token, timeKey, account); + dataStore.setUint(key, factor); + + EventUtils.EventLogData memory eventData; + + eventData.addressItems.initItems(3); + eventData.addressItems.setItem(0, "market", market); + eventData.addressItems.setItem(1, "token", token); + eventData.addressItems.setItem(2, "account", account); + + eventData.uintItems.initItems(2); + eventData.uintItems.setItem(0, "timeKey", timeKey); + eventData.uintItems.setItem(1, "factor", factor); + + eventEmitter.emitEventLog2( + "SetClaimableCollateralReductionFactorForAccount", + Cast.toBytes32(market), + Cast.toBytes32(token), + eventData + ); + } + // @dev initialize the allowed base keys function _initAllowedBaseKeys() internal { allowedBaseKeys[Keys.MAX_POOL_AMOUNT] = true; @@ -144,6 +176,8 @@ contract ConfigSyncer is ReentrancyGuard, RoleModule { allowedBaseKeys[Keys.RESERVE_FACTOR] = true; allowedBaseKeys[Keys.OPEN_INTEREST_RESERVE_FACTOR] = true; + + // allowedBaseKeys[Keys.CLAIMABLE_COLLATERAL_REDUCTION_FACTOR] = true; } // @dev validate that the baseKey is allowed to be used From 34b06315298d335eb97c4cab5cfaaa041d05061d Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 22 Jan 2025 16:01:22 +0200 Subject: [PATCH 140/205] Move claimable reduction method from ConfigSyncer to Config --- contracts/config/Config.sol | 33 ++++++++++++++++++++++++++++-- contracts/config/ConfigSyncer.sol | 34 ------------------------------- 2 files changed, 31 insertions(+), 36 deletions(-) diff --git a/contracts/config/Config.sol b/contracts/config/Config.sol index a2a83255d..be2e9cca8 100644 --- a/contracts/config/Config.sol +++ b/contracts/config/Config.sol @@ -175,6 +175,37 @@ contract Config is ReentrancyGuard, RoleModule, BasicMulticall { ); } + function setClaimableCollateralReductionFactorForAccount( + address market, + address token, + uint256 timeKey, + address account, + uint256 factor + ) external onlyConfigKeeper nonReentrant { + if (factor > Precision.FLOAT_PRECISION) { revert Errors.InvalidClaimableReductionFactor(factor); } + + bytes32 key = Keys.claimableCollateralReductionFactorKey(market, token, timeKey, account); + dataStore.setUint(key, factor); + + EventUtils.EventLogData memory eventData; + + eventData.addressItems.initItems(3); + eventData.addressItems.setItem(0, "market", market); + eventData.addressItems.setItem(1, "token", token); + eventData.addressItems.setItem(2, "account", account); + + eventData.uintItems.initItems(2); + eventData.uintItems.setItem(0, "timeKey", timeKey); + eventData.uintItems.setItem(1, "factor", factor); + + eventEmitter.emitEventLog2( + "SetClaimableCollateralReductionFactorForAccount", + Cast.toBytes32(market), + Cast.toBytes32(token), + eventData + ); + } + function setPositionImpactDistributionRate( address market, uint256 minPositionImpactPoolAmount, @@ -548,8 +579,6 @@ contract Config is ReentrancyGuard, RoleModule, BasicMulticall { allowedLimitedBaseKeys[Keys.GLV_MAX_MARKET_TOKEN_BALANCE_AMOUNT] = true; allowedLimitedBaseKeys[Keys.PRO_TRADER_TIER] = true; - - // allowedBaseKeys[Keys.CLAIMABLE_COLLATERAL_REDUCTION_FACTOR] = true; // TODO: confirm allowing this key here instead of ConfigSyncer.sol + config.ts } // @dev validate that the baseKey is allowed to be used diff --git a/contracts/config/ConfigSyncer.sol b/contracts/config/ConfigSyncer.sol index 7df1e4d31..b5a98dc14 100644 --- a/contracts/config/ConfigSyncer.sol +++ b/contracts/config/ConfigSyncer.sol @@ -11,7 +11,6 @@ import "../feature/FeatureUtils.sol"; // @title ConfigSyncer // @dev Contract to handle market parameter updates contract ConfigSyncer is ReentrancyGuard, RoleModule { - using EventUtils for EventUtils.AddressItems; using EventUtils for EventUtils.UintItems; using EventUtils for EventUtils.BoolItems; @@ -120,37 +119,6 @@ contract ConfigSyncer is ReentrancyGuard, RoleModule { } } - function setClaimableCollateralReductionFactorForAccount( - address market, - address token, - uint256 timeKey, - address account, - uint256 factor - ) external onlyLimitedConfigKeeper nonReentrant { // TODO: confirm modifier - if (factor > Precision.FLOAT_PRECISION) { revert Errors.InvalidClaimableReductionFactor(factor); } - - bytes32 key = Keys.claimableCollateralReductionFactorKey(market, token, timeKey, account); - dataStore.setUint(key, factor); - - EventUtils.EventLogData memory eventData; - - eventData.addressItems.initItems(3); - eventData.addressItems.setItem(0, "market", market); - eventData.addressItems.setItem(1, "token", token); - eventData.addressItems.setItem(2, "account", account); - - eventData.uintItems.initItems(2); - eventData.uintItems.setItem(0, "timeKey", timeKey); - eventData.uintItems.setItem(1, "factor", factor); - - eventEmitter.emitEventLog2( - "SetClaimableCollateralReductionFactorForAccount", - Cast.toBytes32(market), - Cast.toBytes32(token), - eventData - ); - } - // @dev initialize the allowed base keys function _initAllowedBaseKeys() internal { allowedBaseKeys[Keys.MAX_POOL_AMOUNT] = true; @@ -176,8 +144,6 @@ contract ConfigSyncer is ReentrancyGuard, RoleModule { allowedBaseKeys[Keys.RESERVE_FACTOR] = true; allowedBaseKeys[Keys.OPEN_INTEREST_RESERVE_FACTOR] = true; - - // allowedBaseKeys[Keys.CLAIMABLE_COLLATERAL_REDUCTION_FACTOR] = true; } // @dev validate that the baseKey is allowed to be used From fd6411ce19a879ff83884b44373af1bf45d23a1a Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 23 Jan 2025 21:56:21 +0200 Subject: [PATCH 141/205] Move setClaimableCollateralReductionFactorForAccount logic from Config to ConfigUtils --- contracts/config/Config.sol | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/contracts/config/Config.sol b/contracts/config/Config.sol index be2e9cca8..3e15176d2 100644 --- a/contracts/config/Config.sol +++ b/contracts/config/Config.sol @@ -182,27 +182,14 @@ contract Config is ReentrancyGuard, RoleModule, BasicMulticall { address account, uint256 factor ) external onlyConfigKeeper nonReentrant { - if (factor > Precision.FLOAT_PRECISION) { revert Errors.InvalidClaimableReductionFactor(factor); } - - bytes32 key = Keys.claimableCollateralReductionFactorKey(market, token, timeKey, account); - dataStore.setUint(key, factor); - - EventUtils.EventLogData memory eventData; - - eventData.addressItems.initItems(3); - eventData.addressItems.setItem(0, "market", market); - eventData.addressItems.setItem(1, "token", token); - eventData.addressItems.setItem(2, "account", account); - - eventData.uintItems.initItems(2); - eventData.uintItems.setItem(0, "timeKey", timeKey); - eventData.uintItems.setItem(1, "factor", factor); - - eventEmitter.emitEventLog2( - "SetClaimableCollateralReductionFactorForAccount", - Cast.toBytes32(market), - Cast.toBytes32(token), - eventData + ConfigUtils.setClaimableCollateralReductionFactorForAccount( + dataStore, + eventEmitter, + market, + token, + timeKey, + account, + factor ); } From 9b4ba5521e9534dfa4bbbe5d5b462317864bf016 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 28 Jan 2025 12:11:06 +0200 Subject: [PATCH 142/205] Refactor GlvDepositUtils to reduce bytecode size Move _validateFirstGlvDeposit and _getMintAmount internal methods from GlvDepositUtils to the newly created GlvDepositHelper library. --- contracts/glv/glvDeposit/GlvDepositHelper.sol | 68 +++++++++++++++++++ deploy/deployGlvDepositHelper.ts | 8 +++ 2 files changed, 76 insertions(+) create mode 100644 contracts/glv/glvDeposit/GlvDepositHelper.sol create mode 100644 deploy/deployGlvDepositHelper.ts diff --git a/contracts/glv/glvDeposit/GlvDepositHelper.sol b/contracts/glv/glvDeposit/GlvDepositHelper.sol new file mode 100644 index 000000000..8921eb10e --- /dev/null +++ b/contracts/glv/glvDeposit/GlvDepositHelper.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; + +import "./GlvDeposit.sol"; +import "../GlvUtils.sol"; + +library GlvDepositHelper { + using GlvDeposit for GlvDeposit.Props; + using SafeCast for int256; + + address public constant RECEIVER_FOR_FIRST_GLV_DEPOSIT = address(1); + + function validateFirstGlvDeposit( + DataStore dataStore, + GlvDeposit.Props memory glvDeposit + ) external view { + address glv = glvDeposit.glv(); + uint256 initialGlvTokenSupply = GlvToken(payable(glv)).totalSupply(); + + // return if this is not the first glv deposit + if (initialGlvTokenSupply != 0) { + return; + } + + uint256 minGlvTokens = dataStore.getUint(Keys.minGlvTokensForFirstGlvDepositKey(glv)); + + // return if there is no minGlvTokens requirement + if (minGlvTokens == 0) { + return; + } + + if (glvDeposit.receiver() != RECEIVER_FOR_FIRST_GLV_DEPOSIT) { + revert Errors.InvalidReceiverForFirstGlvDeposit(glvDeposit.receiver(), RECEIVER_FOR_FIRST_GLV_DEPOSIT); + } + + if (glvDeposit.minGlvTokens() < minGlvTokens) { + revert Errors.InvalidMinGlvTokensForFirstGlvDeposit(glvDeposit.minGlvTokens(), minGlvTokens); + } + } + + function getMintAmount( + DataStore dataStore, + Oracle oracle, + GlvDeposit.Props memory glvDeposit, + uint256 receivedMarketTokens, + uint256 glvValue, + uint256 glvSupply + ) external view returns (uint256) { + Market.Props memory market = MarketUtils.getEnabledMarket(dataStore, glvDeposit.market()); + MarketPoolValueInfo.Props memory poolValueInfo = MarketUtils.getPoolValueInfo( + dataStore, + market, + oracle.getPrimaryPrice(market.indexToken), + oracle.getPrimaryPrice(market.longToken), + oracle.getPrimaryPrice(market.shortToken), + Keys.MAX_PNL_FACTOR_FOR_DEPOSITS, + false // maximize + ); + uint256 marketTokenSupply = MarketUtils.getMarketTokenSupply(MarketToken(payable(market.marketToken))); + uint256 receivedMarketTokensUsd = MarketUtils.marketTokenAmountToUsd( + receivedMarketTokens, + poolValueInfo.poolValue.toUint256(), + marketTokenSupply + ); + return GlvUtils.usdToGlvTokenAmount(receivedMarketTokensUsd, glvValue, glvSupply); + } +} diff --git a/deploy/deployGlvDepositHelper.ts b/deploy/deployGlvDepositHelper.ts new file mode 100644 index 000000000..0a4f4ad3d --- /dev/null +++ b/deploy/deployGlvDepositHelper.ts @@ -0,0 +1,8 @@ +import { createDeployFunction } from "../utils/deploy"; + +const func = createDeployFunction({ + contractName: "GlvDepositHelper", + libraryNames: ["MarketUtils", "MarketStoreUtils"], +}); + +export default func; From ed2082a5244b0b51b52e842e34f187b8774e4dd7 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 31 Jan 2025 07:46:42 +0200 Subject: [PATCH 143/205] Rename glv deposit utility lib --- contracts/glv/glvDeposit/GlvDepositHelper.sol | 68 ------------------- deploy/deployGlvDepositHelper.ts | 2 +- 2 files changed, 1 insertion(+), 69 deletions(-) delete mode 100644 contracts/glv/glvDeposit/GlvDepositHelper.sol diff --git a/contracts/glv/glvDeposit/GlvDepositHelper.sol b/contracts/glv/glvDeposit/GlvDepositHelper.sol deleted file mode 100644 index 8921eb10e..000000000 --- a/contracts/glv/glvDeposit/GlvDepositHelper.sol +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.0; - -import "./GlvDeposit.sol"; -import "../GlvUtils.sol"; - -library GlvDepositHelper { - using GlvDeposit for GlvDeposit.Props; - using SafeCast for int256; - - address public constant RECEIVER_FOR_FIRST_GLV_DEPOSIT = address(1); - - function validateFirstGlvDeposit( - DataStore dataStore, - GlvDeposit.Props memory glvDeposit - ) external view { - address glv = glvDeposit.glv(); - uint256 initialGlvTokenSupply = GlvToken(payable(glv)).totalSupply(); - - // return if this is not the first glv deposit - if (initialGlvTokenSupply != 0) { - return; - } - - uint256 minGlvTokens = dataStore.getUint(Keys.minGlvTokensForFirstGlvDepositKey(glv)); - - // return if there is no minGlvTokens requirement - if (minGlvTokens == 0) { - return; - } - - if (glvDeposit.receiver() != RECEIVER_FOR_FIRST_GLV_DEPOSIT) { - revert Errors.InvalidReceiverForFirstGlvDeposit(glvDeposit.receiver(), RECEIVER_FOR_FIRST_GLV_DEPOSIT); - } - - if (glvDeposit.minGlvTokens() < minGlvTokens) { - revert Errors.InvalidMinGlvTokensForFirstGlvDeposit(glvDeposit.minGlvTokens(), minGlvTokens); - } - } - - function getMintAmount( - DataStore dataStore, - Oracle oracle, - GlvDeposit.Props memory glvDeposit, - uint256 receivedMarketTokens, - uint256 glvValue, - uint256 glvSupply - ) external view returns (uint256) { - Market.Props memory market = MarketUtils.getEnabledMarket(dataStore, glvDeposit.market()); - MarketPoolValueInfo.Props memory poolValueInfo = MarketUtils.getPoolValueInfo( - dataStore, - market, - oracle.getPrimaryPrice(market.indexToken), - oracle.getPrimaryPrice(market.longToken), - oracle.getPrimaryPrice(market.shortToken), - Keys.MAX_PNL_FACTOR_FOR_DEPOSITS, - false // maximize - ); - uint256 marketTokenSupply = MarketUtils.getMarketTokenSupply(MarketToken(payable(market.marketToken))); - uint256 receivedMarketTokensUsd = MarketUtils.marketTokenAmountToUsd( - receivedMarketTokens, - poolValueInfo.poolValue.toUint256(), - marketTokenSupply - ); - return GlvUtils.usdToGlvTokenAmount(receivedMarketTokensUsd, glvValue, glvSupply); - } -} diff --git a/deploy/deployGlvDepositHelper.ts b/deploy/deployGlvDepositHelper.ts index 0a4f4ad3d..e161d3148 100644 --- a/deploy/deployGlvDepositHelper.ts +++ b/deploy/deployGlvDepositHelper.ts @@ -1,7 +1,7 @@ import { createDeployFunction } from "../utils/deploy"; const func = createDeployFunction({ - contractName: "GlvDepositHelper", + contractName: "GlvDepositCalc", libraryNames: ["MarketUtils", "MarketStoreUtils"], }); From 782aaaee4ff0dc36972ff3b76fc6c84554d13baf Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 31 Jan 2025 10:24:08 +0200 Subject: [PATCH 144/205] Rename deploy script --- deploy/deployGlvDepositHelper.ts | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 deploy/deployGlvDepositHelper.ts diff --git a/deploy/deployGlvDepositHelper.ts b/deploy/deployGlvDepositHelper.ts deleted file mode 100644 index e161d3148..000000000 --- a/deploy/deployGlvDepositHelper.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { createDeployFunction } from "../utils/deploy"; - -const func = createDeployFunction({ - contractName: "GlvDepositCalc", - libraryNames: ["MarketUtils", "MarketStoreUtils"], -}); - -export default func; From fbd41c6d71fd2a5bdfcb17d4ee979f08254a9805 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 1 Feb 2025 09:08:20 +0200 Subject: [PATCH 145/205] Rename MultichainHandler in MultichainVaultHandler --- .../{MultichainHandler.sol => MultichainVaultHandler.sol} | 4 ++-- deploy/deployMultichainHandler.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename contracts/multichain/{MultichainHandler.sol => MultichainVaultHandler.sol} (97%) diff --git a/contracts/multichain/MultichainHandler.sol b/contracts/multichain/MultichainVaultHandler.sol similarity index 97% rename from contracts/multichain/MultichainHandler.sol rename to contracts/multichain/MultichainVaultHandler.sol index 876f47a56..e13812e39 100644 --- a/contracts/multichain/MultichainHandler.sol +++ b/contracts/multichain/MultichainVaultHandler.sol @@ -21,9 +21,9 @@ import { MultichainUtils } from "./MultichainUtils.sol"; import { MultichainEventUtils } from "./MultichainEventUtils.sol"; /** - * @title MultichainHandler + * @title MultichainVaultHandler */ -contract MultichainHandler is RoleModule, GlobalReentrancyGuard, OracleModule { +contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModule { using SafeERC20 for IERC20; MultichainVault public multichainVault; diff --git a/deploy/deployMultichainHandler.ts b/deploy/deployMultichainHandler.ts index cf8559ed3..3f28c9645 100644 --- a/deploy/deployMultichainHandler.ts +++ b/deploy/deployMultichainHandler.ts @@ -4,7 +4,7 @@ import { createDeployFunction } from "../utils/deploy"; const constructorContracts = ["RoleStore", "DataStore", "EventEmitter", "Oracle", "MultichainVault", "ExchangeRouter"]; const func = createDeployFunction({ - contractName: "MultichainHandler", + contractName: "MultichainVaultHandler", dependencyNames: constructorContracts, getDeployArgs: async ({ dependencyContracts }) => { return constructorContracts.map((dependencyName) => dependencyContracts[dependencyName].address); From 59e948959b758faa398fbd116176384c26b92a7c Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 1 Feb 2025 13:49:50 +0200 Subject: [PATCH 146/205] Add chainId to Deposit.Props --- contracts/migration/GlpMigrator.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/migration/GlpMigrator.sol b/contracts/migration/GlpMigrator.sol index 5108609d5..062161e48 100644 --- a/contracts/migration/GlpMigrator.sol +++ b/contracts/migration/GlpMigrator.sol @@ -210,6 +210,7 @@ contract GlpMigrator is ReentrancyGuard, RoleModule { false, // shouldUnwrapNativeToken; migrationItem.executionFee, // executionFee; 0, // callbackGasLimit; + 0, // chainId new bytes32[](0) // dataList; ); From a3006f2c5f91a6ed62764ae60cf94ddef692bc80 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 1 Feb 2025 13:55:31 +0200 Subject: [PATCH 147/205] Update multichain createDeposit logic --- contracts/multichain/MultichainVaultHandler.sol | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/contracts/multichain/MultichainVaultHandler.sol b/contracts/multichain/MultichainVaultHandler.sol index e13812e39..cf4ce016d 100644 --- a/contracts/multichain/MultichainVaultHandler.sol +++ b/contracts/multichain/MultichainVaultHandler.sol @@ -66,17 +66,15 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu } /** - * @dev transfer the specified amount of tokens from account to receiver - * @param account the account for which the tokens are subtracted + * @dev transfer the specified amount of tokens from MultichainVault to receiver and decrement multichain account balance + * @param account the account for which the tokens are transferred and multichain balance decremented * @param token the token to transfer * @param receiver the account to transfer to * @param amount the amount of tokens to transfer */ - function pluginTransfer(address token, address account, address receiver, uint256 amount) external onlyRouterPlugin { // TODO: confirm access control - // TODO: tokens should come from MultichainVault and the user's multichain balance should be decreased - // dataStore.decrementUint(Keys.sourceChainBalanceKey(sourceChainId, account, token), amount); - - IERC20(token).safeTransferFrom(account, receiver, amount); + function pluginTransfer(uint256 chainId, address token, address account, address receiver, uint256 amount) external onlyRouterPlugin { // TODO: confirm access control + dataStore.decrementUint(Keys.sourceChainBalanceKey(chainId, account, token), amount); + IERC20(token).safeTransferFrom(address(multichainVault), receiver, amount); } /** From 81be37c2108f6dc4f534d3a12fb679bb921c6b46 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 2 Feb 2025 14:57:38 +0200 Subject: [PATCH 148/205] Emit dataList for createDeposit, createWithdrawal, createOrder, updateOrder, createShift, createGlvDeposit, createGlvWithdrawal --- contracts/deposit/DepositEventUtils.sol | 3 +++ contracts/glv/glvDeposit/GlvDepositEventUtils.sol | 3 +++ contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol | 3 +++ contracts/order/OrderEventUtils.sol | 3 +++ contracts/position/Position.sol | 2 +- contracts/shift/ShiftEventUtils.sol | 3 +++ contracts/withdrawal/WithdrawalEventUtils.sol | 3 +++ 7 files changed, 19 insertions(+), 1 deletion(-) diff --git a/contracts/deposit/DepositEventUtils.sol b/contracts/deposit/DepositEventUtils.sol index 19d4ee4f8..c524da50d 100644 --- a/contracts/deposit/DepositEventUtils.sol +++ b/contracts/deposit/DepositEventUtils.sol @@ -31,6 +31,9 @@ library DepositEventUtils { eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", deposit.dataList()); + eventEmitter.emitEventLog2( "DepositCreated", key, diff --git a/contracts/glv/glvDeposit/GlvDepositEventUtils.sol b/contracts/glv/glvDeposit/GlvDepositEventUtils.sol index eb97405d9..52525df3e 100644 --- a/contracts/glv/glvDeposit/GlvDepositEventUtils.sol +++ b/contracts/glv/glvDeposit/GlvDepositEventUtils.sol @@ -29,6 +29,9 @@ library GlvDepositEventUtils { eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", glvDeposit.dataList()); + eventEmitter.emitEventLog2( "GlvDepositCreated", key, diff --git a/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol b/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol index 1f15e22c6..166939452 100644 --- a/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol +++ b/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol @@ -29,6 +29,9 @@ library GlvWithdrawalEventUtils { eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", glvWithdrawal.dataList()); + eventEmitter.emitEventLog2("GlvWithdrawalCreated", key, Cast.toBytes32(glvWithdrawal.account()), eventData); } diff --git a/contracts/order/OrderEventUtils.sol b/contracts/order/OrderEventUtils.sol index 6eb649dbb..fdc70ed2d 100644 --- a/contracts/order/OrderEventUtils.sol +++ b/contracts/order/OrderEventUtils.sol @@ -28,6 +28,9 @@ library OrderEventUtils { eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", order.dataList()); + eventEmitter.emitEventLog2( "OrderCreated", key, diff --git a/contracts/position/Position.sol b/contracts/position/Position.sol index 94caef0de..c2066a705 100644 --- a/contracts/position/Position.sol +++ b/contracts/position/Position.sol @@ -200,7 +200,7 @@ library Position { // @param chainId the source chain id // @return the position key function getPositionKey(address _account, address _market, address _collateralToken, bool _isLong/*, uint256 _chainId*/) internal pure returns (bytes32) { - // TODO: confirm orders should have a chainId as well + // TODO: handle the chainId cases bellow // bytes32 _key; // if (_chainId == 0) { // _key = keccak256(abi.encode(_account, _market, _collateralToken, _isLong)); diff --git a/contracts/shift/ShiftEventUtils.sol b/contracts/shift/ShiftEventUtils.sol index 3e6ce453e..6bd62b4b6 100644 --- a/contracts/shift/ShiftEventUtils.sol +++ b/contracts/shift/ShiftEventUtils.sol @@ -29,6 +29,9 @@ library ShiftEventUtils { eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", shift.dataList()); + eventEmitter.emitEventLog2( "ShiftCreated", key, diff --git a/contracts/withdrawal/WithdrawalEventUtils.sol b/contracts/withdrawal/WithdrawalEventUtils.sol index 2d5cf3403..8cc80d2fa 100644 --- a/contracts/withdrawal/WithdrawalEventUtils.sol +++ b/contracts/withdrawal/WithdrawalEventUtils.sol @@ -31,6 +31,9 @@ library WithdrawalEventUtils { eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); + eventData.bytes32Items.initArrayItems(1); + eventData.bytes32Items.setItem(0, "dataList", withdrawal.dataList()); + eventEmitter.emitEventLog2( "WithdrawalCreated", key, From ee08b27efd9894bfab198f49a2b797dc46095880 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 3 Feb 2025 12:20:01 +0200 Subject: [PATCH 149/205] Rename sourceChain* to multichain* --- .../MultichainProviderSignature.sol | 10 ++++---- .../multichain/MultichainVaultHandler.sol | 24 +++++++++---------- contracts/position/Position.sol | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/contracts/multichain/MultichainProviderSignature.sol b/contracts/multichain/MultichainProviderSignature.sol index e2e159503..1903c9262 100644 --- a/contracts/multichain/MultichainProviderSignature.sol +++ b/contracts/multichain/MultichainProviderSignature.sol @@ -14,24 +14,24 @@ contract MultichainProviderSignature is EIP712 { string private constant SIGNATURE_VERSION = "1"; // Define the EIP-712 struct type: - // Message(address token,uint256 amount,address account,uint256 sourceChainId,uint32 srcEid) + // Message(address token,uint256 amount,address account,uint256 multichainId,uint32 srcEid) bytes32 private constant _MESSAGE_TYPEHASH = - keccak256("Message(address token,uint256 amount,address account,uint256 sourceChainId,uint32 srcEid)"); + keccak256("Message(address token,uint256 amount,address account,uint256 multichainId,uint32 srcEid)"); constructor() EIP712(SIGNING_DOMAIN, SIGNATURE_VERSION) {} /** * Check the signature for a given message - * @param message The ABI encoded parameters (token, amount, account, sourceChainId, srcEid). + * @param message The ABI encoded parameters (token, amount, account, multichainId, srcEid). * @param signature The EIP-712 signature. */ function isSigner(bytes calldata message, bytes calldata signature) external view returns (bool) { // Decode the message - (address token, uint256 amount, address account, uint256 sourceChainId, uint32 srcEid) = MultichainProviderUtils + (address token, uint256 amount, address account, uint256 multichainId, uint32 srcEid) = MultichainProviderUtils .decodeWithdrawal(message); // Build the struct hash - bytes32 structHash = keccak256(abi.encode(_MESSAGE_TYPEHASH, token, amount, account, sourceChainId, srcEid)); + bytes32 structHash = keccak256(abi.encode(_MESSAGE_TYPEHASH, token, amount, account, multichainId, srcEid)); // Get the typed data hash for EIP-712 bytes32 hash = _hashTypedDataV4(structHash); diff --git a/contracts/multichain/MultichainVaultHandler.sol b/contracts/multichain/MultichainVaultHandler.sol index cf4ce016d..290790112 100644 --- a/contracts/multichain/MultichainVaultHandler.sol +++ b/contracts/multichain/MultichainVaultHandler.sol @@ -47,12 +47,12 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu * Records a deposit from another chain. IMultichainProvider has CONTROLLER role * @param account user address on the source chain * @param token address of the token being deposited - * @param sourceChainId chain id of the source chain + * @param multichainId chain id of the destination chain */ function recordDeposit( address account, address token, - uint256 sourceChainId + uint256 multichainId ) external onlyController { // token should have been transferred to multichainVault by IMultichainProvider uint256 amount = multichainVault.recordTransferIn(token); @@ -60,9 +60,9 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu revert Errors.EmptyMultichainDepositAmount(); } - dataStore.incrementUint(Keys.sourceChainBalanceKey(sourceChainId, account, token), amount); + dataStore.incrementUint(Keys.multichainBalanceKey(multichainId, account, token), amount); - MultichainEventUtils.emitMultichainDeposit(eventEmitter, token, account, amount, sourceChainId); + MultichainEventUtils.emitMultichainDeposit(eventEmitter, token, account, amount, multichainId); } /** @@ -73,7 +73,7 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu * @param amount the amount of tokens to transfer */ function pluginTransfer(uint256 chainId, address token, address account, address receiver, uint256 amount) external onlyRouterPlugin { // TODO: confirm access control - dataStore.decrementUint(Keys.sourceChainBalanceKey(chainId, account, token), amount); + dataStore.decrementUint(Keys.multichainBalanceKey(chainId, account, token), amount); IERC20(token).safeTransferFrom(address(multichainVault), receiver, amount); } @@ -81,18 +81,18 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu * Executes the multicall for the given args * The multicall arguments contains the function calls to be executed (e.g. createDeposit, createOrder, createWithdrawal, etc) * @param account user address on the source chain - * @param sourceChainId chain id of the source chain + * @param multichainId chain id of the destination chain * @param multicallArgs array of bytes containing the multicall arguments */ function executeMulticall( address account, - uint256 sourceChainId, + uint256 multichainId, bytes[] calldata multicallArgs ) external onlyController { // execute multicall exchangeRouter.multicall(multicallArgs); - MultichainEventUtils.emitMultichainMessage(eventEmitter, account, sourceChainId); + MultichainEventUtils.emitMultichainMessage(eventEmitter, account, multichainId); } /** @@ -100,19 +100,19 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu * @param account user address on the source chain * @param token address of the token being withdrawn * @param amount amount of token being withdrawn - * @param sourceChainId chain id of the source chain + * @param multichainId chain id of the destination chain */ function recordWithdrawal( address account, address token, uint256 amount, - uint256 sourceChainId + uint256 multichainId ) external onlyController { if (amount == 0) { revert Errors.EmptyMultichainWithdrawalAmount(); } - bytes32 balanceKey = Keys.sourceChainBalanceKey(sourceChainId, account, token); + bytes32 balanceKey = Keys.multichainBalanceKey(multichainId, account, token); uint256 balance = dataStore.getUint(balanceKey); if (balance < amount) { @@ -125,6 +125,6 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu // transfer tokens to IMultichainProvider multichainVault.transferOut(token, msg.sender, amount); - MultichainEventUtils.emitMultichainWithdrawal(eventEmitter, token, account, amount, sourceChainId); + MultichainEventUtils.emitMultichainWithdrawal(eventEmitter, token, account, amount, multichainId); } } diff --git a/contracts/position/Position.sol b/contracts/position/Position.sol index c2066a705..713f86b9a 100644 --- a/contracts/position/Position.sol +++ b/contracts/position/Position.sol @@ -197,7 +197,7 @@ library Position { // @param market the position's market // @param collateralToken the position's collateralToken // @param isLong whether the position is long or short - // @param chainId the source chain id + // @param chainId the destination chain id // @return the position key function getPositionKey(address _account, address _market, address _collateralToken, bool _isLong/*, uint256 _chainId*/) internal pure returns (bytes32) { // TODO: handle the chainId cases bellow From b6ff3e4d32971ca180253895b664de46ea80b45d Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 3 Feb 2025 20:42:52 +0200 Subject: [PATCH 150/205] Add multichain increase and decrease utils methods --- contracts/multichain/MultichainVaultHandler.sol | 9 ++++----- utils/multichain.ts | 0 2 files changed, 4 insertions(+), 5 deletions(-) delete mode 100644 utils/multichain.ts diff --git a/contracts/multichain/MultichainVaultHandler.sol b/contracts/multichain/MultichainVaultHandler.sol index 290790112..893912609 100644 --- a/contracts/multichain/MultichainVaultHandler.sol +++ b/contracts/multichain/MultichainVaultHandler.sol @@ -66,15 +66,14 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu } /** - * @dev transfer the specified amount of tokens from MultichainVault to receiver and decrement multichain account balance - * @param account the account for which the tokens are transferred and multichain balance decremented + * @dev transfer the specified amount of tokens from account to receiver * @param token the token to transfer + * @param account the account to transfer from * @param receiver the account to transfer to * @param amount the amount of tokens to transfer */ - function pluginTransfer(uint256 chainId, address token, address account, address receiver, uint256 amount) external onlyRouterPlugin { // TODO: confirm access control - dataStore.decrementUint(Keys.multichainBalanceKey(chainId, account, token), amount); - IERC20(token).safeTransferFrom(address(multichainVault), receiver, amount); + function pluginTransfer(address token, address account, address receiver, uint256 amount) external onlyRouterPlugin { // TODO: confirm access control + IERC20(token).safeTransferFrom(account, receiver, amount); } /** diff --git a/utils/multichain.ts b/utils/multichain.ts deleted file mode 100644 index e69de29bb..000000000 From 0bd9d83e437bbe96825dee0de0102bbb68c50b7f Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 4 Feb 2025 22:34:18 +0200 Subject: [PATCH 151/205] Set chainId for Deposit on executeShift, remove position chainId related comments --- contracts/position/Position.sol | 9 --------- 1 file changed, 9 deletions(-) diff --git a/contracts/position/Position.sol b/contracts/position/Position.sol index 713f86b9a..0679fa515 100644 --- a/contracts/position/Position.sol +++ b/contracts/position/Position.sol @@ -197,17 +197,8 @@ library Position { // @param market the position's market // @param collateralToken the position's collateralToken // @param isLong whether the position is long or short - // @param chainId the destination chain id // @return the position key function getPositionKey(address _account, address _market, address _collateralToken, bool _isLong/*, uint256 _chainId*/) internal pure returns (bytes32) { - // TODO: handle the chainId cases bellow - // bytes32 _key; - // if (_chainId == 0) { - // _key = keccak256(abi.encode(_account, _market, _collateralToken, _isLong)); - // } else { - // _key = keccak256(abi.encode(_account, _chainId, _market, _collateralToken, _isLong)); - // } - bytes32 _key = keccak256(abi.encode(_account, _market, _collateralToken, _isLong)); return _key; From 81699fddb77c8e04f095dcd35176cd158cdf827f Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 4 Feb 2025 22:39:55 +0200 Subject: [PATCH 152/205] WIP multichain deposits and gm logic --- contracts/token/TokenUtils.sol | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/contracts/token/TokenUtils.sol b/contracts/token/TokenUtils.sol index 9a7518f18..996b5dfd3 100644 --- a/contracts/token/TokenUtils.sol +++ b/contracts/token/TokenUtils.sol @@ -10,6 +10,7 @@ import "../data/DataStore.sol"; import "../data/Keys.sol"; import "../error/ErrorUtils.sol"; import "../utils/AccountUtils.sol"; +// import "../multichain/MultichainUtils.sol"; import "./IWNT.sol"; @@ -95,6 +96,23 @@ library TokenUtils { revert Errors.TokenTransferError(token, receiver, amount); } + // function multichainTransfer( + // DataStore dataStore, + // address token, + // address receiver, + // uint256 amount, + // uint256 chainId, + // address multichainVault, + // address account + // ) internal { + // if (chainId == 0) { + // transfer(dataStore, token, receiver, amount); + // } else { + // transfer(dataStore, token, multichainVault, amount); + // MultichainUtils.increaseBalance(dataStore, chainId, account, token, amount); + // } + // } + function sendNativeToken( DataStore dataStore, address receiver, From 57a8b3e9a5dae337647c338113c81306a2527dda Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 5 Feb 2025 10:44:41 +0200 Subject: [PATCH 153/205] Update multichain logic for fee payment --- contracts/token/TokenUtils.sol | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/contracts/token/TokenUtils.sol b/contracts/token/TokenUtils.sol index 996b5dfd3..3f8dfa264 100644 --- a/contracts/token/TokenUtils.sol +++ b/contracts/token/TokenUtils.sol @@ -10,7 +10,7 @@ import "../data/DataStore.sol"; import "../data/Keys.sol"; import "../error/ErrorUtils.sol"; import "../utils/AccountUtils.sol"; -// import "../multichain/MultichainUtils.sol"; +import "../multichain/MultichainUtils.sol"; import "./IWNT.sol"; @@ -96,22 +96,17 @@ library TokenUtils { revert Errors.TokenTransferError(token, receiver, amount); } - // function multichainTransfer( - // DataStore dataStore, - // address token, - // address receiver, - // uint256 amount, - // uint256 chainId, - // address multichainVault, - // address account - // ) internal { - // if (chainId == 0) { - // transfer(dataStore, token, receiver, amount); - // } else { - // transfer(dataStore, token, multichainVault, amount); - // MultichainUtils.increaseBalance(dataStore, chainId, account, token, amount); - // } - // } + function multichainTransfer( + DataStore dataStore, + address token, + address receiver, + uint256 amount, + uint256 chainId, + address account + ) internal { + transfer(dataStore, token, receiver, amount); + MultichainUtils.decreaseBalance(dataStore, chainId, account, token, amount); + } function sendNativeToken( DataStore dataStore, From c0ed26a74510b78198a7a16e6f8e0b0dbf1c8831 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 5 Feb 2025 15:42:23 +0200 Subject: [PATCH 154/205] Remove chainId from multichain balance --- contracts/multichain/MultichainVaultHandler.sol | 5 ++--- contracts/token/TokenUtils.sol | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/contracts/multichain/MultichainVaultHandler.sol b/contracts/multichain/MultichainVaultHandler.sol index 893912609..c1dde6990 100644 --- a/contracts/multichain/MultichainVaultHandler.sol +++ b/contracts/multichain/MultichainVaultHandler.sol @@ -60,7 +60,7 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu revert Errors.EmptyMultichainDepositAmount(); } - dataStore.incrementUint(Keys.multichainBalanceKey(multichainId, account, token), amount); + dataStore.incrementUint(Keys.multichainBalanceKey(account, token), amount); MultichainEventUtils.emitMultichainDeposit(eventEmitter, token, account, amount, multichainId); } @@ -99,7 +99,6 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu * @param account user address on the source chain * @param token address of the token being withdrawn * @param amount amount of token being withdrawn - * @param multichainId chain id of the destination chain */ function recordWithdrawal( address account, @@ -111,7 +110,7 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu revert Errors.EmptyMultichainWithdrawalAmount(); } - bytes32 balanceKey = Keys.multichainBalanceKey(multichainId, account, token); + bytes32 balanceKey = Keys.multichainBalanceKey(account, token); uint256 balance = dataStore.getUint(balanceKey); if (balance < amount) { diff --git a/contracts/token/TokenUtils.sol b/contracts/token/TokenUtils.sol index 3f8dfa264..91db7138b 100644 --- a/contracts/token/TokenUtils.sol +++ b/contracts/token/TokenUtils.sol @@ -101,11 +101,10 @@ library TokenUtils { address token, address receiver, uint256 amount, - uint256 chainId, address account ) internal { transfer(dataStore, token, receiver, amount); - MultichainUtils.decreaseBalance(dataStore, chainId, account, token, amount); + MultichainUtils.decreaseBalance(dataStore, account, token, amount); } function sendNativeToken( From f2f927cfa671b39531ce750371c329d87a8db6b6 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 7 Feb 2025 13:59:56 +0200 Subject: [PATCH 155/205] Add in and out multichain transfers methods to MultichainUtils lib. Refactor all related contracts. --- .../multichain/MultichainVaultHandler.sol | 65 ------------------- contracts/token/TokenUtils.sol | 12 ---- 2 files changed, 77 deletions(-) diff --git a/contracts/multichain/MultichainVaultHandler.sol b/contracts/multichain/MultichainVaultHandler.sol index c1dde6990..4cdc8ac29 100644 --- a/contracts/multichain/MultichainVaultHandler.sol +++ b/contracts/multichain/MultichainVaultHandler.sol @@ -43,39 +43,6 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu exchangeRouter = _exchangeRouter; } - /** - * Records a deposit from another chain. IMultichainProvider has CONTROLLER role - * @param account user address on the source chain - * @param token address of the token being deposited - * @param multichainId chain id of the destination chain - */ - function recordDeposit( - address account, - address token, - uint256 multichainId - ) external onlyController { - // token should have been transferred to multichainVault by IMultichainProvider - uint256 amount = multichainVault.recordTransferIn(token); - if (amount == 0) { - revert Errors.EmptyMultichainDepositAmount(); - } - - dataStore.incrementUint(Keys.multichainBalanceKey(account, token), amount); - - MultichainEventUtils.emitMultichainDeposit(eventEmitter, token, account, amount, multichainId); - } - - /** - * @dev transfer the specified amount of tokens from account to receiver - * @param token the token to transfer - * @param account the account to transfer from - * @param receiver the account to transfer to - * @param amount the amount of tokens to transfer - */ - function pluginTransfer(address token, address account, address receiver, uint256 amount) external onlyRouterPlugin { // TODO: confirm access control - IERC20(token).safeTransferFrom(account, receiver, amount); - } - /** * Executes the multicall for the given args * The multicall arguments contains the function calls to be executed (e.g. createDeposit, createOrder, createWithdrawal, etc) @@ -93,36 +60,4 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu MultichainEventUtils.emitMultichainMessage(eventEmitter, account, multichainId); } - - /** - * Record a withdrawal to another chain. IMultichainProvider has CONTROLLER role - * @param account user address on the source chain - * @param token address of the token being withdrawn - * @param amount amount of token being withdrawn - */ - function recordWithdrawal( - address account, - address token, - uint256 amount, - uint256 multichainId - ) external onlyController { - if (amount == 0) { - revert Errors.EmptyMultichainWithdrawalAmount(); - } - - bytes32 balanceKey = Keys.multichainBalanceKey(account, token); - - uint256 balance = dataStore.getUint(balanceKey); - if (balance < amount) { - revert Errors.InsufficientMultichainBalance(); - // TODO: should amount be capped instead of reverting? i.e. amount = balance; - } - - dataStore.decrementUint(balanceKey, amount); - - // transfer tokens to IMultichainProvider - multichainVault.transferOut(token, msg.sender, amount); - - MultichainEventUtils.emitMultichainWithdrawal(eventEmitter, token, account, amount, multichainId); - } } diff --git a/contracts/token/TokenUtils.sol b/contracts/token/TokenUtils.sol index 91db7138b..9a7518f18 100644 --- a/contracts/token/TokenUtils.sol +++ b/contracts/token/TokenUtils.sol @@ -10,7 +10,6 @@ import "../data/DataStore.sol"; import "../data/Keys.sol"; import "../error/ErrorUtils.sol"; import "../utils/AccountUtils.sol"; -import "../multichain/MultichainUtils.sol"; import "./IWNT.sol"; @@ -96,17 +95,6 @@ library TokenUtils { revert Errors.TokenTransferError(token, receiver, amount); } - function multichainTransfer( - DataStore dataStore, - address token, - address receiver, - uint256 amount, - address account - ) internal { - transfer(dataStore, token, receiver, amount); - MultichainUtils.decreaseBalance(dataStore, account, token, amount); - } - function sendNativeToken( DataStore dataStore, address receiver, From f81166952b051f63e56307f816089993e1b80e6d Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 8 Feb 2025 13:12:50 +0200 Subject: [PATCH 156/205] Split CreateDepositParams into subgroups --- contracts/migration/GlpMigrator.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/migration/GlpMigrator.sol b/contracts/migration/GlpMigrator.sol index 062161e48..c08fb10e8 100644 --- a/contracts/migration/GlpMigrator.sol +++ b/contracts/migration/GlpMigrator.sol @@ -210,7 +210,7 @@ contract GlpMigrator is ReentrancyGuard, RoleModule { false, // shouldUnwrapNativeToken; migrationItem.executionFee, // executionFee; 0, // callbackGasLimit; - 0, // chainId + 0, // srcChainId new bytes32[](0) // dataList; ); From 39c146e7a222041aabbb996bf5e15c738db65763 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 9 Feb 2025 19:40:46 +0200 Subject: [PATCH 157/205] Add srcChainId to orders --- utils/order.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/utils/order.ts b/utils/order.ts index 906141b93..2f5c1e80f 100644 --- a/utils/order.ts +++ b/utils/order.ts @@ -131,6 +131,7 @@ export async function createOrder(fixture, overrides) { callbackGasLimit, minOutputAmount, validFromTime, + srcChainId, }, orderType, decreasePositionSwapType, From b1bcadd840290ad7f7948ca67d36cc5399aae26f Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 10 Feb 2025 10:25:57 +0200 Subject: [PATCH 158/205] Rename multichainId to srcChainId, remove second topic log --- contracts/multichain/MultichainProviderSignature.sol | 10 +++++----- contracts/multichain/MultichainVaultHandler.sol | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/contracts/multichain/MultichainProviderSignature.sol b/contracts/multichain/MultichainProviderSignature.sol index 1903c9262..e96a7a585 100644 --- a/contracts/multichain/MultichainProviderSignature.sol +++ b/contracts/multichain/MultichainProviderSignature.sol @@ -14,24 +14,24 @@ contract MultichainProviderSignature is EIP712 { string private constant SIGNATURE_VERSION = "1"; // Define the EIP-712 struct type: - // Message(address token,uint256 amount,address account,uint256 multichainId,uint32 srcEid) + // Message(address token,uint256 amount,address account,uint256 srcChainId,uint32 srcEid) bytes32 private constant _MESSAGE_TYPEHASH = - keccak256("Message(address token,uint256 amount,address account,uint256 multichainId,uint32 srcEid)"); + keccak256("Message(address token,uint256 amount,address account,uint256 srcChainId,uint32 srcEid)"); constructor() EIP712(SIGNING_DOMAIN, SIGNATURE_VERSION) {} /** * Check the signature for a given message - * @param message The ABI encoded parameters (token, amount, account, multichainId, srcEid). + * @param message The ABI encoded parameters (token, amount, account, srcChainId, srcEid). * @param signature The EIP-712 signature. */ function isSigner(bytes calldata message, bytes calldata signature) external view returns (bool) { // Decode the message - (address token, uint256 amount, address account, uint256 multichainId, uint32 srcEid) = MultichainProviderUtils + (address token, uint256 amount, address account, uint256 srcChainId, uint32 srcEid) = MultichainProviderUtils .decodeWithdrawal(message); // Build the struct hash - bytes32 structHash = keccak256(abi.encode(_MESSAGE_TYPEHASH, token, amount, account, multichainId, srcEid)); + bytes32 structHash = keccak256(abi.encode(_MESSAGE_TYPEHASH, token, amount, account, srcChainId, srcEid)); // Get the typed data hash for EIP-712 bytes32 hash = _hashTypedDataV4(structHash); diff --git a/contracts/multichain/MultichainVaultHandler.sol b/contracts/multichain/MultichainVaultHandler.sol index 4cdc8ac29..0e9a1942f 100644 --- a/contracts/multichain/MultichainVaultHandler.sol +++ b/contracts/multichain/MultichainVaultHandler.sol @@ -47,17 +47,17 @@ contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModu * Executes the multicall for the given args * The multicall arguments contains the function calls to be executed (e.g. createDeposit, createOrder, createWithdrawal, etc) * @param account user address on the source chain - * @param multichainId chain id of the destination chain + * @param srcChainId chain id of the source chain * @param multicallArgs array of bytes containing the multicall arguments */ function executeMulticall( address account, - uint256 multichainId, + uint256 srcChainId, bytes[] calldata multicallArgs ) external onlyController { // execute multicall exchangeRouter.multicall(multicallArgs); - MultichainEventUtils.emitMultichainMessage(eventEmitter, account, multichainId); + MultichainEventUtils.emitMultichainMessage(eventEmitter, account, srcChainId); } } From b701ce886b9c7dbf23de160f6880d5136ea43959 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 10 Feb 2025 13:16:49 +0200 Subject: [PATCH 159/205] Add srcChiainId field to CreateOrderParams struct --- contracts/fee/FeeSwapUtils.sol | 3 ++- contracts/order/IBaseOrderUtils.sol | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/contracts/fee/FeeSwapUtils.sol b/contracts/fee/FeeSwapUtils.sol index 09bffaf68..8bdcc4d6a 100644 --- a/contracts/fee/FeeSwapUtils.sol +++ b/contracts/fee/FeeSwapUtils.sol @@ -130,7 +130,8 @@ library FeeSwapUtils { executionFee, // executionFee maxCallbackGasLimit, // callbackGasLimit minOut, // minOutputAmount - 0 // validFromTime + 0, // validFromTime + 0 // srcChainId ); IBaseOrderUtils.CreateOrderParams memory params = IBaseOrderUtils.CreateOrderParams( diff --git a/contracts/order/IBaseOrderUtils.sol b/contracts/order/IBaseOrderUtils.sol index 7fca4a8c1..b89171784 100644 --- a/contracts/order/IBaseOrderUtils.sol +++ b/contracts/order/IBaseOrderUtils.sol @@ -55,5 +55,6 @@ interface IBaseOrderUtils { uint256 callbackGasLimit; uint256 minOutputAmount; uint256 validFromTime; + uint256 srcChainId; } } From f85d94243c364ac9c67714dc6f6597c2db18d203 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 13 Feb 2025 15:08:17 +0200 Subject: [PATCH 160/205] Add TransferRequest[] param to create* multichain actions and _sendTokens by looping on this array. Refactor all related typehashes and helper functions. --- contracts/multichain/MultichainRouter.sol | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 762f48d92..c19a2b98d 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -56,6 +56,13 @@ abstract contract MultichainRouter is GelatoRelayRouter { } } + function _processTransferRequests(address account, RelayUtils.TransferRequest[] calldata transferRequests, uint256 srcChainId) internal { + for (uint256 i = 0; i < transferRequests.length; i++) { + RelayUtils.TransferRequest calldata transferRequest = transferRequests[i]; + _sendTokens(account, transferRequest.token, transferRequest.receiver, transferRequest.amount, srcChainId); + } + } + function _sendTokens(address account, address token, address receiver, uint256 amount, uint256 srcChainId) internal override { AccountUtils.validateReceiver(receiver); if (srcChainId == 0) { From b2e457d9139c9f85504ce99ea91904614e37e16e Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 14 Feb 2025 11:55:30 +0200 Subject: [PATCH 161/205] Remove outdated contracts --- .../MultichainProviderSignature.sol | 44 ------------- .../multichain/MultichainVaultHandler.sol | 63 ------------------- deploy/deployMultichainHandler.ts | 17 ----- 3 files changed, 124 deletions(-) delete mode 100644 contracts/multichain/MultichainProviderSignature.sol delete mode 100644 contracts/multichain/MultichainVaultHandler.sol delete mode 100644 deploy/deployMultichainHandler.ts diff --git a/contracts/multichain/MultichainProviderSignature.sol b/contracts/multichain/MultichainProviderSignature.sol deleted file mode 100644 index e96a7a585..000000000 --- a/contracts/multichain/MultichainProviderSignature.sol +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.0; - -import "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; -import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; - -import { MultichainProviderUtils } from "./MultichainProviderUtils.sol"; - -contract MultichainProviderSignature is EIP712 { - using ECDSA for bytes32; - - string private constant SIGNING_DOMAIN = "MultichainProviderSignatureDomain"; - string private constant SIGNATURE_VERSION = "1"; - - // Define the EIP-712 struct type: - // Message(address token,uint256 amount,address account,uint256 srcChainId,uint32 srcEid) - bytes32 private constant _MESSAGE_TYPEHASH = - keccak256("Message(address token,uint256 amount,address account,uint256 srcChainId,uint32 srcEid)"); - - constructor() EIP712(SIGNING_DOMAIN, SIGNATURE_VERSION) {} - - /** - * Check the signature for a given message - * @param message The ABI encoded parameters (token, amount, account, srcChainId, srcEid). - * @param signature The EIP-712 signature. - */ - function isSigner(bytes calldata message, bytes calldata signature) external view returns (bool) { - // Decode the message - (address token, uint256 amount, address account, uint256 srcChainId, uint32 srcEid) = MultichainProviderUtils - .decodeWithdrawal(message); - - // Build the struct hash - bytes32 structHash = keccak256(abi.encode(_MESSAGE_TYPEHASH, token, amount, account, srcChainId, srcEid)); - - // Get the typed data hash for EIP-712 - bytes32 hash = _hashTypedDataV4(structHash); - - // Recover the signer from the signature - address signer = ECDSA.recover(hash, signature); - - return signer == account; - } -} diff --git a/contracts/multichain/MultichainVaultHandler.sol b/contracts/multichain/MultichainVaultHandler.sol deleted file mode 100644 index 0e9a1942f..000000000 --- a/contracts/multichain/MultichainVaultHandler.sol +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.0; - -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; - -import { RoleStore } from "../role/RoleStore.sol"; -import { RoleModule } from "../role/RoleModule.sol"; -import { DataStore } from "../data/DataStore.sol"; -import { Keys } from "../data/Keys.sol"; -import { Oracle } from "../oracle/Oracle.sol"; -import { OracleModule } from "../oracle/OracleModule.sol"; -import { EventEmitter } from "../event/EventEmitter.sol"; -import { Errors } from "../error/Errors.sol"; -import { GlobalReentrancyGuard } from "../utils/GlobalReentrancyGuard.sol"; -import { ExchangeRouter } from "../router/ExchangeRouter.sol"; - -import { MultichainVault } from "./MultichainVault.sol"; -import { MultichainUtils } from "./MultichainUtils.sol"; -import { MultichainEventUtils } from "./MultichainEventUtils.sol"; - -/** - * @title MultichainVaultHandler - */ -contract MultichainVaultHandler is RoleModule, GlobalReentrancyGuard, OracleModule { - using SafeERC20 for IERC20; - - MultichainVault public multichainVault; - EventEmitter public eventEmitter; - ExchangeRouter public exchangeRouter; - - constructor( - RoleStore _roleStore, - DataStore _dataStore, - EventEmitter _eventEmitter, - Oracle _oracle, - MultichainVault _multichainVault, - ExchangeRouter _exchangeRouter - ) RoleModule(_roleStore) GlobalReentrancyGuard(_dataStore) OracleModule(_oracle) { - multichainVault = _multichainVault; - eventEmitter = _eventEmitter; - exchangeRouter = _exchangeRouter; - } - - /** - * Executes the multicall for the given args - * The multicall arguments contains the function calls to be executed (e.g. createDeposit, createOrder, createWithdrawal, etc) - * @param account user address on the source chain - * @param srcChainId chain id of the source chain - * @param multicallArgs array of bytes containing the multicall arguments - */ - function executeMulticall( - address account, - uint256 srcChainId, - bytes[] calldata multicallArgs - ) external onlyController { - // execute multicall - exchangeRouter.multicall(multicallArgs); - - MultichainEventUtils.emitMultichainMessage(eventEmitter, account, srcChainId); - } -} diff --git a/deploy/deployMultichainHandler.ts b/deploy/deployMultichainHandler.ts deleted file mode 100644 index 3f28c9645..000000000 --- a/deploy/deployMultichainHandler.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { grantRoleIfNotGranted } from "../utils/role"; -import { createDeployFunction } from "../utils/deploy"; - -const constructorContracts = ["RoleStore", "DataStore", "EventEmitter", "Oracle", "MultichainVault", "ExchangeRouter"]; - -const func = createDeployFunction({ - contractName: "MultichainVaultHandler", - dependencyNames: constructorContracts, - getDeployArgs: async ({ dependencyContracts }) => { - return constructorContracts.map((dependencyName) => dependencyContracts[dependencyName].address); - }, - afterDeploy: async ({ deployedContract }) => { - await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); - }, -}); - -export default func; From 4748b1363d75315ac050447855d32527540baf7c Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 14 Feb 2025 15:55:35 +0200 Subject: [PATCH 162/205] Add srcChainId to depositHandler.createDeposit, small DepositUtils refactor to solve stack too deep error --- contracts/migration/GlpMigrator.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/migration/GlpMigrator.sol b/contracts/migration/GlpMigrator.sol index c08fb10e8..5108609d5 100644 --- a/contracts/migration/GlpMigrator.sol +++ b/contracts/migration/GlpMigrator.sol @@ -210,7 +210,6 @@ contract GlpMigrator is ReentrancyGuard, RoleModule { false, // shouldUnwrapNativeToken; migrationItem.executionFee, // executionFee; 0, // callbackGasLimit; - 0, // srcChainId new bytes32[](0) // dataList; ); From 5b6dc1173be1634b267699c9139c181d5d192ff0 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 14 Feb 2025 16:44:02 +0200 Subject: [PATCH 163/205] Add srcChainId to withdrawalHandler.createWithdrawal, small WithdrawalUtils refactor to solve stack too deep error --- contracts/multichain/MultichainGmRouter.sol | 40 ++++++++++----------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/contracts/multichain/MultichainGmRouter.sol b/contracts/multichain/MultichainGmRouter.sol index 53b648621..eaa517efe 100644 --- a/contracts/multichain/MultichainGmRouter.sol +++ b/contracts/multichain/MultichainGmRouter.sol @@ -36,22 +36,25 @@ contract MultichainGmRouter is MultichainRouter { RelayUtils.RelayParams calldata relayParams, address account, uint256 srcChainId, - RelayUtils.TransferRequests calldata transferRequests, + RelayUtils.TransferRequest[] calldata transferRequests, DepositUtils.CreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { - _validateDesChainId(relayParams.desChainId); + if (relayParams.desChainId != block.chainid) { + revert Errors.InvalidDestinationChainId(); + } bytes32 structHash = RelayUtils.getCreateDepositStructHash(relayParams, transferRequests, params); _validateCall(relayParams, account, structHash, srcChainId); - return _createDeposit(relayParams, account, srcChainId, transferRequests, params); + _processTransferRequests(account, transferRequests, srcChainId); + + return _createDeposit(relayParams, account, srcChainId, params); } function _createDeposit( RelayUtils.RelayParams calldata relayParams, address account, uint256 srcChainId, - RelayUtils.TransferRequests calldata transferRequests, DepositUtils.CreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee ) internal returns (bytes32) { Contracts memory contracts = Contracts({ @@ -66,13 +69,9 @@ contract MultichainGmRouter is MultichainRouter { relayParams, account, address(depositVault), // residualFeeReceiver - false, // isSubaccount srcChainId ); - // process transfer requests after relay fee is paid, otherwise all wnt amount will be recorder as relay fee - _processTransferRequests(account, transferRequests, srcChainId); - return depositHandler.createDeposit(account, srcChainId, params); } @@ -80,10 +79,12 @@ contract MultichainGmRouter is MultichainRouter { RelayUtils.RelayParams calldata relayParams, address account, uint256 srcChainId, - RelayUtils.TransferRequests calldata transferRequests, + RelayUtils.TransferRequest[] calldata transferRequests, WithdrawalUtils.CreateWithdrawalParams memory params // can't use calldata because need to modify params.addresses.receiver & params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { - _validateDesChainId(relayParams.desChainId); + if (relayParams.desChainId != block.chainid) { + revert Errors.InvalidDestinationChainId(); + } bytes32 structHash = RelayUtils.getCreateWithdrawalStructHash(relayParams, transferRequests, params); _validateCall(relayParams, account, structHash, srcChainId); @@ -110,7 +111,6 @@ contract MultichainGmRouter is MultichainRouter { relayParams, account, address(withdrawalVault), // residualFeeReceiver - false, // isSubaccount srcChainId ); @@ -120,24 +120,24 @@ contract MultichainGmRouter is MultichainRouter { function createShift( RelayUtils.RelayParams calldata relayParams, address account, - uint256 srcChainId, - RelayUtils.TransferRequests calldata transferRequests, + RelayUtils.TransferRequest[] calldata transferRequests, ShiftUtils.CreateShiftParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { - _validateDesChainId(relayParams.desChainId); + if (relayParams.desChainId != block.chainid) { + revert Errors.InvalidDestinationChainId(); + } bytes32 structHash = RelayUtils.getCreateShiftStructHash(relayParams, transferRequests, params); - _validateCall(relayParams, account, structHash, srcChainId); + _validateCall(relayParams, account, structHash, params.srcChainId); - _processTransferRequests(account, transferRequests, srcChainId); + _processTransferRequests(account, transferRequests, params.srcChainId); - return _createShift(relayParams, account, srcChainId, params); + return _createShift(relayParams, account, params); } function _createShift( RelayUtils.RelayParams calldata relayParams, address account, - uint256 srcChainId, ShiftUtils.CreateShiftParams memory params ) internal returns (bytes32) { Contracts memory contracts = Contracts({ @@ -151,8 +151,7 @@ contract MultichainGmRouter is MultichainRouter { relayParams, account, address(shiftVault), - false, // isSubaccount - srcChainId + params.srcChainId ); return ShiftUtils.createShift( @@ -160,7 +159,6 @@ contract MultichainGmRouter is MultichainRouter { eventEmitter, shiftVault, account, - srcChainId, params ); } From edd7333c3f8b64ed22610fa7857303190b24e299 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 14 Feb 2025 16:58:58 +0200 Subject: [PATCH 164/205] Add srcChainId to shiftHandler.createShift --- contracts/multichain/MultichainGmRouter.sol | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/contracts/multichain/MultichainGmRouter.sol b/contracts/multichain/MultichainGmRouter.sol index eaa517efe..efae3584c 100644 --- a/contracts/multichain/MultichainGmRouter.sol +++ b/contracts/multichain/MultichainGmRouter.sol @@ -120,6 +120,7 @@ contract MultichainGmRouter is MultichainRouter { function createShift( RelayUtils.RelayParams calldata relayParams, address account, + uint256 srcChainId, RelayUtils.TransferRequest[] calldata transferRequests, ShiftUtils.CreateShiftParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { @@ -128,16 +129,17 @@ contract MultichainGmRouter is MultichainRouter { } bytes32 structHash = RelayUtils.getCreateShiftStructHash(relayParams, transferRequests, params); - _validateCall(relayParams, account, structHash, params.srcChainId); + _validateCall(relayParams, account, structHash, srcChainId); - _processTransferRequests(account, transferRequests, params.srcChainId); + _processTransferRequests(account, transferRequests, srcChainId); - return _createShift(relayParams, account, params); + return _createShift(relayParams, account, srcChainId, params); } function _createShift( RelayUtils.RelayParams calldata relayParams, address account, + uint256 srcChainId, ShiftUtils.CreateShiftParams memory params ) internal returns (bytes32) { Contracts memory contracts = Contracts({ @@ -151,7 +153,7 @@ contract MultichainGmRouter is MultichainRouter { relayParams, account, address(shiftVault), - params.srcChainId + srcChainId ); return ShiftUtils.createShift( @@ -159,6 +161,7 @@ contract MultichainGmRouter is MultichainRouter { eventEmitter, shiftVault, account, + srcChainId, params ); } From 830f30019bf5a1037a478f31a6db89e93748c724 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 15 Feb 2025 12:42:43 +0200 Subject: [PATCH 165/205] Add srcChainId to orderHandler.createOrder, small OrderUtils refactor to solve stack too deep error --- contracts/fee/FeeSwapUtils.sol | 3 +-- contracts/order/IBaseOrderUtils.sol | 1 - utils/order.ts | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/contracts/fee/FeeSwapUtils.sol b/contracts/fee/FeeSwapUtils.sol index 8bdcc4d6a..09bffaf68 100644 --- a/contracts/fee/FeeSwapUtils.sol +++ b/contracts/fee/FeeSwapUtils.sol @@ -130,8 +130,7 @@ library FeeSwapUtils { executionFee, // executionFee maxCallbackGasLimit, // callbackGasLimit minOut, // minOutputAmount - 0, // validFromTime - 0 // srcChainId + 0 // validFromTime ); IBaseOrderUtils.CreateOrderParams memory params = IBaseOrderUtils.CreateOrderParams( diff --git a/contracts/order/IBaseOrderUtils.sol b/contracts/order/IBaseOrderUtils.sol index b89171784..7fca4a8c1 100644 --- a/contracts/order/IBaseOrderUtils.sol +++ b/contracts/order/IBaseOrderUtils.sol @@ -55,6 +55,5 @@ interface IBaseOrderUtils { uint256 callbackGasLimit; uint256 minOutputAmount; uint256 validFromTime; - uint256 srcChainId; } } diff --git a/utils/order.ts b/utils/order.ts index 2f5c1e80f..906141b93 100644 --- a/utils/order.ts +++ b/utils/order.ts @@ -131,7 +131,6 @@ export async function createOrder(fixture, overrides) { callbackGasLimit, minOutputAmount, validFromTime, - srcChainId, }, orderType, decreasePositionSwapType, From 93620875d8cac3e63c65897a59e85a57103d2cc0 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 16 Feb 2025 17:22:54 +0200 Subject: [PATCH 166/205] Rename contract, remove comment, add modifier --- contracts/position/Position.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/position/Position.sol b/contracts/position/Position.sol index 0679fa515..2da0950c0 100644 --- a/contracts/position/Position.sol +++ b/contracts/position/Position.sol @@ -198,7 +198,7 @@ library Position { // @param collateralToken the position's collateralToken // @param isLong whether the position is long or short // @return the position key - function getPositionKey(address _account, address _market, address _collateralToken, bool _isLong/*, uint256 _chainId*/) internal pure returns (bytes32) { + function getPositionKey(address _account, address _market, address _collateralToken, bool _isLong) internal pure returns (bytes32) { bytes32 _key = keccak256(abi.encode(_account, _market, _collateralToken, _isLong)); return _key; From deaac541566ed2b7ca4bdb533a66d005768f9aa9 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 16 Feb 2025 22:49:53 +0200 Subject: [PATCH 167/205] Refactor: add function to validate destination chain --- contracts/multichain/MultichainGmRouter.sol | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/contracts/multichain/MultichainGmRouter.sol b/contracts/multichain/MultichainGmRouter.sol index efae3584c..de8efe104 100644 --- a/contracts/multichain/MultichainGmRouter.sol +++ b/contracts/multichain/MultichainGmRouter.sol @@ -39,9 +39,7 @@ contract MultichainGmRouter is MultichainRouter { RelayUtils.TransferRequest[] calldata transferRequests, DepositUtils.CreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { - if (relayParams.desChainId != block.chainid) { - revert Errors.InvalidDestinationChainId(); - } + _validateDesChainId(relayParams.desChainId); bytes32 structHash = RelayUtils.getCreateDepositStructHash(relayParams, transferRequests, params); _validateCall(relayParams, account, structHash, srcChainId); @@ -82,9 +80,7 @@ contract MultichainGmRouter is MultichainRouter { RelayUtils.TransferRequest[] calldata transferRequests, WithdrawalUtils.CreateWithdrawalParams memory params // can't use calldata because need to modify params.addresses.receiver & params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { - if (relayParams.desChainId != block.chainid) { - revert Errors.InvalidDestinationChainId(); - } + _validateDesChainId(relayParams.desChainId); bytes32 structHash = RelayUtils.getCreateWithdrawalStructHash(relayParams, transferRequests, params); _validateCall(relayParams, account, structHash, srcChainId); @@ -124,9 +120,7 @@ contract MultichainGmRouter is MultichainRouter { RelayUtils.TransferRequest[] calldata transferRequests, ShiftUtils.CreateShiftParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { - if (relayParams.desChainId != block.chainid) { - revert Errors.InvalidDestinationChainId(); - } + _validateDesChainId(relayParams.desChainId); bytes32 structHash = RelayUtils.getCreateShiftStructHash(relayParams, transferRequests, params); _validateCall(relayParams, account, structHash, srcChainId); From 20bd13c877cba1f7b0c7d2a5966bcb8a3a9390a9 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 17 Feb 2025 11:02:11 +0200 Subject: [PATCH 168/205] Fix gasless merge --- contracts/multichain/MultichainGmRouter.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contracts/multichain/MultichainGmRouter.sol b/contracts/multichain/MultichainGmRouter.sol index de8efe104..b1712cd7f 100644 --- a/contracts/multichain/MultichainGmRouter.sol +++ b/contracts/multichain/MultichainGmRouter.sol @@ -67,6 +67,7 @@ contract MultichainGmRouter is MultichainRouter { relayParams, account, address(depositVault), // residualFeeReceiver + false, // isSubaccount srcChainId ); @@ -107,6 +108,7 @@ contract MultichainGmRouter is MultichainRouter { relayParams, account, address(withdrawalVault), // residualFeeReceiver + false, // isSubaccount srcChainId ); @@ -147,6 +149,7 @@ contract MultichainGmRouter is MultichainRouter { relayParams, account, address(shiftVault), + false, // isSubaccount srcChainId ); From dea3c670fd5174eaa40585c1e381fd40900292b5 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 17 Feb 2025 18:52:11 +0200 Subject: [PATCH 169/205] Validate desChainId against block.chainid (for same chain actions, chainId and desChainId are the same) --- contracts/router/relay/SubaccountGelatoRelayRouter.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/router/relay/SubaccountGelatoRelayRouter.sol b/contracts/router/relay/SubaccountGelatoRelayRouter.sol index e26bc0b4c..0003718b8 100644 --- a/contracts/router/relay/SubaccountGelatoRelayRouter.sol +++ b/contracts/router/relay/SubaccountGelatoRelayRouter.sol @@ -274,7 +274,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { function _getRemoveSubaccountStructHash( RelayUtils.RelayParams calldata relayParams, address subaccount - ) internal pure returns (bytes32) { + ) internal view returns (bytes32) { return keccak256(abi.encode(REMOVE_SUBACCOUNT_TYPEHASH, subaccount, RelayUtils._getRelayParamsHash(relayParams))); } @@ -302,7 +302,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { address account, uint256 collateralDeltaAmount, IBaseOrderUtils.CreateOrderParams memory params - ) internal pure returns (bytes32) { + ) internal view returns (bytes32) { bytes32 relayParamsHash = RelayUtils._getRelayParamsHash(relayParams); bytes32 subaccountApprovalHash = keccak256(abi.encode(subaccountApproval)); @@ -371,7 +371,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { bytes32 key, RelayUtils.UpdateOrderParams calldata params, bool increaseExecutionFee - ) internal pure returns (bytes32) { + ) internal view returns (bytes32) { return keccak256( abi.encode( @@ -406,7 +406,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { SubaccountApproval calldata subaccountApproval, address account, bytes32 key - ) internal pure returns (bytes32) { + ) internal view returns (bytes32) { return keccak256( abi.encode( From eb03f912fad93dcbe64b6d0a4f5418e531c8a603 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 19 Feb 2025 11:30:41 +0200 Subject: [PATCH 170/205] Remove double validation for desChainId --- contracts/router/relay/SubaccountGelatoRelayRouter.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/router/relay/SubaccountGelatoRelayRouter.sol b/contracts/router/relay/SubaccountGelatoRelayRouter.sol index 0003718b8..e26bc0b4c 100644 --- a/contracts/router/relay/SubaccountGelatoRelayRouter.sol +++ b/contracts/router/relay/SubaccountGelatoRelayRouter.sol @@ -274,7 +274,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { function _getRemoveSubaccountStructHash( RelayUtils.RelayParams calldata relayParams, address subaccount - ) internal view returns (bytes32) { + ) internal pure returns (bytes32) { return keccak256(abi.encode(REMOVE_SUBACCOUNT_TYPEHASH, subaccount, RelayUtils._getRelayParamsHash(relayParams))); } @@ -302,7 +302,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { address account, uint256 collateralDeltaAmount, IBaseOrderUtils.CreateOrderParams memory params - ) internal view returns (bytes32) { + ) internal pure returns (bytes32) { bytes32 relayParamsHash = RelayUtils._getRelayParamsHash(relayParams); bytes32 subaccountApprovalHash = keccak256(abi.encode(subaccountApproval)); @@ -371,7 +371,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { bytes32 key, RelayUtils.UpdateOrderParams calldata params, bool increaseExecutionFee - ) internal view returns (bytes32) { + ) internal pure returns (bytes32) { return keccak256( abi.encode( @@ -406,7 +406,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { SubaccountApproval calldata subaccountApproval, address account, bytes32 key - ) internal view returns (bytes32) { + ) internal pure returns (bytes32) { return keccak256( abi.encode( From 22768d8941265ed53249bf2ea088185974e51236 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 19 Feb 2025 14:14:20 +0200 Subject: [PATCH 171/205] Change transferRequests structure, update typehashes --- contracts/multichain/MultichainGmRouter.sol | 6 +++--- contracts/multichain/MultichainRouter.sol | 20 ++++++++++++++++---- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/contracts/multichain/MultichainGmRouter.sol b/contracts/multichain/MultichainGmRouter.sol index b1712cd7f..22ffe884c 100644 --- a/contracts/multichain/MultichainGmRouter.sol +++ b/contracts/multichain/MultichainGmRouter.sol @@ -36,7 +36,7 @@ contract MultichainGmRouter is MultichainRouter { RelayUtils.RelayParams calldata relayParams, address account, uint256 srcChainId, - RelayUtils.TransferRequest[] calldata transferRequests, + RelayUtils.TransferRequests calldata transferRequests, DepositUtils.CreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { _validateDesChainId(relayParams.desChainId); @@ -78,7 +78,7 @@ contract MultichainGmRouter is MultichainRouter { RelayUtils.RelayParams calldata relayParams, address account, uint256 srcChainId, - RelayUtils.TransferRequest[] calldata transferRequests, + RelayUtils.TransferRequests calldata transferRequests, WithdrawalUtils.CreateWithdrawalParams memory params // can't use calldata because need to modify params.addresses.receiver & params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { _validateDesChainId(relayParams.desChainId); @@ -119,7 +119,7 @@ contract MultichainGmRouter is MultichainRouter { RelayUtils.RelayParams calldata relayParams, address account, uint256 srcChainId, - RelayUtils.TransferRequest[] calldata transferRequests, + RelayUtils.TransferRequests calldata transferRequests, ShiftUtils.CreateShiftParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { _validateDesChainId(relayParams.desChainId); diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index c19a2b98d..0dedc371d 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -56,10 +56,22 @@ abstract contract MultichainRouter is GelatoRelayRouter { } } - function _processTransferRequests(address account, RelayUtils.TransferRequest[] calldata transferRequests, uint256 srcChainId) internal { - for (uint256 i = 0; i < transferRequests.length; i++) { - RelayUtils.TransferRequest calldata transferRequest = transferRequests[i]; - _sendTokens(account, transferRequest.token, transferRequest.receiver, transferRequest.amount, srcChainId); + function _processTransferRequests(address account, RelayUtils.TransferRequests calldata transferRequests, uint256 srcChainId) internal { + if ( + transferRequests.tokens.length != transferRequests.receivers.length || + transferRequests.tokens.length != transferRequests.amounts.length + ) { + revert Errors.InvalidTransferRequestsLength(); + } + + for (uint256 i = 0; i < transferRequests.tokens.length; i++) { + _sendTokens( + account, + transferRequests.tokens[i], + transferRequests.receivers[i], + transferRequests.amounts[i], + srcChainId + ); } } From b4bde601ccb91d695acbcb223e42ea96a5cab66f Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 20 Feb 2025 09:25:36 +0200 Subject: [PATCH 172/205] Fix ETH deposit and fee payment --- contracts/multichain/MultichainGmRouter.sol | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/contracts/multichain/MultichainGmRouter.sol b/contracts/multichain/MultichainGmRouter.sol index 22ffe884c..53b648621 100644 --- a/contracts/multichain/MultichainGmRouter.sol +++ b/contracts/multichain/MultichainGmRouter.sol @@ -44,15 +44,14 @@ contract MultichainGmRouter is MultichainRouter { bytes32 structHash = RelayUtils.getCreateDepositStructHash(relayParams, transferRequests, params); _validateCall(relayParams, account, structHash, srcChainId); - _processTransferRequests(account, transferRequests, srcChainId); - - return _createDeposit(relayParams, account, srcChainId, params); + return _createDeposit(relayParams, account, srcChainId, transferRequests, params); } function _createDeposit( RelayUtils.RelayParams calldata relayParams, address account, uint256 srcChainId, + RelayUtils.TransferRequests calldata transferRequests, DepositUtils.CreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee ) internal returns (bytes32) { Contracts memory contracts = Contracts({ @@ -71,6 +70,9 @@ contract MultichainGmRouter is MultichainRouter { srcChainId ); + // process transfer requests after relay fee is paid, otherwise all wnt amount will be recorder as relay fee + _processTransferRequests(account, transferRequests, srcChainId); + return depositHandler.createDeposit(account, srcChainId, params); } From 3c3bd5858dfcf6285990b12f3d8e81c1e88a8913 Mon Sep 17 00:00:00 2001 From: Solar Date: Fri, 21 Feb 2025 10:50:00 +0300 Subject: [PATCH 173/205] squash - refactor callbacks to eventdata --- contracts/deposit/DepositEventUtils.sol | 3 --- contracts/glv/glvDeposit/GlvDepositEventUtils.sol | 3 --- contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol | 3 --- contracts/order/OrderEventUtils.sol | 3 --- contracts/shift/ShiftEventUtils.sol | 3 --- contracts/withdrawal/WithdrawalEventUtils.sol | 3 --- 6 files changed, 18 deletions(-) diff --git a/contracts/deposit/DepositEventUtils.sol b/contracts/deposit/DepositEventUtils.sol index c524da50d..19d4ee4f8 100644 --- a/contracts/deposit/DepositEventUtils.sol +++ b/contracts/deposit/DepositEventUtils.sol @@ -31,9 +31,6 @@ library DepositEventUtils { eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); - eventData.bytes32Items.initArrayItems(1); - eventData.bytes32Items.setItem(0, "dataList", deposit.dataList()); - eventEmitter.emitEventLog2( "DepositCreated", key, diff --git a/contracts/glv/glvDeposit/GlvDepositEventUtils.sol b/contracts/glv/glvDeposit/GlvDepositEventUtils.sol index 52525df3e..eb97405d9 100644 --- a/contracts/glv/glvDeposit/GlvDepositEventUtils.sol +++ b/contracts/glv/glvDeposit/GlvDepositEventUtils.sol @@ -29,9 +29,6 @@ library GlvDepositEventUtils { eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); - eventData.bytes32Items.initArrayItems(1); - eventData.bytes32Items.setItem(0, "dataList", glvDeposit.dataList()); - eventEmitter.emitEventLog2( "GlvDepositCreated", key, diff --git a/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol b/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol index 166939452..1f15e22c6 100644 --- a/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol +++ b/contracts/glv/glvWithdrawal/GlvWithdrawalEventUtils.sol @@ -29,9 +29,6 @@ library GlvWithdrawalEventUtils { eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); - eventData.bytes32Items.initArrayItems(1); - eventData.bytes32Items.setItem(0, "dataList", glvWithdrawal.dataList()); - eventEmitter.emitEventLog2("GlvWithdrawalCreated", key, Cast.toBytes32(glvWithdrawal.account()), eventData); } diff --git a/contracts/order/OrderEventUtils.sol b/contracts/order/OrderEventUtils.sol index fdc70ed2d..6eb649dbb 100644 --- a/contracts/order/OrderEventUtils.sol +++ b/contracts/order/OrderEventUtils.sol @@ -28,9 +28,6 @@ library OrderEventUtils { eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); - eventData.bytes32Items.initArrayItems(1); - eventData.bytes32Items.setItem(0, "dataList", order.dataList()); - eventEmitter.emitEventLog2( "OrderCreated", key, diff --git a/contracts/shift/ShiftEventUtils.sol b/contracts/shift/ShiftEventUtils.sol index 6bd62b4b6..3e6ce453e 100644 --- a/contracts/shift/ShiftEventUtils.sol +++ b/contracts/shift/ShiftEventUtils.sol @@ -29,9 +29,6 @@ library ShiftEventUtils { eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); - eventData.bytes32Items.initArrayItems(1); - eventData.bytes32Items.setItem(0, "dataList", shift.dataList()); - eventEmitter.emitEventLog2( "ShiftCreated", key, diff --git a/contracts/withdrawal/WithdrawalEventUtils.sol b/contracts/withdrawal/WithdrawalEventUtils.sol index 8cc80d2fa..2d5cf3403 100644 --- a/contracts/withdrawal/WithdrawalEventUtils.sol +++ b/contracts/withdrawal/WithdrawalEventUtils.sol @@ -31,9 +31,6 @@ library WithdrawalEventUtils { eventData.bytes32Items.initItems(1); eventData.bytes32Items.setItem(0, "key", key); - eventData.bytes32Items.initArrayItems(1); - eventData.bytes32Items.setItem(0, "dataList", withdrawal.dataList()); - eventEmitter.emitEventLog2( "WithdrawalCreated", key, From d2aa689472d77d45f4823326ce02573337606eae Mon Sep 17 00:00:00 2001 From: Solar Date: Fri, 21 Feb 2025 09:54:29 +0300 Subject: [PATCH 174/205] add timelockconfigcontroller --- contracts/config/ConfigTimelockController.sol | 47 +++ contracts/config/ITimelockController.sol | 13 + contracts/config/TimelockConfig.sol | 321 ++++++++++++++++++ deploy/deployConfigTimelockController.ts | 21 ++ deploy/deployTimelock.ts | 2 +- 5 files changed, 403 insertions(+), 1 deletion(-) create mode 100644 contracts/config/ConfigTimelockController.sol create mode 100644 contracts/config/ITimelockController.sol create mode 100644 contracts/config/TimelockConfig.sol create mode 100644 deploy/deployConfigTimelockController.ts diff --git a/contracts/config/ConfigTimelockController.sol b/contracts/config/ConfigTimelockController.sol new file mode 100644 index 000000000..9342e711c --- /dev/null +++ b/contracts/config/ConfigTimelockController.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; + +import {DataStore} from "../data/DataStore.sol"; +import {Keys} from "../data/Keys.sol"; +import {Errors} from "../error/Errors.sol"; +import {EventEmitter} from "../event/EventEmitter.sol"; +import {EventUtils} from "../event/EventUtils.sol"; +import {Multicall3} from "../mock/Multicall3.sol"; +import {OracleStore} from "../oracle/OracleStore.sol"; +import {RoleStore} from "../role/RoleStore.sol"; +import {Precision} from "../utils/Precision.sol"; +import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol"; +import {ITimelockController} from "./ITimelockController.sol"; + +contract ConfigTimelockController is TimelockController, ITimelockController { + + constructor( + uint256 minDelay + ) TimelockController(minDelay, address[](0), address[](0), msg.sender) {} + + function signal(address target, bytes32 callData) external override onlyRole(PROPOSER_ROLE) { + schedule( + target, + 0, + callData, + 0, + 0, + getMinDelay() + ); + } + + function signalBatch( + address[] calldata targets, bytes32[] calldata payloads + ) external override onlyRole(PROPOSER_ROLE) { + uint256 values = new uint256[](targets.length); + scheduleBatch( + targets, + values, + payloads, + 0, + 0, + getMinDelay() + ); + } +} diff --git a/contracts/config/ITimelockController.sol b/contracts/config/ITimelockController.sol new file mode 100644 index 000000000..132552da6 --- /dev/null +++ b/contracts/config/ITimelockController.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; + +interface ITimelockController { + function signal( + address target, bytes32 payload + ) external; + + function signalBatch( + address[] calldata targets, bytes32[] calldata payloads + ) external; +} diff --git a/contracts/config/TimelockConfig.sol b/contracts/config/TimelockConfig.sol new file mode 100644 index 000000000..edff1df01 --- /dev/null +++ b/contracts/config/TimelockConfig.sol @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; + +import {DataStore} from "../data/DataStore.sol"; +import {Keys} from "../data/Keys.sol"; +import {Errors} from "../error/Errors.sol"; +import {EventEmitter} from "../event/EventEmitter.sol"; +import {EventUtils} from "../event/EventUtils.sol"; +import {OracleStore} from "../oracle/OracleStore.sol"; +import {RoleStore} from "../role/RoleStore.sol"; +import {Precision} from "../utils/Precision.sol"; +import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol"; +import "../utils/BasicMulticall.sol"; +import "./ITimelockController.sol"; +import "../role/RoleModule.sol"; + +contract TimelockConfig is RoleModule, BasicMulticall { + using EventUtils for EventUtils.AddressItems; + using EventUtils for EventUtils.UintItems; + using EventUtils for EventUtils.IntItems; + using EventUtils for EventUtils.BoolItems; + using EventUtils for EventUtils.Bytes32Items; + using EventUtils for EventUtils.BytesItems; + using EventUtils for EventUtils.StringItems; + + uint256 public constant MAX_TIMELOCK_DELAY = 5 days; + + EventEmitter public immutable eventEmitter; + ITimelockController public immutable timelockController; + + address public immutable dataStore; + address public immutable oracleStore; + address public immutable roleStore; + + constructor( + EventEmitter _eventEmitter, + DataStore _dataStore, + OracleStore _oracleStore, + RoleStore _roleStore, + ITimelockController _timelockController + ) { + eventEmitter = _eventEmitter; + dataStore = _dataStore; + eventEmitter = _eventEmitter; + oracleStore = _oracleStore; + roleStore = _roleStore; + timelockController = _timelockController; + } + + // @dev signal granting of a role + // @param account the account to grant the role + // @param roleKey the role to grant + function signalGrantRole(address account, bytes32 roleKey) external onlyTimelockAdmin { + bytes memory callData = abi.encodeWithSignature("grantRole(address,bytes32)", account, roleKey); + timelockController.signal(roleStore, callData); + + EventUtils.EventLogData memory eventData; + eventData.addressItems.initItems(1); + eventData.addressItems.setItem(0, "account", account); + eventData.bytes32Items.initItems(1); + eventData.bytes32Items.setItem(0, "roleKey", roleKey); + eventEmitter.emitEventLog( + "SignalGrantRole", + eventData + ); + } + + function signalSetOracleProviderEnabled(address provider, bool value) external onlyTimelockAdmin { + bytes memory callData = abi.encodeWithSignature("setBool(bytes32,bool)", + Keys.isOracleProviderEnabledKey(provider), value); + timelockController.signal(dataStore, callData); + + EventUtils.EventLogData memory eventData; + eventData.addressItems.initItems(1); + eventData.addressItems.setItem(0, "provider", provider); + eventData.boolItems.initItems(1); + eventData.boolItems.setItem(0, "value", value); + eventEmitter.emitEventLog( + "SignalSetOracleProviderEnabled", + eventData + ); + } + + function signalSetOracleProviderForToken(address token, address provider) external onlyTimelockAdmin { + bytes memory callData = abi.encodeWithSignature("setAddress(bytes32,address)", + Keys.oracleProviderForTokenKey(token), provider); + timelockController.signal(dataStore, callData); + + EventUtils.EventLogData memory eventData; + eventData.addressItems.initItems(2); + eventData.addressItems.setItem(0, "token", token); + eventData.addressItems.setItem(1, "provider", provider); + eventEmitter.emitEventLog( + "SignalSetOracleProviderForToken", + eventData + ); + } + + function signalSetAtomicOracleProvider(address provider, bool value) external onlyTimelockAdmin { + bytes memory callData = abi.encodeWithSignature("setBool(bytes32,bool)", + Keys.isAtomicOracleProviderKey(provider), value); + timelockController.signal(dataStore, callData); + + EventUtils.EventLogData memory eventData; + eventData.addressItems.initItems(1); + eventData.addressItems.setItem(0, "provider", provider); + eventData.boolItems.initItems(1); + eventData.boolItems.setItem(0, "value", value); + eventEmitter.emitEventLog( + "SignalSetAtomicOracleProvider", + eventData + ); + } + + function signalAddOracleSigner(address account) external onlyTimelockAdmin { + if (account == address(0)) { + revert Errors.InvalidOracleSigner(account); + } + + bytes memory callData = abi.encodeWithSignature("addSigner(address)", account); + timelockController.signal(oracleStore, callData); + + EventUtils.EventLogData memory eventData; + eventData.addressItems.initItems(1); + eventData.addressItems.setItem(0, "account", account); + eventEmitter.emitEventLog( + "SignalAddOracleSigner", + eventData + ); + } + + function signalRemoveOracleSigner(address account) external onlyTimelockAdmin { + if (account == address(0)) { + revert Errors.InvalidOracleSigner(account); + } + + bytes memory callData = abi.encodeWithSignature("removeSigner(address)", account); + timelockController.signal(oracleStore, callData); + + EventUtils.EventLogData memory eventData; + eventData.addressItems.initItems(1); + eventData.addressItems.setItem(0, "account", account); + eventEmitter.emitEventLog( + "SignalRemoveOracleSigner", + eventData + ); + } + + // @dev signal setting of the fee receiver + // @param account the new fee receiver + function signalSetFeeReceiver(address account) external onlyTimelockAdmin { + if (account == address(0)) { + revert Errors.InvalidFeeReceiver(account); + } + dataStore.setAddress(Keys.FEE_RECEIVER, account); + bytes memory callData = abi.encodeWithSignature("setAddress(bytes32,address)", + Keys.FEE_RECEIVER, account); + timelockController.signal(dataStore, callData); + + EventUtils.EventLogData memory eventData; + eventData.addressItems.initItems(1); + eventData.addressItems.setItem(0, "account", account); + eventEmitter.emitEventLog( + "SignalSetFeeReceiver", + eventData + ); + } + + // @dev signal revoking of a role + // @param account the account to revoke the role for + // @param roleKey the role to revoke + function signalRevokeRole(address account, bytes32 roleKey) external onlyTimelockAdmin { + + bytes memory callData = abi.encodeWithSignature("revokeRole(address,bytes32)", + account, roleKey); + timelockController.signal(roleStore, callData); + + EventUtils.EventLogData memory eventData; + eventData.addressItems.initItems(1); + eventData.addressItems.setItem(0, "account", account); + eventData.bytes32Items.initItems(1); + eventData.bytes32Items.setItem(0, "roleKey", roleKey); + eventEmitter.emitEventLog( + "SignalRevokeRole", + eventData + ); + } + + // @dev signal setting of a price feed + // @param token the token to set the price feed for + // @param priceFeed the address of the price feed + // @param priceFeedMultiplier the multiplier to apply to the price feed results + // @param stablePrice the stable price to set a range for the price feed results + function signalSetPriceFeed( + address token, + address priceFeed, + uint256 priceFeedMultiplier, + uint256 priceFeedHeartbeatDuration, + uint256 stablePrice + ) external onlyTimelockAdmin { + + address[] memory targets = new address[](4); + targets[0] = dataStore; + targets[1] = dataStore; + targets[2] = dataStore; + targets[3] = dataStore; + + bytes memory callData1 = abi.encodeWithSignature("setAddress(bytes32,address)", + Keys.priceFeedKey(token), priceFeed); + bytes memory callData2 = abi.encodeWithSignature("setUint(bytes32,uint)", + Keys.priceFeedMultiplierKey(token), priceFeedMultiplier); + bytes memory callData3 = abi.encodeWithSignature("setUint(bytes32,uint)", + Keys.priceFeedHeartbeatDurationKey(token), priceFeedHeartbeatDuration); + bytes memory callData4 = abi.encodeWithSignature("setUint(bytes32,uint)", + Keys.stablePriceKey(token), stablePrice); + + bytes32[] memory payloads = new bytes32[](4); + payloads[0] = abi.encodeWithSignature("setAddress(bytes32,address)", + Keys.priceFeedKey(token), priceFeed); + payloads[1] = abi.encodeWithSignature("setUint(bytes32,uint)", + Keys.priceFeedMultiplierKey(token), priceFeedMultiplier); + payloads[2] = abi.encodeWithSignature("setUint(bytes32,uint)", + Keys.priceFeedHeartbeatDurationKey(token), priceFeedHeartbeatDuration); + payloads[3] = abi.encodeWithSignature("setUint(bytes32,uint)", + Keys.stablePriceKey(token), stablePrice); + + timelockController.signalBatch(dataStore, payloads); + + EventUtils.EventLogData memory eventData; + eventData.addressItems.initItems(2); + eventData.addressItems.setItem(0, "token", token); + eventData.addressItems.setItem(1, "priceFeed", priceFeed); + eventData.uintItems.initItems(3); + eventData.uintItems.setItem(0, "priceFeedMultiplier", priceFeedMultiplier); + eventData.uintItems.setItem(1, "priceFeedHeartbeatDuration", priceFeedHeartbeatDuration); + eventData.uintItems.setItem(2, "stablePrice", stablePrice); + eventEmitter.emitEventLog( + "SignalSetPriceFeed", + eventData + ); + } + + // @dev signal setting of a data stream feed + // @param token the token to set the data stream feed for + // @param feedId the ID of the data stream feed + // @param dataStreamMultiplier the multiplier to apply to the data stream feed results + // @param dataStreamSpreadReductionFactor the factor to apply to the data stream price spread + function signalSetDataStream( + address token, + bytes32 feedId, + uint256 dataStreamMultiplier, + uint256 dataStreamSpreadReductionFactor + ) external onlyTimelockAdmin { + if (dataStreamSpreadReductionFactor > Precision.FLOAT_PRECISION) { + revert Errors.ConfigValueExceedsAllowedRange(Keys.DATA_STREAM_SPREAD_REDUCTION_FACTOR, dataStreamSpreadReductionFactor); + } + + address[] memory targets = new address[](3); + targets[0] = dataStore; + targets[1] = dataStore; + targets[2] = dataStore; + + bytes memory callData1 = abi.encodeWithSignature("setBytes32(bytes32,bytes32)", + Keys.dataStreamIdKey(token), feedId); + bytes memory callData2 = abi.encodeWithSignature("setUint(bytes32,uint)", + Keys.dataStreamMultiplierKey(token), dataStreamMultiplier); + bytes memory callData3 = abi.encodeWithSignature("setUint(bytes32,uint)", + Keys.dataStreamSpreadReductionFactorKey(token), dataStreamSpreadReductionFactor); + + bytes32[] memory payloads = new bytes32[](3); + payloads[0] = abi.encodeWithSignature("setBytes32(bytes32,bytes32)", + Keys.dataStreamIdKey(token), feedId); + payloads[1] = abi.encodeWithSignature("setUint(bytes32,uint)", + Keys.dataStreamMultiplierKey(token), dataStreamMultiplier); + payloads[2] = abi.encodeWithSignature("setUint(bytes32,uint)", + Keys.dataStreamSpreadReductionFactorKey(token), dataStreamSpreadReductionFactor); + + timelockController.signalBatch(dataStore, payloads); + + EventUtils.EventLogData memory eventData; + eventData.addressItems.initItems(1); + eventData.addressItems.setItem(0, "token", token); + eventData.bytes32Items.initItems(1); + eventData.bytes32Items.setItem(0, "feedId", feedId); + eventData.uintItems.initItems(2); + eventData.uintItems.setItem(0, "dataStreamMultiplier", dataStreamMultiplier); + eventData.uintItems.setItem(1, "dataStreamSpreadReductionFactor", dataStreamSpreadReductionFactor); + eventEmitter.emitEventLog( + "SignalSetDataStream", + eventData + ); + } + + // @dev increase the timelock delay + // @param the new timelock delay + function increaseTimelockDelay(uint256 _timelockDelay) external onlyTimelockAdmin { + if (_timelockDelay <= _minDelay) { + revert Errors.InvalidTimelockDelay(_timelockDelay); + } + + _validateTimelockDelay(_timelockDelay); + + bytes memory callData = abi.encodeWithSignature("updateDelay(uint256)", _timelockDelay); + timelockController.signal(address(timelockController), callData); + + EventUtils.EventLogData memory eventData; + eventData.uintItems.initItems(1); + eventData.uintItems.setItem(0, "timelockDelay", _timelockDelay); + eventEmitter.emitEventLog( + "IncreaseTimelockDelay", + eventData + ); + } + + function _validateTimelockDelay(uint256 delay) internal view { + if (delay > MAX_TIMELOCK_DELAY) { + revert Errors.MaxTimelockDelayExceeded(delay); + } + } +} diff --git a/deploy/deployConfigTimelockController.ts b/deploy/deployConfigTimelockController.ts new file mode 100644 index 000000000..39c7d19f2 --- /dev/null +++ b/deploy/deployConfigTimelockController.ts @@ -0,0 +1,21 @@ +import { grantRoleIfNotGranted } from "../utils/role"; +import { createDeployFunction } from "../utils/deploy"; + +const constructorContracts = ["EventEmitter", "DataStore", "OracleStore", "RoleStore"]; +const timelockDelay = 24 * 60 * 60; + +const func = createDeployFunction({ + contractName: "ConfigTimelockController", + dependencyNames: constructorContracts, + getDeployArgs: async ({ dependencyContracts }) => { + return constructorContracts + .map((dependencyName) => dependencyContracts[dependencyName].address) + .concat(timelockDelay); + }, + afterDeploy: async ({ deployedContract }) => { + await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); + await grantRoleIfNotGranted(deployedContract.address, "ROLE_ADMIN"); + }, +}); + +export default func; diff --git a/deploy/deployTimelock.ts b/deploy/deployTimelock.ts index cba5e756d..c72b9a669 100644 --- a/deploy/deployTimelock.ts +++ b/deploy/deployTimelock.ts @@ -1,7 +1,7 @@ import { grantRoleIfNotGranted } from "../utils/role"; import { createDeployFunction } from "../utils/deploy"; -const constructorContracts = ["RoleStore", "DataStore", "EventEmitter", "OracleStore"]; +const constructorContracts = ["EventEmitter", "DataStore", "OracleStore", "RoleStore"]; const timelockDelay = 24 * 60 * 60; const func = createDeployFunction({ From 1c9031c2296fd0a93f317c325230ed2628783ef1 Mon Sep 17 00:00:00 2001 From: Solar Date: Mon, 24 Feb 2025 06:22:05 +0300 Subject: [PATCH 175/205] timelock controller --- contracts/config/ConfigTimelockController.sol | 18 +++--- contracts/config/ITimelockController.sol | 14 ++++- contracts/config/TimelockConfig.sol | 57 +++++++++---------- deploy/deployConfigTimelockController.ts | 7 +-- deploy/deployTimelockConfig.ts | 18 ++++++ test/config/Timelock.ts | 9 +-- utils/fixture.ts | 4 ++ 7 files changed, 78 insertions(+), 49 deletions(-) create mode 100644 deploy/deployTimelockConfig.ts diff --git a/contracts/config/ConfigTimelockController.sol b/contracts/config/ConfigTimelockController.sol index 9342e711c..d72da14df 100644 --- a/contracts/config/ConfigTimelockController.sol +++ b/contracts/config/ConfigTimelockController.sol @@ -14,17 +14,19 @@ import {Precision} from "../utils/Precision.sol"; import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol"; import {ITimelockController} from "./ITimelockController.sol"; -contract ConfigTimelockController is TimelockController, ITimelockController { +contract ConfigTimelockController is TimelockController { constructor( - uint256 minDelay - ) TimelockController(minDelay, address[](0), address[](0), msg.sender) {} + uint256 minDelay, + address[] memory proposers, + address[] memory executors + ) TimelockController(minDelay, proposers, executors, msg.sender) {} - function signal(address target, bytes32 callData) external override onlyRole(PROPOSER_ROLE) { + function signal(address target, bytes calldata payload) external override onlyRole(PROPOSER_ROLE) { schedule( target, 0, - callData, + payload, 0, 0, getMinDelay() @@ -32,9 +34,10 @@ contract ConfigTimelockController is TimelockController, ITimelockController { } function signalBatch( - address[] calldata targets, bytes32[] calldata payloads + address[] calldata targets, + bytes[] calldata payloads, + uint256[] calldata values ) external override onlyRole(PROPOSER_ROLE) { - uint256 values = new uint256[](targets.length); scheduleBatch( targets, values, @@ -44,4 +47,5 @@ contract ConfigTimelockController is TimelockController, ITimelockController { getMinDelay() ); } + } diff --git a/contracts/config/ITimelockController.sol b/contracts/config/ITimelockController.sol index 132552da6..80ae2a473 100644 --- a/contracts/config/ITimelockController.sol +++ b/contracts/config/ITimelockController.sol @@ -4,10 +4,20 @@ pragma solidity ^0.8.0; interface ITimelockController { function signal( - address target, bytes32 payload + address target, bytes calldata payload ) external; function signalBatch( - address[] calldata targets, bytes32[] calldata payloads + address[] calldata targets, bytes[] calldata payloads, uint256[] calldata values ) external; + + function execute( + address target, + uint256 value, + bytes calldata payload, + bytes32 predecessor, + bytes32 salt + ) external payable; + + function getMinDelay() external view returns (uint256); } diff --git a/contracts/config/TimelockConfig.sol b/contracts/config/TimelockConfig.sol index edff1df01..b27d4ac92 100644 --- a/contracts/config/TimelockConfig.sol +++ b/contracts/config/TimelockConfig.sol @@ -31,20 +31,17 @@ contract TimelockConfig is RoleModule, BasicMulticall { address public immutable dataStore; address public immutable oracleStore; - address public immutable roleStore; constructor( EventEmitter _eventEmitter, - DataStore _dataStore, - OracleStore _oracleStore, + address _dataStore, + address _oracleStore, RoleStore _roleStore, ITimelockController _timelockController - ) { + ) RoleModule(_roleStore) { eventEmitter = _eventEmitter; dataStore = _dataStore; - eventEmitter = _eventEmitter; oracleStore = _oracleStore; - roleStore = _roleStore; timelockController = _timelockController; } @@ -53,7 +50,7 @@ contract TimelockConfig is RoleModule, BasicMulticall { // @param roleKey the role to grant function signalGrantRole(address account, bytes32 roleKey) external onlyTimelockAdmin { bytes memory callData = abi.encodeWithSignature("grantRole(address,bytes32)", account, roleKey); - timelockController.signal(roleStore, callData); + timelockController.signal(address(roleStore), callData); EventUtils.EventLogData memory eventData; eventData.addressItems.initItems(1); @@ -66,6 +63,14 @@ contract TimelockConfig is RoleModule, BasicMulticall { ); } + // @dev signal granting of a role + // @param account the account to grant the role + // @param roleKey the role to grant + function grantRole(address account, bytes32 roleKey) external onlyTimelockAdmin { + bytes memory callData = abi.encodeWithSignature("grantRole(address,bytes32)", account, roleKey); + timelockController.execute(address(roleStore), 0, callData, 0, 0); + } + function signalSetOracleProviderEnabled(address provider, bool value) external onlyTimelockAdmin { bytes memory callData = abi.encodeWithSignature("setBool(bytes32,bool)", Keys.isOracleProviderEnabledKey(provider), value); @@ -153,7 +158,7 @@ contract TimelockConfig is RoleModule, BasicMulticall { if (account == address(0)) { revert Errors.InvalidFeeReceiver(account); } - dataStore.setAddress(Keys.FEE_RECEIVER, account); + bytes memory callData = abi.encodeWithSignature("setAddress(bytes32,address)", Keys.FEE_RECEIVER, account); timelockController.signal(dataStore, callData); @@ -174,7 +179,7 @@ contract TimelockConfig is RoleModule, BasicMulticall { bytes memory callData = abi.encodeWithSignature("revokeRole(address,bytes32)", account, roleKey); - timelockController.signal(roleStore, callData); + timelockController.signal(address(roleStore), callData); EventUtils.EventLogData memory eventData; eventData.addressItems.initItems(1); @@ -206,16 +211,7 @@ contract TimelockConfig is RoleModule, BasicMulticall { targets[2] = dataStore; targets[3] = dataStore; - bytes memory callData1 = abi.encodeWithSignature("setAddress(bytes32,address)", - Keys.priceFeedKey(token), priceFeed); - bytes memory callData2 = abi.encodeWithSignature("setUint(bytes32,uint)", - Keys.priceFeedMultiplierKey(token), priceFeedMultiplier); - bytes memory callData3 = abi.encodeWithSignature("setUint(bytes32,uint)", - Keys.priceFeedHeartbeatDurationKey(token), priceFeedHeartbeatDuration); - bytes memory callData4 = abi.encodeWithSignature("setUint(bytes32,uint)", - Keys.stablePriceKey(token), stablePrice); - - bytes32[] memory payloads = new bytes32[](4); + bytes[] memory payloads = new bytes[](4); payloads[0] = abi.encodeWithSignature("setAddress(bytes32,address)", Keys.priceFeedKey(token), priceFeed); payloads[1] = abi.encodeWithSignature("setUint(bytes32,uint)", @@ -225,7 +221,8 @@ contract TimelockConfig is RoleModule, BasicMulticall { payloads[3] = abi.encodeWithSignature("setUint(bytes32,uint)", Keys.stablePriceKey(token), stablePrice); - timelockController.signalBatch(dataStore, payloads); + uint256[] memory values = new uint256[](4); + timelockController.signalBatch(targets, payloads, values); EventUtils.EventLogData memory eventData; eventData.addressItems.initItems(2); @@ -261,14 +258,7 @@ contract TimelockConfig is RoleModule, BasicMulticall { targets[1] = dataStore; targets[2] = dataStore; - bytes memory callData1 = abi.encodeWithSignature("setBytes32(bytes32,bytes32)", - Keys.dataStreamIdKey(token), feedId); - bytes memory callData2 = abi.encodeWithSignature("setUint(bytes32,uint)", - Keys.dataStreamMultiplierKey(token), dataStreamMultiplier); - bytes memory callData3 = abi.encodeWithSignature("setUint(bytes32,uint)", - Keys.dataStreamSpreadReductionFactorKey(token), dataStreamSpreadReductionFactor); - - bytes32[] memory payloads = new bytes32[](3); + bytes[] memory payloads = new bytes[](3); payloads[0] = abi.encodeWithSignature("setBytes32(bytes32,bytes32)", Keys.dataStreamIdKey(token), feedId); payloads[1] = abi.encodeWithSignature("setUint(bytes32,uint)", @@ -276,7 +266,8 @@ contract TimelockConfig is RoleModule, BasicMulticall { payloads[2] = abi.encodeWithSignature("setUint(bytes32,uint)", Keys.dataStreamSpreadReductionFactorKey(token), dataStreamSpreadReductionFactor); - timelockController.signalBatch(dataStore, payloads); + uint256[] memory values = new uint256[](3); + timelockController.signalBatch(targets, payloads, values); EventUtils.EventLogData memory eventData; eventData.addressItems.initItems(1); @@ -295,7 +286,7 @@ contract TimelockConfig is RoleModule, BasicMulticall { // @dev increase the timelock delay // @param the new timelock delay function increaseTimelockDelay(uint256 _timelockDelay) external onlyTimelockAdmin { - if (_timelockDelay <= _minDelay) { + if (_timelockDelay <= timelockController.getMinimumDelay()) { revert Errors.InvalidTimelockDelay(_timelockDelay); } @@ -313,7 +304,11 @@ contract TimelockConfig is RoleModule, BasicMulticall { ); } - function _validateTimelockDelay(uint256 delay) internal view { + function execute(address target, bytes calldata payload) external onlyTimelockAdmin { + timelockController.execute(target, 0, payload, 0, 0); + } + + function _validateTimelockDelay(uint256 delay) internal pure { if (delay > MAX_TIMELOCK_DELAY) { revert Errors.MaxTimelockDelayExceeded(delay); } diff --git a/deploy/deployConfigTimelockController.ts b/deploy/deployConfigTimelockController.ts index 39c7d19f2..46b3b03f5 100644 --- a/deploy/deployConfigTimelockController.ts +++ b/deploy/deployConfigTimelockController.ts @@ -1,16 +1,13 @@ import { grantRoleIfNotGranted } from "../utils/role"; import { createDeployFunction } from "../utils/deploy"; -const constructorContracts = ["EventEmitter", "DataStore", "OracleStore", "RoleStore"]; const timelockDelay = 24 * 60 * 60; const func = createDeployFunction({ contractName: "ConfigTimelockController", - dependencyNames: constructorContracts, + dependencyNames: [], getDeployArgs: async ({ dependencyContracts }) => { - return constructorContracts - .map((dependencyName) => dependencyContracts[dependencyName].address) - .concat(timelockDelay); + return [timelockDelay, [], []]; }, afterDeploy: async ({ deployedContract }) => { await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); diff --git a/deploy/deployTimelockConfig.ts b/deploy/deployTimelockConfig.ts new file mode 100644 index 000000000..664d193c2 --- /dev/null +++ b/deploy/deployTimelockConfig.ts @@ -0,0 +1,18 @@ +import { grantRoleIfNotGranted } from "../utils/role"; +import { createDeployFunction } from "../utils/deploy"; + +const constructorContracts = ["EventEmitter", "DataStore", "OracleStore", "RoleStore", "ConfigTimelockController"]; + +const func = createDeployFunction({ + contractName: "TimelockConfig", + dependencyNames: constructorContracts, + getDeployArgs: async ({ dependencyContracts }) => { + return constructorContracts.map((dependencyName) => dependencyContracts[dependencyName].address); + }, + afterDeploy: async ({ deployedContract }) => { + await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); + await grantRoleIfNotGranted(deployedContract.address, "ROLE_ADMIN"); + }, +}); + +export default func; diff --git a/test/config/Timelock.ts b/test/config/Timelock.ts index 1ecb00895..368800d3e 100644 --- a/test/config/Timelock.ts +++ b/test/config/Timelock.ts @@ -11,11 +11,12 @@ import * as keys from "../../utils/keys"; describe("Timelock", () => { let fixture; let timelockAdmin, timelockMultisig, user2, user3, signer0, signer9; - let timelock, dataStore, roleStore, oracleStore, wnt; + let timelock, timelockConfig, configTimelockController, dataStore, roleStore, oracleStore, wnt; beforeEach(async () => { fixture = await deployFixture(); - ({ timelock, dataStore, roleStore, oracleStore, wnt } = fixture.contracts); + ({ timelock, timelockConfig, configTimelockController, dataStore, roleStore, oracleStore, wnt } = + fixture.contracts); ({ user2, user3, signer0, signer9 } = fixture.accounts); timelockAdmin = fixture.accounts.user0; @@ -28,9 +29,9 @@ describe("Timelock", () => { it("multisig revokeRole", async () => { const orderKeeperRole = hashString("ORDER_KEEPER"); - await timelock.connect(timelockAdmin).signalGrantRole(user3.address, orderKeeperRole); + await timelockConfig.connect(timelockAdmin).signalGrantRole(user3.address, orderKeeperRole); await time.increase(1 * 24 * 60 * 60 + 10); - await timelock.connect(timelockAdmin).grantRoleAfterSignal(user3.address, orderKeeperRole); + await timelockConfig.connect(timelockAdmin).grantRoleAfterSignal(user3.address, orderKeeperRole); expect(await roleStore.hasRole(user3.address, orderKeeperRole)).eq(true); diff --git a/utils/fixture.ts b/utils/fixture.ts index 2b954b63c..f09da3b2d 100644 --- a/utils/fixture.ts +++ b/utils/fixture.ts @@ -63,6 +63,8 @@ export async function deployFixture() { const configSyncer = await hre.ethers.getContract("ConfigSyncer"); const mockRiskOracle = await hre.ethers.getContract("MockRiskOracle"); const timelock = await hre.ethers.getContract("Timelock"); + const timelockConfig = await hre.ethers.getContract("TimelockConfig"); + const configTimelockController = await hre.ethers.getContract("ConfigTimelockController"); const reader = await hre.ethers.getContract("Reader"); const glvReader = await hre.ethers.getContract("GlvReader"); const roleStore = await hre.ethers.getContract("RoleStore"); @@ -259,6 +261,8 @@ export async function deployFixture() { configSyncer, mockRiskOracle, timelock, + timelockConfig, + configTimelockController, reader, roleStore, dataStore, From 305759f768782c82a37b93d51e4e99ecdd7084cd Mon Sep 17 00:00:00 2001 From: Solar Date: Tue, 11 Feb 2025 07:52:51 +0300 Subject: [PATCH 176/205] pr fix --- scripts/validateContractDeployment.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/validateContractDeployment.ts b/scripts/validateContractDeployment.ts index ddad9398f..d86839f46 100644 --- a/scripts/validateContractDeployment.ts +++ b/scripts/validateContractDeployment.ts @@ -28,7 +28,7 @@ async function main() { } console.log(`Checking deployment against commit: ${AUDITED_COMMIT}`); - execSync(`git checkout ${AUDITED_COMMIT}`, { stdio: "inherit" }); + // execSync(`git checkout ${AUDITED_COMMIT}`, { stdio: "inherit" }); try { const contractInfos = await extractRolesFromTx(tx); @@ -48,7 +48,7 @@ async function main() { } // Restore git to previous state - execSync(`git checkout -`, { stdio: "inherit" }); + // execSync(`git checkout -`, { stdio: "inherit" }); console.log("✅ Verification completed."); } From ae796818ee34c6e0b7bcc5cf3fd50cedd451710e Mon Sep 17 00:00:00 2001 From: Solar Date: Tue, 11 Feb 2025 07:53:11 +0300 Subject: [PATCH 177/205] pr fix --- scripts/validateContractDeployment.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/validateContractDeployment.ts b/scripts/validateContractDeployment.ts index d86839f46..ddad9398f 100644 --- a/scripts/validateContractDeployment.ts +++ b/scripts/validateContractDeployment.ts @@ -28,7 +28,7 @@ async function main() { } console.log(`Checking deployment against commit: ${AUDITED_COMMIT}`); - // execSync(`git checkout ${AUDITED_COMMIT}`, { stdio: "inherit" }); + execSync(`git checkout ${AUDITED_COMMIT}`, { stdio: "inherit" }); try { const contractInfos = await extractRolesFromTx(tx); @@ -48,7 +48,7 @@ async function main() { } // Restore git to previous state - // execSync(`git checkout -`, { stdio: "inherit" }); + execSync(`git checkout -`, { stdio: "inherit" }); console.log("✅ Verification completed."); } From 1d0362f8d6ed7401ca8996028f6103c2bd1617f8 Mon Sep 17 00:00:00 2001 From: An Date: Thu, 13 Feb 2025 13:53:48 +0400 Subject: [PATCH 178/205] set default maxPnlFactorForTraders for VIRTUAL and PENGU markets --- config/markets.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/config/markets.ts b/config/markets.ts index 2ec781c39..a7fce0049 100644 --- a/config/markets.ts +++ b/config/markets.ts @@ -2588,7 +2588,8 @@ const config: { reserveFactor: percentageToFloat("75%"), // default is 95% openInterestReserveFactor: percentageToFloat("70%"), // default is 90% - maxPnlFactorForTraders: percentageToFloat("90%"), // default is 60% + // waiting for confirmation on this, use default for now + // maxPnlFactorForTraders: percentageToFloat("90%"), // default is 60% maxOpenInterest: decimalToFloat(1_000_000), maxPoolUsdForDeposit: decimalToFloat(1_500_000), // 1.5x the max open interest @@ -2617,7 +2618,8 @@ const config: { reserveFactor: percentageToFloat("75%"), // default is 95% openInterestReserveFactor: percentageToFloat("70%"), // default is 90% - maxPnlFactorForTraders: percentageToFloat("90%"), // default is 60% + // waiting for confirmation on this, use default for now + // maxPnlFactorForTraders: percentageToFloat("90%"), // default is 60% maxOpenInterest: decimalToFloat(1_000_000), maxPoolUsdForDeposit: decimalToFloat(1_500_000), // 1.5x the max open interest From 0f21edac2f811ec57ff833400bcb21564a28e08c Mon Sep 17 00:00:00 2001 From: An Date: Thu, 13 Feb 2025 17:32:18 +0400 Subject: [PATCH 179/205] upd maxPnlFactorForTraders for new markets --- config/markets.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/config/markets.ts b/config/markets.ts index a7fce0049..2ec781c39 100644 --- a/config/markets.ts +++ b/config/markets.ts @@ -2588,8 +2588,7 @@ const config: { reserveFactor: percentageToFloat("75%"), // default is 95% openInterestReserveFactor: percentageToFloat("70%"), // default is 90% - // waiting for confirmation on this, use default for now - // maxPnlFactorForTraders: percentageToFloat("90%"), // default is 60% + maxPnlFactorForTraders: percentageToFloat("90%"), // default is 60% maxOpenInterest: decimalToFloat(1_000_000), maxPoolUsdForDeposit: decimalToFloat(1_500_000), // 1.5x the max open interest @@ -2618,8 +2617,7 @@ const config: { reserveFactor: percentageToFloat("75%"), // default is 95% openInterestReserveFactor: percentageToFloat("70%"), // default is 90% - // waiting for confirmation on this, use default for now - // maxPnlFactorForTraders: percentageToFloat("90%"), // default is 60% + maxPnlFactorForTraders: percentageToFloat("90%"), // default is 60% maxOpenInterest: decimalToFloat(1_000_000), maxPoolUsdForDeposit: decimalToFloat(1_500_000), // 1.5x the max open interest From f3215098aa065d2604e19ebf95f4816ce02dc2c5 Mon Sep 17 00:00:00 2001 From: Solar Date: Tue, 25 Feb 2025 06:09:59 +0300 Subject: [PATCH 180/205] rebase --- contracts/config/Config.sol | 18 --------- contracts/data/Keys.sol | 3 -- contracts/fee/FeeUtils.sol | 30 +++++++++++++++ contracts/multichain/MultichainRouter.sol | 19 ---------- contracts/referral/ReferralUtils.sol | 37 ++++++++++++++++++ contracts/router/ExchangeRouter.sol | 46 ++--------------------- 6 files changed, 71 insertions(+), 82 deletions(-) diff --git a/contracts/config/Config.sol b/contracts/config/Config.sol index 3e15176d2..1f5952764 100644 --- a/contracts/config/Config.sol +++ b/contracts/config/Config.sol @@ -175,24 +175,6 @@ contract Config is ReentrancyGuard, RoleModule, BasicMulticall { ); } - function setClaimableCollateralReductionFactorForAccount( - address market, - address token, - uint256 timeKey, - address account, - uint256 factor - ) external onlyConfigKeeper nonReentrant { - ConfigUtils.setClaimableCollateralReductionFactorForAccount( - dataStore, - eventEmitter, - market, - token, - timeKey, - account, - factor - ); - } - function setPositionImpactDistributionRate( address market, uint256 minPositionImpactPoolAmount, diff --git a/contracts/data/Keys.sol b/contracts/data/Keys.sol index b9f9b8739..c1028999a 100644 --- a/contracts/data/Keys.sol +++ b/contracts/data/Keys.sol @@ -485,9 +485,6 @@ library Keys { // @dev key for the maximum length for data list array of bytes32 bytes32 public constant MAX_DATA_LENGTH = keccak256(abi.encode("MAX_DATA_LENGTH")); - // @dev key for the maximum length for data list array of bytes32 - bytes32 public constant MAX_DATA_LENGTH = keccak256(abi.encode("MAX_DATA_LENGTH")); - // @dev constant for user initiated cancel reason string public constant USER_INITIATED_CANCEL = "USER_INITIATED_CANCEL"; diff --git a/contracts/fee/FeeUtils.sol b/contracts/fee/FeeUtils.sol index a815050ad..28f597517 100644 --- a/contracts/fee/FeeUtils.sol +++ b/contracts/fee/FeeUtils.sol @@ -165,6 +165,36 @@ library FeeUtils { return feeAmount; } + function batchClaimUiFees( + DataStore dataStore, + EventEmitter eventEmitter, + address[] memory markets, + address[] memory tokens, + address receiver, + address uiFeeReceiver + ) external returns (uint256[] memory) { + if (markets.length != tokens.length) { + revert Errors.InvalidClaimUiFeesInput(markets.length, tokens.length); + } + + FeatureUtils.validateFeature(dataStore, Keys.claimUiFeesFeatureDisabledKey(address(this))); + + uint256[] memory claimedAmounts = new uint256[](markets.length); + + for (uint256 i; i < markets.length; i++) { + claimedAmounts[i] = claimUiFees( + dataStore, + eventEmitter, + uiFeeReceiver, + markets[i], + tokens[i], + receiver + ); + } + + return claimedAmounts; + } + function claimUiFees( DataStore dataStore, EventEmitter eventEmitter, diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 0dedc371d..762f48d92 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -56,25 +56,6 @@ abstract contract MultichainRouter is GelatoRelayRouter { } } - function _processTransferRequests(address account, RelayUtils.TransferRequests calldata transferRequests, uint256 srcChainId) internal { - if ( - transferRequests.tokens.length != transferRequests.receivers.length || - transferRequests.tokens.length != transferRequests.amounts.length - ) { - revert Errors.InvalidTransferRequestsLength(); - } - - for (uint256 i = 0; i < transferRequests.tokens.length; i++) { - _sendTokens( - account, - transferRequests.tokens[i], - transferRequests.receivers[i], - transferRequests.amounts[i], - srcChainId - ); - } - } - function _sendTokens(address account, address token, address receiver, uint256 amount, uint256 srcChainId) internal override { AccountUtils.validateReceiver(receiver); if (srcChainId == 0) { diff --git a/contracts/referral/ReferralUtils.sol b/contracts/referral/ReferralUtils.sol index 2f5c435b8..d6932c4d9 100644 --- a/contracts/referral/ReferralUtils.sol +++ b/contracts/referral/ReferralUtils.sol @@ -106,6 +106,43 @@ library ReferralUtils { ); } + // @dev Claims affiliate rewards for the given markets and tokens and sends the rewards to the specified receiver. + // @param dataStore The data store instance to use. + // @param eventEmitter The event emitter instance to use. + // @param markets An array of market addresses + // @param tokens An array of token addresses, corresponding to the given markets + // @param receiver The address to which the claimed rewards should be sent + // @param account The affiliate's address. + function batchClaimAffiliateRewards( + DataStore dataStore, + EventEmitter eventEmitter, + address[] memory markets, + address[] memory tokens, + address receiver, + address account + ) external returns (uint256[] memory) { + if (markets.length != tokens.length) { + revert Errors.InvalidClaimAffiliateRewardsInput(markets.length, tokens.length); + } + + FeatureUtils.validateFeature(dataStore, Keys.claimAffiliateRewardsFeatureDisabledKey(address(this))); + + uint256[] memory claimedAmounts = new uint256[](markets.length); + + for (uint256 i; i < markets.length; i++) { + claimedAmounts[i] = claimAffiliateReward( + dataStore, + eventEmitter, + markets[i], + tokens[i], + account, + receiver + ); + } + + return claimedAmounts; + } + // @dev Claims the affiliate's reward balance and transfers it to the specified receiver. // @param dataStore The data store instance to use. // @param eventEmitter The event emitter instance to use. diff --git a/contracts/router/ExchangeRouter.sol b/contracts/router/ExchangeRouter.sol index 474a00160..f2257d1e7 100644 --- a/contracts/router/ExchangeRouter.sol +++ b/contracts/router/ExchangeRouter.sol @@ -398,28 +398,10 @@ contract ExchangeRouter is IExchangeRouter, BaseRouter { address[] memory tokens, address receiver ) external payable nonReentrant returns (uint256[] memory) { - if (markets.length != tokens.length) { - revert Errors.InvalidClaimAffiliateRewardsInput(markets.length, tokens.length); - } - - FeatureUtils.validateFeature(dataStore, Keys.claimAffiliateRewardsFeatureDisabledKey(address(this))); - address account = msg.sender; - - uint256[] memory claimedAmounts = new uint256[](markets.length); - - for (uint256 i; i < markets.length; i++) { - claimedAmounts[i] = ReferralUtils.claimAffiliateReward( - dataStore, - eventEmitter, - markets[i], - tokens[i], - account, - receiver - ); - } - - return claimedAmounts; + return ReferralUtils.batchClaimAffiliateRewards( + dataStore, eventEmitter, markets, tokens, receiver, account + ); } function setUiFeeFactor(uint256 uiFeeFactor) external payable nonReentrant { @@ -432,27 +414,7 @@ contract ExchangeRouter is IExchangeRouter, BaseRouter { address[] memory tokens, address receiver ) external payable nonReentrant returns (uint256[] memory) { - if (markets.length != tokens.length) { - revert Errors.InvalidClaimUiFeesInput(markets.length, tokens.length); - } - - FeatureUtils.validateFeature(dataStore, Keys.claimUiFeesFeatureDisabledKey(address(this))); - address uiFeeReceiver = msg.sender; - - uint256[] memory claimedAmounts = new uint256[](markets.length); - - for (uint256 i; i < markets.length; i++) { - claimedAmounts[i] = FeeUtils.claimUiFees( - dataStore, - eventEmitter, - uiFeeReceiver, - markets[i], - tokens[i], - receiver - ); - } - - return claimedAmounts; + return FeeUtils.batchClaimUiFees(dataStore, eventEmitter, markets, tokens, receiver, uiFeeReceiver); } } From 6c510fe5310dd30e9839ed39d2506f81c19739a1 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 25 Feb 2025 13:48:26 +0200 Subject: [PATCH 181/205] Test multichain glv withdrawal --- contracts/router/relay/RelayUtils.sol | 38 ++----- test/multichain/MultichainGmRouter.ts | 157 +++++++++++++++++++++++--- utils/relay/multichain.ts | 86 ++++++++++++++ 3 files changed, 235 insertions(+), 46 deletions(-) diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index 543642ced..4959b66c1 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -161,21 +161,11 @@ library RelayUtils { bytes32 public constant CREATE_GLV_WITHDRAWAL_TYPEHASH = keccak256( - bytes( - "CreateGlvWithdrawal(address[] transferTokens,address[] transferReceivers,uint256[] transferAmounts,CreateGlvWithdrawalParams params,bytes32 relayParams)CreateGlvWithdrawalParams(CreateGlvWithdrawalParamsAddresses addresses,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)CreateGlvWithdrawalParamsAddresses(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath)" - ) - ); - bytes32 public constant CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH = - keccak256( - bytes( - "CreateGlvWithdrawalParams(CreateGlvWithdrawalParamsAddresses addresses,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList)" - ) + "CreateGlvWithdrawal(address[] transferTokens,address[] transferReceivers,uint256[] transferAmounts,CreateGlvWithdrawalAddresses addresses,uint256 minLongTokenAmount,uint256 minShortTokenAmount,bool shouldUnwrapNativeToken,uint256 executionFee,uint256 callbackGasLimit,bytes32[] dataList,bytes32 relayParams)CreateGlvWithdrawalAddresses(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath)" ); - bytes32 public constant CREATE_GLV_WITHDRAWAL_PARAMS_ADDRESSES_TYPEHASH = + bytes32 public constant CREATE_GLV_WITHDRAWAL_ADDRESSES_TYPEHASH = keccak256( - bytes( - "CreateGlvWithdrawalParamsAddresses(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath)" - ) + "CreateGlvWithdrawalAddresses(address receiver,address callbackContract,address uiFeeReceiver,address market,address glv,address[] longTokenSwapPath,address[] shortTokenSwapPath)" ); @@ -489,37 +479,25 @@ library RelayUtils { keccak256(abi.encodePacked(transferRequests.tokens)), keccak256(abi.encodePacked(transferRequests.receivers)), keccak256(abi.encodePacked(transferRequests.amounts)), - _getCreateGlvWithdrawalParamsStructHash(params), - _getRelayParamsHash(relayParams) - ) - ); - } - - function _getCreateGlvWithdrawalParamsStructHash( - GlvWithdrawalUtils.CreateGlvWithdrawalParams memory params - ) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - CREATE_GLV_WITHDRAWAL_PARAMS_TYPEHASH, - _getCreateGlvWithdrawalParamsAddressesStructHash(params.addresses), + _getCreateGlvWithdrawalAddressesStructHash(params.addresses), params.minLongTokenAmount, params.minShortTokenAmount, params.shouldUnwrapNativeToken, params.executionFee, params.callbackGasLimit, - keccak256(abi.encodePacked(params.dataList)) + keccak256(abi.encodePacked(params.dataList)), + _getRelayParamsHash(relayParams) ) ); } - function _getCreateGlvWithdrawalParamsAddressesStructHash( + function _getCreateGlvWithdrawalAddressesStructHash( GlvWithdrawalUtils.CreateGlvWithdrawalParamsAddresses memory addresses ) internal pure returns (bytes32) { return keccak256( abi.encode( - CREATE_GLV_WITHDRAWAL_PARAMS_ADDRESSES_TYPEHASH, + CREATE_GLV_WITHDRAWAL_ADDRESSES_TYPEHASH, addresses.receiver, addresses.callbackContract, addresses.uiFeeReceiver, diff --git a/test/multichain/MultichainGmRouter.ts b/test/multichain/MultichainGmRouter.ts index dd9962dcc..8e9f38930 100644 --- a/test/multichain/MultichainGmRouter.ts +++ b/test/multichain/MultichainGmRouter.ts @@ -9,6 +9,7 @@ import { sendCreateWithdrawal, sendCreateShift, sendCreateGlvDeposit, + sendCreateGlvWithdrawal, } from "../../utils/relay/multichain"; import * as keys from "../../utils/keys"; import { executeDeposit, getDepositCount, getDepositKeys } from "../../utils/deposit"; @@ -16,9 +17,16 @@ import { executeWithdrawal, getWithdrawalCount, getWithdrawalKeys } from "../../ import { getBalanceOf } from "../../utils/token"; import { BigNumberish, Contract } from "ethers"; import { executeShift, getShiftCount, getShiftKeys } from "../../utils/shift"; -import { executeGlvDeposit, getGlvDepositCount } from "../../utils/glv"; - -describe("MultichainGmRouter", () => { +import { + executeGlvDeposit, + executeGlvWithdrawal, + expectGlvWithdrawal, + getGlvDepositCount, + getGlvWithdrawalCount, + getGlvWithdrawalKeys, +} from "../../utils/glv"; + +describe("MultichainRouter", () => { let fixture; let user0, user1, user2, user3; let reader, @@ -414,9 +422,11 @@ describe("MultichainGmRouter", () => { }); }); - describe("createGlvDeposit", () => { + describe("multichain glv", () => { let defaultGlvDepositParams; let createGlvDepositParams: Parameters[0]; + const feeAmount = expandDecimals(4, 15); + beforeEach(async () => { defaultGlvDepositParams = { addresses: { @@ -443,7 +453,7 @@ describe("MultichainGmRouter", () => { signer: user1, feeParams: { feeToken: wnt.address, - feeAmount: expandDecimals(7, 15), // 0.007 ETH + feeAmount: feeAmount, // 0.004 ETH feeSwapPath: [], }, transferRequests: { @@ -459,21 +469,136 @@ describe("MultichainGmRouter", () => { desChainId: chainId, relayRouter: multichainGlvRouter, relayFeeToken: wnt.address, - relayFeeAmount: expandDecimals(3, 15), // 0.003 ETH + relayFeeAmount: expandDecimals(2, 15), // 0.002 ETH }; }); - it("creates glvDeposit and sends relayer fee", async () => { - await sendCreateDeposit(createDepositParams); // leaves the residualFee (i.e. executionfee) of 0.004 ETH fee in multichainVault/user's multichain balance - await mintAndBridge(fixture, { account: user1, token: wnt, tokenAmount: expandDecimals(3, 15) }); // add additional fee to user1's multichain balance - await executeDeposit(fixture, { gasUsageLabel: "executeDeposit" }); - - expect(await getGlvDepositCount(dataStore)).eq(0); - await sendCreateGlvDeposit(createGlvDepositParams); - expect(await getGlvDepositCount(dataStore)).eq(1); + describe("createGlvDeposit", () => { + it("creates glvDeposit and sends relayer fee", async () => { + await sendCreateDeposit(createDepositParams); // leaves the residualFee (i.e. executionfee) of 0.004 ETH fee in multichainVault/user's multichain balance + await mintAndBridge(fixture, { account: user1, token: wnt, tokenAmount: feeAmount }); // add additional fee to user1's multichain balance + await executeDeposit(fixture, { gasUsageLabel: "executeDeposit" }); + + // before glv deposit is created (user has 95k GM and 0 GLV) + expect(await getGlvDepositCount(dataStore)).eq(0); + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, ethUsdMarket.marketToken))).eq( + expandDecimals(95_000, 18) + ); // GM + expect(await getBalanceOf(ethUsdMarket.marketToken, ethUsdGlvAddress)).eq(0); // GM + expect(await getBalanceOf(ethUsdMarket.marketToken, glvVault.address)).eq(0); // GM + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, ethUsdGlvAddress))).eq(0); // GLV + + await sendCreateGlvDeposit(createGlvDepositParams); + + // after glv deposit is created (user has 0 GM and 0 GLV, his 95k GM moved from user to glvVault) + expect(await getGlvDepositCount(dataStore)).eq(1); + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, ethUsdMarket.marketToken))).eq(0); // GM + expect(await getBalanceOf(ethUsdMarket.marketToken, ethUsdGlvAddress)).eq(0); // GM + expect(await getBalanceOf(ethUsdMarket.marketToken, glvVault.address)).eq(expandDecimals(95_000, 18)); // GM + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, ethUsdGlvAddress))).eq(0); // GLV + + await executeGlvDeposit(fixture, { gasUsageLabel: "executeGlvDeposit" }); + + // after glv deposit is executed (user has 0 GM and 95k GLV, his 95k GM moved from glvVault to glv pool) + expect(await getGlvDepositCount(dataStore)).eq(0); + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, ethUsdMarket.marketToken))).eq(0); // GM + expect(await getBalanceOf(ethUsdMarket.marketToken, ethUsdGlvAddress)).eq(expandDecimals(95_000, 18)); // GM + expect(await getBalanceOf(ethUsdMarket.marketToken, glvVault.address)).eq(0); // GM + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, ethUsdGlvAddress))).eq( + expandDecimals(95_000, 18) + ); // GLV + }); + }); - await executeGlvDeposit(fixture, { gasUsageLabel: "executeGlvDeposit" }); - expect(await getGlvDepositCount(dataStore)).eq(0); + describe("createGlvWithdrawal", () => { + let defaultGlvWithdrawalParams; + let createGlvWithdrawalParams: Parameters[0]; + beforeEach(async () => { + defaultGlvWithdrawalParams = { + addresses: { + receiver: user1.address, + callbackContract: user2.address, + uiFeeReceiver: user3.address, + market: ethUsdMarket.marketToken, + glv: ethUsdGlvAddress, + longTokenSwapPath: [], + shortTokenSwapPath: [], + }, + minLongTokenAmount: 0, + minShortTokenAmount: 0, + shouldUnwrapNativeToken: false, + executionFee: 0, // expandDecimals(1, 15), // 0.001 ETH + callbackGasLimit: "200000", + dataList: [], + }; + + createGlvWithdrawalParams = { + sender: relaySigner, + signer: user1, + feeParams: { + feeToken: wnt.address, + feeAmount: feeAmount, // 0.004 ETH + feeSwapPath: [], + }, + transferRequests: { + tokens: [ethUsdGlvAddress], + receivers: [glvVault.address], + amounts: [expandDecimals(95_000, 18)], + }, + account: user1.address, + params: defaultGlvWithdrawalParams, + deadline: 9999999999, + chainId, + srcChainId: chainId, + desChainId: chainId, + relayRouter: multichainGlvRouter, + relayFeeToken: wnt.address, + relayFeeAmount: expandDecimals(2, 15), // 0.002 ETH + }; + }); + + it("creates glvWithdrawal and sends relayer fee", async () => { + await sendCreateDeposit(createDepositParams); + await mintAndBridge(fixture, { account: user1, token: wnt, tokenAmount: feeAmount }); // add additional fee to user1's multichain balance + await executeDeposit(fixture, { gasUsageLabel: "executeDeposit" }); + await sendCreateGlvDeposit(createGlvDepositParams); + await executeGlvDeposit(fixture, { gasUsageLabel: "executeGlvDeposit" }); + + // before glv withdrawal is created (user has 0 GM and 95k GLV, the 95k GM tokens user initially had are now in ethUsdGlv) + expect(await getGlvWithdrawalCount(dataStore)).eq(0); + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, ethUsdMarket.marketToken))).eq(0); // user's GM + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, ethUsdGlvAddress))).eq( + expandDecimals(95_000, 18) + ); // user's GLV + expect(await getBalanceOf(ethUsdGlvAddress, glvVault.address)).eq(0); // GLV in glvVault + expect(await getBalanceOf(ethUsdMarket.marketToken, ethUsdGlvAddress)).eq(expandDecimals(95_000, 18)); // GM in ethUsdGlv + + // create glvWithdrawal + await sendCreateGlvWithdrawal(createGlvWithdrawalParams); + + // before glv withdrawal is executed (user has 0 GM and 95k GLV) + expect(await getGlvWithdrawalCount(dataStore)).eq(1); + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, ethUsdMarket.marketToken))).eq(0); // user's GM + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, ethUsdGlvAddress))).eq(0); // user's GLV moved to glvVault + expect(await getBalanceOf(ethUsdGlvAddress, glvVault.address)).eq(expandDecimals(95_000, 18)); // GLV in glvVault + expect(await getBalanceOf(ethUsdMarket.marketToken, ethUsdGlvAddress)).eq(expandDecimals(95_000, 18)); // GM + // user's multicahin assets + // expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, wnt.address))).eq(0); + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, usdc.address))).eq(0); + + await executeGlvWithdrawal(fixture, { gasUsageLabel: "executeGlvWithdrawal" }); + + // after glv withdrawal is executed (user has 0 GM, 0 GLV and receives back 10 ETH and 45,000 USDC) + expect(await getGlvWithdrawalCount(dataStore)).eq(0); + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, ethUsdGlvAddress))).eq(0); // GLV + // user's multicahin assets + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, wnt.address))).eq( + expandDecimals(10_004, 15) + ); // 10.004 ETH + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, usdc.address))).eq( + expandDecimals(45_000, 6) + ); // 45,000 USDC + }); }); }); }); diff --git a/utils/relay/multichain.ts b/utils/relay/multichain.ts index f877f2494..61d32576f 100644 --- a/utils/relay/multichain.ts +++ b/utils/relay/multichain.ts @@ -3,6 +3,7 @@ import { GELATO_RELAY_ADDRESS } from "./addresses"; import { hashRelayParams, signTypedData } from "./helpers"; import { getDomain } from "./helpers"; import { getRelayParams } from "./helpers"; +import { exec } from "child_process"; interface SendCreate { signer: ethers.Signer; @@ -151,6 +152,31 @@ export async function sendCreateGlvDeposit(p: SendCreate) { }); } +export async function sendCreateGlvWithdrawal(p: SendCreate) { + const relayParams = await getRelayParams(p); + + let signature = p.signature; + if (!signature) { + signature = await getCreateGlvWithdrawalSignature({ ...p, relayParams, verifyingContract: p.relayRouter.address }); + } + + const createGlvWithdrawalCalldata = p.relayRouter.interface.encodeFunctionData("createGlvWithdrawal", [ + { ...relayParams, signature }, + p.account, + p.srcChainId, + p.transferRequests, + p.params, + ]); + const calldata = ethers.utils.solidityPack( + ["bytes", "address", "address", "uint256"], + [createGlvWithdrawalCalldata, GELATO_RELAY_ADDRESS, p.relayFeeToken, p.relayFeeAmount] + ); + return p.sender.sendTransaction({ + to: p.relayRouter.address, + data: calldata, + }); +} + async function getCreateDepositSignature({ signer, relayParams, @@ -397,3 +423,63 @@ async function getCreateGlvDepositSignature({ return signTypedData(signer, domain, types, typedData); } + +async function getCreateGlvWithdrawalSignature({ + signer, + relayParams, + transferRequests, + verifyingContract, + params, + chainId, +}: { + signer: ethers.Signer; + relayParams: any; + transferRequests: { tokens: string[]; receivers: string[]; amounts: BigNumberish[] }; + verifyingContract: string; + params: any; + chainId: BigNumberish; +}) { + if (relayParams.userNonce === undefined) { + throw new Error("userNonce is required"); + } + const types = { + CreateGlvWithdrawal: [ + { name: "transferTokens", type: "address[]" }, + { name: "transferReceivers", type: "address[]" }, + { name: "transferAmounts", type: "uint256[]" }, + { name: "addresses", type: "CreateGlvWithdrawalAddresses" }, + { name: "minLongTokenAmount", type: "uint256" }, + { name: "minShortTokenAmount", type: "uint256" }, + { name: "shouldUnwrapNativeToken", type: "bool" }, + { name: "executionFee", type: "uint256" }, + { name: "callbackGasLimit", type: "uint256" }, + { name: "dataList", type: "bytes32[]" }, + { name: "relayParams", type: "bytes32" }, + ], + CreateGlvWithdrawalAddresses: [ + { name: "receiver", type: "address" }, + { name: "callbackContract", type: "address" }, + { name: "uiFeeReceiver", type: "address" }, + { name: "market", type: "address" }, + { name: "glv", type: "address" }, + { name: "longTokenSwapPath", type: "address[]" }, + { name: "shortTokenSwapPath", type: "address[]" }, + ], + }; + const typedData = { + transferTokens: transferRequests.tokens, + transferReceivers: transferRequests.receivers, + transferAmounts: transferRequests.amounts, + addresses: params.addresses, + minLongTokenAmount: params.minLongTokenAmount, + minShortTokenAmount: params.minShortTokenAmount, + shouldUnwrapNativeToken: params.shouldUnwrapNativeToken, + executionFee: params.executionFee, + callbackGasLimit: params.callbackGasLimit, + dataList: params.dataList, + relayParams: hashRelayParams(relayParams), + }; + const domain = getDomain(chainId, verifyingContract); + + return signTypedData(signer, domain, types, typedData); +} From 4cfccceacc0c5880ca9faa876bbf8fc0bae0468f Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 25 Feb 2025 14:38:48 +0200 Subject: [PATCH 182/205] Fix refund fee. Skip updating user's multichain balance if the refund amount is zero --- contracts/deposit/ExecuteDepositUtils.sol | 5 +++-- contracts/gas/GasUtils.sol | 15 +++++++++------ .../glv/glvDeposit/ExecuteGlvDepositUtils.sol | 5 +++-- .../glv/glvWithdrawal/GlvWithdrawalUtils.sol | 5 +++-- contracts/shift/ShiftUtils.sol | 5 +++-- contracts/withdrawal/ExecuteWithdrawalUtils.sol | 5 +++-- 6 files changed, 24 insertions(+), 16 deletions(-) diff --git a/contracts/deposit/ExecuteDepositUtils.sol b/contracts/deposit/ExecuteDepositUtils.sol index 8cd83e650..9bb6b79c9 100644 --- a/contracts/deposit/ExecuteDepositUtils.sol +++ b/contracts/deposit/ExecuteDepositUtils.sol @@ -97,6 +97,7 @@ library ExecuteDepositUtils { bool balanceWasImproved; uint256 marketTokensSupply; EventUtils.EventLogData callbackEventData; + uint256 refundFeeAmount; } address public constant RECEIVER_FOR_FIRST_DEPOSIT = address(1); @@ -284,7 +285,7 @@ library ExecuteDepositUtils { cache.callbackEventData.uintItems.setItem(0, "receivedMarketTokens", cache.receivedMarketTokens); CallbackUtils.afterDepositExecution(params.key, deposit, cache.callbackEventData); - GasUtils.payExecutionFee( + cache.refundFeeAmount = GasUtils.payExecutionFee( params.dataStore, params.eventEmitter, params.depositVault, @@ -298,7 +299,7 @@ library ExecuteDepositUtils { ); // for multichain action, receiver is the multichainVault; increase user's multichain wnt balance for the fee refund - if (deposit.srcChainId() != 0) { + if (deposit.srcChainId() != 0 && cache.refundFeeAmount > 0) { address wnt = params.dataStore.getAddress(Keys.WNT); MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, wnt, deposit.receiver(), 0); // srcChainId is the current block.chainId } diff --git a/contracts/gas/GasUtils.sol b/contracts/gas/GasUtils.sol index 7a41d29ea..830863907 100644 --- a/contracts/gas/GasUtils.sol +++ b/contracts/gas/GasUtils.sol @@ -99,6 +99,7 @@ library GasUtils { struct PayExecutionFeeCache { uint256 refundFeeAmount; bool refundWasSent; + address wnt; } // @dev pay the keeper the execution fee and refund any excess amount @@ -122,9 +123,9 @@ library GasUtils { uint256 oraclePriceCount, address keeper, address refundReceiver - ) external { + ) external returns (uint256) { if (executionFee == 0) { - return; + return 0; } // 63/64 gas is forwarded to external calls, reduce the startingGas to account for this @@ -146,13 +147,13 @@ library GasUtils { cache.refundFeeAmount = executionFee - executionFeeForKeeper; if (cache.refundFeeAmount == 0) { - return; + return 0; } - address _wnt = dataStore.getAddress(Keys.WNT); - bank.transferOut(_wnt, address(this), cache.refundFeeAmount); + cache.wnt = dataStore.getAddress(Keys.WNT); + bank.transferOut(cache.wnt, address(this), cache.refundFeeAmount); - IWNT(_wnt).withdraw(cache.refundFeeAmount); + IWNT(cache.wnt).withdraw(cache.refundFeeAmount); EventUtils.EventLogData memory eventData; @@ -166,9 +167,11 @@ library GasUtils { if (cache.refundWasSent) { emitExecutionFeeRefundCallback(eventEmitter, callbackContract, cache.refundFeeAmount); + return 0; } else { TokenUtils.sendNativeToken(dataStore, refundReceiver, cache.refundFeeAmount); emitExecutionFeeRefund(eventEmitter, refundReceiver, cache.refundFeeAmount); + return cache.refundFeeAmount; } } diff --git a/contracts/glv/glvDeposit/ExecuteGlvDepositUtils.sol b/contracts/glv/glvDeposit/ExecuteGlvDepositUtils.sol index 656695b4f..cfa6dff39 100644 --- a/contracts/glv/glvDeposit/ExecuteGlvDepositUtils.sol +++ b/contracts/glv/glvDeposit/ExecuteGlvDepositUtils.sol @@ -41,6 +41,7 @@ library ExecuteGlvDepositUtils { uint256 oraclePriceCount; uint256 glvValue; uint256 glvSupply; + uint256 refundFeeAmount; } function executeGlvDeposit( @@ -137,7 +138,7 @@ library ExecuteGlvDepositUtils { cache.marketCount, glvDeposit.longTokenSwapPath().length + glvDeposit.shortTokenSwapPath().length ); - GasUtils.payExecutionFee( + cache.refundFeeAmount =GasUtils.payExecutionFee( params.dataStore, params.eventEmitter, params.glvVault, @@ -151,7 +152,7 @@ library ExecuteGlvDepositUtils { ); // for multichain action, receiver is the multichainVault; increase user's multichain wnt balance for the fee refund - if (glvDeposit.srcChainId() != 0) { + if (glvDeposit.srcChainId() != 0 && cache.refundFeeAmount > 0) { address wnt = params.dataStore.getAddress(Keys.WNT); MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, wnt, glvDeposit.receiver(), 0); // srcChainId is the current block.chainId } diff --git a/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol b/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol index 68177b17b..09f0d049d 100644 --- a/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol +++ b/contracts/glv/glvWithdrawal/GlvWithdrawalUtils.sol @@ -54,6 +54,7 @@ library GlvWithdrawalUtils { uint256 marketCount; uint256 oraclePriceCount; uint256 marketTokenAmount; + uint256 refundFeeAmount; } struct CancelGlvWithdrawalParams { @@ -195,7 +196,7 @@ library GlvWithdrawalUtils { glvWithdrawal.longTokenSwapPath().length + glvWithdrawal.shortTokenSwapPath().length ); - GasUtils.payExecutionFee( + cache.refundFeeAmount = GasUtils.payExecutionFee( params.dataStore, params.eventEmitter, params.glvVault, @@ -209,7 +210,7 @@ library GlvWithdrawalUtils { ); // for multichain action, receiver is the multichainVault; increase user's multichain wnt balance for the fee refund - if (glvWithdrawal.srcChainId() != 0) { + if (glvWithdrawal.srcChainId() != 0 && cache.refundFeeAmount > 0) { address wnt = params.dataStore.getAddress(Keys.WNT); MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, wnt, glvWithdrawal.receiver(), 0); // srcChainId is the current block.chainId } diff --git a/contracts/shift/ShiftUtils.sol b/contracts/shift/ShiftUtils.sol index 26a6389fa..c33faeeaa 100644 --- a/contracts/shift/ShiftUtils.sol +++ b/contracts/shift/ShiftUtils.sol @@ -75,6 +75,7 @@ library ShiftUtils { bytes32 depositKey; ExecuteDepositUtils.ExecuteDepositParams executeDepositParams; uint256 receivedMarketTokens; + uint256 refundFeeAmount; } function createShift( @@ -318,7 +319,7 @@ library ShiftUtils { eventData.uintItems.setItem(0, "receivedMarketTokens", cache.receivedMarketTokens); CallbackUtils.afterShiftExecution(params.key, shift, eventData); - GasUtils.payExecutionFee( + cache.refundFeeAmount = GasUtils.payExecutionFee( params.dataStore, params.eventEmitter, params.shiftVault, @@ -332,7 +333,7 @@ library ShiftUtils { ); // for multichain action, receiver is the multichainVault; increase user's multichain wnt balance for the fee refund - if (shift.srcChainId() != 0) { + if (shift.srcChainId() != 0 && cache.refundFeeAmount > 0) { address wnt = params.dataStore.getAddress(Keys.WNT); MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, wnt, shift.receiver(), 0); // srcChainId is the current block.chainId } diff --git a/contracts/withdrawal/ExecuteWithdrawalUtils.sol b/contracts/withdrawal/ExecuteWithdrawalUtils.sol index 85a00638c..218f1af4b 100644 --- a/contracts/withdrawal/ExecuteWithdrawalUtils.sol +++ b/contracts/withdrawal/ExecuteWithdrawalUtils.sol @@ -54,6 +54,7 @@ library ExecuteWithdrawalUtils { Market.Props market; MarketUtils.MarketPrices prices; ExecuteWithdrawalResult result; + uint256 refundFeeAmount; } struct _ExecuteWithdrawalCache { @@ -156,7 +157,7 @@ library ExecuteWithdrawalUtils { withdrawal.longTokenSwapPath().length + withdrawal.shortTokenSwapPath().length ); - GasUtils.payExecutionFee( + cache.refundFeeAmount = GasUtils.payExecutionFee( params.dataStore, params.eventEmitter, params.withdrawalVault, @@ -170,7 +171,7 @@ library ExecuteWithdrawalUtils { ); // for multichain action, receiver is the multichainVault; increase user's multichain wnt balance for the fee refund - if (withdrawal.srcChainId() != 0) { + if (withdrawal.srcChainId() != 0 && cache.refundFeeAmount > 0) { address wnt = params.dataStore.getAddress(Keys.WNT); MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, wnt, withdrawal.receiver(), 0); // srcChainId is the current block.chainId } From fdfb6b761637cb22edf32804c6517ceb00c18eca Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 26 Feb 2025 10:15:49 +0200 Subject: [PATCH 183/205] Fix params order for gasless _createOrder --- contracts/router/relay/GelatoRelayRouter.sol | 3 +-- contracts/router/relay/SubaccountGelatoRelayRouter.sol | 2 +- test/router/relay/GelatoRelayRouter.ts | 4 ++-- test/router/relay/SubaccountGelatoRelayRouter.ts | 4 ++-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/contracts/router/relay/GelatoRelayRouter.sol b/contracts/router/relay/GelatoRelayRouter.sol index 34856900b..46452bfee 100644 --- a/contracts/router/relay/GelatoRelayRouter.sol +++ b/contracts/router/relay/GelatoRelayRouter.sol @@ -46,9 +46,8 @@ contract GelatoRelayRouter is BaseGelatoRelayRouter { _createOrder( relayParams, account, - - 0, // srcChainId collateralDeltaAmount, + 0, // srcChainId params, false // isSubaccount ); diff --git a/contracts/router/relay/SubaccountGelatoRelayRouter.sol b/contracts/router/relay/SubaccountGelatoRelayRouter.sol index e26bc0b4c..ec494b094 100644 --- a/contracts/router/relay/SubaccountGelatoRelayRouter.sol +++ b/contracts/router/relay/SubaccountGelatoRelayRouter.sol @@ -119,8 +119,8 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { _createOrder( relayParams, account, - 0, // srcChainId collateralDeltaAmount, + 0, // srcChainId params, true // isSubaccount ); diff --git a/test/router/relay/GelatoRelayRouter.ts b/test/router/relay/GelatoRelayRouter.ts index 77979c016..e4cf80f86 100644 --- a/test/router/relay/GelatoRelayRouter.ts +++ b/test/router/relay/GelatoRelayRouter.ts @@ -283,7 +283,7 @@ describe("GelatoRelayRouter", () => { // allowance was set expect(await wnt.allowance(user0.address, router.address)).to.eq( - expandDecimals(1, 18).sub(collateralDeltaAmount).sub(gelatoRelayFee).sub(expandDecimals(1, 15)) // TODO: fails --> collateralDeltaAmount is 0 instead of 0.1 + expandDecimals(1, 18).sub(collateralDeltaAmount).sub(gelatoRelayFee).sub(expandDecimals(1, 15)) ); // relay fee was sent await expectBalance(wnt.address, GELATO_RELAY_ADDRESS, gelatoRelayFee); @@ -300,7 +300,7 @@ describe("GelatoRelayRouter", () => { expect(order.numbers.orderType).eq(OrderType.LimitIncrease); expect(order.numbers.decreasePositionSwapType).eq(DecreasePositionSwapType.SwapCollateralTokenToPnlToken); expect(order.numbers.sizeDeltaUsd).eq(decimalToFloat(1000)); - expect(order.numbers.initialCollateralDeltaAmount).eq(collateralDeltaAmount); // TODO: fails --> collateralDeltaAmount is 0 instead of 0.1 + expect(order.numbers.initialCollateralDeltaAmount).eq(collateralDeltaAmount); expect(order.numbers.triggerPrice).eq(decimalToFloat(4800)); expect(order.numbers.acceptablePrice).eq(decimalToFloat(4900)); expect(order.numbers.executionFee).eq(expandDecimals(1, 15)); diff --git a/test/router/relay/SubaccountGelatoRelayRouter.ts b/test/router/relay/SubaccountGelatoRelayRouter.ts index 27b4f4615..40a559208 100644 --- a/test/router/relay/SubaccountGelatoRelayRouter.ts +++ b/test/router/relay/SubaccountGelatoRelayRouter.ts @@ -585,7 +585,7 @@ describe("SubaccountGelatoRelayRouter", () => { // allowance was set expect(await wnt.allowance(user1.address, router.address)).to.eq( - expandDecimals(1, 18).sub(collateralDeltaAmount).sub(gelatoRelayFee).sub(expandDecimals(1, 15)) // TODO: fails --> collateralDeltaAmount is 0 instead of 0.1 + expandDecimals(1, 18).sub(collateralDeltaAmount).sub(gelatoRelayFee).sub(expandDecimals(1, 15)) ); // relay fee was sent await expectBalance(wnt.address, GELATO_RELAY_ADDRESS, gelatoRelayFee); @@ -603,7 +603,7 @@ describe("SubaccountGelatoRelayRouter", () => { expect(order.numbers.orderType).eq(OrderType.LimitIncrease); expect(order.numbers.decreasePositionSwapType).eq(DecreasePositionSwapType.SwapCollateralTokenToPnlToken); expect(order.numbers.sizeDeltaUsd).eq(decimalToFloat(1000)); - expect(order.numbers.initialCollateralDeltaAmount).eq(collateralDeltaAmount); // TODO: fails --> collateralDeltaAmount is 0 instead of 0.1 + expect(order.numbers.initialCollateralDeltaAmount).eq(collateralDeltaAmount); expect(order.numbers.triggerPrice).eq(decimalToFloat(4800)); expect(order.numbers.acceptablePrice).eq(decimalToFloat(4900)); expect(order.numbers.executionFee).eq(expandDecimals(1, 15)); From f6aa7763d284ddb3a3865fc96b29967180a10130 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 26 Feb 2025 16:14:42 +0200 Subject: [PATCH 184/205] Add multichain support to orders --- .../glv/glvDeposit/ExecuteGlvDepositUtils.sol | 2 +- contracts/order/BaseOrderUtils.sol | 3 ++ contracts/order/DecreaseOrderUtils.sol | 37 +++++++++++++++++-- contracts/order/ExecuteOrderUtils.sol | 16 +++++++- contracts/order/SwapOrderUtils.sol | 14 ++++++- deploy/deployDecreaseOrderUtils.ts | 2 +- deploy/deployExecuteOrderUtils.ts | 2 +- deploy/deploySwapOrderUtils.ts | 2 +- test/router/SubaccountRouter.ts | 4 +- 9 files changed, 70 insertions(+), 12 deletions(-) diff --git a/contracts/glv/glvDeposit/ExecuteGlvDepositUtils.sol b/contracts/glv/glvDeposit/ExecuteGlvDepositUtils.sol index cfa6dff39..402ed9aaa 100644 --- a/contracts/glv/glvDeposit/ExecuteGlvDepositUtils.sol +++ b/contracts/glv/glvDeposit/ExecuteGlvDepositUtils.sol @@ -138,7 +138,7 @@ library ExecuteGlvDepositUtils { cache.marketCount, glvDeposit.longTokenSwapPath().length + glvDeposit.shortTokenSwapPath().length ); - cache.refundFeeAmount =GasUtils.payExecutionFee( + cache.refundFeeAmount = GasUtils.payExecutionFee( params.dataStore, params.eventEmitter, params.glvVault, diff --git a/contracts/order/BaseOrderUtils.sol b/contracts/order/BaseOrderUtils.sol index 83ac71c28..3b9aea7be 100644 --- a/contracts/order/BaseOrderUtils.sol +++ b/contracts/order/BaseOrderUtils.sol @@ -14,6 +14,8 @@ import "../order/OrderVault.sol"; import "../oracle/Oracle.sol"; import "../swap/SwapHandler.sol"; +import "../multichain/MultichainVault.sol"; + // @title Order // @dev Library for common order functions used in OrderUtils, IncreaseOrderUtils // DecreaseOrderUtils, SwapOrderUtils @@ -59,6 +61,7 @@ library BaseOrderUtils { struct ExecuteOrderParamsContracts { DataStore dataStore; EventEmitter eventEmitter; + MultichainVault multichainVault; OrderVault orderVault; Oracle oracle; SwapHandler swapHandler; diff --git a/contracts/order/DecreaseOrderUtils.sol b/contracts/order/DecreaseOrderUtils.sol index f47bd5105..1e7e64166 100644 --- a/contracts/order/DecreaseOrderUtils.sol +++ b/contracts/order/DecreaseOrderUtils.sol @@ -6,6 +6,7 @@ import "./BaseOrderUtils.sol"; import "../swap/SwapUtils.sol"; import "../position/DecreasePositionUtils.sol"; import "../error/ErrorUtils.sol"; +import "../multichain/MultichainUtils.sol"; // @title DecreaseOrderUtils // @dev Library for functions to help with processing a decrease order @@ -74,18 +75,37 @@ library DecreaseOrderUtils { MarketToken(payable(order.market())).transferOut( result.outputToken, - order.receiver(), + order.srcChainId() == 0 ? order.receiver(): address(params.contracts.multichainVault), result.outputAmount, order.shouldUnwrapNativeToken() ); MarketToken(payable(order.market())).transferOut( result.secondaryOutputToken, - order.receiver(), + order.srcChainId() == 0 ? order.receiver(): address(params.contracts.multichainVault), result.secondaryOutputAmount, order.shouldUnwrapNativeToken() ); + if (order.srcChainId() != 0) { + MultichainUtils.recordTransferIn( + params.contracts.dataStore, + params.contracts.eventEmitter, + params.contracts.multichainVault, + result.outputToken, + order.receiver(), + order.srcChainId() + ); + MultichainUtils.recordTransferIn( + params.contracts.dataStore, + params.contracts.eventEmitter, + params.contracts.multichainVault, + result.secondaryOutputToken, + order.receiver(), + order.srcChainId() + ); + } + return getOutputEventData( result.outputToken, result.outputAmount, @@ -107,7 +127,7 @@ library DecreaseOrderUtils { result.outputAmount, params.swapPathMarkets, 0, - order.receiver(), + order.srcChainId() == 0 ? order.receiver(): address(params.contracts.multichainVault), order.uiFeeReceiver(), order.shouldUnwrapNativeToken(), ISwapPricingUtils.SwapPricingType.Swap @@ -120,6 +140,17 @@ library DecreaseOrderUtils { order.minOutputAmount() ); + if (order.srcChainId() != 0 && swapOutputAmount > 0) { + MultichainUtils.recordTransferIn( + params.contracts.dataStore, + params.contracts.eventEmitter, + params.contracts.multichainVault, + tokenOut, + order.receiver(), + order.srcChainId() + ); + } + return getOutputEventData( tokenOut, swapOutputAmount, diff --git a/contracts/order/ExecuteOrderUtils.sol b/contracts/order/ExecuteOrderUtils.sol index d3c9df2b2..ee483bf56 100644 --- a/contracts/order/ExecuteOrderUtils.sol +++ b/contracts/order/ExecuteOrderUtils.sol @@ -95,7 +95,7 @@ library ExecuteOrderUtils { // the order.executionFee for liquidation / adl orders is zero // gas costs for liquidations / adl is subsidised by the treasury - GasUtils.payExecutionFee( + uint256 refundFeeAmount = GasUtils.payExecutionFee( params.contracts.dataStore, params.contracts.eventEmitter, params.contracts.orderVault, @@ -105,9 +105,21 @@ library ExecuteOrderUtils { params.startingGas, GasUtils.estimateOrderOraclePriceCount(params.order.swapPath().length), params.keeper, - params.order.receiver() + params.order.srcChainId() == 0 ? params.order.receiver(): address(params.contracts.multichainVault) ); + if (params.order.srcChainId() != 0 && refundFeeAmount > 0) { + address wnt = params.contracts.dataStore.getAddress(Keys.WNT); + MultichainUtils.recordTransferIn( + params.contracts.dataStore, + params.contracts.eventEmitter, + params.contracts.multichainVault, + wnt, + params.order.receiver(), + 0 + ); + } + // clearAutoCancelOrders should be called after the main execution fee // is called // this is because clearAutoCancelOrders loops through each order for diff --git a/contracts/order/SwapOrderUtils.sol b/contracts/order/SwapOrderUtils.sol index 95ed0dde1..08537e0fa 100644 --- a/contracts/order/SwapOrderUtils.sol +++ b/contracts/order/SwapOrderUtils.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.0; import "./BaseOrderUtils.sol"; import "../swap/SwapUtils.sol"; +import "../multichain/MultichainUtils.sol"; // @title SwapOrderUtils // @dev Library for functions to help with processing a swap order @@ -66,12 +67,23 @@ library SwapOrderUtils { params.order.initialCollateralDeltaAmount(), params.swapPathMarkets, params.order.minOutputAmount(), - params.order.receiver(), + params.order.srcChainId() == 0 ? params.order.receiver(): address(params.contracts.multichainVault), params.order.uiFeeReceiver(), params.order.shouldUnwrapNativeToken(), ISwapPricingUtils.SwapPricingType.Swap )); + if (params.order.srcChainId() != 0) { + MultichainUtils.recordTransferIn( + params.contracts.dataStore, + params.contracts.eventEmitter, + params.contracts.multichainVault, + outputToken, + params.order.receiver(), + params.order.srcChainId() + ); + } + EventUtils.EventLogData memory eventData; eventData.addressItems.initItems(1); eventData.addressItems.setItem(0, "outputToken", outputToken); diff --git a/deploy/deployDecreaseOrderUtils.ts b/deploy/deployDecreaseOrderUtils.ts index 67f8f75c1..3959f601f 100644 --- a/deploy/deployDecreaseOrderUtils.ts +++ b/deploy/deployDecreaseOrderUtils.ts @@ -2,7 +2,7 @@ import { createDeployFunction } from "../utils/deploy"; const func = createDeployFunction({ contractName: "DecreaseOrderUtils", - libraryNames: ["SwapUtils", "PositionStoreUtils", "DecreasePositionUtils", "OrderStoreUtils"], + libraryNames: ["MultichainUtils", "DecreasePositionUtils", "PositionStoreUtils"], }); export default func; diff --git a/deploy/deployExecuteOrderUtils.ts b/deploy/deployExecuteOrderUtils.ts index 061db7f45..67dfc06d3 100644 --- a/deploy/deployExecuteOrderUtils.ts +++ b/deploy/deployExecuteOrderUtils.ts @@ -3,7 +3,7 @@ import { createDeployFunction } from "../utils/deploy"; const func = createDeployFunction({ contractName: "ExecuteOrderUtils", libraryNames: [ - "MarketStoreUtils", + "MultichainUtils", "MarketUtils", "OrderStoreUtils", "OrderEventUtils", diff --git a/deploy/deploySwapOrderUtils.ts b/deploy/deploySwapOrderUtils.ts index 6520bbb75..dc4d1188f 100644 --- a/deploy/deploySwapOrderUtils.ts +++ b/deploy/deploySwapOrderUtils.ts @@ -2,7 +2,7 @@ import { createDeployFunction } from "../utils/deploy"; const func = createDeployFunction({ contractName: "SwapOrderUtils", - libraryNames: ["OrderStoreUtils", "SwapUtils"], + libraryNames: ["MultichainUtils", "SwapUtils"], }); export default func; diff --git a/test/router/SubaccountRouter.ts b/test/router/SubaccountRouter.ts index 3a6694cea..79baa2d21 100644 --- a/test/router/SubaccountRouter.ts +++ b/test/router/SubaccountRouter.ts @@ -267,8 +267,8 @@ describe("SubaccountRouter", () => { expect(order._dataList).deep.eq(dataList); // 0.1 WETH in total - expect(order.numbers.executionFee).eq("2111032140000000"); - await expectBalance(wnt.address, user2.address, "97888967860000000"); + expect(order.numbers.executionFee).eq("2111028240000000"); + await expectBalance(wnt.address, user2.address, "97888971760000000"); expect( await dataStore.getUint( From 4509a694c74d861d7c457b85d92121195cd06d4d Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 27 Feb 2025 11:35:20 +0200 Subject: [PATCH 185/205] Add script to deployMultichainOrderRouter --- deploy/deployMultichainOrderRouter.ts | 42 +++++++++++++++++++++++++++ utils/fixture.ts | 2 ++ 2 files changed, 44 insertions(+) create mode 100644 deploy/deployMultichainOrderRouter.ts diff --git a/deploy/deployMultichainOrderRouter.ts b/deploy/deployMultichainOrderRouter.ts new file mode 100644 index 000000000..32d34aed7 --- /dev/null +++ b/deploy/deployMultichainOrderRouter.ts @@ -0,0 +1,42 @@ +import { grantRoleIfNotGranted } from "../utils/role"; +import { createDeployFunction } from "../utils/deploy"; + +const baseConstructorContracts = [ + "Router", + "DataStore", + "EventEmitter", + "Oracle", + "OrderVault", + "OrderHandler", + "ExternalHandler", + "MultichainVault", +]; + +const orderConstructorContracts = []; + +const func = createDeployFunction({ + contractName: "MultichainOrderRouter", + dependencyNames: [...baseConstructorContracts, ...orderConstructorContracts], + getDeployArgs: async ({ dependencyContracts }) => { + const baseParams = { + router: dependencyContracts.Router.address, + dataStore: dependencyContracts.DataStore.address, + eventEmitter: dependencyContracts.EventEmitter.address, + oracle: dependencyContracts.Oracle.address, + orderVault: dependencyContracts.OrderVault.address, + orderHandler: dependencyContracts.OrderHandler.address, + externalHandler: dependencyContracts.ExternalHandler.address, + multichainVault: dependencyContracts.MultichainVault.address, + }; + + return [baseParams]; + }, + libraryNames: ["MarketUtils", "MultichainUtils", "OrderStoreUtils", "RelayUtils", "SwapUtils"], + + afterDeploy: async ({ deployedContract }) => { + await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); + await grantRoleIfNotGranted(deployedContract.address, "ROUTER_PLUGIN"); + }, +}); + +export default func; diff --git a/utils/fixture.ts b/utils/fixture.ts index 2b954b63c..76a6e6c92 100644 --- a/utils/fixture.ts +++ b/utils/fixture.ts @@ -100,6 +100,7 @@ export async function deployFixture() { const subaccountGelatoRelayRouter = await hre.ethers.getContract("SubaccountGelatoRelayRouter"); const subaccountRouter = await hre.ethers.getContract("SubaccountRouter"); const multichainGmRouter = await hre.ethers.getContract("MultichainGmRouter"); + const multichainOrderRouter = await hre.ethers.getContract("MultichainOrderRouter"); const multichainGlvRouter = await hre.ethers.getContract("MultichainGlvRouter"); const relayUtils = await hre.ethers.getContract("RelayUtils"); const oracle = await hre.ethers.getContract("Oracle"); @@ -285,6 +286,7 @@ export async function deployFixture() { subaccountGelatoRelayRouter, subaccountRouter, multichainGmRouter, + multichainOrderRouter, multichainGlvRouter, relayUtils, oracle, From 5d7e6d483165b91a2f716f724b0c2cc1645f431c Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 27 Feb 2025 15:52:10 +0200 Subject: [PATCH 186/205] Fix fee refund error to non initialised multichain vault --- contracts/exchange/AdlHandler.sol | 1 + contracts/exchange/BaseOrderHandler.sol | 4 ++++ contracts/exchange/LiquidationHandler.sol | 1 + contracts/exchange/OrderHandler.sol | 3 +++ deploy/deployOrderHandler.ts | 11 ++--------- test/router/SubaccountRouter.ts | 4 ++-- 6 files changed, 13 insertions(+), 11 deletions(-) diff --git a/contracts/exchange/AdlHandler.sol b/contracts/exchange/AdlHandler.sol index 7ce889aae..2efa2bb8d 100644 --- a/contracts/exchange/AdlHandler.sol +++ b/contracts/exchange/AdlHandler.sol @@ -39,6 +39,7 @@ contract AdlHandler is BaseOrderHandler { _dataStore, _eventEmitter, _oracle, + MultichainVault(payable(0)), // TODO: confirm no possibility of multichain ADL _orderVault, _swapHandler, _referralStorage diff --git a/contracts/exchange/BaseOrderHandler.sol b/contracts/exchange/BaseOrderHandler.sol index 2c782b0bc..c3721489a 100644 --- a/contracts/exchange/BaseOrderHandler.sol +++ b/contracts/exchange/BaseOrderHandler.sol @@ -19,6 +19,7 @@ contract BaseOrderHandler is BaseHandler { using Order for Order.Props; using Array for uint256[]; + MultichainVault public immutable multichainVault; OrderVault public immutable orderVault; SwapHandler public immutable swapHandler; IReferralStorage public immutable referralStorage; @@ -28,10 +29,12 @@ contract BaseOrderHandler is BaseHandler { DataStore _dataStore, EventEmitter _eventEmitter, Oracle _oracle, + MultichainVault _multichainVault, OrderVault _orderVault, SwapHandler _swapHandler, IReferralStorage _referralStorage ) BaseHandler(_roleStore, _dataStore, _eventEmitter, _oracle) { + multichainVault = _multichainVault; orderVault = _orderVault; swapHandler = _swapHandler; referralStorage = _referralStorage; @@ -57,6 +60,7 @@ contract BaseOrderHandler is BaseHandler { params.contracts.dataStore = dataStore; params.contracts.eventEmitter = eventEmitter; + params.contracts.multichainVault = multichainVault; params.contracts.orderVault = orderVault; params.contracts.oracle = oracle; params.contracts.swapHandler = swapHandler; diff --git a/contracts/exchange/LiquidationHandler.sol b/contracts/exchange/LiquidationHandler.sol index 40de69dc5..91c99252b 100644 --- a/contracts/exchange/LiquidationHandler.sol +++ b/contracts/exchange/LiquidationHandler.sol @@ -26,6 +26,7 @@ contract LiquidationHandler is BaseOrderHandler { _dataStore, _eventEmitter, _oracle, + MultichainVault(payable(0)), // TODO: confirm no possibility of multichain liquidations _orderVault, _swapHandler, _referralStorage diff --git a/contracts/exchange/OrderHandler.sol b/contracts/exchange/OrderHandler.sol index e3c22cd79..bb85e069f 100644 --- a/contracts/exchange/OrderHandler.sol +++ b/contracts/exchange/OrderHandler.sol @@ -7,6 +7,7 @@ import "../error/ErrorUtils.sol"; import "./IOrderHandler.sol"; import "../order/OrderUtils.sol"; import "../order/ExecuteOrderUtils.sol"; +import "../multichain/MultichainVault.sol"; // @title OrderHandler // @dev Contract to handle creation, execution and cancellation of orders @@ -20,6 +21,7 @@ contract OrderHandler is IOrderHandler, BaseOrderHandler { DataStore _dataStore, EventEmitter _eventEmitter, Oracle _oracle, + MultichainVault _multichainVault, OrderVault _orderVault, SwapHandler _swapHandler, IReferralStorage _referralStorage @@ -28,6 +30,7 @@ contract OrderHandler is IOrderHandler, BaseOrderHandler { _dataStore, _eventEmitter, _oracle, + _multichainVault, _orderVault, _swapHandler, _referralStorage diff --git a/deploy/deployOrderHandler.ts b/deploy/deployOrderHandler.ts index f796b1dc7..20dc0cfb0 100644 --- a/deploy/deployOrderHandler.ts +++ b/deploy/deployOrderHandler.ts @@ -6,6 +6,7 @@ const constructorContracts = [ "DataStore", "EventEmitter", "Oracle", + "MultichainVault", "OrderVault", "SwapHandler", "ReferralStorage", @@ -17,15 +18,7 @@ const func = createDeployFunction({ getDeployArgs: async ({ dependencyContracts }) => { return constructorContracts.map((dependencyName) => dependencyContracts[dependencyName].address); }, - libraryNames: [ - "MarketStoreUtils", - "OrderUtils", - "ExecuteOrderUtils", - "OrderStoreUtils", - "OrderEventUtils", - "GasUtils", - "MarketUtils", - ], + libraryNames: ["OrderUtils", "ExecuteOrderUtils", "OrderStoreUtils", "OrderEventUtils", "GasUtils", "MarketUtils"], afterDeploy: async ({ deployedContract, getNamedAccounts, deployments, network }) => { const { deployer } = await getNamedAccounts(); const { execute } = deployments; diff --git a/test/router/SubaccountRouter.ts b/test/router/SubaccountRouter.ts index 79baa2d21..34d88b370 100644 --- a/test/router/SubaccountRouter.ts +++ b/test/router/SubaccountRouter.ts @@ -267,8 +267,8 @@ describe("SubaccountRouter", () => { expect(order._dataList).deep.eq(dataList); // 0.1 WETH in total - expect(order.numbers.executionFee).eq("2111028240000000"); - await expectBalance(wnt.address, user2.address, "97888971760000000"); + expect(order.numbers.executionFee).eq("2111029140000000"); + await expectBalance(wnt.address, user2.address, "97888970860000000"); expect( await dataStore.getUint( From a6300af98c80c01d9975c72a9bb0f32ed9b79b17 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 27 Feb 2025 15:53:32 +0200 Subject: [PATCH 187/205] Test create multichain order --- .../multichain/MultichainOrderRouter.sol | 4 +- test/multichain/MultichainGmRouter.ts | 113 ++++++++++++++++-- utils/relay/gelatoRelay.ts | 2 +- utils/relay/multichain.ts | 68 +++++++++++ 4 files changed, 173 insertions(+), 14 deletions(-) diff --git a/contracts/multichain/MultichainOrderRouter.sol b/contracts/multichain/MultichainOrderRouter.sol index ac21d11aa..59b812b05 100644 --- a/contracts/multichain/MultichainOrderRouter.sol +++ b/contracts/multichain/MultichainOrderRouter.sol @@ -10,7 +10,7 @@ contract MultichainOrderRouter is MultichainRouter { // TODO: handle partial fee payment - function createOrder( + function createMultichainOrder( RelayUtils.RelayParams calldata relayParams, address account, uint256 srcChainId, @@ -22,7 +22,7 @@ contract MultichainOrderRouter is MultichainRouter { bytes32 structHash = RelayUtils.getCreateOrderStructHash(relayParams, collateralDeltaAmount, params); _validateCall(relayParams, account, structHash, srcChainId); - return _createOrder(relayParams, account, srcChainId, collateralDeltaAmount, params, false); + return _createOrder(relayParams, account, collateralDeltaAmount, srcChainId, params, false); } function updateOrder( diff --git a/test/multichain/MultichainGmRouter.ts b/test/multichain/MultichainGmRouter.ts index 8e9f38930..2acaa13ff 100644 --- a/test/multichain/MultichainGmRouter.ts +++ b/test/multichain/MultichainGmRouter.ts @@ -1,7 +1,7 @@ import { expect } from "chai"; import { impersonateAccount, setBalance } from "@nomicfoundation/hardhat-network-helpers"; -import { expandDecimals } from "../../utils/math"; +import { decimalToFloat, expandDecimals } from "../../utils/math"; import { deployFixture } from "../../utils/fixture"; import { GELATO_RELAY_ADDRESS } from "../../utils/relay/addresses"; import { @@ -10,6 +10,7 @@ import { sendCreateShift, sendCreateGlvDeposit, sendCreateGlvWithdrawal, + sendCreateOrder, } from "../../utils/relay/multichain"; import * as keys from "../../utils/keys"; import { executeDeposit, getDepositCount, getDepositKeys } from "../../utils/deposit"; @@ -17,14 +18,10 @@ import { executeWithdrawal, getWithdrawalCount, getWithdrawalKeys } from "../../ import { getBalanceOf } from "../../utils/token"; import { BigNumberish, Contract } from "ethers"; import { executeShift, getShiftCount, getShiftKeys } from "../../utils/shift"; -import { - executeGlvDeposit, - executeGlvWithdrawal, - expectGlvWithdrawal, - getGlvDepositCount, - getGlvWithdrawalCount, - getGlvWithdrawalKeys, -} from "../../utils/glv"; +import { executeGlvDeposit, executeGlvWithdrawal, getGlvDepositCount, getGlvWithdrawalCount } from "../../utils/glv"; +import { DecreasePositionSwapType, executeOrder, getOrderCount, OrderType } from "../../utils/order"; +import { hashString } from "../../utils/hash"; +import { getPositionCount } from "../../utils/position"; describe("MultichainRouter", () => { let fixture; @@ -32,12 +29,14 @@ describe("MultichainRouter", () => { let reader, dataStore, multichainGmRouter, + multichainOrderRouter, multichainGlvRouter, multichainVault, depositVault, withdrawalVault, shiftVault, glvVault, + orderVault, ethUsdMarket, solUsdMarket, ethUsdGlvAddress, @@ -87,12 +86,14 @@ describe("MultichainRouter", () => { reader, dataStore, multichainGmRouter, + multichainOrderRouter, multichainGlvRouter, multichainVault, depositVault, withdrawalVault, shiftVault, glvVault, + orderVault, ethUsdMarket, solUsdMarket, ethUsdGlvAddress, @@ -159,7 +160,7 @@ describe("MultichainRouter", () => { describe("createDeposit", () => { it("creates deposit and sends relayer fee", async () => { - // funds have already been bridged to multichainVault and recorder under user's multichain balance + // funds have already been bridged to multichainVault and recorded under user's multichain balance expect(await wnt.balanceOf(multichainVault.address)).eq(expandDecimals(10_006, 15)); // 10 + 0.006 = 10.006 ETH expect(await usdc.balanceOf(multichainVault.address)).eq(expandDecimals(45_000, 6)); expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, wnt.address))).to.eq( @@ -422,7 +423,7 @@ describe("MultichainRouter", () => { }); }); - describe("multichain glv", () => { + describe("MultichainGlvRouter", () => { let defaultGlvDepositParams; let createGlvDepositParams: Parameters[0]; const feeAmount = expandDecimals(4, 15); @@ -601,4 +602,94 @@ describe("MultichainRouter", () => { }); }); }); + + describe("MultichainOrderRouter", () => { + let defaultOrderParams; + const collateralDeltaAmount = expandDecimals(1, 18); // 1 ETH + beforeEach(async () => { + defaultOrderParams = { + addresses: { + receiver: user1.address, + cancellationReceiver: user1.address, + callbackContract: user1.address, + uiFeeReceiver: user2.address, + market: ethUsdMarket.marketToken, + initialCollateralToken: wnt.address, + swapPath: [], + }, + numbers: { + sizeDeltaUsd: decimalToFloat(25_000), // 5x leverage + initialCollateralDeltaAmount: 0, + triggerPrice: decimalToFloat(5000), + acceptablePrice: decimalToFloat(4800), + executionFee: 0, + callbackGasLimit: "200000", + minOutputAmount: 0, + validFromTime: 0, + }, + orderType: OrderType.MarketIncrease, + decreasePositionSwapType: DecreasePositionSwapType.SwapCollateralTokenToPnlToken, + isLong: true, + shouldUnwrapNativeToken: true, + referralCode: hashString("referralCode"), + dataList: [], + }; + }); + const feeAmount = expandDecimals(6, 15); + + let createOrderParams: Parameters[0]; + beforeEach(async () => { + createOrderParams = { + sender: relaySigner, + signer: user1, + feeParams: { + feeToken: wnt.address, + feeAmount: feeAmount, // 0.004 ETH + feeSwapPath: [], + }, + tokenPermits: [], + transferRequests: { + tokens: [ethUsdMarket.longToken], + receivers: [orderVault.address], + amounts: [collateralDeltaAmount], + }, + collateralDeltaAmount: collateralDeltaAmount, // 1.23 ETH + account: user1.address, + params: defaultOrderParams, + deadline: 9999999999, + srcChainId: chainId, // 0 means non-multichain action + desChainId: chainId, // for non-multichain actions, desChainId is the same as chainId + relayRouter: multichainOrderRouter, + chainId, + relayFeeToken: wnt.address, + relayFeeAmount: feeAmount, + }; + }); + + it("creates multichain order and sends relayer fee", async () => { + await sendCreateDeposit(createDepositParams); + await executeDeposit(fixture, { gasUsageLabel: "executeMultichainDeposit" }); + const initialUser1Balance = await dataStore.getUint(keys.multichainBalanceKey(user1.address, wnt.address)); // user1 has some residual fee from deposit (i.e. the diff between feeAmount and relayFeeAmount) + const initialFeeReceiverBalance = await wnt.balanceOf(GELATO_RELAY_ADDRESS); + + await mintAndBridge(fixture, { account: user1, token: wnt, tokenAmount: collateralDeltaAmount.add(feeAmount) }); + + expect(await getOrderCount(dataStore)).to.eq(0); + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, wnt.address))).to.eq( + initialUser1Balance.add(collateralDeltaAmount).add(feeAmount) + ); + + await sendCreateOrder(createOrderParams); + + expect(await getOrderCount(dataStore)).to.eq(1); + expect(await getPositionCount(dataStore)).to.eq(0); + expect(await wnt.balanceOf(GELATO_RELAY_ADDRESS)).to.eq(initialFeeReceiverBalance.add(feeAmount)); + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, wnt.address))).to.eq(initialUser1Balance); + + await executeOrder(fixture, { gasUsageLabel: "executeOrder" }); + + expect(await getOrderCount(dataStore)).to.eq(0); + expect(await getPositionCount(dataStore)).to.eq(1); + }); + }); }); diff --git a/utils/relay/gelatoRelay.ts b/utils/relay/gelatoRelay.ts index 1712df84e..ba85a3a78 100644 --- a/utils/relay/gelatoRelay.ts +++ b/utils/relay/gelatoRelay.ts @@ -64,7 +64,7 @@ export async function sendCreateOrder(p: { }); } -async function getCreateOrderSignature({ +export async function getCreateOrderSignature({ signer, relayParams, collateralDeltaAmount, diff --git a/utils/relay/multichain.ts b/utils/relay/multichain.ts index 61d32576f..21261bc13 100644 --- a/utils/relay/multichain.ts +++ b/utils/relay/multichain.ts @@ -4,6 +4,7 @@ import { hashRelayParams, signTypedData } from "./helpers"; import { getDomain } from "./helpers"; import { getRelayParams } from "./helpers"; import { exec } from "child_process"; +import { getCreateOrderSignature } from "./gelatoRelay"; interface SendCreate { signer: ethers.Signer; @@ -152,6 +153,73 @@ export async function sendCreateGlvDeposit(p: SendCreate) { }); } +export async function sendCreateOrder(p: { + signer: ethers.Signer; + sender: ethers.Signer; + oracleParams?: { + tokens: string[]; + providers: string[]; + data: string[]; + }; + externalCalls?: { + externalCallTargets: string[]; + externalCallDataList: string[]; + refundTokens: string[]; + refundReceivers: string[]; + }; + tokenPermits?: { + token: string; + spender: string; + value: BigNumberish; + deadline: BigNumberish; + }[]; + feeParams: { + feeToken: string; + feeAmount: BigNumberish; + feeSwapPath: string[]; + }; + transferRequests: { + tokens: string[]; + receivers: string[]; + amounts: BigNumberish[]; + }; + collateralDeltaAmount: BigNumberish; + account: string; + params: any; + signature?: string; + userNonce?: BigNumberish; + srcChainId: BigNumberish; + deadline: BigNumberish; + desChainId: BigNumberish; + relayRouter: ethers.Contract; + chainId: BigNumberish; + relayFeeToken: string; + relayFeeAmount: BigNumberish; +}) { + const relayParams = await getRelayParams(p); + + let signature = p.signature; + if (!signature) { + signature = await getCreateOrderSignature({ ...p, relayParams, verifyingContract: p.relayRouter.address }); + } + + const createOrderCalldata = p.relayRouter.interface.encodeFunctionData("createMultichainOrder", [ + { ...relayParams, signature }, + p.account, + p.srcChainId, + p.collateralDeltaAmount, + p.params, + ]); + const calldata = ethers.utils.solidityPack( + ["bytes", "address", "address", "uint256"], + [createOrderCalldata, GELATO_RELAY_ADDRESS, p.relayFeeToken, p.relayFeeAmount] + ); + return p.sender.sendTransaction({ + to: p.relayRouter.address, + data: calldata, + }); +} + export async function sendCreateGlvWithdrawal(p: SendCreate) { const relayParams = await getRelayParams(p); From 64e46830364cbb0ae662dc5139c394a6317f8962 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 27 Feb 2025 18:17:28 +0200 Subject: [PATCH 188/205] Test update multichain order --- .../multichain/MultichainOrderRouter.sol | 2 +- test/multichain/MultichainGmRouter.ts | 131 +++++++++++++----- utils/relay/gelatoRelay.ts | 4 +- utils/relay/multichain.ts | 72 ++++++++-- 4 files changed, 161 insertions(+), 48 deletions(-) diff --git a/contracts/multichain/MultichainOrderRouter.sol b/contracts/multichain/MultichainOrderRouter.sol index 59b812b05..eb13a0b31 100644 --- a/contracts/multichain/MultichainOrderRouter.sol +++ b/contracts/multichain/MultichainOrderRouter.sol @@ -25,7 +25,7 @@ contract MultichainOrderRouter is MultichainRouter { return _createOrder(relayParams, account, collateralDeltaAmount, srcChainId, params, false); } - function updateOrder( + function updateMultichainOrder( RelayUtils.RelayParams calldata relayParams, address account, uint256 srcChainId, diff --git a/test/multichain/MultichainGmRouter.ts b/test/multichain/MultichainGmRouter.ts index 2acaa13ff..5c57fcd72 100644 --- a/test/multichain/MultichainGmRouter.ts +++ b/test/multichain/MultichainGmRouter.ts @@ -11,6 +11,7 @@ import { sendCreateGlvDeposit, sendCreateGlvWithdrawal, sendCreateOrder, + sendUpdateOrder, } from "../../utils/relay/multichain"; import * as keys from "../../utils/keys"; import { executeDeposit, getDepositCount, getDepositKeys } from "../../utils/deposit"; @@ -19,9 +20,10 @@ import { getBalanceOf } from "../../utils/token"; import { BigNumberish, Contract } from "ethers"; import { executeShift, getShiftCount, getShiftKeys } from "../../utils/shift"; import { executeGlvDeposit, executeGlvWithdrawal, getGlvDepositCount, getGlvWithdrawalCount } from "../../utils/glv"; -import { DecreasePositionSwapType, executeOrder, getOrderCount, OrderType } from "../../utils/order"; +import { DecreasePositionSwapType, executeOrder, getOrderCount, getOrderKeys, OrderType } from "../../utils/order"; import { hashString } from "../../utils/hash"; import { getPositionCount } from "../../utils/position"; +import { expectBalance } from "../../utils/validation"; describe("MultichainRouter", () => { let fixture; @@ -36,7 +38,6 @@ describe("MultichainRouter", () => { withdrawalVault, shiftVault, glvVault, - orderVault, ethUsdMarket, solUsdMarket, ethUsdGlvAddress, @@ -93,7 +94,6 @@ describe("MultichainRouter", () => { withdrawalVault, shiftVault, glvVault, - orderVault, ethUsdMarket, solUsdMarket, ethUsdGlvAddress, @@ -620,14 +620,14 @@ describe("MultichainRouter", () => { numbers: { sizeDeltaUsd: decimalToFloat(25_000), // 5x leverage initialCollateralDeltaAmount: 0, - triggerPrice: decimalToFloat(5000), - acceptablePrice: decimalToFloat(4800), + triggerPrice: decimalToFloat(4800), + acceptablePrice: decimalToFloat(4900), executionFee: 0, callbackGasLimit: "200000", - minOutputAmount: 0, + minOutputAmount: 700, validFromTime: 0, }, - orderType: OrderType.MarketIncrease, + orderType: OrderType.LimitIncrease, decreasePositionSwapType: DecreasePositionSwapType.SwapCollateralTokenToPnlToken, isLong: true, shouldUnwrapNativeToken: true, @@ -644,16 +644,10 @@ describe("MultichainRouter", () => { signer: user1, feeParams: { feeToken: wnt.address, - feeAmount: feeAmount, // 0.004 ETH + feeAmount: feeAmount, feeSwapPath: [], }, - tokenPermits: [], - transferRequests: { - tokens: [ethUsdMarket.longToken], - receivers: [orderVault.address], - amounts: [collateralDeltaAmount], - }, - collateralDeltaAmount: collateralDeltaAmount, // 1.23 ETH + collateralDeltaAmount: collateralDeltaAmount, account: user1.address, params: defaultOrderParams, deadline: 9999999999, @@ -666,30 +660,101 @@ describe("MultichainRouter", () => { }; }); - it("creates multichain order and sends relayer fee", async () => { - await sendCreateDeposit(createDepositParams); - await executeDeposit(fixture, { gasUsageLabel: "executeMultichainDeposit" }); - const initialUser1Balance = await dataStore.getUint(keys.multichainBalanceKey(user1.address, wnt.address)); // user1 has some residual fee from deposit (i.e. the diff between feeAmount and relayFeeAmount) - const initialFeeReceiverBalance = await wnt.balanceOf(GELATO_RELAY_ADDRESS); + describe("createOrder", () => { + it("creates multichain order and sends relayer fee", async () => { + await sendCreateDeposit(createDepositParams); + await executeDeposit(fixture, { gasUsageLabel: "executeMultichainDeposit" }); + const initialUser1Balance = await dataStore.getUint(keys.multichainBalanceKey(user1.address, wnt.address)); // user1 has some residual fee from deposit (i.e. the diff between feeAmount and relayFeeAmount) + const initialFeeReceiverBalance = await wnt.balanceOf(GELATO_RELAY_ADDRESS); - await mintAndBridge(fixture, { account: user1, token: wnt, tokenAmount: collateralDeltaAmount.add(feeAmount) }); + await mintAndBridge(fixture, { account: user1, token: wnt, tokenAmount: collateralDeltaAmount.add(feeAmount) }); - expect(await getOrderCount(dataStore)).to.eq(0); - expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, wnt.address))).to.eq( - initialUser1Balance.add(collateralDeltaAmount).add(feeAmount) - ); + expect(await getOrderCount(dataStore)).to.eq(0); + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, wnt.address))).to.eq( + initialUser1Balance.add(collateralDeltaAmount).add(feeAmount) + ); + + await sendCreateOrder(createOrderParams); - await sendCreateOrder(createOrderParams); + expect(await getOrderCount(dataStore)).to.eq(1); + expect(await getPositionCount(dataStore)).to.eq(0); + expect(await wnt.balanceOf(GELATO_RELAY_ADDRESS)).to.eq(initialFeeReceiverBalance.add(feeAmount)); + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, wnt.address))).to.eq( + initialUser1Balance + ); - expect(await getOrderCount(dataStore)).to.eq(1); - expect(await getPositionCount(dataStore)).to.eq(0); - expect(await wnt.balanceOf(GELATO_RELAY_ADDRESS)).to.eq(initialFeeReceiverBalance.add(feeAmount)); - expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, wnt.address))).to.eq(initialUser1Balance); + await executeOrder(fixture, { gasUsageLabel: "executeOrder" }); - await executeOrder(fixture, { gasUsageLabel: "executeOrder" }); + expect(await getOrderCount(dataStore)).to.eq(0); + expect(await getPositionCount(dataStore)).to.eq(1); + }); + }); + + describe("updateOrder", () => { + let updateOrderParams: Parameters[0]; + + beforeEach(() => { + updateOrderParams = { + sender: relaySigner, + signer: user1, + feeParams: { + feeToken: wnt.address, + feeAmount: expandDecimals(2, 15), // 0.002 ETH + feeSwapPath: [], + }, + tokenPermits: [], + account: user1.address, + params: { + sizeDeltaUsd: decimalToFloat(1), + acceptablePrice: decimalToFloat(2), + triggerPrice: decimalToFloat(3), + minOutputAmount: 4, + validFromTime: 5, + autoCancel: true, + }, + key: ethers.constants.HashZero, + deadline: 9999999999, + srcChainId: chainId, // 0 means non-multichain action + desChainId: chainId, // for non-multichain actions, desChainId is the same as chainId + relayRouter: multichainOrderRouter, + chainId, + relayFeeToken: wnt.address, + relayFeeAmount: expandDecimals(1, 15), + increaseExecutionFee: false, + }; + }); - expect(await getOrderCount(dataStore)).to.eq(0); - expect(await getPositionCount(dataStore)).to.eq(1); + it("updates multichain order and sends relayer fee", async () => { + await sendCreateDeposit(createDepositParams); + await executeDeposit(fixture, { gasUsageLabel: "executeMultichainDeposit" }); + await mintAndBridge(fixture, { account: user1, token: wnt, tokenAmount: collateralDeltaAmount.add(feeAmount) }); + await sendCreateOrder(createOrderParams); + const initialFeeReceiverBalance = await wnt.balanceOf(GELATO_RELAY_ADDRESS); + + const orderKeys = await getOrderKeys(dataStore, 0, 1); + let order = await reader.getOrder(dataStore.address, orderKeys[0]); + expect(order.numbers.sizeDeltaUsd).eq(decimalToFloat(25000)); + expect(order.numbers.acceptablePrice).eq(decimalToFloat(4900)); + expect(order.numbers.triggerPrice).eq(decimalToFloat(4800)); + expect(order.numbers.minOutputAmount).eq(700); + expect(order.numbers.validFromTime).eq(0); + expect(order.flags.autoCancel).eq(false); + + await sendUpdateOrder({ ...updateOrderParams, key: orderKeys[0] }); + + order = await reader.getOrder(dataStore.address, orderKeys[0]); + expect(order.numbers.sizeDeltaUsd).eq(decimalToFloat(1)); + expect(order.numbers.acceptablePrice).eq(decimalToFloat(2)); + expect(order.numbers.triggerPrice).eq(decimalToFloat(3)); + expect(order.numbers.minOutputAmount).eq(4); + expect(order.numbers.validFromTime).eq(5); + expect(order.flags.autoCancel).eq(true); + await expectBalance( + wnt.address, + GELATO_RELAY_ADDRESS, + initialFeeReceiverBalance.add(updateOrderParams.relayFeeAmount) + ); + }); }); }); }); diff --git a/utils/relay/gelatoRelay.ts b/utils/relay/gelatoRelay.ts index ba85a3a78..0d6100141 100644 --- a/utils/relay/gelatoRelay.ts +++ b/utils/relay/gelatoRelay.ts @@ -194,7 +194,7 @@ export async function sendUpdateOrder(p: { }); } -async function getUpdateOrderSignature({ +export async function getUpdateOrderSignature({ signer, relayParams, verifyingContract, @@ -287,7 +287,7 @@ export async function sendCancelOrder(p: { }); } -async function getCancelOrderSignature({ signer, relayParams, verifyingContract, key, chainId }) { +export async function getCancelOrderSignature({ signer, relayParams, verifyingContract, key, chainId }) { if (relayParams.userNonce === undefined) { throw new Error("userNonce is required"); } diff --git a/utils/relay/multichain.ts b/utils/relay/multichain.ts index 21261bc13..6da814464 100644 --- a/utils/relay/multichain.ts +++ b/utils/relay/multichain.ts @@ -4,7 +4,7 @@ import { hashRelayParams, signTypedData } from "./helpers"; import { getDomain } from "./helpers"; import { getRelayParams } from "./helpers"; import { exec } from "child_process"; -import { getCreateOrderSignature } from "./gelatoRelay"; +import { getCreateOrderSignature, getUpdateOrderSignature } from "./gelatoRelay"; interface SendCreate { signer: ethers.Signer; @@ -167,22 +167,11 @@ export async function sendCreateOrder(p: { refundTokens: string[]; refundReceivers: string[]; }; - tokenPermits?: { - token: string; - spender: string; - value: BigNumberish; - deadline: BigNumberish; - }[]; feeParams: { feeToken: string; feeAmount: BigNumberish; feeSwapPath: string[]; }; - transferRequests: { - tokens: string[]; - receivers: string[]; - amounts: BigNumberish[]; - }; collateralDeltaAmount: BigNumberish; account: string; params: any; @@ -220,6 +209,65 @@ export async function sendCreateOrder(p: { }); } +export async function sendUpdateOrder(p: { + sender: ethers.Signer; + signer: ethers.Signer; + oracleParams?: { + tokens: string[]; + providers: string[]; + data: string[]; + }; + feeParams: { + feeToken: string; + feeAmount: BigNumberish; + feeSwapPath: string[]; + }; + key: string; + chainId: BigNumberish; + account: string; + params: { + sizeDeltaUsd: BigNumberish; + acceptablePrice: BigNumberish; + triggerPrice: BigNumberish; + minOutputAmount: BigNumberish; + validFromTime: BigNumberish; + autoCancel: boolean; + }; + deadline: BigNumberish; + srcChainId: BigNumberish; + desChainId: BigNumberish; + userNonce?: BigNumberish; + relayRouter: ethers.Contract; + signature?: string; + relayFeeToken: string; + relayFeeAmount: BigNumberish; + increaseExecutionFee: boolean; +}) { + const relayParams = await getRelayParams(p); + + let signature = p.signature; + if (!signature) { + signature = await getUpdateOrderSignature({ ...p, relayParams, verifyingContract: p.relayRouter.address }); + } + + const UpdateOrderCalldata = p.relayRouter.interface.encodeFunctionData("updateMultichainOrder", [ + { ...relayParams, signature }, + p.account, + p.srcChainId, + p.key, + p.params, + p.increaseExecutionFee, + ]); + const calldata = ethers.utils.solidityPack( + ["bytes", "address", "address", "uint256"], + [UpdateOrderCalldata, GELATO_RELAY_ADDRESS, p.relayFeeToken, p.relayFeeAmount] + ); + return p.sender.sendTransaction({ + to: p.relayRouter.address, + data: calldata, + }); +} + export async function sendCreateGlvWithdrawal(p: SendCreate) { const relayParams = await getRelayParams(p); From c86c2974ba3a9d5d8a1bafdfe090df2eea779604 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 27 Feb 2025 19:28:48 +0200 Subject: [PATCH 189/205] Test cancel multichain order --- .../multichain/MultichainOrderRouter.sol | 2 +- test/multichain/MultichainGmRouter.ts | 47 +++++++++++++++++- utils/relay/multichain.ts | 49 ++++++++++++++++++- 3 files changed, 95 insertions(+), 3 deletions(-) diff --git a/contracts/multichain/MultichainOrderRouter.sol b/contracts/multichain/MultichainOrderRouter.sol index eb13a0b31..63f311acb 100644 --- a/contracts/multichain/MultichainOrderRouter.sol +++ b/contracts/multichain/MultichainOrderRouter.sol @@ -42,7 +42,7 @@ contract MultichainOrderRouter is MultichainRouter { _updateOrder(relayParams, account, key, params, increaseExecutionFee, false); } - function cancelOrder( + function cancelMultichainOrder( RelayUtils.RelayParams calldata relayParams, address account, uint256 srcChainId, diff --git a/test/multichain/MultichainGmRouter.ts b/test/multichain/MultichainGmRouter.ts index 5c57fcd72..389922ccf 100644 --- a/test/multichain/MultichainGmRouter.ts +++ b/test/multichain/MultichainGmRouter.ts @@ -12,6 +12,7 @@ import { sendCreateGlvWithdrawal, sendCreateOrder, sendUpdateOrder, + sendCancelOrder, } from "../../utils/relay/multichain"; import * as keys from "../../utils/keys"; import { executeDeposit, getDepositCount, getDepositKeys } from "../../utils/deposit"; @@ -702,7 +703,6 @@ describe("MultichainRouter", () => { feeAmount: expandDecimals(2, 15), // 0.002 ETH feeSwapPath: [], }, - tokenPermits: [], account: user1.address, params: { sizeDeltaUsd: decimalToFloat(1), @@ -756,5 +756,50 @@ describe("MultichainRouter", () => { ); }); }); + + describe("cancelOrder", () => { + let cancelOrderParams: Parameters[0]; + + beforeEach(() => { + cancelOrderParams = { + sender: relaySigner, + signer: user1, + feeParams: { + feeToken: wnt.address, + feeAmount: expandDecimals(2, 15), // 0.002 ETH + feeSwapPath: [], + }, + account: user1.address, + key: ethers.constants.HashZero, + deadline: 9999999999, + srcChainId: chainId, // 0 means non-multichain action + desChainId: chainId, // for non-multichain actions, desChainId is the same as chainId + relayRouter: multichainOrderRouter, + chainId, + relayFeeToken: wnt.address, + relayFeeAmount: expandDecimals(1, 15), + }; + }); + + it("cancels multichain order and sends relayer fee", async () => { + await sendCreateDeposit(createDepositParams); + await executeDeposit(fixture, { gasUsageLabel: "executeMultichainDeposit" }); + await mintAndBridge(fixture, { account: user1, token: wnt, tokenAmount: collateralDeltaAmount.add(feeAmount) }); + await sendCreateOrder(createOrderParams); + const initialFeeReceiverBalance = await wnt.balanceOf(GELATO_RELAY_ADDRESS); + + const orderKeys = await getOrderKeys(dataStore, 0, 1); + expect(await getOrderCount(dataStore)).to.eq(1); + + await sendCancelOrder({ ...cancelOrderParams, key: orderKeys[0] }); + + expect(await getOrderCount(dataStore)).to.eq(0); + await expectBalance( + wnt.address, + GELATO_RELAY_ADDRESS, + initialFeeReceiverBalance.add(cancelOrderParams.relayFeeAmount) + ); + }); + }); }); }); diff --git a/utils/relay/multichain.ts b/utils/relay/multichain.ts index 6da814464..da3d01fd3 100644 --- a/utils/relay/multichain.ts +++ b/utils/relay/multichain.ts @@ -4,7 +4,7 @@ import { hashRelayParams, signTypedData } from "./helpers"; import { getDomain } from "./helpers"; import { getRelayParams } from "./helpers"; import { exec } from "child_process"; -import { getCreateOrderSignature, getUpdateOrderSignature } from "./gelatoRelay"; +import { getCancelOrderSignature, getCreateOrderSignature, getUpdateOrderSignature } from "./gelatoRelay"; interface SendCreate { signer: ethers.Signer; @@ -267,6 +267,53 @@ export async function sendUpdateOrder(p: { data: calldata, }); } +export async function sendCancelOrder(p: { + sender: ethers.Signer; + signer: ethers.Signer; + oracleParams?: { + tokens: string[]; + providers: string[]; + data: string[]; + }; + feeParams: { + feeToken: string; + feeAmount: BigNumberish; + feeSwapPath: string[]; + }; + key: string; + chainId: BigNumberish; + account: string; + deadline: BigNumberish; + srcChainId: BigNumberish; + desChainId: BigNumberish; + userNonce?: BigNumberish; + relayRouter: ethers.Contract; + signature?: string; + relayFeeToken: string; + relayFeeAmount: BigNumberish; +}) { + const relayParams = await getRelayParams(p); + + let signature = p.signature; + if (!signature) { + signature = await getCancelOrderSignature({ ...p, relayParams, verifyingContract: p.relayRouter.address }); + } + + const CancelOrderCalldata = p.relayRouter.interface.encodeFunctionData("cancelMultichainOrder", [ + { ...relayParams, signature }, + p.account, + p.srcChainId, + p.key, + ]); + const calldata = ethers.utils.solidityPack( + ["bytes", "address", "address", "uint256"], + [CancelOrderCalldata, GELATO_RELAY_ADDRESS, p.relayFeeToken, p.relayFeeAmount] + ); + return p.sender.sendTransaction({ + to: p.relayRouter.address, + data: calldata, + }); +} export async function sendCreateGlvWithdrawal(p: SendCreate) { const relayParams = await getRelayParams(p); From fe4ed43b8e21be2904f6a8adcab5765e5bb5294f Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 27 Feb 2025 23:28:15 +0200 Subject: [PATCH 190/205] Add back "immutable" which was temporarily added to reduce contract size, add gasless validation --- contracts/multichain/MultichainGmRouter.sol | 10 +++++----- contracts/multichain/MultichainOrderRouter.sol | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/contracts/multichain/MultichainGmRouter.sol b/contracts/multichain/MultichainGmRouter.sol index 53b648621..d0e813e8a 100644 --- a/contracts/multichain/MultichainGmRouter.sol +++ b/contracts/multichain/MultichainGmRouter.sol @@ -11,11 +11,11 @@ import "./MultichainRouter.sol"; contract MultichainGmRouter is MultichainRouter { - DepositVault public depositVault; - IDepositHandler public depositHandler; - WithdrawalVault public withdrawalVault; - WithdrawalHandler public withdrawalHandler; - ShiftVault public shiftVault; + DepositVault public immutable depositVault; + IDepositHandler public immutable depositHandler; + WithdrawalVault public immutable withdrawalVault; + WithdrawalHandler public immutable withdrawalHandler; + ShiftVault public immutable shiftVault; constructor( BaseConstructorParams memory params, diff --git a/contracts/multichain/MultichainOrderRouter.sol b/contracts/multichain/MultichainOrderRouter.sol index 63f311acb..a02197824 100644 --- a/contracts/multichain/MultichainOrderRouter.sol +++ b/contracts/multichain/MultichainOrderRouter.sol @@ -18,6 +18,7 @@ contract MultichainOrderRouter is MultichainRouter { IBaseOrderUtils.CreateOrderParams memory params // can't use calldata because need to modify params.numbers.executionFee ) external nonReentrant withOraclePricesForAtomicAction(relayParams.oracleParams) onlyGelatoRelay returns (bytes32) { _validateDesChainId(relayParams.desChainId); + _validateGaslessFeature(); bytes32 structHash = RelayUtils.getCreateOrderStructHash(relayParams, collateralDeltaAmount, params); _validateCall(relayParams, account, structHash, srcChainId); From b64484ec3e4a1e3e5e9ab44eee144a26110f21a6 Mon Sep 17 00:00:00 2001 From: Solar Date: Fri, 28 Feb 2025 06:31:35 +0300 Subject: [PATCH 191/205] fix tests --- contracts/config/ConfigTimelockController.sol | 27 - contracts/config/ITimelockController.sol | 23 - contracts/config/Timelock.sol | 692 ------------------ contracts/config/TimelockConfig.sol | 130 ++-- contracts/fee/FeeUtils.sol | 2 +- contracts/referral/ReferralUtils.sol | 2 +- deploy/deployTimelock.ts | 21 - deploy/deployTimelockConfig.ts | 19 + scripts/updateOracleConfigForTokens.ts | 2 +- scripts/validateContractDeployment.ts | 5 +- test/config/Timelock.ts | 201 ++--- utils/fixture.ts | 2 - 12 files changed, 204 insertions(+), 922 deletions(-) delete mode 100644 contracts/config/ITimelockController.sol delete mode 100644 contracts/config/Timelock.sol delete mode 100644 deploy/deployTimelock.ts diff --git a/contracts/config/ConfigTimelockController.sol b/contracts/config/ConfigTimelockController.sol index d72da14df..476c9f4c1 100644 --- a/contracts/config/ConfigTimelockController.sol +++ b/contracts/config/ConfigTimelockController.sol @@ -12,7 +12,6 @@ import {OracleStore} from "../oracle/OracleStore.sol"; import {RoleStore} from "../role/RoleStore.sol"; import {Precision} from "../utils/Precision.sol"; import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol"; -import {ITimelockController} from "./ITimelockController.sol"; contract ConfigTimelockController is TimelockController { @@ -22,30 +21,4 @@ contract ConfigTimelockController is TimelockController { address[] memory executors ) TimelockController(minDelay, proposers, executors, msg.sender) {} - function signal(address target, bytes calldata payload) external override onlyRole(PROPOSER_ROLE) { - schedule( - target, - 0, - payload, - 0, - 0, - getMinDelay() - ); - } - - function signalBatch( - address[] calldata targets, - bytes[] calldata payloads, - uint256[] calldata values - ) external override onlyRole(PROPOSER_ROLE) { - scheduleBatch( - targets, - values, - payloads, - 0, - 0, - getMinDelay() - ); - } - } diff --git a/contracts/config/ITimelockController.sol b/contracts/config/ITimelockController.sol deleted file mode 100644 index 80ae2a473..000000000 --- a/contracts/config/ITimelockController.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.0; - -interface ITimelockController { - function signal( - address target, bytes calldata payload - ) external; - - function signalBatch( - address[] calldata targets, bytes[] calldata payloads, uint256[] calldata values - ) external; - - function execute( - address target, - uint256 value, - bytes calldata payload, - bytes32 predecessor, - bytes32 salt - ) external payable; - - function getMinDelay() external view returns (uint256); -} diff --git a/contracts/config/Timelock.sol b/contracts/config/Timelock.sol deleted file mode 100644 index 2533470d0..000000000 --- a/contracts/config/Timelock.sol +++ /dev/null @@ -1,692 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.0; - -import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; - -import "../role/RoleModule.sol"; -import "../event/EventEmitter.sol"; -import "../utils/BasicMulticall.sol"; -import "../utils/Precision.sol"; -import "../oracle/OracleStore.sol"; -import "../data/DataStore.sol"; -import "../data/Keys.sol"; -import "../chain/Chain.sol"; - -// @title Timelock -contract Timelock is ReentrancyGuard, RoleModule, BasicMulticall { - using EventUtils for EventUtils.AddressItems; - using EventUtils for EventUtils.UintItems; - using EventUtils for EventUtils.IntItems; - using EventUtils for EventUtils.BoolItems; - using EventUtils for EventUtils.Bytes32Items; - using EventUtils for EventUtils.BytesItems; - using EventUtils for EventUtils.StringItems; - - using EnumerableSet for EnumerableSet.Bytes32Set; - using EnumerableValues for EnumerableSet.Bytes32Set; - - uint256 public constant MAX_TIMELOCK_DELAY = 5 days; - - DataStore public immutable dataStore; - EventEmitter public immutable eventEmitter; - OracleStore public immutable oracleStore; - uint256 public timelockDelay; - - mapping (bytes32 => uint256) public pendingActions; - EnumerableSet.Bytes32Set internal pendingActionsList; - - constructor( - RoleStore _roleStore, - DataStore _dataStore, - EventEmitter _eventEmitter, - OracleStore _oracleStore, - uint256 _timelockDelay - ) RoleModule(_roleStore) { - dataStore = _dataStore; - eventEmitter = _eventEmitter; - oracleStore = _oracleStore; - timelockDelay = _timelockDelay; - - _validateTimelockDelay(); - } - - function getPendingActionsCount() internal view returns (uint256) { - return pendingActionsList.length(); - } - - function getPendingActionsList(uint256 start, uint256 end) internal view returns (bytes32[] memory) { - return pendingActionsList.valuesAt(start, end); - } - - // @dev immediately revoke the role of an account - // @param account the account to revoke the role for - // @param roleKey the role to revoke - function revokeRole(address account, bytes32 roleKey) external onlyTimelockMultisig nonReentrant { - roleStore.revokeRole(account, roleKey); - - EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(1); - eventData.addressItems.setItem(0, "account", account); - eventData.bytes32Items.initItems(1); - eventData.bytes32Items.setItem(0, "roleKey", roleKey); - eventEmitter.emitEventLog( - "RevokeRole", - eventData - ); - } - - // @dev increase the timelock delay - // @param the new timelock delay - function increaseTimelockDelay(uint256 _timelockDelay) external onlyTimelockAdmin nonReentrant { - if (_timelockDelay <= timelockDelay) { - revert Errors.InvalidTimelockDelay(_timelockDelay); - } - - timelockDelay = _timelockDelay; - - _validateTimelockDelay(); - - EventUtils.EventLogData memory eventData; - eventData.uintItems.initItems(1); - eventData.uintItems.setItem(0, "_timelockDelay", _timelockDelay); - eventEmitter.emitEventLog( - "IncreaseTimelockDelay", - eventData - ); - } - - function setOracleProviderEnabled(address provider, bool value) external onlyTimelockMultisig nonReentrant { - dataStore.setBool(Keys.isOracleProviderEnabledKey(provider), value); - - EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(1); - eventData.addressItems.setItem(0, "provider", provider); - eventData.boolItems.initItems(1); - eventData.boolItems.setItem(0, "value", value); - eventEmitter.emitEventLog( - "SetOracleProviderEnabled", - eventData - ); - } - - function signalSetOracleProviderForToken(address token, address provider) external onlyTimelockAdmin nonReentrant { - bytes32 actionKey = _setOracleProviderForTokenKey(token, provider); - _signalPendingAction(actionKey, "setOracleProviderForToken"); - - EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(2); - eventData.addressItems.setItem(0, "token", token); - eventData.addressItems.setItem(1, "provider", provider); - eventEmitter.emitEventLog( - "SignalSetOracleProviderForToken", - eventData - ); - } - - function setOracleProviderForTokenAfterSignal(address token, address provider) external onlyTimelockAdmin nonReentrant { - bytes32 actionKey = _setOracleProviderForTokenKey(token, provider); - _validateAndClearAction(actionKey, "setOracleProviderForToken"); - - dataStore.setAddress(Keys.oracleProviderForTokenKey(token), provider); - - EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(2); - eventData.addressItems.setItem(0, "token", token); - eventData.addressItems.setItem(1, "provider", provider); - eventEmitter.emitEventLog( - "SetOracleProviderForToken", - eventData - ); - } - - function signalSetOracleProviderEnabled(address provider, bool value) external onlyTimelockAdmin nonReentrant { - bytes32 actionKey = _setOracleProviderEnabledKey(provider, value); - _signalPendingAction(actionKey, "setOracleProviderEnabled"); - - EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(1); - eventData.addressItems.setItem(0, "provider", provider); - eventData.boolItems.initItems(1); - eventData.boolItems.setItem(0, "value", value); - eventEmitter.emitEventLog( - "SignalSetOracleProviderEnabled", - eventData - ); - } - - function setOracleProviderEnabledAfterSignal(address provider, bool value) external onlyTimelockAdmin nonReentrant { - bytes32 actionKey = _setOracleProviderEnabledKey(provider, value); - _validateAndClearAction(actionKey, "setOracleProviderEnabled"); - - dataStore.setBool(Keys.isOracleProviderEnabledKey(provider), value); - - EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(1); - eventData.addressItems.setItem(0, "provider", provider); - eventData.boolItems.initItems(1); - eventData.boolItems.setItem(0, "value", value); - eventEmitter.emitEventLog( - "SetOracleProviderEnabled", - eventData - ); - } - - function signalSetAtomicOracleProvider(address provider, bool value) external onlyTimelockAdmin nonReentrant { - bytes32 actionKey = _setAtomicOracleProviderKey(provider, value); - _signalPendingAction(actionKey, "setAtomicOracleProvider"); - - EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(1); - eventData.addressItems.setItem(0, "provider", provider); - eventData.boolItems.initItems(1); - eventData.boolItems.setItem(0, "value", value); - eventEmitter.emitEventLog( - "SignalSetAtomicOracleProvider", - eventData - ); - } - - function setAtomicOracleProviderAfterSignal(address provider, bool value) external onlyTimelockAdmin nonReentrant { - bytes32 actionKey = _setAtomicOracleProviderKey(provider, value); - _validateAndClearAction(actionKey, "setAtomicOracleProvider"); - - dataStore.setBool(Keys.isAtomicOracleProviderKey(provider), value); - - EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(1); - eventData.addressItems.setItem(0, "provider", provider); - eventData.boolItems.initItems(1); - eventData.boolItems.setItem(0, "value", value); - eventEmitter.emitEventLog( - "SetAtomicOracleProvider", - eventData - ); - } - - function signalAddOracleSigner(address account) external onlyTimelockAdmin nonReentrant { - if (account == address(0)) { - revert Errors.InvalidOracleSigner(account); - } - - bytes32 actionKey = _addOracleSignerActionKey(account); - _signalPendingAction(actionKey, "addOracleSigner"); - - EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(1); - eventData.addressItems.setItem(0, "account", account); - eventEmitter.emitEventLog1( - "SignalAddOracleSigner", - actionKey, - eventData - ); - } - - function addOracleSignerAfterSignal(address account) external onlyTimelockAdmin nonReentrant { - bytes32 actionKey = _addOracleSignerActionKey(account); - _validateAndClearAction(actionKey, "addOracleSigner"); - - oracleStore.addSigner(account); - - EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(1); - eventData.addressItems.setItem(0, "account", account); - eventEmitter.emitEventLog1( - "AddOracleSigner", - actionKey, - eventData - ); - } - - function signalRemoveOracleSigner(address account) external onlyTimelockAdmin nonReentrant { - if (account == address(0)) { - revert Errors.InvalidOracleSigner(account); - } - - bytes32 actionKey = _removeOracleSignerActionKey(account); - _signalPendingAction(actionKey, "removeOracleSigner"); - - EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(1); - eventData.addressItems.setItem(0, "account", account); - eventEmitter.emitEventLog1( - "SignalRemoveOracleSigner", - actionKey, - eventData - ); - } - - function removeOracleSignerAfterSignal(address account) external onlyTimelockAdmin nonReentrant { - bytes32 actionKey = _removeOracleSignerActionKey(account); - _validateAndClearAction(actionKey, "removeOracleSigner"); - - oracleStore.removeSigner(account); - - EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(1); - eventData.addressItems.setItem(0, "account", account); - eventEmitter.emitEventLog1( - "RemoveOracleSigner", - actionKey, - eventData - ); - } - - // @dev signal setting of the fee receiver - // @param account the new fee receiver - function signalSetFeeReceiver(address account) external onlyTimelockAdmin nonReentrant { - if (account == address(0)) { - revert Errors.InvalidFeeReceiver(account); - } - - bytes32 actionKey = _setFeeReceiverActionKey(account); - _signalPendingAction(actionKey, "setFeeReceiver"); - - EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(1); - eventData.addressItems.setItem(0, "account", account); - eventEmitter.emitEventLog1( - "SignalSetFeeReceiver", - actionKey, - eventData - ); - } - - // @dev set the fee receiver - // @param account the new fee receiver - function setFeeReceiverAfterSignal(address account) external onlyTimelockAdmin nonReentrant { - bytes32 actionKey = _setFeeReceiverActionKey(account); - _validateAndClearAction(actionKey, "setFeeReceiver"); - - dataStore.setAddress(Keys.FEE_RECEIVER, account); - - EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(1); - eventData.addressItems.setItem(0, "account", account); - eventEmitter.emitEventLog1( - "SetFeeReceiver", - actionKey, - eventData - ); - } - - // @dev signal granting of a role - // @param account the account to grant the role - // @param roleKey the role to grant - function signalGrantRole(address account, bytes32 roleKey) external onlyTimelockAdmin nonReentrant { - bytes32 actionKey = _grantRoleActionKey(account, roleKey); - _signalPendingAction(actionKey, "grantRole"); - - EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(1); - eventData.addressItems.setItem(0, "account", account); - eventData.bytes32Items.initItems(1); - eventData.bytes32Items.setItem(0, "roleKey", roleKey); - eventEmitter.emitEventLog1( - "SignalGrantRole", - actionKey, - eventData - ); - } - - // @dev grant a role - // @param account the account to grant the role - // @param roleKey the role to grant - function grantRoleAfterSignal(address account, bytes32 roleKey) external onlyTimelockAdmin nonReentrant { - bytes32 actionKey = _grantRoleActionKey(account, roleKey); - _validateAndClearAction(actionKey, "grantRole"); - - roleStore.grantRole(account, roleKey); - - EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(1); - eventData.addressItems.setItem(0, "account", account); - eventData.bytes32Items.initItems(1); - eventData.bytes32Items.setItem(0, "roleKey", roleKey); - eventEmitter.emitEventLog1( - "GrantRole", - actionKey, - eventData - ); - } - - // @dev signal revoking of a role - // @param account the account to revoke the role for - // @param roleKey the role to revoke - function signalRevokeRole(address account, bytes32 roleKey) external onlyTimelockAdmin nonReentrant { - bytes32 actionKey = _revokeRoleActionKey(account, roleKey); - _signalPendingAction(actionKey, "revokeRole"); - - EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(1); - eventData.addressItems.setItem(0, "account", account); - eventData.bytes32Items.initItems(1); - eventData.bytes32Items.setItem(0, "roleKey", roleKey); - eventEmitter.emitEventLog1( - "SignalRevokeRole", - actionKey, - eventData - ); - } - - // @dev revoke a role - // @param account the account to revoke the role for - // @param roleKey the role to revoke - function revokeRoleAfterSignal(address account, bytes32 roleKey) external onlyTimelockAdmin nonReentrant { - bytes32 actionKey = _revokeRoleActionKey(account, roleKey); - _validateAndClearAction(actionKey, "revokeRole"); - - roleStore.revokeRole(account, roleKey); - - EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(1); - eventData.addressItems.setItem(0, "account", account); - eventData.bytes32Items.initItems(1); - eventData.bytes32Items.setItem(0, "roleKey", roleKey); - eventEmitter.emitEventLog1( - "RevokeRole", - actionKey, - eventData - ); - } - - // @dev signal setting of a price feed - // @param token the token to set the price feed for - // @param priceFeed the address of the price feed - // @param priceFeedMultiplier the multiplier to apply to the price feed results - // @param stablePrice the stable price to set a range for the price feed results - function signalSetPriceFeed( - address token, - address priceFeed, - uint256 priceFeedMultiplier, - uint256 priceFeedHeartbeatDuration, - uint256 stablePrice - ) external onlyTimelockAdmin nonReentrant { - bytes32 actionKey = _setPriceFeedActionKey( - token, - priceFeed, - priceFeedMultiplier, - priceFeedHeartbeatDuration, - stablePrice - ); - - _signalPendingAction(actionKey, "setPriceFeed"); - - EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(2); - eventData.addressItems.setItem(0, "token", token); - eventData.addressItems.setItem(1, "priceFeed", priceFeed); - eventData.uintItems.initItems(3); - eventData.uintItems.setItem(0, "priceFeedMultiplier", priceFeedMultiplier); - eventData.uintItems.setItem(1, "priceFeedHeartbeatDuration", priceFeedHeartbeatDuration); - eventData.uintItems.setItem(2, "stablePrice", stablePrice); - eventEmitter.emitEventLog1( - "SignalSetPriceFeed", - actionKey, - eventData - ); - } - - // @dev sets a price feed - // @param token the token to set the price feed for - // @param priceFeed the address of the price feed - // @param priceFeedMultiplier the multiplier to apply to the price feed results - // @param stablePrice the stable price to set a range for the price feed results - function setPriceFeedAfterSignal( - address token, - address priceFeed, - uint256 priceFeedMultiplier, - uint256 priceFeedHeartbeatDuration, - uint256 stablePrice - ) external onlyTimelockAdmin nonReentrant { - bytes32 actionKey = _setPriceFeedActionKey( - token, - priceFeed, - priceFeedMultiplier, - priceFeedHeartbeatDuration, - stablePrice - ); - - _validateAndClearAction(actionKey, "setPriceFeed"); - - dataStore.setAddress(Keys.priceFeedKey(token), priceFeed); - dataStore.setUint(Keys.priceFeedMultiplierKey(token), priceFeedMultiplier); - dataStore.setUint(Keys.priceFeedHeartbeatDurationKey(token), priceFeedHeartbeatDuration); - dataStore.setUint(Keys.stablePriceKey(token), stablePrice); - - EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(2); - eventData.addressItems.setItem(0, "token", token); - eventData.addressItems.setItem(1, "priceFeed", priceFeed); - eventData.uintItems.initItems(3); - eventData.uintItems.setItem(0, "priceFeedMultiplier", priceFeedMultiplier); - eventData.uintItems.setItem(1, "priceFeedHeartbeatDuration", priceFeedHeartbeatDuration); - eventData.uintItems.setItem(2, "stablePrice", stablePrice); - eventEmitter.emitEventLog1( - "SetPriceFeed", - actionKey, - eventData - ); - } - - // @dev signal setting of a data stream feed - // @param token the token to set the data stream feed for - // @param feedId the ID of the data stream feed - // @param dataStreamMultiplier the multiplier to apply to the data stream feed results - // @param dataStreamSpreadReductionFactor the factor to apply to the data stream price spread - function signalSetDataStream( - address token, - bytes32 feedId, - uint256 dataStreamMultiplier, - uint256 dataStreamSpreadReductionFactor - ) external onlyTimelockAdmin nonReentrant { - if (dataStreamSpreadReductionFactor > Precision.FLOAT_PRECISION) { - revert Errors.ConfigValueExceedsAllowedRange(Keys.DATA_STREAM_SPREAD_REDUCTION_FACTOR, dataStreamSpreadReductionFactor); - } - - bytes32 actionKey = _setDataStreamActionKey( - token, - feedId, - dataStreamMultiplier, - dataStreamSpreadReductionFactor - ); - - _signalPendingAction(actionKey, "setDataStream"); - - EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(1); - eventData.addressItems.setItem(0, "token", token); - eventData.bytes32Items.initItems(1); - eventData.bytes32Items.setItem(0, "feedId", feedId); - eventData.uintItems.initItems(2); - eventData.uintItems.setItem(0, "dataStreamMultiplier", dataStreamMultiplier); - eventData.uintItems.setItem(1, "dataStreamSpreadReductionFactor", dataStreamSpreadReductionFactor); - eventEmitter.emitEventLog1( - "SignalSetDataStream", - actionKey, - eventData - ); - } - - // @dev sets a data stream feed - // @param token the token to set the data stream feed for - // @param feedId the ID of the data stream feed - // @param dataStreamMultiplier the multiplier to apply to the data stream feed results - // @param dataStreamMultiplier the factor to apply to the data stream price spread - function setDataStreamAfterSignal( - address token, - bytes32 feedId, - uint256 dataStreamMultiplier, - uint256 dataStreamSpreadReductionFactor - ) external onlyTimelockAdmin nonReentrant { - bytes32 actionKey = _setDataStreamActionKey( - token, - feedId, - dataStreamMultiplier, - dataStreamSpreadReductionFactor - ); - - _validateAndClearAction(actionKey, "setDataStream"); - - dataStore.setBytes32(Keys.dataStreamIdKey(token), feedId); - dataStore.setUint(Keys.dataStreamMultiplierKey(token), dataStreamMultiplier); - dataStore.setUint(Keys.dataStreamSpreadReductionFactorKey(token), dataStreamSpreadReductionFactor); - - EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(1); - eventData.addressItems.setItem(0, "token", token); - eventData.bytes32Items.initItems(1); - eventData.bytes32Items.setItem(0, "feedId", feedId); - eventData.uintItems.initItems(2); - eventData.uintItems.setItem(0, "dataStreamMultiplier", dataStreamMultiplier); - eventData.uintItems.setItem(1, "dataStreamSpreadReductionFactor", dataStreamSpreadReductionFactor); - eventEmitter.emitEventLog1( - "SetDataStream", - actionKey, - eventData - ); - } - - // @dev cancels a previously signalled pending action - // @param actionKey the key of the action to cancel - function cancelAction(bytes32 actionKey) external onlyTimelockAdmin nonReentrant { - _clearAction(actionKey, "cancelAction"); - } - - // @dev signal a pending action - // @param actionKey the key of the action - // @param actionLabel a label for the action - function _signalPendingAction(bytes32 actionKey, string memory actionLabel) internal { - if (pendingActions[actionKey] != 0) { - revert Errors.ActionAlreadySignalled(); - } - - pendingActions[actionKey] = Chain.currentTimestamp() + timelockDelay; - pendingActionsList.add(actionKey); - - EventUtils.EventLogData memory eventData; - - eventData.bytes32Items.initItems(1); - eventData.bytes32Items.setItem(0, "actionKey", actionKey); - - eventData.stringItems.initItems(1); - eventData.stringItems.setItem(0, "actionLabel", actionLabel); - - eventEmitter.emitEventLog1( - "SignalPendingAction", - actionKey, - eventData - ); - } - - function _setOracleProviderForTokenKey(address token, address provider) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("setOracleProviderForToken", token, provider)); - } - - function _setOracleProviderEnabledKey(address provider, bool value) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("setOracleProviderEnabled", provider, value)); - } - - function _setAtomicOracleProviderKey(address provider, bool value) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("setAtomicOracleProvider", provider, value)); - } - - function _addOracleSignerActionKey(address account) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("addOracleSigner", account)); - } - - function _removeOracleSignerActionKey(address account) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("removeOracleSigner", account)); - } - - function _setFeeReceiverActionKey(address account) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("setFeeReceiver", account)); - } - - function _grantRoleActionKey(address account, bytes32 roleKey) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("grantRole", account, roleKey)); - } - - function _revokeRoleActionKey(address account, bytes32 roleKey) internal pure returns (bytes32) { - return keccak256(abi.encodePacked("revokeRole", account, roleKey)); - } - - function _setPriceFeedActionKey( - address token, - address priceFeed, - uint256 priceFeedMultiplier, - uint256 priceFeedHeartbeatDuration, - uint256 stablePrice - ) internal pure returns (bytes32) { - return keccak256(abi.encodePacked( - "setPriceFeed", - token, - priceFeed, - priceFeedMultiplier, - priceFeedHeartbeatDuration, - stablePrice - )); - } - - function _setDataStreamActionKey( - address token, - bytes32 feedId, - uint256 dataStreamMultiplier, - uint256 dataStreamSpreadReductionFactor - ) internal pure returns (bytes32) { - return keccak256(abi.encodePacked( - "setDataStream", - token, - feedId, - dataStreamMultiplier, - dataStreamSpreadReductionFactor - )); - } - - // @dev validate that the action has been signalled and sufficient time has - // passed, clear the action after - function _validateAndClearAction(bytes32 actionKey, string memory actionLabel) internal { - _validateAction(actionKey); - _clearAction(actionKey, actionLabel); - } - - // @dev validate that the action has been signalled and sufficient time has passed - function _validateAction(bytes32 actionKey) internal view { - if (pendingActions[actionKey] == 0) { - revert Errors.ActionNotSignalled(); - } - - if (pendingActions[actionKey] > Chain.currentTimestamp()) { - revert Errors.SignalTimeNotYetPassed(pendingActions[actionKey]); - } - } - - // @dev clear a previously signalled action - function _clearAction(bytes32 actionKey, string memory actionLabel) internal { - if (pendingActions[actionKey] == 0) { - revert Errors.ActionNotSignalled(); - } - delete pendingActions[actionKey]; - pendingActionsList.remove(actionKey); - - EventUtils.EventLogData memory eventData; - - eventData.bytes32Items.initItems(1); - eventData.bytes32Items.setItem(0, "actionKey", actionKey); - - eventData.stringItems.initItems(1); - eventData.stringItems.setItem(0, "actionLabel", actionLabel); - - eventEmitter.emitEventLog1( - "ClearPendingAction", - actionKey, - eventData - ); - } - - function _validateTimelockDelay() internal view { - if (timelockDelay > MAX_TIMELOCK_DELAY) { - revert Errors.MaxTimelockDelayExceeded(timelockDelay); - } - } -} diff --git a/contracts/config/TimelockConfig.sol b/contracts/config/TimelockConfig.sol index b27d4ac92..acad23ac4 100644 --- a/contracts/config/TimelockConfig.sol +++ b/contracts/config/TimelockConfig.sol @@ -2,18 +2,15 @@ pragma solidity ^0.8.0; -import {DataStore} from "../data/DataStore.sol"; import {Keys} from "../data/Keys.sol"; import {Errors} from "../error/Errors.sol"; import {EventEmitter} from "../event/EventEmitter.sol"; import {EventUtils} from "../event/EventUtils.sol"; -import {OracleStore} from "../oracle/OracleStore.sol"; +import {RoleModule} from "../role/RoleModule.sol"; import {RoleStore} from "../role/RoleStore.sol"; +import {BasicMulticall} from "../utils/BasicMulticall.sol"; import {Precision} from "../utils/Precision.sol"; import {TimelockController} from "@openzeppelin/contracts/governance/TimelockController.sol"; -import "../utils/BasicMulticall.sol"; -import "./ITimelockController.sol"; -import "../role/RoleModule.sol"; contract TimelockConfig is RoleModule, BasicMulticall { using EventUtils for EventUtils.AddressItems; @@ -27,7 +24,7 @@ contract TimelockConfig is RoleModule, BasicMulticall { uint256 public constant MAX_TIMELOCK_DELAY = 5 days; EventEmitter public immutable eventEmitter; - ITimelockController public immutable timelockController; + TimelockController public immutable timelockController; address public immutable dataStore; address public immutable oracleStore; @@ -37,7 +34,7 @@ contract TimelockConfig is RoleModule, BasicMulticall { address _dataStore, address _oracleStore, RoleStore _roleStore, - ITimelockController _timelockController + TimelockController _timelockController ) RoleModule(_roleStore) { eventEmitter = _eventEmitter; dataStore = _dataStore; @@ -49,8 +46,8 @@ contract TimelockConfig is RoleModule, BasicMulticall { // @param account the account to grant the role // @param roleKey the role to grant function signalGrantRole(address account, bytes32 roleKey) external onlyTimelockAdmin { - bytes memory callData = abi.encodeWithSignature("grantRole(address,bytes32)", account, roleKey); - timelockController.signal(address(roleStore), callData); + bytes memory payload = abi.encodeWithSignature("grantRole(address,bytes32)", account, roleKey); + timelockController.schedule(address(roleStore), 0, payload, 0, 0, timelockController.getMinDelay()); EventUtils.EventLogData memory eventData; eventData.addressItems.initItems(1); @@ -63,18 +60,47 @@ contract TimelockConfig is RoleModule, BasicMulticall { ); } - // @dev signal granting of a role - // @param account the account to grant the role - // @param roleKey the role to grant - function grantRole(address account, bytes32 roleKey) external onlyTimelockAdmin { - bytes memory callData = abi.encodeWithSignature("grantRole(address,bytes32)", account, roleKey); - timelockController.execute(address(roleStore), 0, callData, 0, 0); + // @dev signal revoking of a role + // @param account the account to revoke the role for + // @param roleKey the role to revoke + function signalRevokeRole(address account, bytes32 roleKey) external onlyTimelockAdmin { + + bytes memory payload = abi.encodeWithSignature("revokeRole(address,bytes32)", + account, roleKey); + timelockController.schedule(address(roleStore), 0, payload, 0, 0, timelockController.getMinDelay()); + + EventUtils.EventLogData memory eventData; + eventData.addressItems.initItems(1); + eventData.addressItems.setItem(0, "account", account); + eventData.bytes32Items.initItems(1); + eventData.bytes32Items.setItem(0, "roleKey", roleKey); + eventEmitter.emitEventLog( + "SignalRevokeRole", + eventData + ); + } + + // @dev immediately revoke the role of an account + // @param account the account to revoke the role for + // @param roleKey the role to revoke + function revokeRole(address account, bytes32 roleKey) external onlyTimelockMultisig { + roleStore.revokeRole(account, roleKey); + + EventUtils.EventLogData memory eventData; + eventData.addressItems.initItems(1); + eventData.addressItems.setItem(0, "account", account); + eventData.bytes32Items.initItems(1); + eventData.bytes32Items.setItem(0, "roleKey", roleKey); + eventEmitter.emitEventLog( + "RevokeRole", + eventData + ); } function signalSetOracleProviderEnabled(address provider, bool value) external onlyTimelockAdmin { - bytes memory callData = abi.encodeWithSignature("setBool(bytes32,bool)", + bytes memory payload = abi.encodeWithSignature("setBool(bytes32,bool)", Keys.isOracleProviderEnabledKey(provider), value); - timelockController.signal(dataStore, callData); + timelockController.schedule(dataStore, 0, payload, 0, 0, timelockController.getMinDelay()); EventUtils.EventLogData memory eventData; eventData.addressItems.initItems(1); @@ -88,9 +114,9 @@ contract TimelockConfig is RoleModule, BasicMulticall { } function signalSetOracleProviderForToken(address token, address provider) external onlyTimelockAdmin { - bytes memory callData = abi.encodeWithSignature("setAddress(bytes32,address)", + bytes memory payload = abi.encodeWithSignature("setAddress(bytes32,address)", Keys.oracleProviderForTokenKey(token), provider); - timelockController.signal(dataStore, callData); + timelockController.schedule(dataStore, 0, payload, 0, 0, timelockController.getMinDelay()); EventUtils.EventLogData memory eventData; eventData.addressItems.initItems(2); @@ -103,9 +129,9 @@ contract TimelockConfig is RoleModule, BasicMulticall { } function signalSetAtomicOracleProvider(address provider, bool value) external onlyTimelockAdmin { - bytes memory callData = abi.encodeWithSignature("setBool(bytes32,bool)", + bytes memory payload = abi.encodeWithSignature("setBool(bytes32,bool)", Keys.isAtomicOracleProviderKey(provider), value); - timelockController.signal(dataStore, callData); + timelockController.schedule(dataStore, 0, payload, 0, 0, timelockController.getMinDelay()); EventUtils.EventLogData memory eventData; eventData.addressItems.initItems(1); @@ -123,8 +149,8 @@ contract TimelockConfig is RoleModule, BasicMulticall { revert Errors.InvalidOracleSigner(account); } - bytes memory callData = abi.encodeWithSignature("addSigner(address)", account); - timelockController.signal(oracleStore, callData); + bytes memory payload = abi.encodeWithSignature("addSigner(address)", account); + timelockController.schedule(oracleStore, 0, payload, 0, 0, timelockController.getMinDelay()); EventUtils.EventLogData memory eventData; eventData.addressItems.initItems(1); @@ -140,8 +166,8 @@ contract TimelockConfig is RoleModule, BasicMulticall { revert Errors.InvalidOracleSigner(account); } - bytes memory callData = abi.encodeWithSignature("removeSigner(address)", account); - timelockController.signal(oracleStore, callData); + bytes memory payload = abi.encodeWithSignature("removeSigner(address)", account); + timelockController.schedule(oracleStore, 0, payload, 0, 0, timelockController.getMinDelay()); EventUtils.EventLogData memory eventData; eventData.addressItems.initItems(1); @@ -159,9 +185,9 @@ contract TimelockConfig is RoleModule, BasicMulticall { revert Errors.InvalidFeeReceiver(account); } - bytes memory callData = abi.encodeWithSignature("setAddress(bytes32,address)", + bytes memory payload = abi.encodeWithSignature("setAddress(bytes32,address)", Keys.FEE_RECEIVER, account); - timelockController.signal(dataStore, callData); + timelockController.schedule(dataStore, 0, payload, 0, 0, timelockController.getMinDelay()); EventUtils.EventLogData memory eventData; eventData.addressItems.initItems(1); @@ -172,26 +198,6 @@ contract TimelockConfig is RoleModule, BasicMulticall { ); } - // @dev signal revoking of a role - // @param account the account to revoke the role for - // @param roleKey the role to revoke - function signalRevokeRole(address account, bytes32 roleKey) external onlyTimelockAdmin { - - bytes memory callData = abi.encodeWithSignature("revokeRole(address,bytes32)", - account, roleKey); - timelockController.signal(address(roleStore), callData); - - EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(1); - eventData.addressItems.setItem(0, "account", account); - eventData.bytes32Items.initItems(1); - eventData.bytes32Items.setItem(0, "roleKey", roleKey); - eventEmitter.emitEventLog( - "SignalRevokeRole", - eventData - ); - } - // @dev signal setting of a price feed // @param token the token to set the price feed for // @param priceFeed the address of the price feed @@ -214,15 +220,15 @@ contract TimelockConfig is RoleModule, BasicMulticall { bytes[] memory payloads = new bytes[](4); payloads[0] = abi.encodeWithSignature("setAddress(bytes32,address)", Keys.priceFeedKey(token), priceFeed); - payloads[1] = abi.encodeWithSignature("setUint(bytes32,uint)", + payloads[1] = abi.encodeWithSignature("setUint(bytes32,uint256)", Keys.priceFeedMultiplierKey(token), priceFeedMultiplier); - payloads[2] = abi.encodeWithSignature("setUint(bytes32,uint)", + payloads[2] = abi.encodeWithSignature("setUint(bytes32,uint256)", Keys.priceFeedHeartbeatDurationKey(token), priceFeedHeartbeatDuration); - payloads[3] = abi.encodeWithSignature("setUint(bytes32,uint)", + payloads[3] = abi.encodeWithSignature("setUint(bytes32,uint256)", Keys.stablePriceKey(token), stablePrice); uint256[] memory values = new uint256[](4); - timelockController.signalBatch(targets, payloads, values); + timelockController.scheduleBatch(targets, values, payloads, 0, 0, timelockController.getMinDelay()); EventUtils.EventLogData memory eventData; eventData.addressItems.initItems(2); @@ -261,13 +267,13 @@ contract TimelockConfig is RoleModule, BasicMulticall { bytes[] memory payloads = new bytes[](3); payloads[0] = abi.encodeWithSignature("setBytes32(bytes32,bytes32)", Keys.dataStreamIdKey(token), feedId); - payloads[1] = abi.encodeWithSignature("setUint(bytes32,uint)", + payloads[1] = abi.encodeWithSignature("setUint(bytes32,uint256)", Keys.dataStreamMultiplierKey(token), dataStreamMultiplier); - payloads[2] = abi.encodeWithSignature("setUint(bytes32,uint)", + payloads[2] = abi.encodeWithSignature("setUint(bytes32,uint256)", Keys.dataStreamSpreadReductionFactorKey(token), dataStreamSpreadReductionFactor); uint256[] memory values = new uint256[](3); - timelockController.signalBatch(targets, payloads, values); + timelockController.scheduleBatch(targets, values, payloads, 0, 0, timelockController.getMinDelay()); EventUtils.EventLogData memory eventData; eventData.addressItems.initItems(1); @@ -286,14 +292,16 @@ contract TimelockConfig is RoleModule, BasicMulticall { // @dev increase the timelock delay // @param the new timelock delay function increaseTimelockDelay(uint256 _timelockDelay) external onlyTimelockAdmin { - if (_timelockDelay <= timelockController.getMinimumDelay()) { + if (_timelockDelay <= timelockController.getMinDelay()) { revert Errors.InvalidTimelockDelay(_timelockDelay); } - _validateTimelockDelay(_timelockDelay); + if (_timelockDelay > MAX_TIMELOCK_DELAY) { + revert Errors.MaxTimelockDelayExceeded(_timelockDelay); + } - bytes memory callData = abi.encodeWithSignature("updateDelay(uint256)", _timelockDelay); - timelockController.signal(address(timelockController), callData); + bytes memory payload = abi.encodeWithSignature("updateDelay(uint256)", _timelockDelay); + timelockController.schedule(address(timelockController), 0, payload, 0, 0, timelockController.getMinDelay()); EventUtils.EventLogData memory eventData; eventData.uintItems.initItems(1); @@ -308,9 +316,7 @@ contract TimelockConfig is RoleModule, BasicMulticall { timelockController.execute(target, 0, payload, 0, 0); } - function _validateTimelockDelay(uint256 delay) internal pure { - if (delay > MAX_TIMELOCK_DELAY) { - revert Errors.MaxTimelockDelayExceeded(delay); - } + function executeBatch(address[] calldata targets, uint256[] calldata values, bytes[] calldata payloads) external onlyTimelockAdmin { + timelockController.executeBatch(targets, values, payloads, 0, 0); } } diff --git a/contracts/fee/FeeUtils.sol b/contracts/fee/FeeUtils.sol index 28f597517..a95074fb8 100644 --- a/contracts/fee/FeeUtils.sol +++ b/contracts/fee/FeeUtils.sol @@ -202,7 +202,7 @@ library FeeUtils { address market, address token, address receiver - ) external returns (uint256) { + ) public returns (uint256) { AccountUtils.validateReceiver(receiver); bytes32 key = Keys.claimableUiFeeAmountKey(market, token, uiFeeReceiver); diff --git a/contracts/referral/ReferralUtils.sol b/contracts/referral/ReferralUtils.sol index d6932c4d9..c4d74e636 100644 --- a/contracts/referral/ReferralUtils.sol +++ b/contracts/referral/ReferralUtils.sol @@ -157,7 +157,7 @@ library ReferralUtils { address token, address account, address receiver - ) external returns (uint256) { + ) public returns (uint256) { bytes32 key = Keys.affiliateRewardKey(market, token, account); uint256 rewardAmount = dataStore.getUint(key); diff --git a/deploy/deployTimelock.ts b/deploy/deployTimelock.ts deleted file mode 100644 index c72b9a669..000000000 --- a/deploy/deployTimelock.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { grantRoleIfNotGranted } from "../utils/role"; -import { createDeployFunction } from "../utils/deploy"; - -const constructorContracts = ["EventEmitter", "DataStore", "OracleStore", "RoleStore"]; -const timelockDelay = 24 * 60 * 60; - -const func = createDeployFunction({ - contractName: "Timelock", - dependencyNames: constructorContracts, - getDeployArgs: async ({ dependencyContracts }) => { - return constructorContracts - .map((dependencyName) => dependencyContracts[dependencyName].address) - .concat(timelockDelay); - }, - afterDeploy: async ({ deployedContract }) => { - await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); - await grantRoleIfNotGranted(deployedContract.address, "ROLE_ADMIN"); - }, -}); - -export default func; diff --git a/deploy/deployTimelockConfig.ts b/deploy/deployTimelockConfig.ts index 664d193c2..d64cfdeb8 100644 --- a/deploy/deployTimelockConfig.ts +++ b/deploy/deployTimelockConfig.ts @@ -1,8 +1,26 @@ import { grantRoleIfNotGranted } from "../utils/role"; import { createDeployFunction } from "../utils/deploy"; +import hre, { getNamedAccounts } from "hardhat"; +import { CANCELLER_ROLE, EXECUTOR_ROLE, PROPOSER_ROLE, TIMELOCK_ADMIN_ROLE } from "../utils/gov"; +import { TimelockConfig } from "../typechain-types"; const constructorContracts = ["EventEmitter", "DataStore", "OracleStore", "RoleStore", "ConfigTimelockController"]; +async function grantProposerRole(timelockConfig: string) { + const { deployer } = await getNamedAccounts(); + + const configTimelockController = await ethers.getContract("ConfigTimelockController"); + + if (await configTimelockController.hasRole(TIMELOCK_ADMIN_ROLE, deployer)) { + await configTimelockController.grantRole(PROPOSER_ROLE, timelockConfig); + await configTimelockController.grantRole(CANCELLER_ROLE, timelockConfig); + await configTimelockController.grantRole(EXECUTOR_ROLE, timelockConfig); + await configTimelockController.revokeRole(TIMELOCK_ADMIN_ROLE, deployer); + } else { + console.info("skipping configTimelockController role config, as deployer does not have access to update roles"); + } +} + const func = createDeployFunction({ contractName: "TimelockConfig", dependencyNames: constructorContracts, @@ -10,6 +28,7 @@ const func = createDeployFunction({ return constructorContracts.map((dependencyName) => dependencyContracts[dependencyName].address); }, afterDeploy: async ({ deployedContract }) => { + await grantProposerRole(deployedContract.address); await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); await grantRoleIfNotGranted(deployedContract.address, "ROLE_ADMIN"); }, diff --git a/scripts/updateOracleConfigForTokens.ts b/scripts/updateOracleConfigForTokens.ts index 0772266d4..afff3ad05 100644 --- a/scripts/updateOracleConfigForTokens.ts +++ b/scripts/updateOracleConfigForTokens.ts @@ -14,7 +14,7 @@ export async function updateOracleConfigForTokens() { const dataStore = await hre.ethers.getContract("DataStore"); const multicall = await hre.ethers.getContract("Multicall3"); - const timelock = await hre.ethers.getContract("Timelock"); + const timelock = await hre.ethers.getContract("TimelockConfig"); const multicallReadParams = []; diff --git a/scripts/validateContractDeployment.ts b/scripts/validateContractDeployment.ts index ddad9398f..749b8b01a 100644 --- a/scripts/validateContractDeployment.ts +++ b/scripts/validateContractDeployment.ts @@ -68,7 +68,7 @@ interface ContractInfo { const expectedRoles = { CONFIG_KEEPER: ["ConfigSyncer"], - ROLE_ADMIN: ["Timelock"], + ROLE_ADMIN: ["ConfigTimelockController", "TimelockConfig"], ROUTER_PLUGIN: ["ExchangeRouter", "SubaccountRouter", "GlvRouter"], CONTROLLER: [ "OracleStore", @@ -76,7 +76,8 @@ const expectedRoles = { "GlvFactory", "Config", "ConfigSyncer", - "Timelock", + "TimelockConfig", + "ConfigTimelockController", "Oracle", "SwapHandler", "AdlHandler", diff --git a/test/config/Timelock.ts b/test/config/Timelock.ts index 368800d3e..ae6cf5e25 100644 --- a/test/config/Timelock.ts +++ b/test/config/Timelock.ts @@ -11,12 +11,11 @@ import * as keys from "../../utils/keys"; describe("Timelock", () => { let fixture; let timelockAdmin, timelockMultisig, user2, user3, signer0, signer9; - let timelock, timelockConfig, configTimelockController, dataStore, roleStore, oracleStore, wnt; + let timelockConfig, configTimelockController, dataStore, roleStore, oracleStore, wnt; beforeEach(async () => { fixture = await deployFixture(); - ({ timelock, timelockConfig, configTimelockController, dataStore, roleStore, oracleStore, wnt } = - fixture.contracts); + ({ timelockConfig, configTimelockController, dataStore, roleStore, oracleStore, wnt } = fixture.contracts); ({ user2, user3, signer0, signer9 } = fixture.accounts); timelockAdmin = fixture.accounts.user0; @@ -29,112 +28,122 @@ describe("Timelock", () => { it("multisig revokeRole", async () => { const orderKeeperRole = hashString("ORDER_KEEPER"); + expect(await roleStore.hasRole(user3.address, orderKeeperRole)).eq(false); + await timelockConfig.connect(timelockAdmin).signalGrantRole(user3.address, orderKeeperRole); await time.increase(1 * 24 * 60 * 60 + 10); - await timelockConfig.connect(timelockAdmin).grantRoleAfterSignal(user3.address, orderKeeperRole); + const grantRolePayload = roleStore.interface.encodeFunctionData("grantRole", [user3.address, orderKeeperRole]); + await timelockConfig.connect(timelockAdmin).execute(roleStore.address, grantRolePayload); expect(await roleStore.hasRole(user3.address, orderKeeperRole)).eq(true); - await expect(timelock.connect(timelockAdmin).revokeRole(user3.address, orderKeeperRole)) + await expect(timelockConfig.connect(timelockAdmin).revokeRole(user3.address, orderKeeperRole)) .to.be.revertedWithCustomError(errorsContract, "Unauthorized") .withArgs(timelockAdmin.address, "TIMELOCK_MULTISIG"); expect(await roleStore.hasRole(user3.address, orderKeeperRole)).eq(true); - expect(timelock.connect(timelockMultisig).revokeRole(user3.address, orderKeeperRole)); + expect(await timelockConfig.connect(timelockMultisig).revokeRole(user3.address, orderKeeperRole)); expect(await roleStore.hasRole(user3.address, orderKeeperRole)).eq(false); }); it("increaseTimelockDelay", async () => { - await expect(timelock.connect(user2).increaseTimelockDelay(2 * 24 * 60 * 60)) + await expect(timelockConfig.connect(user2).increaseTimelockDelay(2 * 24 * 60 * 60)) .to.be.revertedWithCustomError(errorsContract, "Unauthorized") .withArgs(user2.address, "TIMELOCK_ADMIN"); - await expect(timelock.connect(timelockAdmin).increaseTimelockDelay(1 * 24 * 60 * 60 - 10)) + await expect(timelockConfig.connect(timelockAdmin).increaseTimelockDelay(1 * 24 * 60 * 60 - 10)) .to.be.revertedWithCustomError(errorsContract, "InvalidTimelockDelay") .withArgs(1 * 24 * 60 * 60 - 10); - await expect(timelock.connect(timelockAdmin).increaseTimelockDelay(5 * 24 * 60 * 60 + 10)) + await expect(timelockConfig.connect(timelockAdmin).increaseTimelockDelay(5 * 24 * 60 * 60 + 10)) .to.be.revertedWithCustomError(errorsContract, "MaxTimelockDelayExceeded") .withArgs(5 * 24 * 60 * 60 + 10); - expect(await timelock.timelockDelay()).eq(1 * 24 * 60 * 60); - await timelock.connect(timelockAdmin).increaseTimelockDelay(2 * 24 * 60 * 60); - expect(await timelock.timelockDelay()).eq(2 * 24 * 60 * 60); + expect(await configTimelockController.getMinDelay()).eq(1 * 24 * 60 * 60); + await timelockConfig.connect(timelockAdmin).increaseTimelockDelay(2 * 24 * 60 * 60); + await time.increase(1 * 24 * 60 * 60 + 10); + const payload = configTimelockController.interface.encodeFunctionData("updateDelay", [2 * 24 * 60 * 60]); + await timelockConfig.connect(timelockAdmin).execute(configTimelockController.address, payload); + expect(await configTimelockController.getMinDelay()).eq(2 * 24 * 60 * 60); }); it("addOracleSigner", async () => { - await expect(timelock.connect(user2).signalAddOracleSigner(user3.address)) + await expect(timelockConfig.connect(user2).signalAddOracleSigner(user3.address)) .to.be.revertedWithCustomError(errorsContract, "Unauthorized") .withArgs(user2.address, "TIMELOCK_ADMIN"); - await timelock.connect(timelockAdmin).signalAddOracleSigner(user3.address); + await timelockConfig.connect(timelockAdmin).signalAddOracleSigner(user3.address); - await expect(timelock.connect(user2).addOracleSignerAfterSignal(user3.address)) + const payload = oracleStore.interface.encodeFunctionData("addSigner", [user3.address]); + await expect(timelockConfig.connect(user2).execute(oracleStore.address, payload)) .to.be.revertedWithCustomError(errorsContract, "Unauthorized") .withArgs(user2.address, "TIMELOCK_ADMIN"); - await expect( - timelock.connect(timelockAdmin).addOracleSignerAfterSignal(user3.address) - ).to.be.revertedWithCustomError(errorsContract, "SignalTimeNotYetPassed"); + await expect(timelockConfig.connect(timelockAdmin).execute(oracleStore.address, payload)).to.be.revertedWith( + "TimelockController: operation is not ready" + ); await time.increase(1 * 24 * 60 * 60 + 10); expect(await oracleStore.getSignerCount()).eq(10); - await timelock.connect(timelockAdmin).addOracleSignerAfterSignal(user3.address); + await timelockConfig.connect(timelockAdmin).execute(oracleStore.address, payload); expect(await oracleStore.getSignerCount()).eq(11); expect(await oracleStore.getSigner(10)).eq(user3.address); }); it("removeOracleSigner", async () => { - await expect(timelock.connect(user2).signalRemoveOracleSigner(signer0.address)) + await expect(timelockConfig.connect(user2).signalRemoveOracleSigner(signer0.address)) .to.be.revertedWithCustomError(errorsContract, "Unauthorized") .withArgs(user2.address, "TIMELOCK_ADMIN"); - await timelock.connect(timelockAdmin).signalRemoveOracleSigner(signer0.address); + await timelockConfig.connect(timelockAdmin).signalRemoveOracleSigner(signer0.address); - await expect(timelock.connect(user2).removeOracleSignerAfterSignal(signer0.address)) + const payload = oracleStore.interface.encodeFunctionData("removeSigner", [signer0.address]); + await expect(timelockConfig.connect(user2).execute(oracleStore.address, payload)) .to.be.revertedWithCustomError(errorsContract, "Unauthorized") .withArgs(user2.address, "TIMELOCK_ADMIN"); - await expect( - timelock.connect(timelockAdmin).removeOracleSignerAfterSignal(signer0.address) - ).to.be.revertedWithCustomError(errorsContract, "SignalTimeNotYetPassed"); + await expect(timelockConfig.connect(timelockAdmin).execute(oracleStore.address, payload)).to.be.revertedWith( + "TimelockController: operation is not ready" + ); await time.increase(1 * 24 * 60 * 60 + 10); expect(await oracleStore.getSignerCount()).eq(10); expect(await oracleStore.getSigner(0)).eq(signer0.address); - await timelock.connect(timelockAdmin).removeOracleSignerAfterSignal(signer0.address); + await timelockConfig.connect(timelockAdmin).execute(oracleStore.address, payload); expect(await oracleStore.getSignerCount()).eq(9); expect(await oracleStore.getSigner(0)).eq(signer9.address); }); it("setFeeReceiver", async () => { - await expect(timelock.connect(user2).signalSetFeeReceiver(user3.address)) + await expect(timelockConfig.connect(user2).signalSetFeeReceiver(user3.address)) .to.be.revertedWithCustomError(errorsContract, "Unauthorized") .withArgs(user2.address, "TIMELOCK_ADMIN"); - await timelock.connect(timelockAdmin).signalSetFeeReceiver(user3.address); + await timelockConfig.connect(timelockAdmin).signalSetFeeReceiver(user3.address); - await expect(timelock.connect(user2).setFeeReceiverAfterSignal(user3.address)) + const feeReceiverKey = hashString("FEE_RECEIVER"); + const payload = dataStore.interface.encodeFunctionData("setAddress", [feeReceiverKey, user3.address]); + await expect(timelockConfig.connect(user2).execute(dataStore.address, payload)) .to.be.revertedWithCustomError(errorsContract, "Unauthorized") .withArgs(user2.address, "TIMELOCK_ADMIN"); - await expect( - timelock.connect(timelockAdmin).setFeeReceiverAfterSignal(user3.address) - ).to.be.revertedWithCustomError(errorsContract, "SignalTimeNotYetPassed"); + await expect(timelockConfig.connect(timelockAdmin).execute(dataStore.address, payload)).to.be.revertedWith( + "TimelockController: operation is not ready" + ); await time.increase(1 * 24 * 60 * 60 + 10); expect(await dataStore.getAddress(keys.FEE_RECEIVER)).eq(ethers.constants.AddressZero); - await timelock.connect(timelockAdmin).setFeeReceiverAfterSignal(user3.address); + await timelockConfig.connect(timelockAdmin).execute(dataStore.address, payload); expect(await dataStore.getAddress(keys.FEE_RECEIVER)).eq(user3.address); }); @@ -142,25 +151,26 @@ describe("Timelock", () => { it("grantRole", async () => { const orderKeeperRole = hashString("ORDER_KEEPER"); - await expect(timelock.connect(user2).signalGrantRole(user3.address, orderKeeperRole)) + await expect(timelockConfig.connect(user2).signalGrantRole(user3.address, orderKeeperRole)) .to.be.revertedWithCustomError(errorsContract, "Unauthorized") .withArgs(user2.address, "TIMELOCK_ADMIN"); - await timelock.connect(timelockAdmin).signalGrantRole(user3.address, orderKeeperRole); + await timelockConfig.connect(timelockAdmin).signalGrantRole(user3.address, orderKeeperRole); - await expect(timelock.connect(user2).grantRoleAfterSignal(user3.address, orderKeeperRole)) + const payload = roleStore.interface.encodeFunctionData("grantRole", [user3.address, orderKeeperRole]); + await expect(timelockConfig.connect(user2).execute(roleStore.address, payload)) .to.be.revertedWithCustomError(errorsContract, "Unauthorized") .withArgs(user2.address, "TIMELOCK_ADMIN"); - await expect( - timelock.connect(timelockAdmin).grantRoleAfterSignal(user3.address, orderKeeperRole) - ).to.be.revertedWithCustomError(errorsContract, "SignalTimeNotYetPassed"); + await expect(timelockConfig.connect(timelockAdmin).execute(roleStore.address, payload)).to.be.revertedWith( + "TimelockController: operation is not ready" + ); await time.increase(1 * 24 * 60 * 60 + 10); expect(await roleStore.hasRole(user3.address, orderKeeperRole)).eq(false); - await timelock.connect(timelockAdmin).grantRoleAfterSignal(user3.address, orderKeeperRole); + await timelockConfig.connect(timelockAdmin).execute(roleStore.address, payload); expect(await roleStore.hasRole(user3.address, orderKeeperRole)).eq(true); }); @@ -169,50 +179,56 @@ describe("Timelock", () => { const orderKeeperRole = hashString("ORDER_KEEPER"); expect(await roleStore.hasRole(user3.address, orderKeeperRole)).eq(false); + const payloadGrantRole = roleStore.interface.encodeFunctionData("grantRole", [user3.address, orderKeeperRole]); + const payloadRevokeRole = roleStore.interface.encodeFunctionData("revokeRole", [user3.address, orderKeeperRole]); - await timelock.connect(timelockAdmin).signalGrantRole(user3.address, orderKeeperRole); + await timelockConfig.connect(timelockAdmin).signalGrantRole(user3.address, orderKeeperRole); await time.increase(1 * 24 * 60 * 60 + 10); - await timelock.connect(timelockAdmin).grantRoleAfterSignal(user3.address, orderKeeperRole); + await timelockConfig.connect(timelockAdmin).execute(roleStore.address, payloadGrantRole); expect(await roleStore.hasRole(user3.address, orderKeeperRole)).eq(true); - await expect(timelock.connect(user2).signalRevokeRole(user3.address, orderKeeperRole)) + await expect(timelockConfig.connect(user2).signalRevokeRole(user3.address, orderKeeperRole)) .to.be.revertedWithCustomError(errorsContract, "Unauthorized") .withArgs(user2.address, "TIMELOCK_ADMIN"); - await timelock.connect(timelockAdmin).signalRevokeRole(user3.address, orderKeeperRole); + await timelockConfig.connect(timelockAdmin).signalRevokeRole(user3.address, orderKeeperRole); - await expect(timelock.connect(user2).revokeRoleAfterSignal(user3.address, orderKeeperRole)) + await expect(timelockConfig.connect(user2).execute(roleStore.address, payloadRevokeRole)) .to.be.revertedWithCustomError(errorsContract, "Unauthorized") .withArgs(user2.address, "TIMELOCK_ADMIN"); await expect( - timelock.connect(timelockAdmin).revokeRoleAfterSignal(user3.address, orderKeeperRole) - ).to.be.revertedWithCustomError(errorsContract, "SignalTimeNotYetPassed"); + timelockConfig.connect(timelockAdmin).execute(roleStore.address, payloadRevokeRole) + ).to.be.revertedWith("TimelockController: operation is not ready"); await time.increase(1 * 24 * 60 * 60 + 10); expect(await roleStore.hasRole(user3.address, orderKeeperRole)).eq(true); - await timelock.connect(timelockAdmin).revokeRoleAfterSignal(user3.address, orderKeeperRole); + await timelockConfig.connect(timelockAdmin).execute(roleStore.address, payloadRevokeRole); expect(await roleStore.hasRole(user3.address, orderKeeperRole)).eq(false); }); it("setOracleProviderForToken", async () => { - await expect(timelock.connect(user2).signalSetOracleProviderForToken(wnt.address, user3.address)) + await expect(timelockConfig.connect(user2).signalSetOracleProviderForToken(wnt.address, user3.address)) .to.be.revertedWithCustomError(errorsContract, "Unauthorized") .withArgs(user2.address, "TIMELOCK_ADMIN"); - await timelock.connect(timelockAdmin).signalSetOracleProviderForToken(wnt.address, user3.address); + await timelockConfig.connect(timelockAdmin).signalSetOracleProviderForToken(wnt.address, user3.address); - await expect(timelock.connect(user2).setOracleProviderForTokenAfterSignal(wnt.address, user3.address)) + const payload = dataStore.interface.encodeFunctionData("setAddress", [ + keys.oracleProviderForTokenKey(wnt.address), + user3.address, + ]); + await expect(timelockConfig.connect(user2).execute(dataStore.address, payload)) .to.be.revertedWithCustomError(errorsContract, "Unauthorized") .withArgs(user2.address, "TIMELOCK_ADMIN"); - await expect( - timelock.connect(timelockAdmin).setOracleProviderForTokenAfterSignal(wnt.address, user3.address) - ).to.be.revertedWithCustomError(errorsContract, "SignalTimeNotYetPassed"); + await expect(timelockConfig.connect(timelockAdmin).execute(dataStore.address, payload)).to.be.revertedWith( + "TimelockController: operation is not ready" + ); await time.increase(1 * 24 * 60 * 60 + 10); @@ -220,7 +236,7 @@ describe("Timelock", () => { fixture.contracts.gmOracleProvider.address ); - await timelock.connect(timelockAdmin).setOracleProviderForTokenAfterSignal(wnt.address, user3.address); + await timelockConfig.connect(timelockAdmin).execute(dataStore.address, payload); expect(await dataStore.getAddress(keys.oracleProviderForTokenKey(wnt.address))).eq(user3.address); }); @@ -230,28 +246,36 @@ describe("Timelock", () => { await dataStore.setUint(keys.priceFeedMultiplierKey(wnt.address), 0); await expect( - timelock.connect(user2).signalSetPriceFeed(wnt.address, user3.address, 1000, 24 * 60 * 60, decimalToFloat(5000)) + timelockConfig + .connect(user2) + .signalSetPriceFeed(wnt.address, user3.address, 1000, 24 * 60 * 60, decimalToFloat(5000)) ) .to.be.revertedWithCustomError(errorsContract, "Unauthorized") .withArgs(user2.address, "TIMELOCK_ADMIN"); - await timelock + await timelockConfig .connect(timelockAdmin) .signalSetPriceFeed(wnt.address, user3.address, 1000, 24 * 60 * 60, decimalToFloat(5000)); - await expect( - timelock - .connect(user2) - .setPriceFeedAfterSignal(wnt.address, user3.address, 1000, 24 * 60 * 60, decimalToFloat(5000)) - ) + const targets = [dataStore.address, dataStore.address, dataStore.address, dataStore.address]; + const values = [0, 0, 0, 0]; + const payloads = [ + dataStore.interface.encodeFunctionData("setAddress", [keys.priceFeedKey(wnt.address), user3.address]), + dataStore.interface.encodeFunctionData("setUint", [keys.priceFeedMultiplierKey(wnt.address), 1000]), + dataStore.interface.encodeFunctionData("setUint", [ + keys.priceFeedHeartbeatDurationKey(wnt.address), + 24 * 60 * 60, + ]), + dataStore.interface.encodeFunctionData("setUint", [keys.stablePriceKey(wnt.address), decimalToFloat(5000)]), + ]; + + await expect(timelockConfig.connect(user2).executeBatch(targets, values, payloads)) .to.be.revertedWithCustomError(errorsContract, "Unauthorized") .withArgs(user2.address, "TIMELOCK_ADMIN"); - await expect( - timelock - .connect(timelockAdmin) - .setPriceFeedAfterSignal(wnt.address, user3.address, 1000, 24 * 60 * 60, decimalToFloat(5000)) - ).to.be.revertedWithCustomError(errorsContract, "SignalTimeNotYetPassed"); + await expect(timelockConfig.connect(timelockAdmin).executeBatch(targets, values, payloads)).to.be.revertedWith( + "TimelockController: operation is not ready" + ); await time.increase(1 * 24 * 60 * 60 + 10); @@ -259,9 +283,7 @@ describe("Timelock", () => { expect(await dataStore.getUint(keys.priceFeedMultiplierKey(wnt.address))).eq(0); expect(await dataStore.getUint(keys.stablePriceKey(wnt.address))).eq(0); - await timelock - .connect(timelockAdmin) - .setPriceFeedAfterSignal(wnt.address, user3.address, 1000, 24 * 60 * 60, decimalToFloat(5000)); + await timelockConfig.connect(timelockAdmin).executeBatch(targets, values, payloads); expect(await dataStore.getAddress(keys.priceFeedKey(wnt.address))).eq(user3.address); expect(await dataStore.getUint(keys.priceFeedMultiplierKey(wnt.address))).eq(1000); @@ -273,38 +295,39 @@ describe("Timelock", () => { const p99 = percentageToFloat("99%"); await expect( - timelock.connect(user2).signalSetDataStream(wnt.address, hashString("WNT"), expandDecimals(1, 34), p99) + timelockConfig.connect(user2).signalSetDataStream(wnt.address, hashString("WNT"), expandDecimals(1, 34), p99) ) .to.be.revertedWithCustomError(errorsContract, "Unauthorized") .withArgs(user2.address, "TIMELOCK_ADMIN"); await expect( - timelock + timelockConfig .connect(timelockAdmin) .signalSetDataStream(wnt.address, hashString("WNT"), expandDecimals(1, 34), percentageToFloat("101%")) ).to.be.revertedWithCustomError(errorsContract, "ConfigValueExceedsAllowedRange"); - await timelock + await timelockConfig .connect(timelockAdmin) .signalSetDataStream(wnt.address, hashString("WNT"), expandDecimals(1, 34), p99); - await expect( - timelock.connect(user2).setDataStreamAfterSignal(wnt.address, hashString("WNT"), expandDecimals(1, 34), p99) - ) + const targets = [dataStore.address, dataStore.address, dataStore.address]; + const values = [0, 0, 0]; + const payloads = [ + dataStore.interface.encodeFunctionData("setBytes32", [keys.dataStreamIdKey(wnt.address), hashString("WNT")]), + dataStore.interface.encodeFunctionData("setUint", [ + keys.dataStreamMultiplierKey(wnt.address), + expandDecimals(1, 34), + ]), + dataStore.interface.encodeFunctionData("setUint", [keys.dataStreamSpreadReductionFactorKey(wnt.address), p99]), + ]; + + await expect(timelockConfig.connect(user2).executeBatch(targets, values, payloads)) .to.be.revertedWithCustomError(errorsContract, "Unauthorized") .withArgs(user2.address, "TIMELOCK_ADMIN"); - await expect( - timelock.connect(user2).setDataStreamAfterSignal(wnt.address, hashString("WNT"), expandDecimals(1, 34), p99) - ) - .to.be.revertedWithCustomError(errorsContract, "Unauthorized") - .withArgs(user2.address, "TIMELOCK_ADMIN"); - - await expect( - timelock - .connect(timelockAdmin) - .setDataStreamAfterSignal(wnt.address, hashString("WNT"), expandDecimals(1, 34), p99) - ).to.be.revertedWithCustomError(errorsContract, "SignalTimeNotYetPassed"); + await expect(timelockConfig.connect(timelockAdmin).executeBatch(targets, values, payloads)).to.be.revertedWith( + "TimelockController: operation is not ready" + ); await time.increase(1 * 24 * 60 * 60 + 10); @@ -312,9 +335,7 @@ describe("Timelock", () => { expect(await dataStore.getUint(keys.dataStreamMultiplierKey(wnt.address))).eq(0); expect(await dataStore.getUint(keys.dataStreamSpreadReductionFactorKey(wnt.address))).eq(0); - await timelock - .connect(timelockAdmin) - .setDataStreamAfterSignal(wnt.address, hashString("WNT"), expandDecimals(1, 34), p99); + await timelockConfig.connect(timelockAdmin).executeBatch(targets, values, payloads); expect(await dataStore.getBytes32(keys.dataStreamIdKey(wnt.address))).eq(hashString("WNT")); expect(await dataStore.getUint(keys.dataStreamMultiplierKey(wnt.address))).eq(expandDecimals(1, 34)); diff --git a/utils/fixture.ts b/utils/fixture.ts index f09da3b2d..39e0685d1 100644 --- a/utils/fixture.ts +++ b/utils/fixture.ts @@ -62,7 +62,6 @@ export async function deployFixture() { const configUtils = await hre.ethers.getContract("ConfigUtils"); const configSyncer = await hre.ethers.getContract("ConfigSyncer"); const mockRiskOracle = await hre.ethers.getContract("MockRiskOracle"); - const timelock = await hre.ethers.getContract("Timelock"); const timelockConfig = await hre.ethers.getContract("TimelockConfig"); const configTimelockController = await hre.ethers.getContract("ConfigTimelockController"); const reader = await hre.ethers.getContract("Reader"); @@ -260,7 +259,6 @@ export async function deployFixture() { configUtils, configSyncer, mockRiskOracle, - timelock, timelockConfig, configTimelockController, reader, From 5a345db7e6442229f1b073622e696cb748729d9a Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 28 Feb 2025 11:19:08 +0200 Subject: [PATCH 192/205] Inherit BaseGelatoRelayRouter instead of GelatoRelayRouter into MultichainRouter --- contracts/exchange/AdlHandler.sol | 2 +- contracts/exchange/LiquidationHandler.sol | 2 +- contracts/multichain/MultichainOrderRouter.sol | 6 +++--- contracts/multichain/MultichainRouter.sol | 6 +++--- deploy/deployMultichainOrderRouter.ts | 2 +- test/router/SubaccountRouter.ts | 4 ++-- utils/relay/multichain.ts | 6 +++--- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/contracts/exchange/AdlHandler.sol b/contracts/exchange/AdlHandler.sol index 2efa2bb8d..fdad8399d 100644 --- a/contracts/exchange/AdlHandler.sol +++ b/contracts/exchange/AdlHandler.sol @@ -39,7 +39,7 @@ contract AdlHandler is BaseOrderHandler { _dataStore, _eventEmitter, _oracle, - MultichainVault(payable(0)), // TODO: confirm no possibility of multichain ADL + MultichainVault(payable(0)), _orderVault, _swapHandler, _referralStorage diff --git a/contracts/exchange/LiquidationHandler.sol b/contracts/exchange/LiquidationHandler.sol index 91c99252b..109fd474d 100644 --- a/contracts/exchange/LiquidationHandler.sol +++ b/contracts/exchange/LiquidationHandler.sol @@ -26,7 +26,7 @@ contract LiquidationHandler is BaseOrderHandler { _dataStore, _eventEmitter, _oracle, - MultichainVault(payable(0)), // TODO: confirm no possibility of multichain liquidations + MultichainVault(payable(0)), _orderVault, _swapHandler, _referralStorage diff --git a/contracts/multichain/MultichainOrderRouter.sol b/contracts/multichain/MultichainOrderRouter.sol index a02197824..25b5af2b0 100644 --- a/contracts/multichain/MultichainOrderRouter.sol +++ b/contracts/multichain/MultichainOrderRouter.sol @@ -10,7 +10,7 @@ contract MultichainOrderRouter is MultichainRouter { // TODO: handle partial fee payment - function createMultichainOrder( + function createOrder( RelayUtils.RelayParams calldata relayParams, address account, uint256 srcChainId, @@ -26,7 +26,7 @@ contract MultichainOrderRouter is MultichainRouter { return _createOrder(relayParams, account, collateralDeltaAmount, srcChainId, params, false); } - function updateMultichainOrder( + function updateOrder( RelayUtils.RelayParams calldata relayParams, address account, uint256 srcChainId, @@ -43,7 +43,7 @@ contract MultichainOrderRouter is MultichainRouter { _updateOrder(relayParams, account, key, params, increaseExecutionFee, false); } - function cancelMultichainOrder( + function cancelOrder( RelayUtils.RelayParams calldata relayParams, address account, uint256 srcChainId, diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 762f48d92..185bbed4a 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -2,11 +2,11 @@ pragma solidity ^0.8.0; -import "../router/relay/GelatoRelayRouter.sol"; +import "../router/relay/BaseGelatoRelayRouter.sol"; import "./MultichainUtils.sol"; -abstract contract MultichainRouter is GelatoRelayRouter { +abstract contract MultichainRouter is BaseGelatoRelayRouter { struct BaseConstructorParams { Router router; @@ -24,7 +24,7 @@ abstract contract MultichainRouter is GelatoRelayRouter { constructor( BaseConstructorParams memory params ) - GelatoRelayRouter( + BaseGelatoRelayRouter( params.router, params.dataStore, params.eventEmitter, diff --git a/deploy/deployMultichainOrderRouter.ts b/deploy/deployMultichainOrderRouter.ts index 32d34aed7..4b0a75a12 100644 --- a/deploy/deployMultichainOrderRouter.ts +++ b/deploy/deployMultichainOrderRouter.ts @@ -31,7 +31,7 @@ const func = createDeployFunction({ return [baseParams]; }, - libraryNames: ["MarketUtils", "MultichainUtils", "OrderStoreUtils", "RelayUtils", "SwapUtils"], + libraryNames: ["MarketUtils", "MultichainUtils", "OrderStoreUtils", "PositionStoreUtils", "RelayUtils", "SwapUtils"], afterDeploy: async ({ deployedContract }) => { await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); diff --git a/test/router/SubaccountRouter.ts b/test/router/SubaccountRouter.ts index 34d88b370..6363e2866 100644 --- a/test/router/SubaccountRouter.ts +++ b/test/router/SubaccountRouter.ts @@ -267,8 +267,8 @@ describe("SubaccountRouter", () => { expect(order._dataList).deep.eq(dataList); // 0.1 WETH in total - expect(order.numbers.executionFee).eq("2111029140000000"); - await expectBalance(wnt.address, user2.address, "97888970860000000"); + expect(order.numbers.executionFee).eq("2111033000000000"); + await expectBalance(wnt.address, user2.address, "97888967000000000"); expect( await dataStore.getUint( diff --git a/utils/relay/multichain.ts b/utils/relay/multichain.ts index da3d01fd3..6b1cfa667 100644 --- a/utils/relay/multichain.ts +++ b/utils/relay/multichain.ts @@ -192,7 +192,7 @@ export async function sendCreateOrder(p: { signature = await getCreateOrderSignature({ ...p, relayParams, verifyingContract: p.relayRouter.address }); } - const createOrderCalldata = p.relayRouter.interface.encodeFunctionData("createMultichainOrder", [ + const createOrderCalldata = p.relayRouter.interface.encodeFunctionData("createOrder", [ { ...relayParams, signature }, p.account, p.srcChainId, @@ -250,7 +250,7 @@ export async function sendUpdateOrder(p: { signature = await getUpdateOrderSignature({ ...p, relayParams, verifyingContract: p.relayRouter.address }); } - const UpdateOrderCalldata = p.relayRouter.interface.encodeFunctionData("updateMultichainOrder", [ + const UpdateOrderCalldata = p.relayRouter.interface.encodeFunctionData("updateOrder", [ { ...relayParams, signature }, p.account, p.srcChainId, @@ -299,7 +299,7 @@ export async function sendCancelOrder(p: { signature = await getCancelOrderSignature({ ...p, relayParams, verifyingContract: p.relayRouter.address }); } - const CancelOrderCalldata = p.relayRouter.interface.encodeFunctionData("cancelMultichainOrder", [ + const CancelOrderCalldata = p.relayRouter.interface.encodeFunctionData("cancelOrder", [ { ...relayParams, signature }, p.account, p.srcChainId, From 12c7f767abd8b4b007ee857a64537652c8e39abe Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 28 Feb 2025 13:25:36 +0200 Subject: [PATCH 193/205] Handle partial fee payment for multichain update/cancel orders --- contracts/error/Errors.sol | 2 + .../multichain/MultichainOrderRouter.sol | 75 ++++++++++++++++++- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/contracts/error/Errors.sol b/contracts/error/Errors.sol index 3357b7e1b..682e84a24 100644 --- a/contracts/error/Errors.sol +++ b/contracts/error/Errors.sol @@ -436,6 +436,8 @@ library Errors { error InsufficientMultichainBalance(address account, address token, uint256 balance, uint256 amount); error InvalidDestinationChainId(uint256 desChainId); error InvalidMultichainProvider(address provider); + error UnableToPayOrderFee(); + error UnableToPayOrderFeeFromCollateral(); enum SignatureType { Call, diff --git a/contracts/multichain/MultichainOrderRouter.sol b/contracts/multichain/MultichainOrderRouter.sol index 25b5af2b0..2f612e059 100644 --- a/contracts/multichain/MultichainOrderRouter.sol +++ b/contracts/multichain/MultichainOrderRouter.sol @@ -3,13 +3,14 @@ pragma solidity ^0.8.0; import "./MultichainRouter.sol"; +import "../position/PositionStoreUtils.sol"; contract MultichainOrderRouter is MultichainRouter { + using Order for Order.Props; + using Position for Position.Props; constructor(BaseConstructorParams memory params) MultichainRouter(params) {} - // TODO: handle partial fee payment - function createOrder( RelayUtils.RelayParams calldata relayParams, address account, @@ -40,6 +41,8 @@ contract MultichainOrderRouter is MultichainRouter { bytes32 structHash = RelayUtils.getUpdateOrderStructHash(relayParams, key, params, increaseExecutionFee); _validateCall(relayParams, account, structHash, srcChainId); + _handleFeePayment(relayParams, account, srcChainId, key); + _updateOrder(relayParams, account, key, params, increaseExecutionFee, false); } @@ -55,6 +58,74 @@ contract MultichainOrderRouter is MultichainRouter { bytes32 structHash = RelayUtils.getCancelOrderStructHash(relayParams, key); _validateCall(relayParams, account, structHash, srcChainId); + _handleFeePayment(relayParams, account, srcChainId, key); + _cancelOrder(relayParams, account, key, false /* isSubaccount */); } + + function _handleFeePayment( + RelayUtils.RelayParams calldata relayParams, + address account, + uint256 srcChainId, + bytes32 key + ) internal { + // check if user has sufficient Multichain balance to pay for fee + uint256 balance = MultichainUtils.getMultichainBalanceAmount(dataStore, account, relayParams.fee.feeToken); + if (balance >= relayParams.fee.feeAmount) { + return; + } + + Order.Props memory order = OrderStoreUtils.get(dataStore, key); + bytes32 positionKey = Position.getPositionKey( + order.account(), + order.market(), + order.initialCollateralToken(), + order.isLong() + ); + Position.Props memory position = PositionStoreUtils.get(dataStore, positionKey); + + if (relayParams.fee.feeToken != position.collateralToken()) { + revert Errors.UnableToPayOrderFee(); + } + + if (BaseOrderUtils.isSwapOrder(order.orderType())) { + revert Errors.UnableToPayOrderFee(); + } + + uint256 unpaidAmount = relayParams.fee.feeAmount - balance; + + // First try to deduct from order collateral + uint256 initialCollateralDeltaAmount = order.initialCollateralDeltaAmount(); + if (initialCollateralDeltaAmount > 0) { + uint256 deductFromOrder = initialCollateralDeltaAmount > unpaidAmount + ? unpaidAmount + : initialCollateralDeltaAmount; + + unpaidAmount -= deductFromOrder; + dataStore.setUint( + keccak256(abi.encode(key, OrderStoreUtils.INITIAL_COLLATERAL_DELTA_AMOUNT)), + initialCollateralDeltaAmount - deductFromOrder + ); + orderVault.transferOut(relayParams.fee.feeToken, address(multichainVault), deductFromOrder); + MultichainUtils.recordTransferIn(dataStore, eventEmitter, multichainVault, relayParams.fee.feeToken, account, srcChainId); + + if (unpaidAmount == 0) { + return; + } + } + + // Second try to deduct from position collateral + uint256 positionCollateralAmount = position.collateralAmount(); + if (positionCollateralAmount < unpaidAmount) { + revert Errors.UnableToPayOrderFeeFromCollateral(); + } + + position.setCollateralAmount(positionCollateralAmount - unpaidAmount); + dataStore.setUint( + keccak256(abi.encode(key, PositionStoreUtils.COLLATERAL_AMOUNT)), + positionCollateralAmount + ); + orderVault.transferOut(relayParams.fee.feeToken, address(multichainVault), unpaidAmount); + MultichainUtils.recordTransferIn(dataStore, eventEmitter, multichainVault, relayParams.fee.feeToken, account, srcChainId); + } } From 0759a698a67188719f1685817c1896c29ace8ffc Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 28 Feb 2025 15:41:49 +0200 Subject: [PATCH 194/205] Inherit BaseRouter into MultichainTransferRouter for multicall functionality It's actually inherited into BaseGelatoRelayRouter instead of MultichainTransferRouter due to inheritance structure. Since SubaccountGelatoRelayRouter was getting too big, some of it's logic is moved into a new external lib SubaccountRelayUtils --- contracts/mock/MockGelatoRelay.sol | 3 +- contracts/multichain/MultichainGlvRouter.sol | 2 +- contracts/multichain/MultichainGmRouter.sol | 2 +- .../multichain/MultichainOrderRouter.sol | 4 +- contracts/multichain/MultichainRouter.sol | 4 +- .../multichain/MultichainTransferRouter.sol | 7 +- .../router/relay/BaseGelatoRelayRouter.sol | 12 +- contracts/router/relay/GelatoRelayRouter.sol | 4 +- .../relay/SubaccountGelatoRelayRouter.sol | 230 ++---------------- .../router/relay/SubaccountRelayUtils.sol | 220 +++++++++++++++++ deploy/deployGelatoRelayRouter.ts | 3 +- deploy/deployMultichainGlvRouter.ts | 4 +- deploy/deployMultichainGmRouter.ts | 4 +- deploy/deployMultichainOrderRouter.ts | 2 + deploy/deploySubaccountGelatoRelayRouter.ts | 3 +- deploy/deploySunaccountRelayUtils.ts | 7 + test/router/relay/signatures.ts | 3 + utils/fixture.ts | 2 + 18 files changed, 277 insertions(+), 239 deletions(-) create mode 100644 contracts/router/relay/SubaccountRelayUtils.sol create mode 100644 deploy/deploySunaccountRelayUtils.ts diff --git a/contracts/mock/MockGelatoRelay.sol b/contracts/mock/MockGelatoRelay.sol index 8cc456d7c..fb4070016 100644 --- a/contracts/mock/MockGelatoRelay.sol +++ b/contracts/mock/MockGelatoRelay.sol @@ -17,13 +17,14 @@ contract MockGelatoRelayRouter is GelatoRelayRouter { constructor( Router _router, + RoleStore _roleStore, DataStore _dataStore, EventEmitter _eventEmitter, Oracle _oracle, IOrderHandler _orderHandler, OrderVault _orderVault, IExternalHandler _externalHandler - ) GelatoRelayRouter(_router, _dataStore, _eventEmitter, _oracle, _orderHandler, _orderVault, _externalHandler) {} + ) GelatoRelayRouter(_router, _roleStore, _dataStore, _eventEmitter, _oracle, _orderHandler, _orderVault, _externalHandler) {} function testCancelOrderSignature( RelayUtils.RelayParams calldata relayParams, diff --git a/contracts/multichain/MultichainGlvRouter.sol b/contracts/multichain/MultichainGlvRouter.sol index aaf9230f9..72a4fcf05 100644 --- a/contracts/multichain/MultichainGlvRouter.sol +++ b/contracts/multichain/MultichainGlvRouter.sol @@ -16,7 +16,7 @@ contract MultichainGlvRouter is MultichainRouter { BaseConstructorParams memory params, GlvHandler _glvHandler, GlvVault _glvVault - ) MultichainRouter(params) { + ) MultichainRouter(params) BaseRouter(params.router, params.roleStore, params.dataStore, params.eventEmitter) { glvHandler = _glvHandler; glvVault = _glvVault; } diff --git a/contracts/multichain/MultichainGmRouter.sol b/contracts/multichain/MultichainGmRouter.sol index d0e813e8a..b976f1954 100644 --- a/contracts/multichain/MultichainGmRouter.sol +++ b/contracts/multichain/MultichainGmRouter.sol @@ -24,7 +24,7 @@ contract MultichainGmRouter is MultichainRouter { WithdrawalVault _withdrawalVault, WithdrawalHandler _withdrawalHandler, ShiftVault _shiftVault - ) MultichainRouter(params) { + ) MultichainRouter(params) BaseRouter(params.router, params.roleStore, params.dataStore, params.eventEmitter) { depositVault = _depositVault; depositHandler = _depositHandler; withdrawalVault = _withdrawalVault; diff --git a/contracts/multichain/MultichainOrderRouter.sol b/contracts/multichain/MultichainOrderRouter.sol index 2f612e059..70e84d940 100644 --- a/contracts/multichain/MultichainOrderRouter.sol +++ b/contracts/multichain/MultichainOrderRouter.sol @@ -9,7 +9,9 @@ contract MultichainOrderRouter is MultichainRouter { using Order for Order.Props; using Position for Position.Props; - constructor(BaseConstructorParams memory params) MultichainRouter(params) {} + constructor( + BaseConstructorParams memory params + ) MultichainRouter(params) BaseRouter(params.router, params.roleStore, params.dataStore, params.eventEmitter) {} function createOrder( RelayUtils.RelayParams calldata relayParams, diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 185bbed4a..7fdbea553 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -10,6 +10,7 @@ abstract contract MultichainRouter is BaseGelatoRelayRouter { struct BaseConstructorParams { Router router; + RoleStore roleStore; DataStore dataStore; EventEmitter eventEmitter; Oracle oracle; @@ -25,9 +26,6 @@ abstract contract MultichainRouter is BaseGelatoRelayRouter { BaseConstructorParams memory params ) BaseGelatoRelayRouter( - params.router, - params.dataStore, - params.eventEmitter, params.oracle, params.orderHandler, params.orderVault, diff --git a/contracts/multichain/MultichainTransferRouter.sol b/contracts/multichain/MultichainTransferRouter.sol index a7fdf8d69..9f1d0619a 100644 --- a/contracts/multichain/MultichainTransferRouter.sol +++ b/contracts/multichain/MultichainTransferRouter.sol @@ -6,10 +6,13 @@ import "./MultichainRouter.sol"; import "./MultichainUtils.sol"; import "./IMultichainProvider.sol"; -contract MultichainTransferRouter is MultichainRouter { // TODO: inherit BaseRouter for bridgeIn multicall +contract MultichainTransferRouter is MultichainRouter { IMultichainProvider multichainProvider; - constructor(BaseConstructorParams memory params, IMultichainProvider _multichainProvider) MultichainRouter(params) { + constructor( + BaseConstructorParams memory params, + IMultichainProvider _multichainProvider + ) MultichainRouter(params) BaseRouter(params.router, params.roleStore, params.dataStore, params.eventEmitter) { multichainProvider = _multichainProvider; } diff --git a/contracts/router/relay/BaseGelatoRelayRouter.sol b/contracts/router/relay/BaseGelatoRelayRouter.sol index e87dcccc9..160619eb3 100644 --- a/contracts/router/relay/BaseGelatoRelayRouter.sol +++ b/contracts/router/relay/BaseGelatoRelayRouter.sol @@ -16,13 +16,14 @@ import "../../oracle/OracleModule.sol"; import "../../order/IBaseOrderUtils.sol"; import "../../order/OrderStoreUtils.sol"; import "../../order/OrderVault.sol"; +import "../../router/BaseRouter.sol"; import "../../router/Router.sol"; import "../../swap/SwapUtils.sol"; import "../../token/TokenUtils.sol"; import "./RelayUtils.sol"; -abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, OracleModule { +abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, OracleModule, BaseRouter { using Order for Order.Props; struct Contracts { @@ -33,9 +34,6 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, IOrderHandler public immutable orderHandler; OrderVault public immutable orderVault; - Router public immutable router; - DataStore public immutable dataStore; - EventEmitter public immutable eventEmitter; IExternalHandler public immutable externalHandler; bytes32 public constant DOMAIN_SEPARATOR_TYPEHASH = @@ -47,9 +45,6 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, mapping(address => uint256) public userNonces; constructor( - Router _router, - DataStore _dataStore, - EventEmitter _eventEmitter, Oracle _oracle, IOrderHandler _orderHandler, OrderVault _orderVault, @@ -57,9 +52,6 @@ abstract contract BaseGelatoRelayRouter is GelatoRelayContext, ReentrancyGuard, ) OracleModule(_oracle) { orderHandler = _orderHandler; orderVault = _orderVault; - router = _router; - dataStore = _dataStore; - eventEmitter = _eventEmitter; externalHandler = _externalHandler; } diff --git a/contracts/router/relay/GelatoRelayRouter.sol b/contracts/router/relay/GelatoRelayRouter.sol index 46452bfee..de1912d6c 100644 --- a/contracts/router/relay/GelatoRelayRouter.sol +++ b/contracts/router/relay/GelatoRelayRouter.sol @@ -15,6 +15,7 @@ contract GelatoRelayRouter is BaseGelatoRelayRouter { constructor( Router _router, + RoleStore _roleStore, DataStore _dataStore, EventEmitter _eventEmitter, Oracle _oracle, @@ -22,7 +23,8 @@ contract GelatoRelayRouter is BaseGelatoRelayRouter { OrderVault _orderVault, IExternalHandler _externalHandler ) - BaseGelatoRelayRouter(_router, _dataStore, _eventEmitter, _oracle, _orderHandler, _orderVault, _externalHandler) + BaseGelatoRelayRouter(_oracle, _orderHandler, _orderVault, _externalHandler) + BaseRouter(_router, _roleStore, _dataStore, _eventEmitter) {} // @note all params except account should be part of the corresponding struct hash diff --git a/contracts/router/relay/SubaccountGelatoRelayRouter.sol b/contracts/router/relay/SubaccountGelatoRelayRouter.sol index ec494b094..e48dcf702 100644 --- a/contracts/router/relay/SubaccountGelatoRelayRouter.sol +++ b/contracts/router/relay/SubaccountGelatoRelayRouter.sol @@ -7,70 +7,16 @@ import "../../router/Router.sol"; import "../../feature/FeatureUtils.sol"; import "../../subaccount/SubaccountUtils.sol"; import "./BaseGelatoRelayRouter.sol"; +import "./SubaccountRelayUtils.sol"; contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { using Order for Order.Props; - struct SubaccountApproval { - address subaccount; - bool shouldAdd; - uint256 expiresAt; - uint256 maxAllowedCount; - bytes32 actionType; - uint256 nonce; // for replay attack protection - uint256 deadline; - bytes signature; - } - - bytes32 public constant UPDATE_ORDER_TYPEHASH = - keccak256( - bytes( - "UpdateOrder(address account,bytes32 key,UpdateOrderParams params,bool increaseExecutionFee,bytes32 relayParams,bytes32 subaccountApproval)UpdateOrderParams(uint256 sizeDeltaUsd,uint256 acceptablePrice,uint256 triggerPrice,uint256 minOutputAmount,uint256 validFromTime,bool autoCancel)" - ) - ); - bytes32 public constant UPDATE_ORDER_PARAMS_TYPEHASH = - keccak256( - bytes( - "UpdateOrderParams(uint256 sizeDeltaUsd,uint256 acceptablePrice,uint256 triggerPrice,uint256 minOutputAmount,uint256 validFromTime,bool autoCancel)" - ) - ); - - bytes32 public constant CANCEL_ORDER_TYPEHASH = - keccak256(bytes("CancelOrder(address account,bytes32 key,bytes32 relayParams,bytes32 subaccountApproval)")); - - bytes32 public constant CREATE_ORDER_TYPEHASH = - keccak256( - bytes( - "CreateOrder(uint256 collateralDeltaAmount,address account,CreateOrderAddresses addresses,CreateOrderNumbers numbers,uint256 orderType,uint256 decreasePositionSwapType,bool isLong,bool shouldUnwrapNativeToken,bool autoCancel,bytes32 referralCode,bytes32[] dataList,bytes32 relayParams,bytes32 subaccountApproval)CreateOrderAddresses(address receiver,address cancellationReceiver,address callbackContract,address uiFeeReceiver,address market,address initialCollateralToken,address[] swapPath)CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime)" - ) - ); - bytes32 public constant CREATE_ORDER_NUMBERS_TYPEHASH = - keccak256( - bytes( - "CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime)" - ) - ); - bytes32 public constant CREATE_ORDER_ADDRESSES_TYPEHASH = - keccak256( - bytes( - "CreateOrderAddresses(address receiver,address cancellationReceiver,address callbackContract,address uiFeeReceiver,address market,address initialCollateralToken,address[] swapPath)" - ) - ); - - bytes32 public constant SUBACCOUNT_APPROVAL_TYPEHASH = - keccak256( - bytes( - "SubaccountApproval(address subaccount,bool shouldAdd,uint256 expiresAt,uint256 maxAllowedCount,bytes32 actionType,uint256 nonce,uint256 deadline)" - ) - ); - - bytes32 public constant REMOVE_SUBACCOUNT_TYPEHASH = - keccak256(bytes("RemoveSubaccount(address subaccount,bytes32 relayParams)")); - mapping(address => uint256) public subaccountApprovalNonces; constructor( Router _router, + RoleStore _roleStore, DataStore _dataStore, EventEmitter _eventEmitter, Oracle _oracle, @@ -78,13 +24,15 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { OrderVault _orderVault, IExternalHandler _externalHandler ) - BaseGelatoRelayRouter(_router, _dataStore, _eventEmitter, _oracle, _orderHandler, _orderVault, _externalHandler) + BaseGelatoRelayRouter(_oracle, _orderHandler, _orderVault, _externalHandler) + BaseRouter(_router, _roleStore, _dataStore, _eventEmitter) + {} // @note all params except subaccount should be part of the corresponding struct hash function createOrder( RelayUtils.RelayParams calldata relayParams, - SubaccountApproval calldata subaccountApproval, + SubaccountRelayUtils.SubaccountApproval calldata subaccountApproval, address account, // main account address subaccount, uint256 collateralDeltaAmount, @@ -97,7 +45,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { returns (bytes32) { _validateGaslessFeature(); - bytes32 structHash = _getCreateOrderStructHash( + bytes32 structHash = SubaccountRelayUtils.getCreateOrderStructHash( relayParams, subaccountApproval, account, @@ -129,7 +77,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { // @note all params except subaccount should be part of the corresponding struct hash function updateOrder( RelayUtils.RelayParams calldata relayParams, - SubaccountApproval calldata subaccountApproval, + SubaccountRelayUtils.SubaccountApproval calldata subaccountApproval, address account, // main account address subaccount, bytes32 key, @@ -138,7 +86,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { ) external nonReentrant withOraclePricesForAtomicAction(relayParams.oracleParams) onlyGelatoRelay { _validateGaslessFeature(); - bytes32 structHash = _getUpdateOrderStructHash( + bytes32 structHash = SubaccountRelayUtils.getUpdateOrderStructHash( relayParams, subaccountApproval, account, @@ -161,14 +109,14 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { // @note all params except subaccount should be part of the corresponding struct hash function cancelOrder( RelayUtils.RelayParams calldata relayParams, - SubaccountApproval calldata subaccountApproval, + SubaccountRelayUtils.SubaccountApproval calldata subaccountApproval, address account, // main account address subaccount, bytes32 key ) external nonReentrant withOraclePricesForAtomicAction(relayParams.oracleParams) onlyGelatoRelay { _validateGaslessFeature(); - bytes32 structHash = _getCancelOrderStructHash(relayParams, subaccountApproval, account, key); + bytes32 structHash = SubaccountRelayUtils.getCancelOrderStructHash(relayParams, subaccountApproval, account, key); _validateCall(relayParams, subaccount, structHash, 0 /* srcChainId */); _handleSubaccountAction(account, subaccount, Keys.SUBACCOUNT_ORDER_ACTION, subaccountApproval); @@ -187,7 +135,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { address subaccount ) external nonReentrant withOraclePricesForAtomicAction(relayParams.oracleParams) onlyGelatoRelay { _validateGaslessFeature(); - bytes32 structHash = _getRemoveSubaccountStructHash(relayParams, subaccount); + bytes32 structHash = SubaccountRelayUtils.getRemoveSubaccountStructHash(relayParams, subaccount); _validateCall(relayParams, account, structHash, 0 /* srcChainId */); Contracts memory contracts = Contracts({ @@ -211,7 +159,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { address account, address subaccount, bytes32 actionType, - SubaccountApproval calldata subaccountApproval + SubaccountRelayUtils.SubaccountApproval calldata subaccountApproval ) internal { FeatureUtils.validateFeature(dataStore, Keys.subaccountFeatureDisabledKey(address(this))); @@ -220,7 +168,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { SubaccountUtils.handleSubaccountAction(dataStore, eventEmitter, account, subaccount, actionType); } - function _handleSubaccountApproval(address account, SubaccountApproval calldata subaccountApproval) internal { + function _handleSubaccountApproval(address account, SubaccountRelayUtils.SubaccountApproval calldata subaccountApproval) internal { if (subaccountApproval.signature.length == 0) { return; } @@ -240,7 +188,7 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { subaccountApprovalNonces[account] = storedNonce + 1; bytes32 domainSeparator = _getDomainSeparator(block.chainid); - bytes32 structHash = _getSubaccountApprovalStructHash(subaccountApproval); + bytes32 structHash = SubaccountRelayUtils.getSubaccountApprovalStructHash(subaccountApproval); bytes32 digest = ECDSA.toTypedDataHash(domainSeparator, structHash); _validateSignature(digest, subaccountApproval.signature, account, "subaccount approval"); @@ -270,152 +218,4 @@ contract SubaccountGelatoRelayRouter is BaseGelatoRelayRouter { SubaccountUtils.addSubaccount(dataStore, eventEmitter, account, subaccountApproval.subaccount); } } - - function _getRemoveSubaccountStructHash( - RelayUtils.RelayParams calldata relayParams, - address subaccount - ) internal pure returns (bytes32) { - return keccak256(abi.encode(REMOVE_SUBACCOUNT_TYPEHASH, subaccount, RelayUtils._getRelayParamsHash(relayParams))); - } - - function _getSubaccountApprovalStructHash( - SubaccountApproval calldata subaccountApproval - ) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - SUBACCOUNT_APPROVAL_TYPEHASH, - subaccountApproval.subaccount, - subaccountApproval.shouldAdd, - subaccountApproval.expiresAt, - subaccountApproval.maxAllowedCount, - subaccountApproval.actionType, - subaccountApproval.nonce, - subaccountApproval.deadline - ) - ); - } - - function _getCreateOrderStructHash( - RelayUtils.RelayParams calldata relayParams, - SubaccountApproval calldata subaccountApproval, - address account, - uint256 collateralDeltaAmount, - IBaseOrderUtils.CreateOrderParams memory params - ) internal pure returns (bytes32) { - bytes32 relayParamsHash = RelayUtils._getRelayParamsHash(relayParams); - bytes32 subaccountApprovalHash = keccak256(abi.encode(subaccountApproval)); - - return - keccak256( - abi.encode( - CREATE_ORDER_TYPEHASH, - collateralDeltaAmount, - account, - _getCreateOrderAddressesStructHash(params.addresses), - _getCreateOrderNumbersStructHash(params.numbers), - uint256(params.orderType), - uint256(params.decreasePositionSwapType), - params.isLong, - params.shouldUnwrapNativeToken, - params.autoCancel, - params.referralCode, - keccak256(abi.encodePacked(params.dataList)), - relayParamsHash, - subaccountApprovalHash - ) - ); - } - - function _getCreateOrderAddressesStructHash( - IBaseOrderUtils.CreateOrderParamsAddresses memory addresses - ) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - CREATE_ORDER_ADDRESSES_TYPEHASH, - addresses.receiver, - addresses.cancellationReceiver, - addresses.callbackContract, - addresses.uiFeeReceiver, - addresses.market, - addresses.initialCollateralToken, - keccak256(abi.encodePacked(addresses.swapPath)) - ) - ); - } - - function _getCreateOrderNumbersStructHash( - IBaseOrderUtils.CreateOrderParamsNumbers memory numbers - ) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - CREATE_ORDER_NUMBERS_TYPEHASH, - numbers.sizeDeltaUsd, - numbers.initialCollateralDeltaAmount, - numbers.triggerPrice, - numbers.acceptablePrice, - numbers.executionFee, - numbers.callbackGasLimit, - numbers.minOutputAmount, - numbers.validFromTime - ) - ); - } - - function _getUpdateOrderStructHash( - RelayUtils.RelayParams calldata relayParams, - SubaccountApproval calldata subaccountApproval, - address account, - bytes32 key, - RelayUtils.UpdateOrderParams calldata params, - bool increaseExecutionFee - ) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - UPDATE_ORDER_TYPEHASH, - account, - key, - _getUpdateOrderParamsStructHash(params), - increaseExecutionFee, - RelayUtils._getRelayParamsHash(relayParams), - keccak256(abi.encode(subaccountApproval)) - ) - ); - } - - function _getUpdateOrderParamsStructHash(RelayUtils.UpdateOrderParams calldata params) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - UPDATE_ORDER_PARAMS_TYPEHASH, - params.sizeDeltaUsd, - params.acceptablePrice, - params.triggerPrice, - params.minOutputAmount, - params.validFromTime, - params.autoCancel - ) - ); - } - - function _getCancelOrderStructHash( - RelayUtils.RelayParams calldata relayParams, - SubaccountApproval calldata subaccountApproval, - address account, - bytes32 key - ) internal pure returns (bytes32) { - return - keccak256( - abi.encode( - CANCEL_ORDER_TYPEHASH, - account, - key, - RelayUtils._getRelayParamsHash(relayParams), - keccak256(abi.encode(subaccountApproval)) - ) - ); - } } diff --git a/contracts/router/relay/SubaccountRelayUtils.sol b/contracts/router/relay/SubaccountRelayUtils.sol new file mode 100644 index 000000000..7db0d7006 --- /dev/null +++ b/contracts/router/relay/SubaccountRelayUtils.sol @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; + +import "../../order/IBaseOrderUtils.sol"; +import "../../router/Router.sol"; +import "../../feature/FeatureUtils.sol"; +import "../../subaccount/SubaccountUtils.sol"; +import "./BaseGelatoRelayRouter.sol"; + +library SubaccountRelayUtils { + using Order for Order.Props; + + struct SubaccountApproval { + address subaccount; + bool shouldAdd; + uint256 expiresAt; + uint256 maxAllowedCount; + bytes32 actionType; + uint256 nonce; // for replay attack protection + uint256 deadline; + bytes signature; + } + + bytes32 public constant UPDATE_ORDER_TYPEHASH = + keccak256( + bytes( + "UpdateOrder(address account,bytes32 key,UpdateOrderParams params,bool increaseExecutionFee,bytes32 relayParams,bytes32 subaccountApproval)UpdateOrderParams(uint256 sizeDeltaUsd,uint256 acceptablePrice,uint256 triggerPrice,uint256 minOutputAmount,uint256 validFromTime,bool autoCancel)" + ) + ); + bytes32 public constant UPDATE_ORDER_PARAMS_TYPEHASH = + keccak256( + bytes( + "UpdateOrderParams(uint256 sizeDeltaUsd,uint256 acceptablePrice,uint256 triggerPrice,uint256 minOutputAmount,uint256 validFromTime,bool autoCancel)" + ) + ); + + bytes32 public constant CANCEL_ORDER_TYPEHASH = + keccak256(bytes("CancelOrder(address account,bytes32 key,bytes32 relayParams,bytes32 subaccountApproval)")); + + bytes32 public constant CREATE_ORDER_TYPEHASH = + keccak256( + bytes( + "CreateOrder(uint256 collateralDeltaAmount,address account,CreateOrderAddresses addresses,CreateOrderNumbers numbers,uint256 orderType,uint256 decreasePositionSwapType,bool isLong,bool shouldUnwrapNativeToken,bool autoCancel,bytes32 referralCode,bytes32[] dataList,bytes32 relayParams,bytes32 subaccountApproval)CreateOrderAddresses(address receiver,address cancellationReceiver,address callbackContract,address uiFeeReceiver,address market,address initialCollateralToken,address[] swapPath)CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime)" + ) + ); + bytes32 public constant CREATE_ORDER_NUMBERS_TYPEHASH = + keccak256( + bytes( + "CreateOrderNumbers(uint256 sizeDeltaUsd,uint256 initialCollateralDeltaAmount,uint256 triggerPrice,uint256 acceptablePrice,uint256 executionFee,uint256 callbackGasLimit,uint256 minOutputAmount,uint256 validFromTime)" + ) + ); + bytes32 public constant CREATE_ORDER_ADDRESSES_TYPEHASH = + keccak256( + bytes( + "CreateOrderAddresses(address receiver,address cancellationReceiver,address callbackContract,address uiFeeReceiver,address market,address initialCollateralToken,address[] swapPath)" + ) + ); + + bytes32 public constant SUBACCOUNT_APPROVAL_TYPEHASH = + keccak256( + bytes( + "SubaccountApproval(address subaccount,bool shouldAdd,uint256 expiresAt,uint256 maxAllowedCount,bytes32 actionType,uint256 nonce,uint256 deadline)" + ) + ); + + bytes32 public constant REMOVE_SUBACCOUNT_TYPEHASH = + keccak256(bytes("RemoveSubaccount(address subaccount,bytes32 relayParams)")); + + function getRemoveSubaccountStructHash( + RelayUtils.RelayParams calldata relayParams, + address subaccount + ) external pure returns (bytes32) { + return + keccak256(abi.encode(REMOVE_SUBACCOUNT_TYPEHASH, subaccount, RelayUtils._getRelayParamsHash(relayParams))); + } + + function getSubaccountApprovalStructHash( + SubaccountApproval calldata subaccountApproval + ) external pure returns (bytes32) { + return + keccak256( + abi.encode( + SUBACCOUNT_APPROVAL_TYPEHASH, + subaccountApproval.subaccount, + subaccountApproval.shouldAdd, + subaccountApproval.expiresAt, + subaccountApproval.maxAllowedCount, + subaccountApproval.actionType, + subaccountApproval.nonce, + subaccountApproval.deadline + ) + ); + } + + function getCreateOrderStructHash( + RelayUtils.RelayParams calldata relayParams, + SubaccountApproval calldata subaccountApproval, + address account, + uint256 collateralDeltaAmount, + IBaseOrderUtils.CreateOrderParams memory params + ) external pure returns (bytes32) { + bytes32 relayParamsHash = RelayUtils._getRelayParamsHash(relayParams); + bytes32 subaccountApprovalHash = keccak256(abi.encode(subaccountApproval)); + + return + keccak256( + abi.encode( + CREATE_ORDER_TYPEHASH, + collateralDeltaAmount, + account, + _getCreateOrderAddressesStructHash(params.addresses), + _getCreateOrderNumbersStructHash(params.numbers), + uint256(params.orderType), + uint256(params.decreasePositionSwapType), + params.isLong, + params.shouldUnwrapNativeToken, + params.autoCancel, + params.referralCode, + keccak256(abi.encodePacked(params.dataList)), + relayParamsHash, + subaccountApprovalHash + ) + ); + } + + function _getCreateOrderAddressesStructHash( + IBaseOrderUtils.CreateOrderParamsAddresses memory addresses + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + CREATE_ORDER_ADDRESSES_TYPEHASH, + addresses.receiver, + addresses.cancellationReceiver, + addresses.callbackContract, + addresses.uiFeeReceiver, + addresses.market, + addresses.initialCollateralToken, + keccak256(abi.encodePacked(addresses.swapPath)) + ) + ); + } + + function _getCreateOrderNumbersStructHash( + IBaseOrderUtils.CreateOrderParamsNumbers memory numbers + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + CREATE_ORDER_NUMBERS_TYPEHASH, + numbers.sizeDeltaUsd, + numbers.initialCollateralDeltaAmount, + numbers.triggerPrice, + numbers.acceptablePrice, + numbers.executionFee, + numbers.callbackGasLimit, + numbers.minOutputAmount, + numbers.validFromTime + ) + ); + } + + function getUpdateOrderStructHash( + RelayUtils.RelayParams calldata relayParams, + SubaccountApproval calldata subaccountApproval, + address account, + bytes32 key, + RelayUtils.UpdateOrderParams calldata params, + bool increaseExecutionFee + ) external pure returns (bytes32) { + return + keccak256( + abi.encode( + UPDATE_ORDER_TYPEHASH, + account, + key, + _getUpdateOrderParamsStructHash(params), + increaseExecutionFee, + RelayUtils._getRelayParamsHash(relayParams), + keccak256(abi.encode(subaccountApproval)) + ) + ); + } + + function _getUpdateOrderParamsStructHash( + RelayUtils.UpdateOrderParams calldata params + ) internal pure returns (bytes32) { + return + keccak256( + abi.encode( + UPDATE_ORDER_PARAMS_TYPEHASH, + params.sizeDeltaUsd, + params.acceptablePrice, + params.triggerPrice, + params.minOutputAmount, + params.validFromTime, + params.autoCancel + ) + ); + } + + function getCancelOrderStructHash( + RelayUtils.RelayParams calldata relayParams, + SubaccountApproval calldata subaccountApproval, + address account, + bytes32 key + ) external pure returns (bytes32) { + return + keccak256( + abi.encode( + CANCEL_ORDER_TYPEHASH, + account, + key, + RelayUtils._getRelayParamsHash(relayParams), + keccak256(abi.encode(subaccountApproval)) + ) + ); + } +} diff --git a/deploy/deployGelatoRelayRouter.ts b/deploy/deployGelatoRelayRouter.ts index b95d53133..4120b9000 100644 --- a/deploy/deployGelatoRelayRouter.ts +++ b/deploy/deployGelatoRelayRouter.ts @@ -3,6 +3,7 @@ import { createDeployFunction } from "../utils/deploy"; const constructorContracts = [ "Router", + "RoleStore", "DataStore", "EventEmitter", "Oracle", @@ -17,7 +18,7 @@ const func = createDeployFunction({ getDeployArgs: async ({ dependencyContracts }) => { return constructorContracts.map((dependencyName) => dependencyContracts[dependencyName].address); }, - libraryNames: ["MarketStoreUtils", "MarketUtils", "OrderStoreUtils", "SwapUtils", "RelayUtils"], + libraryNames: ["MarketUtils", "OrderStoreUtils", "SwapUtils", "RelayUtils"], afterDeploy: async ({ deployedContract }) => { await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); await grantRoleIfNotGranted(deployedContract.address, "ROUTER_PLUGIN"); diff --git a/deploy/deployMultichainGlvRouter.ts b/deploy/deployMultichainGlvRouter.ts index b92e3e388..3c97488e4 100644 --- a/deploy/deployMultichainGlvRouter.ts +++ b/deploy/deployMultichainGlvRouter.ts @@ -3,6 +3,7 @@ import { createDeployFunction } from "../utils/deploy"; const baseConstructorContracts = [ "Router", + "RoleStore", "DataStore", "EventEmitter", "Oracle", @@ -20,6 +21,7 @@ const func = createDeployFunction({ getDeployArgs: async ({ dependencyContracts }) => { const baseParams = { router: dependencyContracts.Router.address, + roleStore: dependencyContracts.RoleStore.address, dataStore: dependencyContracts.DataStore.address, eventEmitter: dependencyContracts.EventEmitter.address, oracle: dependencyContracts.Oracle.address, @@ -31,7 +33,7 @@ const func = createDeployFunction({ return [baseParams, dependencyContracts.GlvHandler.address, dependencyContracts.GlvVault.address]; }, - libraryNames: ["MultichainUtils", "OrderStoreUtils", "RelayUtils", "SwapUtils", "MarketUtils", "GlvWithdrawalUtils"], + libraryNames: ["MultichainUtils", "RelayUtils", "SwapUtils", "MarketUtils", "GlvWithdrawalUtils"], afterDeploy: async ({ deployedContract }) => { await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); diff --git a/deploy/deployMultichainGmRouter.ts b/deploy/deployMultichainGmRouter.ts index 18bed8d09..90766dd04 100644 --- a/deploy/deployMultichainGmRouter.ts +++ b/deploy/deployMultichainGmRouter.ts @@ -3,6 +3,7 @@ import { createDeployFunction } from "../utils/deploy"; const baseConstructorContracts = [ "Router", + "RoleStore", "DataStore", "EventEmitter", "Oracle", @@ -20,6 +21,7 @@ const func = createDeployFunction({ getDeployArgs: async ({ dependencyContracts }) => { const baseParams = { router: dependencyContracts.Router.address, + roleStore: dependencyContracts.RoleStore.address, dataStore: dependencyContracts.DataStore.address, eventEmitter: dependencyContracts.EventEmitter.address, oracle: dependencyContracts.Oracle.address, @@ -38,7 +40,7 @@ const func = createDeployFunction({ dependencyContracts.ShiftVault.address, ]; }, - libraryNames: ["MultichainUtils", "OrderStoreUtils", "RelayUtils", "ShiftUtils", "SwapUtils", "MarketUtils"], + libraryNames: ["MultichainUtils", "RelayUtils", "ShiftUtils", "SwapUtils", "MarketUtils"], afterDeploy: async ({ deployedContract }) => { await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); diff --git a/deploy/deployMultichainOrderRouter.ts b/deploy/deployMultichainOrderRouter.ts index 4b0a75a12..3d37eae1d 100644 --- a/deploy/deployMultichainOrderRouter.ts +++ b/deploy/deployMultichainOrderRouter.ts @@ -3,6 +3,7 @@ import { createDeployFunction } from "../utils/deploy"; const baseConstructorContracts = [ "Router", + "RoleStore", "DataStore", "EventEmitter", "Oracle", @@ -20,6 +21,7 @@ const func = createDeployFunction({ getDeployArgs: async ({ dependencyContracts }) => { const baseParams = { router: dependencyContracts.Router.address, + roleStore: dependencyContracts.RoleStore.address, dataStore: dependencyContracts.DataStore.address, eventEmitter: dependencyContracts.EventEmitter.address, oracle: dependencyContracts.Oracle.address, diff --git a/deploy/deploySubaccountGelatoRelayRouter.ts b/deploy/deploySubaccountGelatoRelayRouter.ts index 9616821d5..f1c913fdb 100644 --- a/deploy/deploySubaccountGelatoRelayRouter.ts +++ b/deploy/deploySubaccountGelatoRelayRouter.ts @@ -3,6 +3,7 @@ import { createDeployFunction } from "../utils/deploy"; const constructorContracts = [ "Router", + "RoleStore", "DataStore", "EventEmitter", "Oracle", @@ -17,7 +18,7 @@ const func = createDeployFunction({ getDeployArgs: async ({ dependencyContracts }) => { return constructorContracts.map((dependencyName) => dependencyContracts[dependencyName].address); }, - libraryNames: ["MarketStoreUtils", "OrderStoreUtils", "SwapUtils", "SubaccountUtils", "MarketUtils"], + libraryNames: ["OrderStoreUtils", "SwapUtils", "SubaccountUtils", "MarketUtils", "SubaccountRelayUtils"], afterDeploy: async ({ deployedContract }) => { await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); await grantRoleIfNotGranted(deployedContract.address, "ROUTER_PLUGIN"); diff --git a/deploy/deploySunaccountRelayUtils.ts b/deploy/deploySunaccountRelayUtils.ts new file mode 100644 index 000000000..6f455159f --- /dev/null +++ b/deploy/deploySunaccountRelayUtils.ts @@ -0,0 +1,7 @@ +import { createDeployFunction } from "../utils/deploy"; + +const func = createDeployFunction({ + contractName: "SubaccountRelayUtils", +}); + +export default func; diff --git a/test/router/relay/signatures.ts b/test/router/relay/signatures.ts index 8abc101fa..592ef5644 100644 --- a/test/router/relay/signatures.ts +++ b/test/router/relay/signatures.ts @@ -18,6 +18,7 @@ describe("Relay signatures", () => { orderHandler, orderVault, router, + roleStore, marketStoreUtils, orderStoreUtils, swapUtils, @@ -32,6 +33,7 @@ describe("Relay signatures", () => { dataStore, orderVault, router, + roleStore, eventEmitter, oracle, orderHandler, @@ -48,6 +50,7 @@ describe("Relay signatures", () => { "MockGelatoRelayRouter", [ router.address, + roleStore.address, dataStore.address, eventEmitter.address, oracle.address, diff --git a/utils/fixture.ts b/utils/fixture.ts index 76a6e6c92..cd6cc7a60 100644 --- a/utils/fixture.ts +++ b/utils/fixture.ts @@ -99,6 +99,7 @@ export async function deployFixture() { const gelatoRelayRouter = await hre.ethers.getContract("GelatoRelayRouter"); const subaccountGelatoRelayRouter = await hre.ethers.getContract("SubaccountGelatoRelayRouter"); const subaccountRouter = await hre.ethers.getContract("SubaccountRouter"); + const subaccountRelayUtils = await hre.ethers.getContract("SubaccountRelayUtils"); const multichainGmRouter = await hre.ethers.getContract("MultichainGmRouter"); const multichainOrderRouter = await hre.ethers.getContract("MultichainOrderRouter"); const multichainGlvRouter = await hre.ethers.getContract("MultichainGlvRouter"); @@ -285,6 +286,7 @@ export async function deployFixture() { gelatoRelayRouter, subaccountGelatoRelayRouter, subaccountRouter, + subaccountRelayUtils, multichainGmRouter, multichainOrderRouter, multichainGlvRouter, From dc6ed96b7987bb5472da82d74fc0e6c2ec519a77 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 1 Mar 2025 12:18:33 +0200 Subject: [PATCH 195/205] Set HOLDING_ADDRESS as the refundReceiverAddress if position collateral was used to pay the execution fee, small refactor to solve stack too deep error in OrderUtils --- contracts/config/Config.sol | 1 + contracts/data/Keys.sol | 11 ++++ contracts/exchange/OrderHandler.sol | 3 ++ .../multichain/MultichainOrderRouter.sol | 15 ++++-- contracts/order/ExecuteOrderUtils.sol | 1 + contracts/order/OrderUtils.sol | 53 ++++++++++++++++--- deploy/deployOrderUtils.ts | 10 +++- test/router/SubaccountRouter.ts | 4 +- 8 files changed, 84 insertions(+), 14 deletions(-) diff --git a/contracts/config/Config.sol b/contracts/config/Config.sol index 1f5952764..6b8b3502d 100644 --- a/contracts/config/Config.sol +++ b/contracts/config/Config.sol @@ -520,6 +520,7 @@ contract Config is ReentrancyGuard, RoleModule, BasicMulticall { allowedBaseKeys[Keys.MULTICHAIN_BALANCE] = true; allowedBaseKeys[Keys.IS_MULTICHAIN_PROVIDER_ENABLED] = true; + allowedBaseKeys[Keys.WAS_POSITION_COLLATERAL_USED_FOR_EXECUTION_FEE] = true; allowedBaseKeys[Keys.MAX_DATA_LENGTH] = true; diff --git a/contracts/data/Keys.sol b/contracts/data/Keys.sol index c1028999a..3769f3aa0 100644 --- a/contracts/data/Keys.sol +++ b/contracts/data/Keys.sol @@ -481,6 +481,8 @@ library Keys { bytes32 public constant MULTICHAIN_BALANCE = keccak256(abi.encode("MULTICHAIN_BALANCE")); // @dev key for user's multichain balance bytes32 public constant IS_MULTICHAIN_PROVIDER_ENABLED = keccak256(abi.encode("IS_MULTICHAIN_PROVIDER_ENABLED")); + // @dev key for the flag if user's position collateral was used for execution fee + bytes32 public constant WAS_POSITION_COLLATERAL_USED_FOR_EXECUTION_FEE = keccak256(abi.encode("WAS_POSITION_COLLATERAL_USED_FOR_EXECUTION_FEE")); // @dev key for the maximum length for data list array of bytes32 bytes32 public constant MAX_DATA_LENGTH = keccak256(abi.encode("MAX_DATA_LENGTH")); @@ -2147,6 +2149,15 @@ library Keys { )); } + // @dev key for the flag if position collateral was used for execution fee + // @return key for the flag if position collateral was used for execution fee + function wasPositionCollateralUsedForExecutionFeeKey(bytes32 orderKey) internal pure returns (bytes32) { + return keccak256(abi.encode( + WAS_POSITION_COLLATERAL_USED_FOR_EXECUTION_FEE, + orderKey + )); + } + // @dev key for user's multichain balance // @param account the account for which to retreive the user balance key // @param token the token for which to retreive the user balance key diff --git a/contracts/exchange/OrderHandler.sol b/contracts/exchange/OrderHandler.sol index bb85e069f..46b3f5839 100644 --- a/contracts/exchange/OrderHandler.sol +++ b/contracts/exchange/OrderHandler.sol @@ -187,6 +187,7 @@ contract OrderHandler is IOrderHandler, BaseOrderHandler { OrderUtils.CancelOrderParams( dataStore, eventEmitter, + multichainVault, orderVault, key, order.account(), @@ -350,6 +351,7 @@ contract OrderHandler is IOrderHandler, BaseOrderHandler { OrderUtils.CancelOrderParams( dataStore, eventEmitter, + multichainVault, orderVault, key, msg.sender, @@ -378,6 +380,7 @@ contract OrderHandler is IOrderHandler, BaseOrderHandler { OrderUtils.freezeOrder( dataStore, eventEmitter, + multichainVault, orderVault, key, msg.sender, diff --git a/contracts/multichain/MultichainOrderRouter.sol b/contracts/multichain/MultichainOrderRouter.sol index 70e84d940..d574ae825 100644 --- a/contracts/multichain/MultichainOrderRouter.sol +++ b/contracts/multichain/MultichainOrderRouter.sol @@ -90,10 +90,6 @@ contract MultichainOrderRouter is MultichainRouter { revert Errors.UnableToPayOrderFee(); } - if (BaseOrderUtils.isSwapOrder(order.orderType())) { - revert Errors.UnableToPayOrderFee(); - } - uint256 unpaidAmount = relayParams.fee.feeAmount - balance; // First try to deduct from order collateral @@ -116,12 +112,23 @@ contract MultichainOrderRouter is MultichainRouter { } } + // position collateral cannot be used for a swap order, since there is no position + if (BaseOrderUtils.isSwapOrder(order.orderType())) { + revert Errors.UnableToPayOrderFee(); + } + // Second try to deduct from position collateral uint256 positionCollateralAmount = position.collateralAmount(); if (positionCollateralAmount < unpaidAmount) { revert Errors.UnableToPayOrderFeeFromCollateral(); } + // if wasPositionCollateralUsedForExecutionFee is true, during order execution, and + // if the order is cancelled or frozen, excess fees will be sent to the HOLDING_ADDRESS + // instead of being refunded to the user, to prevent gaming by using the execution fee + // to reduce collateral and such that negative pnl or other costs cannot be fully paid + dataStore.setBool(Keys.wasPositionCollateralUsedForExecutionFeeKey(key), true); + position.setCollateralAmount(positionCollateralAmount - unpaidAmount); dataStore.setUint( keccak256(abi.encode(key, PositionStoreUtils.COLLATERAL_AMOUNT)), diff --git a/contracts/order/ExecuteOrderUtils.sol b/contracts/order/ExecuteOrderUtils.sol index ee483bf56..630552343 100644 --- a/contracts/order/ExecuteOrderUtils.sol +++ b/contracts/order/ExecuteOrderUtils.sol @@ -134,6 +134,7 @@ library ExecuteOrderUtils { OrderUtils.clearAutoCancelOrders( params.contracts.dataStore, params.contracts.eventEmitter, + params.contracts.multichainVault, params.contracts.orderVault, positionKey, params.keeper diff --git a/contracts/order/OrderUtils.sol b/contracts/order/OrderUtils.sol index 789b7b679..e8aa45230 100644 --- a/contracts/order/OrderUtils.sol +++ b/contracts/order/OrderUtils.sol @@ -24,6 +24,7 @@ import "../callback/CallbackUtils.sol"; import "../utils/Array.sol"; import "../utils/AccountUtils.sol"; import "../referral/ReferralUtils.sol"; +import "../multichain/MultichainUtils.sol"; // @title OrderUtils // @dev Library for order functions @@ -36,6 +37,7 @@ library OrderUtils { struct CancelOrderParams { DataStore dataStore; EventEmitter eventEmitter; + MultichainVault multichainVault; OrderVault orderVault; bytes32 key; address keeper; @@ -55,6 +57,11 @@ library OrderUtils { uint256 executionFeeDiff; } + struct FreezeOrderCache { + address executionFeeReceiver; + uint256 refundFeeAmount; + } + // @dev creates an order in the order store // @param dataStore DataStore // @param eventEmitter EventEmitter @@ -264,10 +271,19 @@ library OrderUtils { executionFeeReceiver = order.receiver(); } - EventUtils.EventLogData memory eventData; - CallbackUtils.afterOrderCancellation(params.key, order, eventData); + { + EventUtils.EventLogData memory eventData; + CallbackUtils.afterOrderCancellation(params.key, order, eventData); + } - GasUtils.payExecutionFee( + if (order.srcChainId() != 0) { + executionFeeReceiver = address(params.multichainVault); + } + if (params.dataStore.getBool(Keys.wasPositionCollateralUsedForExecutionFeeKey(params.key))) { + executionFeeReceiver = params.dataStore.getAddress(Keys.HOLDING_ADDRESS); + } + + uint256 refundFeeAmount = GasUtils.payExecutionFee( params.dataStore, params.eventEmitter, params.orderVault, @@ -279,6 +295,10 @@ library OrderUtils { params.keeper, executionFeeReceiver ); + if (refundFeeAmount > 0 && executionFeeReceiver == address(params.multichainVault)) { + address wnt = params.dataStore.getAddress(Keys.WNT); + MultichainUtils.recordTransferIn(params.dataStore, params.eventEmitter, params.multichainVault, wnt, order.receiver(), 0); // srcChainId is the current block.chainId + } } // @dev freezes an order @@ -292,6 +312,7 @@ library OrderUtils { function freezeOrder( DataStore dataStore, EventEmitter eventEmitter, + MultichainVault multichainVault, OrderVault orderVault, bytes32 key, address keeper, @@ -315,10 +336,22 @@ library OrderUtils { OrderEventUtils.emitOrderFrozen(eventEmitter, key, order.account(), reason, reasonBytes); - EventUtils.EventLogData memory eventData; - CallbackUtils.afterOrderFrozen(key, order, eventData); + { + EventUtils.EventLogData memory eventData; + CallbackUtils.afterOrderFrozen(key, order, eventData); + } + + FreezeOrderCache memory cache; + + cache.executionFeeReceiver = order.receiver(); + if (order.srcChainId() != 0) { + cache.executionFeeReceiver = address(multichainVault); + } + if (dataStore.getBool(Keys.wasPositionCollateralUsedForExecutionFeeKey(key))) { + cache.executionFeeReceiver = dataStore.getAddress(Keys.HOLDING_ADDRESS); + } - GasUtils.payExecutionFee( + cache.refundFeeAmount = GasUtils.payExecutionFee( dataStore, eventEmitter, orderVault, @@ -328,13 +361,18 @@ library OrderUtils { startingGas, GasUtils.estimateOrderOraclePriceCount(order.swapPath().length), keeper, - order.receiver() + cache.executionFeeReceiver ); + if (cache.refundFeeAmount > 0 && cache.executionFeeReceiver == address(multichainVault)) { + address wnt = dataStore.getAddress(Keys.WNT); + MultichainUtils.recordTransferIn(dataStore, eventEmitter, multichainVault, wnt, order.receiver(), 0); // srcChainId is the current block.chainId + } } function clearAutoCancelOrders( DataStore dataStore, EventEmitter eventEmitter, + MultichainVault multichainVault, OrderVault orderVault, bytes32 positionKey, address keeper @@ -346,6 +384,7 @@ library OrderUtils { CancelOrderParams( dataStore, eventEmitter, + multichainVault, orderVault, orderKeys[i], keeper, // keeper diff --git a/deploy/deployOrderUtils.ts b/deploy/deployOrderUtils.ts index c0362b3aa..c2c54b4ff 100644 --- a/deploy/deployOrderUtils.ts +++ b/deploy/deployOrderUtils.ts @@ -2,7 +2,15 @@ import { createDeployFunction } from "../utils/deploy"; const func = createDeployFunction({ contractName: "OrderUtils", - libraryNames: ["MarketStoreUtils", "OrderStoreUtils", "OrderEventUtils", "GasUtils", "CallbackUtils", "MarketUtils"], + libraryNames: [ + "MarketStoreUtils", + "OrderStoreUtils", + "OrderEventUtils", + "GasUtils", + "CallbackUtils", + "MarketUtils", + "MultichainUtils", + ], }); export default func; diff --git a/test/router/SubaccountRouter.ts b/test/router/SubaccountRouter.ts index 6363e2866..e89e7165e 100644 --- a/test/router/SubaccountRouter.ts +++ b/test/router/SubaccountRouter.ts @@ -267,8 +267,8 @@ describe("SubaccountRouter", () => { expect(order._dataList).deep.eq(dataList); // 0.1 WETH in total - expect(order.numbers.executionFee).eq("2111033000000000"); - await expectBalance(wnt.address, user2.address, "97888967000000000"); + expect(order.numbers.executionFee).eq("2111032600000000"); + await expectBalance(wnt.address, user2.address, "97888967400000000"); expect( await dataStore.getUint( From dcc577d1acd2e42d0ef1cbf1d0a04f7d5c49a4a7 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 1 Mar 2025 15:23:29 +0200 Subject: [PATCH 196/205] Emit PositionCollateralUsedForExecutionFee event --- .../multichain/MultichainOrderRouter.sol | 1 + contracts/order/OrderEventUtils.sol | 24 +++++++++++++++++++ deploy/deployMultichainOrderRouter.ts | 10 +++++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/contracts/multichain/MultichainOrderRouter.sol b/contracts/multichain/MultichainOrderRouter.sol index d574ae825..814a3a816 100644 --- a/contracts/multichain/MultichainOrderRouter.sol +++ b/contracts/multichain/MultichainOrderRouter.sol @@ -128,6 +128,7 @@ contract MultichainOrderRouter is MultichainRouter { // instead of being refunded to the user, to prevent gaming by using the execution fee // to reduce collateral and such that negative pnl or other costs cannot be fully paid dataStore.setBool(Keys.wasPositionCollateralUsedForExecutionFeeKey(key), true); + OrderEventUtils.emitPositionCollateralUsedForExecutionFee(eventEmitter, key, relayParams.fee.feeToken, unpaidAmount); position.setCollateralAmount(positionCollateralAmount - unpaidAmount); dataStore.setUint( diff --git a/contracts/order/OrderEventUtils.sol b/contracts/order/OrderEventUtils.sol index 6eb649dbb..e434db6dc 100644 --- a/contracts/order/OrderEventUtils.sol +++ b/contracts/order/OrderEventUtils.sol @@ -235,4 +235,28 @@ library OrderEventUtils { eventData.bytes32Items.setItem(0, "dataList", order.dataList()); return eventData; } + + function emitPositionCollateralUsedForExecutionFee( + EventEmitter eventEmitter, + bytes32 orderKey, + address feeToken, + uint256 positionCollateralAmountUsed + ) external { + EventUtils.EventLogData memory eventData; + + eventData.bytes32Items.initItems(1); + eventData.bytes32Items.setItem(0, "key", orderKey); + + eventData.addressItems.initItems(1); + eventData.addressItems.setItem(0, "feeToken", feeToken); + + eventData.uintItems.initItems(1); + eventData.uintItems.setItem(0, "positionCollateralAmountUsed", positionCollateralAmountUsed); + + eventEmitter.emitEventLog1( + "PositionCollateralUsedForExecutionFee", + orderKey, + eventData + ); + } } diff --git a/deploy/deployMultichainOrderRouter.ts b/deploy/deployMultichainOrderRouter.ts index 3d37eae1d..f7bce8912 100644 --- a/deploy/deployMultichainOrderRouter.ts +++ b/deploy/deployMultichainOrderRouter.ts @@ -33,7 +33,15 @@ const func = createDeployFunction({ return [baseParams]; }, - libraryNames: ["MarketUtils", "MultichainUtils", "OrderStoreUtils", "PositionStoreUtils", "RelayUtils", "SwapUtils"], + libraryNames: [ + "MarketUtils", + "MultichainUtils", + "OrderStoreUtils", + "OrderEventUtils", + "PositionStoreUtils", + "RelayUtils", + "SwapUtils", + ], afterDeploy: async ({ deployedContract }) => { await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); From ba591f0687726b32b9687a1d834805e3887c6e8c Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 1 Mar 2025 23:30:41 +0200 Subject: [PATCH 197/205] Update multichain events, validate gasless feature for all multichain actions --- contracts/multichain/LayerZeroProvider.sol | 51 ++++--------------- .../LayerZeroProviderEventUtils.sol | 47 ----------------- contracts/multichain/MultichainEventUtils.sol | 38 +++++++++----- contracts/multichain/MultichainGlvRouter.sol | 2 + contracts/multichain/MultichainGmRouter.sol | 3 ++ .../multichain/MultichainTransferRouter.sol | 3 ++ contracts/multichain/MultichainUtils.sol | 39 ++++++++++---- contracts/router/relay/RelayUtils.sol | 1 - 8 files changed, 73 insertions(+), 111 deletions(-) delete mode 100644 contracts/multichain/LayerZeroProviderEventUtils.sol diff --git a/contracts/multichain/LayerZeroProvider.sol b/contracts/multichain/LayerZeroProvider.sol index a879f624c..c3652c59c 100644 --- a/contracts/multichain/LayerZeroProvider.sol +++ b/contracts/multichain/LayerZeroProvider.sol @@ -12,9 +12,7 @@ import "../data/DataStore.sol"; import "./IMultichainProvider.sol"; import "./MultichainVault.sol"; import "./MultichainUtils.sol"; -import "./MultichainEventUtils.sol"; import "./MultichainProviderUtils.sol"; -import "./LayerZeroProviderEventUtils.sol"; /** * @title LayerZeroProvider @@ -51,43 +49,22 @@ contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer { * @dev Non-guarded token address (i.e. require token == USDC) * Any token can be deposited (not just USDC), but current implementation intended to USDC only * TBD if this will change - * @param from The address of the sender (i.e. Stargate address, not user's address). - * @param guid A global unique identifier for tracking the packet. - * @param message Encoded message. Contains the params needed to record the deposit (account, token, srcChainId) - * @param executor The address of the Executor. - * @param extraData Any extra data or options to trigger on receipt. + * param from The address of the sender (i.e. Stargate address, not user's address). + * param guid A global unique identifier for tracking the packet. + * param message Encoded message. Contains the params needed to record the deposit (account, token, srcChainId) + * param executor The address of the Executor. + * param extraData Any extra data or options to trigger on receipt. */ function lzCompose( - address from, - bytes32 guid, + address /*from*/, + bytes32 /*guid*/, bytes calldata message, - address executor, - bytes calldata extraData + address /*executor*/, + bytes calldata /*extraData*/ ) external payable { (address account, address token, uint256 srcChainId) = MultichainProviderUtils.decodeDeposit(message); - _transferToVault(token, address(multichainVault)); - - MultichainUtils.recordTransferIn(dataStore, eventEmitter, multichainVault, token, account, srcChainId); - - // TODO: check what LZ contract already emits --> if it already emits the fields bellow, remove this event - LayerZeroProviderEventUtils.emitComposedMessageReceived( - eventEmitter, - srcChainId, - account, - from, - guid, - message, - executor, - extraData - ); - - MultichainEventUtils.emitBridgeIn( - eventEmitter, - token, - account, - srcChainId - ); + MultichainUtils.recordBridgeIn(dataStore, eventEmitter, multichainVault, this, token, account, srcChainId); } function _transferToVault(address token, address to) private { @@ -95,6 +72,7 @@ contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer { IERC20(token).transfer(to, amount); } + // TODO: enable bridging out (re-check LZ docs) function bridgeOut( address _stargate, address receiver, @@ -121,13 +99,6 @@ contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer { // messagingFee, // receiver // ); - - MultichainEventUtils.emitBridgeOut( - eventEmitter, - receiver, - token, - amount - ); } // function prepareSend( diff --git a/contracts/multichain/LayerZeroProviderEventUtils.sol b/contracts/multichain/LayerZeroProviderEventUtils.sol deleted file mode 100644 index b321470c8..000000000 --- a/contracts/multichain/LayerZeroProviderEventUtils.sol +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 - -pragma solidity ^0.8.0; - -import { EventEmitter } from "../event/EventEmitter.sol"; -import { EventUtils } from "../event/EventUtils.sol"; -import { Cast } from "../utils/Cast.sol"; - -/** - * @title LayerZeroProviderEventUtils - */ -library LayerZeroProviderEventUtils { - using EventUtils for EventUtils.AddressItems; - using EventUtils for EventUtils.UintItems; - using EventUtils for EventUtils.Bytes32Items; - using EventUtils for EventUtils.BytesItems; - - function emitComposedMessageReceived( - EventEmitter eventEmitter, - uint256 srcChainId, - address account, - address from, - bytes32 guid, - bytes calldata message, - address executor, - bytes calldata extraData - ) internal { - EventUtils.EventLogData memory eventData; - - eventData.addressItems.initItems(3); - eventData.addressItems.setItem(0, "account", account); - eventData.addressItems.setItem(1, "from", from); - eventData.addressItems.setItem(2, "executor", executor); - - eventData.uintItems.initItems(1); - eventData.uintItems.setItem(0, "srcChainId", srcChainId); - - eventData.bytes32Items.initItems(1); - eventData.bytes32Items.setItem(0, "guid", guid); - - eventData.bytesItems.initItems(2); - eventData.bytesItems.setItem(0, "message", message); - eventData.bytesItems.setItem(1, "extraData", extraData); - - eventEmitter.emitEventLog1("MessageComposedReceived", Cast.toBytes32(account), eventData); - } -} diff --git a/contracts/multichain/MultichainEventUtils.sol b/contracts/multichain/MultichainEventUtils.sol index b1c24177b..bb0d45425 100644 --- a/contracts/multichain/MultichainEventUtils.sol +++ b/contracts/multichain/MultichainEventUtils.sol @@ -13,22 +13,26 @@ library MultichainEventUtils { using EventUtils for EventUtils.AddressItems; using EventUtils for EventUtils.UintItems; - function emitBridgeIn( + function emitMultichainBridgeIn( EventEmitter eventEmitter, + address provider, address token, address account, + uint256 amount, uint256 srcChainId ) internal { EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(2); - eventData.addressItems.setItem(0, "token", token); - eventData.addressItems.setItem(1, "account", account); + eventData.addressItems.initItems(3); + eventData.addressItems.setItem(0, "provider", provider); + eventData.addressItems.setItem(1, "token", token); + eventData.addressItems.setItem(2, "account", account); - eventData.uintItems.initItems(1); - eventData.uintItems.setItem(0, "srcChainId", srcChainId); + eventData.uintItems.initItems(2); + eventData.uintItems.setItem(0, "amount", amount); + eventData.uintItems.setItem(1, "srcChainId", srcChainId); - eventEmitter.emitEventLog1("BridgeIn", Cast.toBytes32(account), eventData); + eventEmitter.emitEventLog1("MultichainBridgeIn", Cast.toBytes32(account), eventData); } function emitMultichainTransferIn( @@ -51,36 +55,42 @@ library MultichainEventUtils { eventEmitter.emitEventLog1("MultichainTransferIn", Cast.toBytes32(account), eventData); } - function emitBridgeOut( + function emitMultichainBridgeOut( EventEmitter eventEmitter, - address account, + address provider, address token, - uint256 amount + address receiver, + uint256 amount, + uint256 srcChainId ) internal { EventUtils.EventLogData memory eventData; eventData.addressItems.initItems(2); - eventData.addressItems.setItem(0, "account", account); + eventData.addressItems.setItem(0, "provider", provider); eventData.addressItems.setItem(1, "token", token); + eventData.addressItems.setItem(2, "receiver", receiver); - eventData.uintItems.initItems(1); + eventData.uintItems.initItems(2); eventData.uintItems.setItem(0, "amount", amount); + eventData.uintItems.setItem(1, "srcChainId", srcChainId); - eventEmitter.emitEventLog1("BridgeOut", Cast.toBytes32(account), eventData); + eventEmitter.emitEventLog1("MultichainBridgeOut", Cast.toBytes32(receiver), eventData); } function emitMultichainTransferOut( EventEmitter eventEmitter, address token, address account, + address receiver, uint256 amount, uint256 srcChainId ) internal { EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(2); + eventData.addressItems.initItems(3); eventData.addressItems.setItem(0, "token", token); eventData.addressItems.setItem(1, "account", account); + eventData.addressItems.setItem(2, "receiver", receiver); eventData.uintItems.initItems(2); eventData.uintItems.setItem(0, "amount", amount); diff --git a/contracts/multichain/MultichainGlvRouter.sol b/contracts/multichain/MultichainGlvRouter.sol index 72a4fcf05..a77a286e6 100644 --- a/contracts/multichain/MultichainGlvRouter.sol +++ b/contracts/multichain/MultichainGlvRouter.sol @@ -29,6 +29,7 @@ contract MultichainGlvRouter is MultichainRouter { GlvDepositUtils.CreateGlvDepositParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { _validateDesChainId(relayParams.desChainId); + _validateGaslessFeature(); bytes32 structHash = RelayUtils.getCreateGlvDepositStructHash(relayParams, transferRequests, params); _validateCall(relayParams, account, structHash, srcChainId); @@ -71,6 +72,7 @@ contract MultichainGlvRouter is MultichainRouter { GlvWithdrawalUtils.CreateGlvWithdrawalParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { _validateDesChainId(relayParams.desChainId); + _validateGaslessFeature(); bytes32 structHash = RelayUtils.getCreateGlvWithdrawalStructHash(relayParams, transferRequests, params); _validateCall(relayParams, account, structHash, srcChainId); diff --git a/contracts/multichain/MultichainGmRouter.sol b/contracts/multichain/MultichainGmRouter.sol index b976f1954..93b61536e 100644 --- a/contracts/multichain/MultichainGmRouter.sol +++ b/contracts/multichain/MultichainGmRouter.sol @@ -40,6 +40,7 @@ contract MultichainGmRouter is MultichainRouter { DepositUtils.CreateDepositParams memory params // can't use calldata because need to modify params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { _validateDesChainId(relayParams.desChainId); + _validateGaslessFeature(); bytes32 structHash = RelayUtils.getCreateDepositStructHash(relayParams, transferRequests, params); _validateCall(relayParams, account, structHash, srcChainId); @@ -84,6 +85,7 @@ contract MultichainGmRouter is MultichainRouter { WithdrawalUtils.CreateWithdrawalParams memory params // can't use calldata because need to modify params.addresses.receiver & params.numbers.executionFee ) external nonReentrant onlyGelatoRelay returns (bytes32) { _validateDesChainId(relayParams.desChainId); + _validateGaslessFeature(); bytes32 structHash = RelayUtils.getCreateWithdrawalStructHash(relayParams, transferRequests, params); _validateCall(relayParams, account, structHash, srcChainId); @@ -125,6 +127,7 @@ contract MultichainGmRouter is MultichainRouter { ShiftUtils.CreateShiftParams memory params ) external nonReentrant onlyGelatoRelay returns (bytes32) { _validateDesChainId(relayParams.desChainId); + _validateGaslessFeature(); bytes32 structHash = RelayUtils.getCreateShiftStructHash(relayParams, transferRequests, params); _validateCall(relayParams, account, structHash, srcChainId); diff --git a/contracts/multichain/MultichainTransferRouter.sol b/contracts/multichain/MultichainTransferRouter.sol index 9f1d0619a..7d56dac1f 100644 --- a/contracts/multichain/MultichainTransferRouter.sol +++ b/contracts/multichain/MultichainTransferRouter.sol @@ -28,6 +28,7 @@ contract MultichainTransferRouter is MultichainRouter { RelayUtils.BridgeInParams calldata params ) external payable nonReentrant onlyGelatoRelay { _validateDesChainId(relayParams.desChainId); + _validateGaslessFeature(); bytes32 structHash = RelayUtils.getBridgeInStructHash(relayParams, params); _validateCall(relayParams, account, structHash, srcChainId); @@ -51,6 +52,7 @@ contract MultichainTransferRouter is MultichainRouter { RelayUtils.BridgeOutParams calldata params ) external nonReentrant onlyGelatoRelay { _validateDesChainId(relayParams.desChainId); + _validateGaslessFeature(); _validateMultichainProvider(dataStore, provider); bytes32 structHash = RelayUtils.getBridgeOutStructHash(relayParams, params); @@ -63,6 +65,7 @@ contract MultichainTransferRouter is MultichainRouter { params.amount, data ); + MultichainEventUtils.emitMultichainBridgeOut(eventEmitter, provider, params.token, receiver, params.amount, srcChainId); } function _validateMultichainProvider(DataStore dataStore, address provider) internal view { diff --git a/contracts/multichain/MultichainUtils.sol b/contracts/multichain/MultichainUtils.sol index ab8a25c4b..9e80dab94 100644 --- a/contracts/multichain/MultichainUtils.sol +++ b/contracts/multichain/MultichainUtils.sol @@ -11,6 +11,7 @@ import "../event/EventEmitter.sol"; import "./MultichainVault.sol"; import "./MultichainEventUtils.sol"; +import "./IMultichainProvider.sol"; /** * @title MultichainUtils @@ -18,7 +19,20 @@ import "./MultichainEventUtils.sol"; library MultichainUtils { using SafeERC20 for IERC20; - /** + function recordBridgeIn( + DataStore dataStore, + EventEmitter eventEmitter, + MultichainVault multichainVault, + IMultichainProvider provider, + address token, + address account, + uint256 srcChainId + ) external { + uint256 amount = recordTransferIn(dataStore, eventEmitter, multichainVault, token, account, srcChainId); + MultichainEventUtils.emitMultichainBridgeIn(eventEmitter, address(provider), token, account, amount, srcChainId); + } + + /** * Records a deposit from another chain. IMultichainProvider has CONTROLLER role * @param account user address on the source chain * @param token address of the token being deposited @@ -31,7 +45,7 @@ library MultichainUtils { address token, address account, uint256 srcChainId - ) external { + ) public returns (uint256) { // token should have been transferred to multichainVault by IMultichainProvider uint256 amount = multichainVault.recordTransferIn(token); if (amount == 0) { @@ -39,14 +53,15 @@ library MultichainUtils { } dataStore.incrementUint(Keys.multichainBalanceKey(account, token), amount); - MultichainEventUtils.emitMultichainTransferIn(eventEmitter, token, account, amount, srcChainId); + + return amount; } /** - * @dev transfer the specified amount of tokens from account to receiver + * @dev transfer the specified amount of tokens from user's multichain balance to receiver * @param token the token to transfer - * @param account the account to transfer from + * @param account the account for which the multichain balance is decreased * @param receiver the account to transfer to * @param amount the amount of tokens to transfer */ @@ -64,15 +79,21 @@ library MultichainUtils { revert Errors.EmptyMultichainTransferOutAmount(account, token); } - bytes32 balanceKey = Keys.multichainBalanceKey(account, token); - - uint256 balance = dataStore.getUint(balanceKey); + uint256 balance = getMultichainBalanceAmount(dataStore, account, token); if (balance < amount) { revert Errors.InsufficientMultichainBalance(account, token, balance, amount); } multichainVault.transferOut(token, receiver, amount); dataStore.decrementUint(Keys.multichainBalanceKey(account, token), amount); - MultichainEventUtils.emitMultichainTransferOut(eventEmitter, token, account, amount, srcChainId); + MultichainEventUtils.emitMultichainTransferOut(eventEmitter, token, account, receiver, amount, srcChainId); + } + + function getMultichainBalanceAmount( + DataStore dataStore, + address account, + address token + ) public view returns (uint256) { + return dataStore.getUint(Keys.multichainBalanceKey(account, token)); } } diff --git a/contracts/router/relay/RelayUtils.sol b/contracts/router/relay/RelayUtils.sol index 4959b66c1..489cef985 100644 --- a/contracts/router/relay/RelayUtils.sol +++ b/contracts/router/relay/RelayUtils.sol @@ -69,7 +69,6 @@ library RelayUtils { struct BridgeOutParams { address token; - address receiver; uint256 amount; } From c6f02257738dbabf7dc74ebcd3783dfe6b10f028 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 2 Mar 2025 12:04:33 +0200 Subject: [PATCH 198/205] Check keeper fee on multichain deposit --- test/multichain/MultichainGmRouter.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/test/multichain/MultichainGmRouter.ts b/test/multichain/MultichainGmRouter.ts index 389922ccf..a3a974db0 100644 --- a/test/multichain/MultichainGmRouter.ts +++ b/test/multichain/MultichainGmRouter.ts @@ -115,7 +115,7 @@ describe("MultichainRouter", () => { }, minMarketTokens: 100, shouldUnwrapNativeToken: false, - executionFee: expandDecimals(123, 15), // TODO: Why executionFee is not deducted and sent to FEE_RECEIVER when depsoit is executed? + executionFee: 0, // execution fee is subtracted from initialLongTokenAmount callbackGasLimit: "200000", dataList: [], }; @@ -161,6 +161,9 @@ describe("MultichainRouter", () => { describe("createDeposit", () => { it("creates deposit and sends relayer fee", async () => { + // enable keeper fee payment + await dataStore.setUint(keys.EXECUTION_GAS_FEE_MULTIPLIER_FACTOR, decimalToFloat(1)); + // funds have already been bridged to multichainVault and recorded under user's multichain balance expect(await wnt.balanceOf(multichainVault.address)).eq(expandDecimals(10_006, 15)); // 10 + 0.006 = 10.006 ETH expect(await usdc.balanceOf(multichainVault.address)).eq(expandDecimals(45_000, 6)); @@ -203,7 +206,7 @@ describe("MultichainRouter", () => { expect(deposit.numbers.initialLongTokenAmount).eq(createDepositParams.transferRequests.amounts[0]); // 10.006 ETH expect(deposit.numbers.initialShortTokenAmount).eq(createDepositParams.transferRequests.amounts[1]); // 45,000.00 USDC expect(deposit.numbers.minMarketTokens).eq(defaultDepositParams.minMarketTokens); - expect(deposit.numbers.executionFee).eq(expandDecimals(4, 15)); // 0.006 - 0.002 = 0.004 ETH + expect(deposit.numbers.executionFee).eq(expandDecimals(4, 15)); // feeAmount - relayFeeAmount = 0.006 - 0.002 = 0.004 ETH expect(deposit.numbers.callbackGasLimit).eq(defaultDepositParams.callbackGasLimit); expect(deposit.flags.shouldUnwrapNativeToken).eq(defaultDepositParams.shouldUnwrapNativeToken); expect(deposit._dataList).deep.eq(defaultDepositParams.dataList); @@ -223,10 +226,14 @@ describe("MultichainRouter", () => { // state after executing deposit expect(await getDepositCount(dataStore)).eq(0); - expect(await wnt.balanceOf(multichainVault.address)).eq(expandDecimals(4, 15)); - expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, wnt.address))).to.eq( - expandDecimals(4, 15) - ); // TODO: Why was executionFee returned to multichainVault/user1's multichain balance? + expect(await wnt.balanceOf(multichainVault.address)).to.approximately( + expandDecimals(2133, 12), // feeAmount - keeperFee = 0.004 - 0.001867... = 0.002133... (e.g. 2133076985064616) + expandDecimals(1, 12) + ); + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, wnt.address))).to.approximately( + expandDecimals(2133, 12), // feeAmount - keeperFee = 0.004 - 0.001867... = 0.002133... (e.g. 2133076985064616) + expandDecimals(1, 12) + ); expect(await usdc.balanceOf(multichainVault.address)).eq(0); expect(await wnt.balanceOf(depositVault.address)).eq(0); expect(await usdc.balanceOf(depositVault.address)).eq(0); From 0ef8f0b73d1f8cd6948efc78e18bc6983bef5172 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 2 Mar 2025 12:28:45 +0200 Subject: [PATCH 199/205] Install @stargatefinance/stg-evm-v2 stargatefinance/stg-evm-v2 requires node version ">=20" and @poanet/solidity-flattener requires node version ">=16 <=18". Remove poanet/solidity-flattener since hardhat toolbox already has the flattener included. --- package.json | 2 +- yarn.lock | 1061 +++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 918 insertions(+), 145 deletions(-) diff --git a/package.json b/package.json index 7057255dc..e88ad38f2 100644 --- a/package.json +++ b/package.json @@ -52,8 +52,8 @@ "@layerzerolabs/lz-evm-protocol-v2": "^3.0.30", "@nomicfoundation/hardhat-foundry": "^1.1.1", "@openzeppelin/contracts": "4.9.3", - "@poanet/solidity-flattener": "^3.0.9", "@rainbow-me/rainbowkit": "^1.3.0", + "@stargatefinance/stg-evm-v2": "^1.5.1", "@types/react": "^18.2.38", "@vitejs/plugin-react": "^4.2.0", "bn.js": "^5.2.1", diff --git a/yarn.lock b/yarn.lock index 1e45a0ee7..0bfab9370 100644 --- a/yarn.lock +++ b/yarn.lock @@ -35,6 +35,14 @@ tslib "^2.3.0" zen-observable-ts "^1.2.5" +"@aptos-labs/aptos-client@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@aptos-labs/aptos-client/-/aptos-client-0.1.1.tgz#cbcd2a73bad252e344318baec32ecc54d8136ee0" + integrity sha512-kJsoy4fAPTOhzVr7Vwq8s/AUg6BQiJDa7WOqRzev4zsuIS3+JCuIZ6vUd7UBsjnxtmguJJulMRs9qWCzVBt2XA== + dependencies: + axios "1.7.4" + got "^11.8.6" + "@babel/code-frame@7.12.11": version "7.12.11" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz" @@ -264,6 +272,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.25.0": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.9.tgz#aa4c6facc65b9cb3f87d75125ffd47781b475433" + integrity sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/template@^7.22.15": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" @@ -298,6 +313,13 @@ "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" +"@bitcoinerlab/secp256k1@^1.1.1": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@bitcoinerlab/secp256k1/-/secp256k1-1.2.0.tgz#429d043ef4218b9c71915b50172e9aa4a2a8fea4" + integrity sha512-jeujZSzb3JOZfmJYI0ph1PVpCRV5oaexCgy+RvCXV8XlY+XFB/2n3WOcvBsKLsOw78KYgnQrQWb2HrKE4be88Q== + dependencies: + "@noble/curves" "^1.7.0" + "@chainlink/contracts@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@chainlink/contracts/-/contracts-1.1.0.tgz#7158db5771b9504d32d2235a5378ef56601a3e6c" @@ -1124,6 +1146,57 @@ resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@improbable-eng/grpc-web@^0.15.0": + version "0.15.0" + resolved "https://registry.yarnpkg.com/@improbable-eng/grpc-web/-/grpc-web-0.15.0.tgz#3e47e9fdd90381a74abd4b7d26e67422a2a04bef" + integrity sha512-ERft9/0/8CmYalqOVnJnpdDry28q+j+nAlFFARdjyxXDJ+Mhgv9+F600QC8BR9ygOfrXRlAk6CvST2j+JCpQPg== + dependencies: + browser-headers "^0.4.1" + +"@initia/initia.js@^0.2.11": + version "0.2.30" + resolved "https://registry.yarnpkg.com/@initia/initia.js/-/initia.js-0.2.30.tgz#3b6830553b6c17c2e3767ec14f235f9031893d93" + integrity sha512-L38CwqK8fcIC2iiPRD6CQN3kGlya7z1mVWA0OPjSdQMNubMXbk9QJFuQXRw8IhkOqsr+wRPVu4zwFUxI85nhDA== + dependencies: + "@bitcoinerlab/secp256k1" "^1.1.1" + "@initia/initia.proto" "^0.2.6" + "@initia/opinit.proto" "^0.0.11" + "@ledgerhq/hw-transport" "^6.31.4" + "@ledgerhq/hw-transport-webhid" "^6.29.4" + "@ledgerhq/hw-transport-webusb" "^6.29.4" + "@mysten/bcs" "^1.1.0" + axios "^1.7.7" + bech32 "^2.0.0" + bignumber.js "^9.1.2" + bip32 "^5.0.0-rc.0" + bip39 "^3.1.0" + jscrypto "^1.0.3" + keccak256 "^1.0.6" + ripemd160 "^2.0.2" + secp256k1 "^5.0.1" + semver "^7.6.3" + ws "^8.18.0" + +"@initia/initia.proto@^0.2.6": + version "0.2.6" + resolved "https://registry.yarnpkg.com/@initia/initia.proto/-/initia.proto-0.2.6.tgz#662bd0edbb114e570c2a1260b8d56dc55f498732" + integrity sha512-khiCPUxZTkyAl+SQbQCOlcJId/a0ToUhG+ChrVXN9a+1ypPz5355j2UP2IvnUf+lAix/+zzdekcqO/Lig7htAQ== + dependencies: + "@improbable-eng/grpc-web" "^0.15.0" + google-protobuf "^3.21.4" + long "^5.2.3" + protobufjs "^7.3.2" + +"@initia/opinit.proto@^0.0.11": + version "0.0.11" + resolved "https://registry.yarnpkg.com/@initia/opinit.proto/-/opinit.proto-0.0.11.tgz#bb67c43a3eef1d66c4a066ba5562cc617147b370" + integrity sha512-Op9GIlXiV1xhUIjVQ2TFE9a3X8iyFVNtJNHCM34gwLQHJktDNm2KCoW4eHh6pkn4//ECRVH7zuKgV8TdZWogCw== + dependencies: + "@improbable-eng/grpc-web" "^0.15.0" + google-protobuf "^3.21.4" + long "^5.2.3" + protobufjs "^7.3.2" + "@ioredis/commands@^1.1.1": version "1.2.0" resolved "https://registry.yarnpkg.com/@ioredis/commands/-/commands-1.2.0.tgz#6d61b3097470af1fdbbe622795b8921d42018e11" @@ -1191,16 +1264,143 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@layerzerolabs/evm-sdks-core@^3.0.30": + version "3.0.74" + resolved "https://registry.yarnpkg.com/@layerzerolabs/evm-sdks-core/-/evm-sdks-core-3.0.74.tgz#db357a314073ea84b272a1259697ea24b6eee36e" + integrity sha512-2/HRSme6gzXwBl5iHDoffww/HDNaPwQMroWPHc8m35wn+Bf/ADS7WXltk3fS7uOEGq5ke7pDnbNVu3cSt8/Hsg== + dependencies: + ethers "^5.7.2" + +"@layerzerolabs/lz-definitions@^3.0.74", "@layerzerolabs/lz-definitions@~3.0.30": + version "3.0.74" + resolved "https://registry.yarnpkg.com/@layerzerolabs/lz-definitions/-/lz-definitions-3.0.74.tgz#4da9570fe11c88a3d964fd1371f22a9f2c86c863" + integrity sha512-Rpa9O6RV5q009b3+X+gVEawy+QajTALhTPAck6sdnjxZXNuyTZWvcRhp4xJRcyW2cafG8/1udatzr3cXouTPdA== + dependencies: + tiny-invariant "^1.3.1" + +"@layerzerolabs/lz-evm-messagelib-v2@2.0.11": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@layerzerolabs/lz-evm-messagelib-v2/-/lz-evm-messagelib-v2-2.0.11.tgz#8437a2a30e9794c56393d83b3f9d2671aadf2648" + integrity sha512-i6nZvzxzH+3bMzGRxIzf6fFXTzxQIZ1vyduAoFTy3U2zK9xZUHunKFfpC+vqCY1goNzjBquX4VnClWMWlc5ZIA== + +"@layerzerolabs/lz-evm-oapp-v2@~2.3.25": + version "2.3.44" + resolved "https://registry.yarnpkg.com/@layerzerolabs/lz-evm-oapp-v2/-/lz-evm-oapp-v2-2.3.44.tgz#e6a2e9fc60e7f21009138405cc21e3bcfe48177d" + integrity sha512-FNm9+OFKOl+/5JvpV6rMUoHZSePOcLtNJ+GHJuf38xPmEe0ehxFVQBw3iIfaH2dsWzRn/T7ZizV5ouId35FLdg== + +"@layerzerolabs/lz-evm-protocol-v2@2.0.11": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@layerzerolabs/lz-evm-protocol-v2/-/lz-evm-protocol-v2-2.0.11.tgz#4f341e7716f24653bff68c5451ddb3b369df265b" + integrity sha512-hkcNheUjTOYNxoAnBTSatLLzLe71Y7kcmw1+56aazezyUb6SCu330V4sJg+jsp8Ag6Sb7y1tLLYn1Sa5EWgCxg== + "@layerzerolabs/lz-evm-protocol-v2@^3.0.30": version "3.0.30" resolved "https://registry.yarnpkg.com/@layerzerolabs/lz-evm-protocol-v2/-/lz-evm-protocol-v2-3.0.30.tgz#a4319117042c14a29e38420fbfeffcb0dae30de5" integrity sha512-N0eTiMCF4+JFn2tP3CfatingRkdmwgJqJWMhKgCJjsavG+ADwLlFSZ9RgpIWpt3gt6/DCEE4mdWe0qvF63chTA== +"@layerzerolabs/lz-evm-sdk-v2@3.0.30": + version "3.0.30" + resolved "https://registry.yarnpkg.com/@layerzerolabs/lz-evm-sdk-v2/-/lz-evm-sdk-v2-3.0.30.tgz#a6d2a4587875601c24eed9787a6d5bdf33f98889" + integrity sha512-UMP/Z8MWrKeklK7zjjbjn+GS9dPUcZsa1Y70uFXGq1j9V/xkLGIH+T9IltKndLR/W+Z/t3UhgjswUVMPd+OSsw== + dependencies: + "@layerzerolabs/evm-sdks-core" "^3.0.30" + ethers "^5.7.2" + +"@layerzerolabs/lz-evm-v1-0.7@~3.0.7": + version "3.0.74" + resolved "https://registry.yarnpkg.com/@layerzerolabs/lz-evm-v1-0.7/-/lz-evm-v1-0.7-3.0.74.tgz#22562f1130d9ff8a16aeae2ce8f61b16a4216dc2" + integrity sha512-avptr8lryNLHQQm0JKT5JbV/rAHOyq26SNxYIUbD17uMroOzHZDOwCSWB9b+ifut7nOBBFQRH7iNzEZwlsITWA== + +"@layerzerolabs/lz-utilities@~3.0.30": + version "3.0.74" + resolved "https://registry.yarnpkg.com/@layerzerolabs/lz-utilities/-/lz-utilities-3.0.74.tgz#bbb119834f33e2474c2d5619f3d16f081f168768" + integrity sha512-zbtZsdsAAwSDjDb25v9A/b5fTNBpTUxn7kvwVBkhBTY3usRfbMUFfptmJC76KvM+3eRmg5+EqvhOS1sY9Vv0Wg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@initia/initia.js" "^0.2.11" + "@layerzerolabs/lz-definitions" "^3.0.74" + "@solana/web3.js" "1.95.8" + "@ton/core" "^0.59.0" + "@ton/crypto" "^3.3.0" + "@ton/ton" "15.1.0" + aptos "^1.20.0" + bip39 "^3.1.0" + dayjs "^1.11.13" + ed25519-hd-key "^1.3.0" + ethers "^5.7.2" + memoizee "^0.4.17" + picocolors "1.0.0" + pino "^8.16.2" + +"@layerzerolabs/lz-v2-utilities@~3.0.30": + version "3.0.74" + resolved "https://registry.yarnpkg.com/@layerzerolabs/lz-v2-utilities/-/lz-v2-utilities-3.0.74.tgz#20162e6867e629824a2d514c2ab22ca68cea02a9" + integrity sha512-qnnH69ufcVaruSWGlX6CH8PHia4y+mARi/K9kSrqA4lFG1e8cp66OxKpCMzWf2TOXd4P3mr8N9a3zwLHE9r/4w== + dependencies: + "@ethersproject/abi" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/solidity" "^5.7.0" + bs58 "^5.0.0" + tiny-invariant "^1.3.1" + "@ledgerhq/connect-kit-loader@^1.1.0": version "1.1.2" resolved "https://registry.yarnpkg.com/@ledgerhq/connect-kit-loader/-/connect-kit-loader-1.1.2.tgz#d550e3c1f046e4c796f32a75324b03606b7e226a" integrity sha512-mscwGroSJQrCTjtNGBu+18FQbZYA4+q6Tyx6K7CXHl6AwgZKbWfZYdgP2F+fyZcRUdGRsMX8QtvU61VcGGtO1A== +"@ledgerhq/devices@8.4.4", "@ledgerhq/devices@^8.4.4": + version "8.4.4" + resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-8.4.4.tgz#0d195c1650fe57da2fad7f0d9074a0190947cd6f" + integrity sha512-sz/ryhe/R687RHtevIE9RlKaV8kkKykUV4k29e7GAVwzHX1gqG+O75cu1NCJUHLbp3eABV5FdvZejqRUlLis9A== + dependencies: + "@ledgerhq/errors" "^6.19.1" + "@ledgerhq/logs" "^6.12.0" + rxjs "^7.8.1" + semver "^7.3.5" + +"@ledgerhq/errors@^6.19.1": + version "6.19.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-6.19.1.tgz#d9ac45ad4ff839e468b8f63766e665537aaede58" + integrity sha512-75yK7Nnit/Gp7gdrJAz0ipp31CCgncRp+evWt6QawQEtQKYEDfGo10QywgrrBBixeRxwnMy1DP6g2oCWRf1bjw== + +"@ledgerhq/hw-transport-webhid@^6.29.4": + version "6.30.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-webhid/-/hw-transport-webhid-6.30.0.tgz#cd86b0055eb77276892024abbd38e6ed2471b59e" + integrity sha512-HoTzjmYwO7+TVwK+GNbglRepUoDywBL6vjhKnhGqJSUPqAqJJyEXcnKnFDBMN7Phqm55O+YHDYfpcHGBNg5XlQ== + dependencies: + "@ledgerhq/devices" "8.4.4" + "@ledgerhq/errors" "^6.19.1" + "@ledgerhq/hw-transport" "^6.31.4" + "@ledgerhq/logs" "^6.12.0" + +"@ledgerhq/hw-transport-webusb@^6.29.4": + version "6.29.4" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-webusb/-/hw-transport-webusb-6.29.4.tgz#5926421b8db4b474c7aba4851f21ddd4ad4bcc70" + integrity sha512-HoGF1LlBT9HEGBQy2XeCHrFdv/FEOZU0+J+yfKcgAQIAiASr2MLvdzwoJbUS8h6Gn+vc+/BjzBSO3JNn7Loqbg== + dependencies: + "@ledgerhq/devices" "^8.4.4" + "@ledgerhq/errors" "^6.19.1" + "@ledgerhq/hw-transport" "^6.31.4" + "@ledgerhq/logs" "^6.12.0" + +"@ledgerhq/hw-transport@^6.31.4": + version "6.31.4" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.31.4.tgz#9b23a6de4a4caaa5c24b149c2dea8adde46f0eb1" + integrity sha512-6c1ir/cXWJm5dCWdq55NPgCJ3UuKuuxRvf//Xs36Bq9BwkV2YaRQhZITAkads83l07NAdR16hkTWqqpwFMaI6A== + dependencies: + "@ledgerhq/devices" "^8.4.4" + "@ledgerhq/errors" "^6.19.1" + "@ledgerhq/logs" "^6.12.0" + events "^3.3.0" + +"@ledgerhq/logs@^6.12.0": + version "6.12.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-6.12.0.tgz#ad903528bf3687a44da435d7b2479d724d374f5d" + integrity sha512-ExDoj1QV5eC6TEbMdLUMMk9cfvNKhhv5gXol4SmULRVCx/3iyCPhJ74nsb3S0Vb+/f+XujBEj3vQn5+cwS0fNA== + "@lit-labs/ssr-dom-shim@^1.0.0", "@lit-labs/ssr-dom-shim@^1.1.0": version "1.1.2" resolved "https://registry.yarnpkg.com/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.1.2.tgz#d693d972974a354034454ec1317eb6afd0b00312" @@ -1330,6 +1530,13 @@ "@motionone/dom" "^10.16.4" tslib "^2.3.1" +"@mysten/bcs@^1.1.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@mysten/bcs/-/bcs-1.4.0.tgz#7d1ac0d4bbdf97c36281998565f491d221e71dc6" + integrity sha512-YwDYspceLt8b7v6ohPvy8flQEi+smtfSG5d2A98CbUA48XBmOqTSPNmpw9wsZVVnrH2avr+BS5uVhDZT+EquYA== + dependencies: + bs58 "^6.0.0" + "@noble/curves@1.2.0", "@noble/curves@^1.2.0", "@noble/curves@~1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" @@ -1337,6 +1544,13 @@ dependencies: "@noble/hashes" "1.3.2" +"@noble/curves@^1.4.2", "@noble/curves@^1.7.0": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.8.1.tgz#19bc3970e205c99e4bdb1c64a4785706bce497ff" + integrity sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ== + dependencies: + "@noble/hashes" "1.7.1" + "@noble/hashes@1.1.2", "@noble/hashes@~1.1.1": version "1.1.2" resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz" @@ -1347,6 +1561,16 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== +"@noble/hashes@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" + integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== + +"@noble/hashes@1.7.1", "@noble/hashes@^1.2.0", "@noble/hashes@^1.4.0": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.7.1.tgz#5738f6d765710921e7a751e00c20ae091ed8db0f" + integrity sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ== + "@noble/secp256k1@1.6.3", "@noble/secp256k1@~1.6.0": version "1.6.3" resolved "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.6.3.tgz" @@ -1675,15 +1899,58 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@poanet/solidity-flattener@^3.0.9": - version "3.0.9" - resolved "https://registry.yarnpkg.com/@poanet/solidity-flattener/-/solidity-flattener-3.0.9.tgz#9ccb82c125f0bf1958ecb23c12f94e3765dabd85" - integrity sha512-3vTJ05uRGJqP0t81963Ed99uijjXSyqgXfdrBD04su1auBU9enOGzUP6H2H7SyWBAmwUPmBb/z9Qy96ytyvtDw== +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== dependencies: - bunyan "^1.8.12" - decomment "^0.9.1" - glob-promise "^3.4.0" - path "^0.12.7" + "@protobufjs/aspromise" "^1.1.1" + "@protobufjs/inquire" "^1.1.0" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== "@rainbow-me/rainbowkit@^1.3.0": version "1.3.0" @@ -1798,6 +2065,11 @@ resolved "https://registry.yarnpkg.com/@scroll-tech/contracts/-/contracts-0.1.0.tgz#ccea8db1b3df7d740e4b7843ac01b5bd25b4438b" integrity sha512-aBbDOc3WB/WveZdpJYcrfvMYMz7ZTEiW8M9XMJLba8p9FAR5KGYB/cV+8+EUsq3MKt7C1BfR+WnXoTVdvwIY6w== +"@scure/base@^1.1.1": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.2.4.tgz#002eb571a35d69bdb4c214d0995dff76a8dcd2a9" + integrity sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ== + "@scure/base@~1.1.0": version "1.1.1" resolved "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz" @@ -1920,13 +2192,34 @@ resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz" integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== -"@solana/buffer-layout@^4.0.0": +"@solana/buffer-layout@^4.0.0", "@solana/buffer-layout@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz#b996235eaec15b1e0b5092a8ed6028df77fa6c15" integrity sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA== dependencies: buffer "~6.0.3" +"@solana/web3.js@1.95.8": + version "1.95.8" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.95.8.tgz#2d49abda23f7a79a3cc499ab6680f7be11786ee1" + integrity sha512-sBHzNh7dHMrmNS5xPD1d0Xa2QffW/RXaxu/OysRXBfwTp+LYqGGmMtCYYwrHPrN5rjAmJCsQRNAwv4FM0t3B6g== + dependencies: + "@babel/runtime" "^7.25.0" + "@noble/curves" "^1.4.2" + "@noble/hashes" "^1.4.0" + "@solana/buffer-layout" "^4.0.1" + agentkeepalive "^4.5.0" + bigint-buffer "^1.1.5" + bn.js "^5.2.1" + borsh "^0.7.0" + bs58 "^4.0.1" + buffer "6.0.3" + fast-stable-stringify "^1.0.0" + jayson "^4.1.1" + node-fetch "^2.7.0" + rpc-websockets "^9.0.2" + superstruct "^2.0.2" + "@solana/web3.js@^1.70.1": version "1.87.6" resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.87.6.tgz#6744cfc5f4fc81e0f58241c0a92648a7320bb3bf" @@ -2089,6 +2382,43 @@ "@stablelib/random" "^1.0.2" "@stablelib/wipe" "^1.0.1" +"@stargatefinance/stg-definitions-v2@~1.4.10": + version "1.4.10" + resolved "https://registry.yarnpkg.com/@stargatefinance/stg-definitions-v2/-/stg-definitions-v2-1.4.10.tgz#7378d5c790742d36fbfe3c82cd7bd380e3cf33e1" + integrity sha512-uVtRR9hY2ZxNlcCyKdZAF9Nzv8Tg0KZknI9F0zYCH/qzOLTWn/s0kh+36h1CNnnj9j0s/OcRJc+PfPCEKepOLw== + +"@stargatefinance/stg-evm-v1@~1.0.31": + version "1.0.33" + resolved "https://registry.yarnpkg.com/@stargatefinance/stg-evm-v1/-/stg-evm-v1-1.0.33.tgz#53eec9d4c369d5b2225259d58ed75c4224cacfc1" + integrity sha512-funR7QssJn+9m8tAO+2nMB2+9ke1FplY5/Jt1RGReFR4ZXhlMKWvbos4KNh7L6k6tu2c+9oQOjn8bPiGnGwmew== + dependencies: + exponential-backoff "^3.1.1" + +"@stargatefinance/stg-evm-v2@^1.5.1": + version "1.5.1" + resolved "https://registry.yarnpkg.com/@stargatefinance/stg-evm-v2/-/stg-evm-v2-1.5.1.tgz#a122ec11e95dad4ad88e06d8996a1dd29e1cfcfd" + integrity sha512-oo02yv4foy9be7lnGZ4t+cD3BOY1E+WT3p3bV8Twrikx9RrkIRE4qe98lpPzbPxXuLg8ufC9XR5q+jUp4DJXPw== + dependencies: + "@layerzerolabs/lz-definitions" "~3.0.30" + "@layerzerolabs/lz-evm-messagelib-v2" "2.0.11" + "@layerzerolabs/lz-evm-oapp-v2" "~2.3.25" + "@layerzerolabs/lz-evm-protocol-v2" "2.0.11" + "@layerzerolabs/lz-evm-sdk-v2" "3.0.30" + "@layerzerolabs/lz-evm-v1-0.7" "~3.0.7" + "@layerzerolabs/lz-utilities" "~3.0.30" + "@layerzerolabs/lz-v2-utilities" "~3.0.30" + "@stargatefinance/stg-definitions-v2" "~1.4.10" + "@stargatefinance/stg-evm-v1" "~1.0.31" + "@types/sinon" "~17.0.3" + ethers "^5.7.2" + +"@swc/helpers@^0.5.11": + version "0.5.15" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.15.tgz#79efab344c5819ecf83a43f3f9f811fc84b516d7" + integrity sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g== + dependencies: + tslib "^2.8.0" + "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz" @@ -2144,6 +2474,40 @@ "@tanstack/query-core" "4.36.1" use-sync-external-store "^1.2.0" +"@ton/core@^0.59.0": + version "0.59.1" + resolved "https://registry.yarnpkg.com/@ton/core/-/core-0.59.1.tgz#fe568069ccdf2f4191da1eadc930076c0c012789" + integrity sha512-SxFBAvutYJaIllTkv82vbHTJhJI6NxzqUhi499CDEjJEZ9i6i9lHJiK2df4dlLAb/4SiWX6+QUzESkK4DEdnCw== + dependencies: + symbol.inspect "1.0.1" + +"@ton/crypto-primitives@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@ton/crypto-primitives/-/crypto-primitives-2.1.0.tgz#8c9277c250b59aae3c819e0d6bd61e44d998e9ca" + integrity sha512-PQesoyPgqyI6vzYtCXw4/ZzevePc4VGcJtFwf08v10OevVJHVfW238KBdpj1kEDQkxWLeuNHEpTECNFKnP6tow== + dependencies: + jssha "3.2.0" + +"@ton/crypto@^3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@ton/crypto/-/crypto-3.3.0.tgz#019103df6540fbc1d8102979b4587bc85ff9779e" + integrity sha512-/A6CYGgA/H36OZ9BbTaGerKtzWp50rg67ZCH2oIjV1NcrBaCK9Z343M+CxedvM7Haf3f/Ee9EhxyeTp0GKMUpA== + dependencies: + "@ton/crypto-primitives" "2.1.0" + jssha "3.2.0" + tweetnacl "1.0.3" + +"@ton/ton@15.1.0": + version "15.1.0" + resolved "https://registry.yarnpkg.com/@ton/ton/-/ton-15.1.0.tgz#a760b1492dd3e5896238b7154f7377f4e51fa086" + integrity sha512-almetcfTu7jLjcNcEEPB7wAc8yl90ES1M//sOr1QE+kv7RbmEvMkaPSc7kFxzs10qrjIPKxlodBJlMSWP5LuVQ== + dependencies: + axios "^1.6.7" + dataloader "^2.0.0" + symbol.inspect "1.0.1" + teslabot "^1.3.0" + zod "^3.21.4" + "@truffle/error@^0.1.1": version "0.1.1" resolved "https://registry.npmjs.org/@truffle/error/-/error-0.1.1.tgz" @@ -2300,14 +2664,6 @@ dependencies: "@types/node" "*" -"@types/glob@*": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@types/glob/-/glob-8.1.0.tgz#b63e70155391b0584dce44e7ea25190bbc38f2fc" - integrity sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w== - dependencies: - "@types/minimatch" "^5.1.2" - "@types/node" "*" - "@types/glob@^7.1.1": version "7.2.0" resolved "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz" @@ -2350,11 +2706,6 @@ resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz" integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== -"@types/minimatch@^5.1.2": - version "5.1.2" - resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" - integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== - "@types/minimist@^1.2.0": version "1.2.5" resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.5.tgz#ec10755e871497bcd83efe927e43ec46e8c0747e" @@ -2375,6 +2726,13 @@ resolved "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz" integrity sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg== +"@types/node@>=13.7.0": + version "22.13.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.13.8.tgz#57e2450295b33a6518d6fd4f65f47236d3e41d8d" + integrity sha512-G3EfaZS+iOGYWLLRCEAXdWK9my08oHNZ+FHluRiggIYJPOXzhOiDgpVCUHaUvyIC5/fj7C/p637jdzC666AOKQ== + dependencies: + undici-types "~6.20.0" + "@types/node@^10.0.3": version "10.17.60" resolved "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz" @@ -2462,11 +2820,28 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== +"@types/sinon@~17.0.3": + version "17.0.4" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-17.0.4.tgz#fd9a3e8e07eea1a3f4a6f82a972c899e5778f369" + integrity sha512-RHnIrhfPO3+tJT0s7cFaXGZvsL4bbR3/k7z3P312qMS4JaS2Tk+KiwiLx1S0rQ56ERj00u1/BtdyVd0FY+Pdew== + dependencies: + "@types/sinonjs__fake-timers" "*" + +"@types/sinonjs__fake-timers@*": + version "8.1.5" + resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz#5fd3592ff10c1e9695d377020c033116cc2889f2" + integrity sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ== + "@types/trusted-types@^2.0.2": version "2.0.7" resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== +"@types/uuid@^8.3.4": + version "8.3.4" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" + integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== + "@types/ws@^7.4.4": version "7.4.7" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" @@ -2474,6 +2849,13 @@ dependencies: "@types/node" "*" +"@types/ws@^8.2.2": + version "8.5.14" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.14.tgz#93d44b268c9127d96026cf44353725dd9b6c3c21" + integrity sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw== + dependencies: + "@types/node" "*" + "@typescript-eslint/eslint-plugin@^5.42.1": version "5.43.0" resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.43.0.tgz" @@ -3043,6 +3425,13 @@ abitype@0.9.8: resolved "https://registry.yarnpkg.com/abitype/-/abitype-0.9.8.tgz#1f120b6b717459deafd213dfbf3a3dd1bf10ae8c" integrity sha512-puLifILdm+8sjyss4S+fsUN09obiT1g2YW6CtcQF+QDzxR0euzgEB29MZujC6zMk2a6SVmtttq1fc6+YFA7WYQ== +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + abortcontroller-polyfill@^1.7.3: version "1.7.5" resolved "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz" @@ -3115,6 +3504,13 @@ agentkeepalive@^4.3.0: dependencies: humanize-ms "^1.2.1" +agentkeepalive@^4.5.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.6.0.tgz#35f73e94b3f40bf65f105219c623ad19c136ea6a" + integrity sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ== + dependencies: + humanize-ms "^1.2.1" + aggregate-error@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz" @@ -3247,6 +3643,18 @@ anymatch@~3.1.1, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +aptos@^1.20.0: + version "1.21.0" + resolved "https://registry.yarnpkg.com/aptos/-/aptos-1.21.0.tgz#346623967a6d038a85f02d71ed6ec62c4cd2ae5e" + integrity sha512-PRKjoFgL8tVEc9+oS7eJUv8GNxx8n3+0byH2+m7CP3raYOD6yFKOecuwjVMIJmgfpjp6xH0P0HDMGZAXmSyU0Q== + dependencies: + "@aptos-labs/aptos-client" "^0.1.0" + "@noble/hashes" "1.3.3" + "@scure/bip39" "1.2.1" + eventemitter3 "^5.0.1" + form-data "4.0.0" + tweetnacl "1.0.3" + arch@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" @@ -3470,6 +3878,15 @@ aws4@^1.8.0: resolved "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== +axios@1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.4.tgz#4c8ded1b43683c8dd362973c393f3ede24052aa2" + integrity sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + axios@^0.21.1: version "0.21.4" resolved "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz" @@ -3477,6 +3894,15 @@ axios@^0.21.1: dependencies: follow-redirects "^1.14.0" +axios@^1.6.7, axios@^1.7.7: + version "1.8.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.8.1.tgz#7c118d2146e9ebac512b7d1128771cdd738d11e3" + integrity sha512-NN+fvwH/kV01dYUQ3PTOZns4LWtWhOFCAhQ/pHb88WQ1hNe5V/dvFwc4VJcDL11LT9xSX0QtsR8sWUuyOuOq7g== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" @@ -3489,6 +3915,16 @@ base-x@^3.0.2, base-x@^3.0.8: dependencies: safe-buffer "^5.0.1" +base-x@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-4.0.0.tgz#d0e3b7753450c73f8ad2389b5c018a4af7b2224a" + integrity sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw== + +base-x@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-5.0.0.tgz#6d835ceae379130e1a4cb846a70ac4746f28ea9b" + integrity sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ== + base64-js@^1.3.1: version "1.5.1" resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" @@ -3519,6 +3955,11 @@ bech32@1.1.4: resolved "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz" integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== +bech32@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-2.0.0.tgz#078d3686535075c8c79709f054b1b226a133b355" + integrity sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg== + better-path-resolve@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/better-path-resolve/-/better-path-resolve-1.0.0.tgz#13a35a1104cdd48a7b74bf8758f96a1ee613f99d" @@ -3538,7 +3979,7 @@ bigint-buffer@^1.1.5: dependencies: bindings "^1.3.0" -bignumber.js@*: +bignumber.js@*, bignumber.js@^9.1.2: version "9.1.2" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== @@ -3565,6 +4006,24 @@ bindings@^1.3.0: dependencies: file-uri-to-path "1.0.0" +bip32@^5.0.0-rc.0: + version "5.0.0-rc.0" + resolved "https://registry.yarnpkg.com/bip32/-/bip32-5.0.0-rc.0.tgz#848b21541ccf8f35d6f2a6ad553368df70d705ed" + integrity sha512-5hVFGrdCnF8GB1Lj2eEo4PRE7+jp+3xBLnfNjydivOkMvKmUKeJ9GG8uOy8prmWl3Oh154uzgfudR1FRkNBudA== + dependencies: + "@noble/hashes" "^1.2.0" + "@scure/base" "^1.1.1" + uint8array-tools "^0.0.8" + valibot "^0.37.0" + wif "^5.0.0" + +bip39@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bip39/-/bip39-3.1.0.tgz#c55a418deaf48826a6ceb34ac55b3ee1577e18a3" + integrity sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A== + dependencies: + "@noble/hashes" "^1.2.0" + blakejs@^1.1.0: version "1.2.1" resolved "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz" @@ -3720,6 +4179,11 @@ brorand@^1.0.1, brorand@^1.1.0: resolved "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz" integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== +browser-headers@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/browser-headers/-/browser-headers-0.4.1.tgz#4308a7ad3b240f4203dbb45acedb38dc2d65dd02" + integrity sha512-CA9hsySZVo9371qEHjHZtYxV2cFtVj5Wj/ZHi8ooEsrtm4vOnl9Y9HmyYWk9q+05d7K3rdoAE0j3MVEFVvtQtg== + browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz" @@ -3796,6 +4260,20 @@ bs58@^4.0.0, bs58@^4.0.1: dependencies: base-x "^3.0.2" +bs58@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-5.0.0.tgz#865575b4d13c09ea2a84622df6c8cbeb54ffc279" + integrity sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ== + dependencies: + base-x "^4.0.0" + +bs58@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-6.0.0.tgz#a2cda0130558535dd281a2f8697df79caaf425d8" + integrity sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw== + dependencies: + base-x "^5.0.0" + bs58check@^2.1.2: version "2.1.2" resolved "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz" @@ -3805,6 +4283,14 @@ bs58check@^2.1.2: create-hash "^1.1.0" safe-buffer "^5.1.2" +bs58check@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-4.0.0.tgz#46cda52a5713b7542dcb78ec2efdf78f5bf1d23c" + integrity sha512-FsGDOnFg9aVI9erdriULkd/JjEWONV/lQE5aYziB5PoBsXRind56lh8doIZIc9X4HoxT5x4bLjMWN1/NB8Zp5g== + dependencies: + "@noble/hashes" "^1.2.0" + bs58 "^6.0.0" + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" @@ -3848,16 +4334,6 @@ bufio@^1.0.7: resolved "https://registry.yarnpkg.com/bufio/-/bufio-1.2.1.tgz#8d4ab3ddfcd5faa90f996f922f9397d41cbaf2de" integrity sha512-9oR3zNdupcg/Ge2sSHQF3GX+kmvL/fTPvD0nd5AGLq8SjUYnTz+SlFjK/GXidndbZtIj+pVKXiWeR9w6e9wKCA== -bunyan@^1.8.12: - version "1.8.15" - resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.15.tgz#8ce34ca908a17d0776576ca1b2f6cbd916e93b46" - integrity sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig== - optionalDependencies: - dtrace-provider "~0.8" - moment "^2.19.3" - mv "~2" - safe-json-stringify "~1" - busboy@^1.6.0: version "1.6.0" resolved "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz" @@ -4497,7 +4973,7 @@ create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: ripemd160 "^2.0.1" sha.js "^2.4.0" -create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: +create-hmac@1.1.7, create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: version "1.1.7" resolved "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz" integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== @@ -4629,6 +5105,14 @@ d@1, d@^1.0.1: es5-ext "^0.10.50" type "^1.0.1" +d@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.2.tgz#2aefd554b81981e7dccf72d6842ae725cb17e5de" + integrity sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw== + dependencies: + es5-ext "^0.10.64" + type "^2.7.2" + dashdash@^1.12.0: version "1.14.1" resolved "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz" @@ -4668,6 +5152,16 @@ dataloader@^1.4.0: resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-1.4.0.tgz#bca11d867f5d3f1b9ed9f737bd15970c65dff5c8" integrity sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw== +dataloader@^2.0.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.3.tgz#42d10b4913515f5b37c6acedcb4960d6ae1b1517" + integrity sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA== + +dayjs@^1.11.13: + version "1.11.13" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c" + integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== + death@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/death/-/death-1.1.0.tgz" @@ -4727,13 +5221,6 @@ decode-uri-component@^0.2.0, decode-uri-component@^0.2.2: resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz" integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== -decomment@^0.9.1: - version "0.9.5" - resolved "https://registry.yarnpkg.com/decomment/-/decomment-0.9.5.tgz#61753c80b8949620eb6bc3f8246cc0e2720ceac1" - integrity sha512-h0TZ8t6Dp49duwyDHo3iw67mnh9/UpFiSSiOb5gDK1sqoXzrfX/SQxIUQd2R2QEiSnqib0KF2fnKnGfAhAs6lg== - dependencies: - esprima "4.0.1" - decompress-response@^3.3.0: version "3.3.0" resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz" @@ -4981,13 +5468,6 @@ dotenv@^8.1.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== -dtrace-provider@~0.8: - version "0.8.8" - resolved "https://registry.yarnpkg.com/dtrace-provider/-/dtrace-provider-0.8.8.tgz#2996d5490c37e1347be263b423ed7b297fb0d97e" - integrity sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg== - dependencies: - nan "^2.14.0" - duplexer3@^0.1.4: version "0.1.5" resolved "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz" @@ -5016,6 +5496,14 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" +ed25519-hd-key@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/ed25519-hd-key/-/ed25519-hd-key-1.3.0.tgz#e0bd2be4c07e15c753d5ed3aca30744e321b7c78" + integrity sha512-IWwAyiiuJQhgu3L8NaHb68eJxTu2pgCwxIBdgpLJdKpYZM46+AXePSVTr7fkNKaUOfOL4IrjEUaQvyVRIDP7fg== + dependencies: + create-hmac "1.1.7" + tweetnacl "1.0.3" + ee-first@1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" @@ -5039,6 +5527,19 @@ elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5 minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +elliptic@^6.5.7: + version "6.6.1" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.6.1.tgz#3b8ffb02670bf69e382c7f65bf524c97c5405c06" + integrity sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz" @@ -5229,6 +5730,16 @@ es5-ext@^0.10.35, es5-ext@^0.10.50: es6-symbol "^3.1.3" next-tick "^1.1.0" +es5-ext@^0.10.46, es5-ext@^0.10.62, es5-ext@^0.10.64, es5-ext@~0.10.14, es5-ext@~0.10.2: + version "0.10.64" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714" + integrity sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg== + dependencies: + es6-iterator "^2.0.3" + es6-symbol "^3.1.3" + esniff "^2.0.1" + next-tick "^1.1.0" + es6-iterator@^2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz" @@ -5258,6 +5769,16 @@ es6-symbol@^3.1.1, es6-symbol@^3.1.3: d "^1.0.1" ext "^1.1.2" +es6-weak-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" + integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== + dependencies: + d "1" + es5-ext "^0.10.46" + es6-iterator "^2.0.3" + es6-symbol "^3.1.1" + esbuild@^0.19.3: version "0.19.7" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.7.tgz#b9a7235097b81278dcf090e2532ed13c95a2ee84" @@ -5416,6 +5937,16 @@ eslint@^7.32.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" +esniff@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308" + integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg== + dependencies: + d "^1.0.1" + es5-ext "^0.10.62" + event-emitter "^0.3.5" + type "^2.7.2" + espree@^7.3.0, espree@^7.3.1: version "7.3.1" resolved "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz" @@ -5430,7 +5961,7 @@ esprima@2.7.x, esprima@^2.7.1: resolved "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz" integrity sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A== -esprima@4.0.1, esprima@^4.0.0: +esprima@^4.0.0: version "4.0.1" resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -5723,6 +6254,19 @@ ethjs-util@0.1.6, ethjs-util@^0.1.6: is-hex-prefixed "1.0.0" strip-hex-prefix "1.0.0" +event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== + dependencies: + d "1" + es5-ext "~0.10.14" + +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + eventemitter3@4.0.4: version "4.0.4" resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz" @@ -5733,6 +6277,11 @@ eventemitter3@^4.0.7: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + events@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" @@ -5795,6 +6344,11 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" +exponential-backoff@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.2.tgz#a8f26adb96bf78e8cd8ad1037928d5e5c0679d91" + integrity sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA== + express@^4.14.0, express@^4.18.2: version "4.18.2" resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" @@ -5928,6 +6482,11 @@ fast-redact@^3.0.0: resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.3.0.tgz#7c83ce3a7be4898241a46560d51de10f653f7634" integrity sha512-6T5V1QK1u4oF+ATxs1lWUmlEk6P2T9HqJG3e2DnHOdVgZy2rFJBoEnrIedcTXlkAHU/zKC+7KETJ+KGGKwxgMQ== +fast-redact@^3.1.1: + version "3.5.0" + resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.5.0.tgz#e9ea02f7e57d0cd8438180083e93077e496285e4" + integrity sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A== + fast-safe-stringify@^2.0.6: version "2.1.1" resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" @@ -6081,6 +6640,11 @@ follow-redirects@^1.12.1, follow-redirects@^1.14.0: resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz" integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== +follow-redirects@^1.15.6: + version "1.15.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz" @@ -6111,6 +6675,15 @@ form-data-encoder@1.7.1: resolved "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.1.tgz" integrity sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg== +form-data@4.0.0, form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + form-data@^2.2.0, form-data@~2.3.2: version "2.3.3" resolved "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz" @@ -6120,15 +6693,6 @@ form-data@^2.2.0, form-data@~2.3.2: combined-stream "^1.0.6" mime-types "^2.1.12" -form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - forwarded@0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" @@ -6407,13 +6971,6 @@ glob-parent@^5.1.2, glob-parent@~5.1.0, glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" -glob-promise@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/glob-promise/-/glob-promise-3.4.0.tgz#b6b8f084504216f702dc2ce8c9bc9ac8866fdb20" - integrity sha512-q08RJ6O+eJn+dVanerAndJwIcumgbDdYiUT7zFQl3Wm1xD6fBKtah7H8ZJChj4wP+8C+QfeVy8xautR7rdmKEw== - dependencies: - "@types/glob" "*" - glob@10: version "10.4.5" resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" @@ -6473,17 +7030,6 @@ glob@^5.0.15: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^6.0.1: - version "6.0.4" - resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" - integrity sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A== - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" - once "^1.3.0" - path-is-absolute "^1.0.0" - glob@^8.0.3: version "8.0.3" resolved "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz" @@ -6565,6 +7111,11 @@ globby@^11.0.0, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +google-protobuf@^3.21.4: + version "3.21.4" + resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.21.4.tgz#2f933e8b6e5e9f8edde66b7be0024b68f77da6c9" + integrity sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ== + gopd@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz" @@ -6625,7 +7176,7 @@ got@9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" -got@^11.8.5: +got@^11.8.5, got@^11.8.6: version "11.8.6" resolved "https://registry.npmjs.org/got/-/got-11.8.6.tgz" integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g== @@ -7167,11 +7718,6 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, i resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== - ini@^1.3.5: version "1.3.8" resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" @@ -7490,6 +8036,11 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-promise@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== + is-regex@^1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" @@ -7671,6 +8222,24 @@ jayson@^4.1.0: uuid "^8.3.2" ws "^7.4.5" +jayson@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/jayson/-/jayson-4.1.3.tgz#db9be2e4287d9fef4fc05b5fe367abe792c2eee8" + integrity sha512-LtXh5aYZodBZ9Fc3j6f2w+MTNcnxteMOrb+QgIouguGOulWi0lieEkOUg+HkjjFs0DGoWDds6bi4E9hpNFLulQ== + dependencies: + "@types/connect" "^3.4.33" + "@types/node" "^12.12.54" + "@types/ws" "^7.4.4" + JSONStream "^1.3.5" + commander "^2.20.3" + delay "^5.0.0" + es6-promisify "^5.0.0" + eyes "^0.1.8" + isomorphic-ws "^4.0.1" + json-stringify-safe "^5.0.1" + uuid "^8.3.2" + ws "^7.5.10" + jiti@^1.20.0: version "1.21.0" resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d" @@ -7719,6 +8288,11 @@ jsbn@~0.1.0: resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== +jscrypto@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/jscrypto/-/jscrypto-1.0.3.tgz#598febca2a939d6f679c54f56e1fe364cef30cc9" + integrity sha512-lryZl0flhodv4SZHOqyb1bx5sKcJxj0VBo0Kzb4QMAg3L021IC9uGpl0RCZa+9KJwlRGSK2C80ITcwbe19OKLQ== + jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" @@ -7823,6 +8397,20 @@ jsprim@^1.2.2: json-schema "0.4.0" verror "1.10.0" +jssha@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/jssha/-/jssha-3.2.0.tgz#88ec50b866dd1411deaddbe6b3e3692e4c710f16" + integrity sha512-QuruyBENDWdN4tZwJbQq7/eAK85FqrI4oDbXjy5IBhYD+2pTJyBUWZe8ctWaCkrV0gy6AaelgOZZBMeswEa/6Q== + +keccak256@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/keccak256/-/keccak256-1.0.6.tgz#dd32fb771558fed51ce4e45a035ae7515573da58" + integrity sha512-8GLiM01PkdJVGUhR1e6M/AvWnSqYS0HaERI+K/QtStGDGlSTx2B1zTqZk4Zlqu5TxHJNTxWAdP9Y+WI50OApUw== + dependencies: + bn.js "^5.2.0" + buffer "^6.0.3" + keccak "^3.0.2" + keccak@^3.0.0, keccak@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/keccak/-/keccak-3.0.2.tgz" @@ -8070,6 +8658,11 @@ log-symbols@4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" +long@^5.0.0, long@^5.2.3: + version "5.3.1" + resolved "https://registry.yarnpkg.com/long/-/long-5.3.1.tgz#9d4222d3213f38a5ec809674834e0f0ab21abe96" + integrity sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng== + loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -8138,6 +8731,13 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" + integrity sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ== + dependencies: + es5-ext "~0.10.2" + lru_map@^0.3.3: version "0.3.3" resolved "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz" @@ -8250,6 +8850,20 @@ memoize-one@^6.0.0: resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045" integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw== +memoizee@^0.4.17: + version "0.4.17" + resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.17.tgz#942a5f8acee281fa6fb9c620bddc57e3b7382949" + integrity sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA== + dependencies: + d "^1.0.2" + es5-ext "^0.10.64" + es6-weak-map "^2.0.3" + event-emitter "^0.3.5" + is-promise "^2.2.2" + lru-queue "^0.1.0" + next-tick "^1.1.0" + timers-ext "^0.1.7" + memorystream@^0.3.1: version "0.3.1" resolved "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz" @@ -8488,7 +9102,7 @@ mkdirp-promise@^5.0.1: dependencies: mkdirp "*" -mkdirp@*, mkdirp@0.5.x, mkdirp@^0.5.5, mkdirp@~0.5.1: +mkdirp@*, mkdirp@0.5.x, mkdirp@^0.5.5: version "0.5.6" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== @@ -8587,11 +9201,6 @@ mock-fs@^4.1.0: resolved "https://registry.npmjs.org/mock-fs/-/mock-fs-4.14.0.tgz" integrity sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw== -moment@^2.19.3: - version "2.30.0" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.0.tgz#c2a65bb962bdf2a9c49c69b5358c41f6c66ad10e" - integrity sha512-8XSlYFhOSJvnEJOas6RpDCNU2PYeVC+oE33d3Z9tIsXpD8LIgBeqrHPjP8es4b3fcJpf07D1PJWGDUfdbqDLnQ== - motion@10.16.2: version "10.16.2" resolved "https://registry.yarnpkg.com/motion/-/motion-10.16.2.tgz#7dc173c6ad62210a7e9916caeeaf22c51e598d21" @@ -8683,20 +9292,6 @@ murmur-128@^0.2.1: fmix "^0.1.0" imul "^1.0.0" -mv@~2: - version "2.1.1" - resolved "https://registry.yarnpkg.com/mv/-/mv-2.1.1.tgz#ae6ce0d6f6d5e0a4f7d893798d03c1ea9559b6a2" - integrity sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg== - dependencies: - mkdirp "~0.5.1" - ncp "~2.0.0" - rimraf "~2.4.0" - -nan@^2.14.0: - version "2.18.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.18.0.tgz#26a6faae7ffbeb293a39660e88a76b82e30b7554" - integrity sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w== - nano-json-stream-parser@^0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz" @@ -8751,11 +9346,6 @@ natural-compare@^1.4.0: resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -ncp@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" - integrity sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA== - negotiator@0.6.3: version "0.6.3" resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" @@ -8781,6 +9371,11 @@ node-addon-api@^2.0.0: resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz" integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== +node-addon-api@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762" + integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA== + node-addon-api@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.0.0.tgz#8136add2f510997b3b94814f4af1cce0b0e3962e" @@ -8813,7 +9408,7 @@ node-fetch@2.6.7: dependencies: whatwg-url "^5.0.0" -node-fetch@^2.5.0, node-fetch@^2.6.1, node-fetch@^2.6.12: +node-fetch@^2.5.0, node-fetch@^2.6.1, node-fetch@^2.6.12, node-fetch@^2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== @@ -9018,6 +9613,11 @@ on-exit-leak-free@^0.2.0: resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz#b39c9e3bf7690d890f4861558b0d7b90a442d209" integrity sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg== +on-exit-leak-free@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz#fed195c9ebddb7d9e4c3842f93f281ac8dadd3b8" + integrity sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA== + on-finished@2.4.1: version "2.4.1" resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" @@ -9311,14 +9911,6 @@ path-type@^4.0.0: resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -path@^0.12.7: - version "0.12.7" - resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f" - integrity sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q== - dependencies: - process "^0.11.1" - util "^0.10.3" - pathe@^1.1.0, pathe@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.1.tgz#1dd31d382b974ba69809adc9a7a347e65d84829a" @@ -9345,7 +9937,7 @@ performance-now@^2.1.0: resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz" integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== -picocolors@^1.0.0: +picocolors@1.0.0, picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== @@ -9370,6 +9962,14 @@ pify@^5.0.0: resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== +pino-abstract-transport@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz#97f9f2631931e242da531b5c66d3079c12c9d1b5" + integrity sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q== + dependencies: + readable-stream "^4.0.0" + split2 "^4.0.0" + pino-abstract-transport@v0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-0.5.0.tgz#4b54348d8f73713bfd14e3dc44228739aa13d9c0" @@ -9383,6 +9983,11 @@ pino-std-serializers@^4.0.0: resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-4.0.0.tgz#1791ccd2539c091ae49ce9993205e2cd5dbba1e2" integrity sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q== +pino-std-serializers@^6.0.0: + version "6.2.2" + resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz#d9a9b5f2b9a402486a5fc4db0a737570a860aab3" + integrity sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA== + pino@7.11.0: version "7.11.0" resolved "https://registry.yarnpkg.com/pino/-/pino-7.11.0.tgz#0f0ea5c4683dc91388081d44bff10c83125066f6" @@ -9400,6 +10005,23 @@ pino@7.11.0: sonic-boom "^2.2.1" thread-stream "^0.15.1" +pino@^8.16.2: + version "8.21.0" + resolved "https://registry.yarnpkg.com/pino/-/pino-8.21.0.tgz#e1207f3675a2722940d62da79a7a55a98409f00d" + integrity sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q== + dependencies: + atomic-sleep "^1.0.0" + fast-redact "^3.1.1" + on-exit-leak-free "^2.1.0" + pino-abstract-transport "^1.2.0" + pino-std-serializers "^6.0.0" + process-warning "^3.0.0" + quick-format-unescaped "^4.0.3" + real-require "^0.2.0" + safe-stable-stringify "^2.3.1" + sonic-boom "^3.7.0" + thread-stream "^2.6.0" + pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -9500,7 +10122,12 @@ process-warning@^1.0.0: resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-1.0.0.tgz#980a0b25dc38cd6034181be4b7726d89066b4616" integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q== -process@^0.11.1, process@^0.11.10: +process-warning@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-3.0.0.tgz#96e5b88884187a1dce6f5c3166d611132058710b" + integrity sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ== + +process@^0.11.10: version "0.11.10" resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz" integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== @@ -9534,6 +10161,24 @@ prop-types@^15.7.2, prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" +protobufjs@^7.3.2: + version "7.4.0" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.4.0.tgz#7efe324ce9b3b61c82aae5de810d287bc08a248a" + integrity sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/node" ">=13.7.0" + long "^5.0.0" + proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz" @@ -9547,6 +10192,11 @@ proxy-compare@2.5.1: resolved "https://registry.yarnpkg.com/proxy-compare/-/proxy-compare-2.5.1.tgz#17818e33d1653fbac8c2ec31406bce8a2966f600" integrity sha512-oyfc0Tx87Cpwva5ZXezSp5V9vht1c7dZBhvuV/y3ctkgMVUmiAGDVeeB0dKhGSyT0v1ZTEQYpe/RXlBVBNuCLA== +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" @@ -9880,6 +10530,17 @@ readable-stream@^3.6.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@^4.0.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.7.0.tgz#cedbd8a1146c13dfff8dab14068028d58c15ac91" + integrity sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg== + dependencies: + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" + string_decoder "^1.3.0" + readdirp@^2.1.0: version "2.2.1" resolved "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz" @@ -9908,6 +10569,11 @@ real-require@^0.1.0: resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.1.0.tgz#736ac214caa20632847b7ca8c1056a0767df9381" integrity sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg== +real-require@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.2.0.tgz#209632dea1810be2ae063a6ac084fee7e33fba78" + integrity sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg== + rechoir@^0.6.2: version "0.6.2" resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz" @@ -10172,14 +10838,7 @@ rimraf@3.0.2, rimraf@^3.0.2: dependencies: glob "^7.1.3" -rimraf@~2.4.0: - version "2.4.5" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.4.5.tgz#ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da" - integrity sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ== - dependencies: - glob "^6.0.1" - -ripemd160@^2.0.0, ripemd160@^2.0.1: +ripemd160@^2.0.0, ripemd160@^2.0.1, ripemd160@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz" integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== @@ -10226,6 +10885,22 @@ rpc-websockets@^7.5.1: bufferutil "^4.0.1" utf-8-validate "^5.0.2" +rpc-websockets@^9.0.2: + version "9.1.1" + resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-9.1.1.tgz#5764336f3623ee1c5cc8653b7335183e3c0c78bd" + integrity sha512-1IXGM/TfPT6nfYMIXkJdzn+L4JEsmb0FL1O2OBjaH03V3yuUDdKFulGLMFG6ErV+8pZ5HVC0limve01RyO+saA== + dependencies: + "@swc/helpers" "^0.5.11" + "@types/uuid" "^8.3.4" + "@types/ws" "^8.2.2" + buffer "^6.0.3" + eventemitter3 "^5.0.1" + uuid "^8.3.2" + ws "^8.5.0" + optionalDependencies: + bufferutil "^4.0.1" + utf-8-validate "^5.0.2" + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" @@ -10240,6 +10915,13 @@ rxjs@^6.6.3: dependencies: tslib "^1.9.0" +rxjs@^7.8.1: + version "7.8.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.2.tgz#955bc473ed8af11a002a2be52071bf475638607b" + integrity sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA== + dependencies: + tslib "^2.1.0" + safe-array-concat@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" @@ -10260,11 +10942,6 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-json-stringify@~1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz#356e44bc98f1f93ce45df14bcd7c01cda86e0afd" - integrity sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg== - safe-regex-test@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377" @@ -10286,6 +10963,11 @@ safe-stable-stringify@^2.1.0: resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== +safe-stable-stringify@^2.3.1: + version "2.5.0" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd" + integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA== + "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" @@ -10337,6 +11019,15 @@ secp256k1@^4.0.1: node-addon-api "^2.0.0" node-gyp-build "^4.2.0" +secp256k1@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-5.0.1.tgz#dc2c86187d48ff2da756f0f7e96417ee03c414b1" + integrity sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA== + dependencies: + elliptic "^6.5.7" + node-addon-api "^5.0.0" + node-gyp-build "^4.2.0" + seedrandom@^3.0.5: version "3.0.5" resolved "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz" @@ -10369,6 +11060,11 @@ semver@^7.2.1, semver@^7.3.4: dependencies: lru-cache "^6.0.0" +semver@^7.3.5, semver@^7.6.3: + version "7.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" + integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== + semver@^7.3.7: version "7.3.8" resolved "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz" @@ -10709,6 +11405,13 @@ sonic-boom@^2.2.1: dependencies: atomic-sleep "^1.0.0" +sonic-boom@^3.7.0: + version "3.8.1" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-3.8.1.tgz#d5ba8c4e26d6176c9a1d14d549d9ff579a163422" + integrity sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg== + dependencies: + atomic-sleep "^1.0.0" + source-map-js@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" @@ -11000,7 +11703,7 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -string_decoder@^1.1.1: +string_decoder@^1.1.1, string_decoder@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== @@ -11105,6 +11808,11 @@ superstruct@^1.0.3: resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-1.0.3.tgz#de626a5b49c6641ff4d37da3c7598e7a87697046" integrity sha512-8iTn3oSS8nRGn+C2pgXSKPI3jmpm6FExNazNpjvqS6ZUJQCej3PUXEKM8NjHBOs54ExM+LPW/FBRhymrdcCiSg== +superstruct@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-2.0.2.tgz#3f6d32fbdc11c357deff127d591a39b996300c54" + integrity sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A== + supports-color@6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz" @@ -11188,6 +11896,11 @@ symbol-observable@^4.0.0: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205" integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ== +symbol.inspect@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/symbol.inspect/-/symbol.inspect-1.0.1.tgz#e13125b8038c4996eb0dfa1567332ad4dcd0763f" + integrity sha512-YQSL4duoHmLhsTD1Pw8RW6TZ5MaTX5rXJnqacJottr2P2LZBF/Yvrc3ku4NUpMOm8aM0KOCqM+UAkMA5HWQCzQ== + sync-request@^6.0.0: version "6.1.0" resolved "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz" @@ -11248,6 +11961,11 @@ term-size@^2.1.0: resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54" integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg== +teslabot@^1.3.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/teslabot/-/teslabot-1.5.0.tgz#70f544516699ca5f696d8ae94f3d12cd495d5cd6" + integrity sha512-e2MmELhCgrgZEGo7PQu/6bmYG36IDH+YrBI1iGm6jovXkeDIGa3pZ2WSqRjzkuw2vt1EqfkZoV5GpXgqL8QJVg== + text-encoding-utf-8@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13" @@ -11282,6 +12000,13 @@ thread-stream@^0.15.1: dependencies: real-require "^0.1.0" +thread-stream@^2.6.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-2.7.0.tgz#d8a8e1b3fd538a6cca8ce69dbe5d3d097b601e11" + integrity sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw== + dependencies: + real-require "^0.2.0" + "through@>=2.2.7 <3": version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -11292,11 +12017,24 @@ timed-out@^4.0.1: resolved "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz" integrity sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA== +timers-ext@^0.1.7: + version "0.1.8" + resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.8.tgz#b4e442f10b7624a29dd2aa42c295e257150cf16c" + integrity sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww== + dependencies: + es5-ext "^0.10.64" + next-tick "^1.1.0" + tiny-emitter@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz" integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== +tiny-invariant@^1.3.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127" + integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg== + tmp@0.0.33, tmp@^0.0.33: version "0.0.33" resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" @@ -11430,6 +12168,11 @@ tslib@^2.0.0, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== +tslib@^2.8.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + tsort@0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/tsort/-/tsort-0.0.1.tgz" @@ -11467,16 +12210,16 @@ tweetnacl-util@^0.15.1: resolved "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz" integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== +tweetnacl@1.0.3, tweetnacl@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== -tweetnacl@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz" - integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" @@ -11651,6 +12394,11 @@ uglify-js@^3.1.4: resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.16.3.tgz" integrity sha512-uVbFqx9vvLhQg0iBaau9Z75AxWJ8tqM9AV890dIZCLApF4rTcyHwmAvLeEdYRs+BzYWu8Iw81F79ah0EfTXbaw== +uint8array-tools@^0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/uint8array-tools/-/uint8array-tools-0.0.8.tgz#712bab001f8347bd782f45bc47c76ffff32d1e0b" + integrity sha512-xS6+s8e0Xbx++5/0L+yyexukU7pz//Yg6IHg3BKhXotg1JcYtgxVcUctQ0HxLByiJzpAkNFawz1Nz5Xadzo82g== + uint8arrays@^3.0.0, uint8arrays@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.1.1.tgz#2d8762acce159ccd9936057572dade9459f65ae0" @@ -11683,6 +12431,11 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +undici-types@~6.20.0: + version "6.20.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" + integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== + undici@^5.14.0: version "5.22.0" resolved "https://registry.npmjs.org/undici/-/undici-5.22.0.tgz" @@ -11847,13 +12600,6 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -util@^0.10.3: - version "0.10.4" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" - integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== - dependencies: - inherits "2.0.3" - util@^0.12.0, util@^0.12.4, util@^0.12.5: version "0.12.5" resolved "https://registry.npmjs.org/util/-/util-0.12.5.tgz" @@ -11905,6 +12651,11 @@ v8-compile-cache@^2.0.3: resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== +valibot@^0.37.0: + version "0.37.0" + resolved "https://registry.yarnpkg.com/valibot/-/valibot-0.37.0.tgz#b71f597a581c716f6546a3b53cefd2829b559c55" + integrity sha512-FQz52I8RXgFgOHym3XHYSREbNtkgSjF9prvMFH1nBsRyfL6SfCzoT1GuSDTlbsuPubM7/6Kbw0ZMQb8A+V+VsQ== + validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" @@ -12541,6 +13292,13 @@ widest-line@^3.1.0: dependencies: string-width "^4.0.0" +wif@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/wif/-/wif-5.0.0.tgz#445e44b8f62e155144d1c970c01ca2ba3979cc3f" + integrity sha512-iFzrC/9ne740qFbNjTZ2FciSRJlHIXoxqk/Y5EnE08QOXu1WjJyCCswwDTYbohAOEnlCtLaAAQBhyaLRFh2hMA== + dependencies: + bs58check "^4.0.0" + word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" @@ -12646,6 +13404,16 @@ ws@^7.4.5, ws@^7.4.6, ws@^7.5.1: resolved "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz" integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== +ws@^7.5.10: + version "7.5.10" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" + integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== + +ws@^8.18.0: + version "8.18.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.1.tgz#ea131d3784e1dfdff91adb0a4a116b127515e3cb" + integrity sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w== + ws@^8.5.0: version "8.14.2" resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" @@ -12902,6 +13670,11 @@ zksync-web3@^0.14.3: resolved "https://registry.npmjs.org/zksync-web3/-/zksync-web3-0.14.3.tgz" integrity sha512-hT72th4AnqyLW1d5Jlv8N2B/qhEnl2NePK2A3org7tAa24niem/UAaHMkEvmWI3SF9waYUPtqAtjpf+yvQ9zvQ== +zod@^3.21.4: + version "3.24.2" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.2.tgz#8efa74126287c675e92f46871cfc8d15c34372b3" + integrity sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ== + zustand@^4.3.1: version "4.4.6" resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.4.6.tgz#03c78e3e2686c47095c93714c0c600b72a6512bd" From 90d5370da467e58c452f978143789a8b4cded249 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 2 Mar 2025 22:03:42 +0200 Subject: [PATCH 200/205] Enable multichain bridging out, add deploy script and test setup, other small refactoring --- contracts/multichain/IMultichainProvider.sol | 3 +- contracts/multichain/LayerZeroProvider.sol | 130 ++++++++++-------- contracts/multichain/MultichainEventUtils.sol | 2 +- .../multichain/MultichainTransferRouter.sol | 9 +- deploy/deployLayerZeroProvider.ts | 2 +- deploy/deployMultichainTransferRouter.ts | 44 ++++++ test/multichain/LayerZeroProvider.ts | 34 ++--- test/multichain/MultichainGmRouter.ts | 117 +++++++++++----- utils/fixture.ts | 2 + utils/keys.ts | 5 + utils/relay/multichain.ts | 79 +++++++++++ 11 files changed, 309 insertions(+), 118 deletions(-) create mode 100644 deploy/deployMultichainTransferRouter.ts diff --git a/contracts/multichain/IMultichainProvider.sol b/contracts/multichain/IMultichainProvider.sol index d7950d9ff..9b23ca32c 100644 --- a/contracts/multichain/IMultichainProvider.sol +++ b/contracts/multichain/IMultichainProvider.sol @@ -8,9 +8,10 @@ pragma solidity ^0.8.0; interface IMultichainProvider { function bridgeOut( address provider, - address receiver, + address account, address token, uint256 amount, + uint256 srcChainId, bytes calldata data ) external; } diff --git a/contracts/multichain/LayerZeroProvider.sol b/contracts/multichain/LayerZeroProvider.sol index c3652c59c..8ddfc62de 100644 --- a/contracts/multichain/LayerZeroProvider.sol +++ b/contracts/multichain/LayerZeroProvider.sol @@ -5,13 +5,16 @@ pragma solidity ^0.8.20; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ILayerZeroComposer } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroComposer.sol"; +import { MessagingFee, OFTReceipt, SendParam, MessagingReceipt } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/interfaces/IOFT.sol"; + +import { IStargate } from "@stargatefinance/stg-evm-v2/src/interfaces/IStargate.sol"; import "../event/EventEmitter.sol"; import "../data/DataStore.sol"; -import "./IMultichainProvider.sol"; import "./MultichainVault.sol"; import "./MultichainUtils.sol"; +import "./IMultichainProvider.sol"; import "./MultichainProviderUtils.sol"; /** @@ -23,17 +26,21 @@ import "./MultichainProviderUtils.sol"; * Defines bridgeOut function which: * - sends tokens to the Stargate executor for bridging out to the source chain */ -contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer { - +contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer, RoleModule { struct LayerZeroBridgeOutParams { - bytes32 dstEid; + uint32 dstEid; } DataStore public immutable dataStore; EventEmitter public immutable eventEmitter; MultichainVault public immutable multichainVault; - constructor(DataStore _dataStore, EventEmitter _eventEmitter, MultichainVault _multichainVault) { + constructor( + DataStore _dataStore, + RoleStore _roleStore, + EventEmitter _eventEmitter, + MultichainVault _multichainVault + ) RoleModule(_roleStore) { dataStore = _dataStore; eventEmitter = _eventEmitter; multichainVault = _multichainVault; @@ -45,13 +52,9 @@ contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer { * Called by Stargate after tokens have been delivered to this contract. * @dev Non-guarded function caller (i.e. require from == stargatePool AND msg.sender == lzEndpoint) * Anyone (and on behalf of anyone) can call this function to deposit tokens - * TBD if this will change - * @dev Non-guarded token address (i.e. require token == USDC) - * Any token can be deposited (not just USDC), but current implementation intended to USDC only - * TBD if this will change * param from The address of the sender (i.e. Stargate address, not user's address). * param guid A global unique identifier for tracking the packet. - * param message Encoded message. Contains the params needed to record the deposit (account, token, srcChainId) + * @param message Encoded message. Contains the params needed to record the deposit (account, token, srcChainId) * param executor The address of the Executor. * param extraData Any extra data or options to trigger on receipt. */ @@ -72,61 +75,72 @@ contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer { IERC20(token).transfer(to, amount); } - // TODO: enable bridging out (re-check LZ docs) function bridgeOut( address _stargate, - address receiver, + address account, address token, uint256 amount, + uint256 srcChainId, bytes calldata data - ) external { + ) external onlyController { IERC20(token).approve(_stargate, amount); - // IStargate stargate = IStargate(_stargate); - // LayerZeroBridgeOutParams memory info = abi.decode(data, (LayerZeroBridgeOutParams)); - - // (uint256 valueToSend, SendParam memory sendParam, MessagingFee memory messagingFee) = prepareSend( - // stargate, - // amount, - // receiver, - // info.dstEid, - // new bytes(0), // _extraOptions - // new bytes(0) // _composeMsg - // ); - - // (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) = stargate.send{value: valueToSend}( - // sendParam, - // messagingFee, - // receiver - // ); + IStargate stargate = IStargate(_stargate); + + (uint256 valueToSend, SendParam memory sendParam, MessagingFee memory messagingFee) = prepareSend( + stargate, + amount, + account, + abi.decode(data, (uint32)), // dstEid + new bytes(0), // _extraOptions + new bytes(0) // _composeMsg + ); + + // transfer amount + bridging fee from user's multichain balance into this contract + MultichainUtils.transferOut( + dataStore, + eventEmitter, + multichainVault, + token, + account, + address(this), // receiver + valueToSend, // amount + bridging fee + srcChainId + ); + + /*(MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) =*/ stargate.send{ value: valueToSend }( + sendParam, + messagingFee, + account + ); } - // function prepareSend( - // IStargate stargate, - // uint256 amount, - // address receiver, - // uint32 _dstEid, - // bytes memory _composeMsg, - // bytes memory _extraOptions - // ) private view returns (uint256 valueToSend, SendParam memory sendParam, MessagingFee memory messagingFee) { - // sendParam = SendParam({ - // dstEid: _dstEid, - // to: addressToBytes32(receiver), - // amountLD: amount, - // minAmountLD: amount, - // extraOptions: _extraOptions, - // composeMsg: _composeMsg, - // oftCmd: "" - // }); - - // (, , OFTReceipt memory receipt) = stargate.quoteOFT(sendParam); - // sendParam.minAmountLD = receipt.amountReceivedLD; - - // messagingFee = stargate.quoteSend(sendParam, false); - // valueToSend = messagingFee.nativeFee; - - // if (stargate.token() == address(0x0)) { - // valueToSend += sendParam.amountLD; - // } - // } + function prepareSend( + IStargate stargate, + uint256 amount, + address receiver, + uint32 _dstEid, + bytes memory _composeMsg, + bytes memory _extraOptions + ) private view returns (uint256 valueToSend, SendParam memory sendParam, MessagingFee memory messagingFee) { + sendParam = SendParam({ + dstEid: _dstEid, + to: MultichainProviderUtils.addressToBytes32(receiver), + amountLD: amount, + minAmountLD: amount, + extraOptions: _extraOptions, + composeMsg: _composeMsg, + oftCmd: "" + }); + + (, , OFTReceipt memory receipt) = stargate.quoteOFT(sendParam); + sendParam.minAmountLD = receipt.amountReceivedLD; + + messagingFee = stargate.quoteSend(sendParam, false); + valueToSend = messagingFee.nativeFee; + + if (stargate.token() == address(0x0)) { + valueToSend += sendParam.amountLD; + } + } } diff --git a/contracts/multichain/MultichainEventUtils.sol b/contracts/multichain/MultichainEventUtils.sol index bb0d45425..959c85c8b 100644 --- a/contracts/multichain/MultichainEventUtils.sol +++ b/contracts/multichain/MultichainEventUtils.sol @@ -65,7 +65,7 @@ library MultichainEventUtils { ) internal { EventUtils.EventLogData memory eventData; - eventData.addressItems.initItems(2); + eventData.addressItems.initItems(3); eventData.addressItems.setItem(0, "provider", provider); eventData.addressItems.setItem(1, "token", token); eventData.addressItems.setItem(2, "receiver", receiver); diff --git a/contracts/multichain/MultichainTransferRouter.sol b/contracts/multichain/MultichainTransferRouter.sol index 7d56dac1f..a62c447ef 100644 --- a/contracts/multichain/MultichainTransferRouter.sol +++ b/contracts/multichain/MultichainTransferRouter.sol @@ -46,7 +46,7 @@ contract MultichainTransferRouter is MultichainRouter { function bridgeOut( RelayUtils.RelayParams calldata relayParams, address provider, - address receiver, + address account, uint256 srcChainId, bytes calldata data, // encoded provider specific data e.g. dstEid RelayUtils.BridgeOutParams calldata params @@ -56,16 +56,17 @@ contract MultichainTransferRouter is MultichainRouter { _validateMultichainProvider(dataStore, provider); bytes32 structHash = RelayUtils.getBridgeOutStructHash(relayParams, params); - _validateCall(relayParams, receiver, structHash, srcChainId); + _validateCall(relayParams, account, structHash, srcChainId); multichainProvider.bridgeOut( provider, - receiver, + account, params.token, params.amount, + srcChainId, data ); - MultichainEventUtils.emitMultichainBridgeOut(eventEmitter, provider, params.token, receiver, params.amount, srcChainId); + MultichainEventUtils.emitMultichainBridgeOut(eventEmitter, provider, params.token, account, params.amount, srcChainId); } function _validateMultichainProvider(DataStore dataStore, address provider) internal view { diff --git a/deploy/deployLayerZeroProvider.ts b/deploy/deployLayerZeroProvider.ts index 02f1df321..a469fa0a0 100644 --- a/deploy/deployLayerZeroProvider.ts +++ b/deploy/deployLayerZeroProvider.ts @@ -1,7 +1,7 @@ import { grantRoleIfNotGranted } from "../utils/role"; import { createDeployFunction } from "../utils/deploy"; -const constructorContracts = ["DataStore", "EventEmitter", "MultichainVault"]; +const constructorContracts = ["DataStore", "RoleStore", "EventEmitter", "MultichainVault"]; const func = createDeployFunction({ contractName: "LayerZeroProvider", diff --git a/deploy/deployMultichainTransferRouter.ts b/deploy/deployMultichainTransferRouter.ts new file mode 100644 index 000000000..d197bbf2d --- /dev/null +++ b/deploy/deployMultichainTransferRouter.ts @@ -0,0 +1,44 @@ +import { grantRoleIfNotGranted } from "../utils/role"; +import { createDeployFunction } from "../utils/deploy"; + +const baseConstructorContracts = [ + "Router", + "RoleStore", + "DataStore", + "EventEmitter", + "Oracle", + "OrderVault", + "OrderHandler", + "ExternalHandler", + "MultichainVault", +]; + +const transferConstructorContracts = ["LayerZeroProvider"]; + +const func = createDeployFunction({ + contractName: "MultichainTransferRouter", + dependencyNames: [...baseConstructorContracts, ...transferConstructorContracts], + getDeployArgs: async ({ dependencyContracts }) => { + const baseParams = { + router: dependencyContracts.Router.address, + roleStore: dependencyContracts.RoleStore.address, + dataStore: dependencyContracts.DataStore.address, + eventEmitter: dependencyContracts.EventEmitter.address, + oracle: dependencyContracts.Oracle.address, + orderVault: dependencyContracts.OrderVault.address, + orderHandler: dependencyContracts.OrderHandler.address, + externalHandler: dependencyContracts.ExternalHandler.address, + multichainVault: dependencyContracts.MultichainVault.address, + }; + + return [baseParams, dependencyContracts.LayerZeroProvider.address]; + }, + libraryNames: ["MultichainUtils", "RelayUtils", "SwapUtils", "MarketUtils", "GlvWithdrawalUtils"], + + afterDeploy: async ({ deployedContract }) => { + await grantRoleIfNotGranted(deployedContract.address, "CONTROLLER"); + await grantRoleIfNotGranted(deployedContract.address, "ROUTER_PLUGIN"); + }, +}); + +export default func; diff --git a/test/multichain/LayerZeroProvider.ts b/test/multichain/LayerZeroProvider.ts index 26f81996d..0d1d95755 100644 --- a/test/multichain/LayerZeroProvider.ts +++ b/test/multichain/LayerZeroProvider.ts @@ -3,40 +3,30 @@ import { expect } from "chai"; import * as keys from "../../utils/keys"; import { deployFixture } from "../../utils/fixture"; import { expandDecimals } from "../../utils/math"; -import { encodeData } from "../../utils/hash"; +import { mintAndBridge } from "./MultichainGmRouter"; describe("LayerZeroProvider", () => { let fixture; let user0; - let dataStore, usdc, multichainVault, layerZeroProvider, mockStargatePool; + let dataStore, usdc, multichainVault, layerZeroProvider; beforeEach(async () => { fixture = await deployFixture(); ({ user0 } = fixture.accounts); - ({ dataStore, usdc, multichainVault, layerZeroProvider, mockStargatePool } = fixture.contracts); + ({ dataStore, usdc, multichainVault, layerZeroProvider } = fixture.contracts); }); it("lzCompose", async () => { - const srcChainId = 1; - const amountUsdc = expandDecimals(50, 6); + const amount = expandDecimals(1000, 6); - // mint usdc to users and approve StargatePool to spend it - await usdc.mint(user0.address, amountUsdc); - await usdc.connect(user0).approve(mockStargatePool.address, amountUsdc); + await mintAndBridge(fixture, { + token: usdc, + tokenAmount: amount, + }); - // encoded message must match the decoded message in MultichainProviderUtils.decodeDeposit(message) - const message0 = encodeData(["address", "address", "uint256"], [user0.address, usdc.address, srcChainId]); - - // StargatePool would deliver usdc to LayerZeroProvider contract and call LayerZeroProvider.lzCompose - await mockStargatePool.connect(user0).sendToken(usdc.address, layerZeroProvider.address, amountUsdc, message0); - - const lzUsdcBalance = await usdc.balanceOf(layerZeroProvider.address); - const multichainVaultBalance = await usdc.balanceOf(multichainVault.address); - const userBalance = await dataStore.getUint(keys.multichainBalanceKey(user0.address, usdc.address)); - - // usdc has been transterred from LayerZeroProvider to MultichainVault and recorded under the user's chainId + account - expect(lzUsdcBalance).eq(0); - expect(multichainVaultBalance).eq(amountUsdc); - expect(userBalance).eq(amountUsdc); + // usdc has been transterred from LayerZeroProvider to MultichainVault and recorded under the user's multicahin balance + expect(await usdc.balanceOf(layerZeroProvider.address)).eq(0); + expect(await usdc.balanceOf(multichainVault.address)).eq(amount); + expect(await dataStore.getUint(keys.multichainBalanceKey(user0.address, usdc.address))).eq(amount); }); }); diff --git a/test/multichain/MultichainGmRouter.ts b/test/multichain/MultichainGmRouter.ts index a3a974db0..36b1f4321 100644 --- a/test/multichain/MultichainGmRouter.ts +++ b/test/multichain/MultichainGmRouter.ts @@ -13,6 +13,7 @@ import { sendCreateOrder, sendUpdateOrder, sendCancelOrder, + sendBridgeOut, } from "../../utils/relay/multichain"; import * as keys from "../../utils/keys"; import { executeDeposit, getDepositCount, getDepositKeys } from "../../utils/deposit"; @@ -26,6 +27,37 @@ import { hashString } from "../../utils/hash"; import { getPositionCount } from "../../utils/position"; import { expectBalance } from "../../utils/validation"; +export async function mintAndBridge( + fixture, + overrides: { + account?: string; + token: Contract; + tokenAmount: BigNumberish; + srcChainId?: BigNumberish; + } +) { + const { mockStargatePool, layerZeroProvider } = fixture.contracts; + const { user0 } = fixture.accounts; + + const account = overrides.account || user0; + const token = overrides.token; + const tokenAmount = overrides.tokenAmount; + const srcChainId = + overrides.srcChainId || (await hre.ethers.provider.getNetwork().then((network) => network.chainId)); + + await token.mint(account.address, tokenAmount); + + // mock token bridging (increase user's multichain balance) + const encodedMessageEth = ethers.utils.defaultAbiCoder.encode( + ["address", "address", "uint256"], + [account.address, token.address, srcChainId] + ); + await token.connect(account).approve(mockStargatePool.address, tokenAmount); + await mockStargatePool + .connect(account) + .sendToken(token.address, layerZeroProvider.address, tokenAmount, encodedMessageEth); +} + describe("MultichainRouter", () => { let fixture; let user0, user1, user2, user3; @@ -34,6 +66,8 @@ describe("MultichainRouter", () => { multichainGmRouter, multichainOrderRouter, multichainGlvRouter, + multichainTransferRouter, + mockStargatePool, multichainVault, depositVault, withdrawalVault, @@ -50,37 +84,6 @@ describe("MultichainRouter", () => { let defaultDepositParams; let createDepositParams: Parameters[0]; - async function mintAndBridge( - fixture, - overrides: { - account?: string; - token: Contract; - tokenAmount: BigNumberish; - srcChainId?: BigNumberish; - } - ) { - const { mockStargatePool, layerZeroProvider } = fixture.contracts; - const { user0 } = fixture.accounts; - - const account = overrides.account || user0; - const token = overrides.token; - const tokenAmount = overrides.tokenAmount; - const srcChainId = - overrides.srcChainId || (await hre.ethers.provider.getNetwork().then((network) => network.chainId)); - - await token.mint(account.address, tokenAmount); - - // mock token bridging (increase user's multichain balance) - const encodedMessageEth = ethers.utils.defaultAbiCoder.encode( - ["address", "address", "uint256"], - [account.address, token.address, srcChainId] - ); - await token.connect(account).approve(mockStargatePool.address, tokenAmount); - await mockStargatePool - .connect(account) - .sendToken(token.address, layerZeroProvider.address, tokenAmount, encodedMessageEth); - } - beforeEach(async () => { fixture = await deployFixture(); ({ user0, user1, user2, user3 } = fixture.accounts); @@ -90,6 +93,8 @@ describe("MultichainRouter", () => { multichainGmRouter, multichainOrderRouter, multichainGlvRouter, + multichainTransferRouter, + mockStargatePool, multichainVault, depositVault, withdrawalVault, @@ -809,4 +814,54 @@ describe("MultichainRouter", () => { }); }); }); + + describe("MultichainTransferRouter", () => { + const feeAmount = expandDecimals(6, 15); + const bridgeAmount = expandDecimals(1000, 6); + let defaultBridgeOutParams; + beforeEach(async () => { + defaultBridgeOutParams = { + token: usdc.address, + amount: bridgeAmount, + }; + }); + + let bridgeOutParams: Parameters[0]; + beforeEach(async () => { + bridgeOutParams = { + sender: relaySigner, + signer: user1, + feeParams: { + feeToken: wnt.address, + feeAmount: feeAmount, + feeSwapPath: [], + }, + provider: mockStargatePool.address, + account: user1.address, + data: "0x", // e.g. Eid + params: defaultBridgeOutParams, + deadline: 9999999999, + srcChainId: chainId, // 0 means non-multichain action + desChainId: chainId, // for non-multichain actions, desChainId is the same as chainId + relayRouter: multichainTransferRouter, + chainId, + relayFeeToken: wnt.address, + relayFeeAmount: feeAmount, + }; + }); + + // TODO: mock StargatePool and enable bridging out test + it.skip("bridgeOut", async () => { + await dataStore.setBool(keys.isMultichainProviderEnabledKey(mockStargatePool.address), true); + await mintAndBridge(fixture, { account: user1, token: usdc, tokenAmount: bridgeAmount }); + + expect(await usdc.balanceOf(user1.address)).eq(0); + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, usdc.address))).to.eq(bridgeAmount); + + await sendBridgeOut(bridgeOutParams); + + expect(await usdc.balanceOf(user1.address)).eq(bridgeAmount); + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, usdc.address))).to.eq(0); + }); + }); }); diff --git a/utils/fixture.ts b/utils/fixture.ts index cd6cc7a60..9a995d7f4 100644 --- a/utils/fixture.ts +++ b/utils/fixture.ts @@ -103,6 +103,7 @@ export async function deployFixture() { const multichainGmRouter = await hre.ethers.getContract("MultichainGmRouter"); const multichainOrderRouter = await hre.ethers.getContract("MultichainOrderRouter"); const multichainGlvRouter = await hre.ethers.getContract("MultichainGlvRouter"); + const multichainTransferRouter = await hre.ethers.getContract("MultichainTransferRouter"); const relayUtils = await hre.ethers.getContract("RelayUtils"); const oracle = await hre.ethers.getContract("Oracle"); const gmOracleProvider = await hre.ethers.getContract("GmOracleProvider"); @@ -290,6 +291,7 @@ export async function deployFixture() { multichainGmRouter, multichainOrderRouter, multichainGlvRouter, + multichainTransferRouter, relayUtils, oracle, gmOracleProvider, diff --git a/utils/keys.ts b/utils/keys.ts index 984c6f60f..124554852 100644 --- a/utils/keys.ts +++ b/utils/keys.ts @@ -260,6 +260,7 @@ export const BUYBACK_MAX_PRICE_IMPACT_FACTOR = hashString("BUYBACK_MAX_PRICE_IMP export const BUYBACK_MAX_PRICE_AGE = hashString("BUYBACK_MAX_PRICE_AGE"); export const WITHDRAWABLE_BUYBACK_TOKEN_AMOUNT = hashString("WITHDRAWABLE_BUYBACK_TOKEN_AMOUNT"); export const MULTICHAIN_BALANCE = hashString("MULTICHAIN_BALANCE"); +export const IS_MULTICHAIN_PROVIDER_ENABLED = hashString("IS_MULTICHAIN_PROVIDER_ENABLED"); export const VALID_FROM_TIME = hashString("VALID_FROM_TIME"); @@ -824,3 +825,7 @@ export function withdrawableBuybackTokenAmountKey(buybackToken: string) { export function multichainBalanceKey(account: string, token: string) { return hashData(["bytes32", "address", "address"], [MULTICHAIN_BALANCE, account, token]); } + +export function isMultichainProviderEnabledKey(contract: string) { + return hashData(["bytes32", "address"], [IS_MULTICHAIN_PROVIDER_ENABLED, contract]); +} diff --git a/utils/relay/multichain.ts b/utils/relay/multichain.ts index 6b1cfa667..81dd78c4d 100644 --- a/utils/relay/multichain.ts +++ b/utils/relay/multichain.ts @@ -340,6 +340,85 @@ export async function sendCreateGlvWithdrawal(p: SendCreate) { }); } +export async function sendBridgeOut(p: { + signer: ethers.Signer; + sender: ethers.Signer; + feeParams: { + feeToken: string; + feeAmount: BigNumberish; + feeSwapPath: string[]; + }; + provider: string; + account: string; + data: string; + params: any; + signature?: string; + userNonce?: BigNumberish; + deadline: BigNumberish; + chainId: BigNumberish; + srcChainId: BigNumberish; + desChainId: BigNumberish; + relayRouter: ethers.Contract; + relayFeeToken: string; + relayFeeAmount: BigNumberish; +}) { + const relayParams = await getRelayParams(p); + let signature = p.signature; + if (!signature) { + signature = await getBridgeOutSignature({ ...p, relayParams, verifyingContract: p.relayRouter.address }); + } + + const bridgeOutCalldata = p.relayRouter.interface.encodeFunctionData("bridgeOut", [ + { ...relayParams, signature }, + p.provider, + p.account, + p.srcChainId, + p.data, + p.params, + ]); + const calldata = ethers.utils.solidityPack( + ["bytes", "address", "address", "uint256"], + [bridgeOutCalldata, GELATO_RELAY_ADDRESS, p.relayFeeToken, p.relayFeeAmount] + ); + return p.sender.sendTransaction({ + to: p.relayRouter.address, + data: calldata, + }); +} + +async function getBridgeOutSignature({ + signer, + relayParams, + verifyingContract, + params, + chainId, +}: { + signer: ethers.Signer; + relayParams: any; + verifyingContract: string; + params: any; + chainId: BigNumberish; +}) { + if (relayParams.userNonce === undefined) { + throw new Error("userNonce is required"); + } + const types = { + BridgeOut: [ + { name: "token", type: "address" }, + { name: "amount", type: "uint256" }, + { name: "relayParams", type: "bytes32" }, + ], + }; + const typedData = { + token: params.token, + amount: params.amount, + relayParams: hashRelayParams(relayParams), + }; + const domain = getDomain(chainId, verifyingContract); + + return signTypedData(signer, domain, types, typedData); +} + async function getCreateDepositSignature({ signer, relayParams, From 9318680fc18ddacca804e7caf8549c01278278b1 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 2 Mar 2025 22:07:56 +0200 Subject: [PATCH 201/205] Rename multichain test file --- test/multichain/LayerZeroProvider.ts | 2 +- test/multichain/{MultichainGmRouter.ts => MultichainRouter.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename test/multichain/{MultichainGmRouter.ts => MultichainRouter.ts} (100%) diff --git a/test/multichain/LayerZeroProvider.ts b/test/multichain/LayerZeroProvider.ts index 0d1d95755..f52b816c7 100644 --- a/test/multichain/LayerZeroProvider.ts +++ b/test/multichain/LayerZeroProvider.ts @@ -3,7 +3,7 @@ import { expect } from "chai"; import * as keys from "../../utils/keys"; import { deployFixture } from "../../utils/fixture"; import { expandDecimals } from "../../utils/math"; -import { mintAndBridge } from "./MultichainGmRouter"; +import { mintAndBridge } from "./MultichainRouter"; describe("LayerZeroProvider", () => { let fixture; diff --git a/test/multichain/MultichainGmRouter.ts b/test/multichain/MultichainRouter.ts similarity index 100% rename from test/multichain/MultichainGmRouter.ts rename to test/multichain/MultichainRouter.ts From 5aedb23adcc105ea2ed938a6b92d8fe11b4b679a Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 2 Mar 2025 22:27:09 +0200 Subject: [PATCH 202/205] auto format --- contracts/multichain/MultichainEventUtils.sol | 2 +- contracts/multichain/MultichainGlvRouter.sol | 22 ++-------- contracts/multichain/MultichainGmRouter.sol | 16 +------ .../multichain/MultichainOrderRouter.sol | 38 ++++++++++++---- contracts/multichain/MultichainRouter.sol | 43 +++++++++++++------ .../multichain/MultichainTransferRouter.sol | 19 +++----- contracts/multichain/MultichainUtils.sol | 9 +++- 7 files changed, 80 insertions(+), 69 deletions(-) diff --git a/contracts/multichain/MultichainEventUtils.sol b/contracts/multichain/MultichainEventUtils.sol index 959c85c8b..63ae8d7bd 100644 --- a/contracts/multichain/MultichainEventUtils.sol +++ b/contracts/multichain/MultichainEventUtils.sol @@ -54,7 +54,7 @@ library MultichainEventUtils { eventEmitter.emitEventLog1("MultichainTransferIn", Cast.toBytes32(account), eventData); } - + function emitMultichainBridgeOut( EventEmitter eventEmitter, address provider, diff --git a/contracts/multichain/MultichainGlvRouter.sol b/contracts/multichain/MultichainGlvRouter.sol index a77a286e6..1ae380acd 100644 --- a/contracts/multichain/MultichainGlvRouter.sol +++ b/contracts/multichain/MultichainGlvRouter.sol @@ -8,7 +8,6 @@ import "../glv/GlvVault.sol"; import "./MultichainRouter.sol"; contract MultichainGlvRouter is MultichainRouter { - GlvVault public immutable glvVault; GlvHandler public immutable glvHandler; @@ -45,11 +44,7 @@ contract MultichainGlvRouter is MultichainRouter { uint256 srcChainId, GlvDepositUtils.CreateGlvDepositParams memory params ) internal returns (bytes32) { - Contracts memory contracts = Contracts({ - dataStore: dataStore, - eventEmitter: eventEmitter, - bank: glvVault - }); + Contracts memory contracts = Contracts({ dataStore: dataStore, eventEmitter: eventEmitter, bank: glvVault }); // pay relay fee tokens from MultichainVault to GlvVault and decrease user's multichain balance params.executionFee = _handleRelay( @@ -88,11 +83,7 @@ contract MultichainGlvRouter is MultichainRouter { uint256 srcChainId, GlvWithdrawalUtils.CreateGlvWithdrawalParams memory params ) internal returns (bytes32) { - Contracts memory contracts = Contracts({ - dataStore: dataStore, - eventEmitter: eventEmitter, - bank: glvVault - }); + Contracts memory contracts = Contracts({ dataStore: dataStore, eventEmitter: eventEmitter, bank: glvVault }); // pay relay fee tokens from MultichainVault to GlvVault and decrease user's multichain balance params.executionFee = _handleRelay( @@ -104,13 +95,6 @@ contract MultichainGlvRouter is MultichainRouter { srcChainId ); - return GlvWithdrawalUtils.createGlvWithdrawal( - dataStore, - eventEmitter, - glvVault, - account, - srcChainId, - params - ); + return GlvWithdrawalUtils.createGlvWithdrawal(dataStore, eventEmitter, glvVault, account, srcChainId, params); } } diff --git a/contracts/multichain/MultichainGmRouter.sol b/contracts/multichain/MultichainGmRouter.sol index 93b61536e..c6acc876b 100644 --- a/contracts/multichain/MultichainGmRouter.sol +++ b/contracts/multichain/MultichainGmRouter.sol @@ -10,7 +10,6 @@ import "../withdrawal/WithdrawalVault.sol"; import "./MultichainRouter.sol"; contract MultichainGmRouter is MultichainRouter { - DepositVault public immutable depositVault; IDepositHandler public immutable depositHandler; WithdrawalVault public immutable withdrawalVault; @@ -143,11 +142,7 @@ contract MultichainGmRouter is MultichainRouter { uint256 srcChainId, ShiftUtils.CreateShiftParams memory params ) internal returns (bytes32) { - Contracts memory contracts = Contracts({ - dataStore: dataStore, - eventEmitter: eventEmitter, - bank: shiftVault - }); + Contracts memory contracts = Contracts({ dataStore: dataStore, eventEmitter: eventEmitter, bank: shiftVault }); params.executionFee = _handleRelay( contracts, @@ -158,13 +153,6 @@ contract MultichainGmRouter is MultichainRouter { srcChainId ); - return ShiftUtils.createShift( - dataStore, - eventEmitter, - shiftVault, - account, - srcChainId, - params - ); + return ShiftUtils.createShift(dataStore, eventEmitter, shiftVault, account, srcChainId, params); } } diff --git a/contracts/multichain/MultichainOrderRouter.sol b/contracts/multichain/MultichainOrderRouter.sol index 814a3a816..0c5036a3e 100644 --- a/contracts/multichain/MultichainOrderRouter.sol +++ b/contracts/multichain/MultichainOrderRouter.sol @@ -19,7 +19,13 @@ contract MultichainOrderRouter is MultichainRouter { uint256 srcChainId, uint256 collateralDeltaAmount, IBaseOrderUtils.CreateOrderParams memory params // can't use calldata because need to modify params.numbers.executionFee - ) external nonReentrant withOraclePricesForAtomicAction(relayParams.oracleParams) onlyGelatoRelay returns (bytes32) { + ) + external + nonReentrant + withOraclePricesForAtomicAction(relayParams.oracleParams) + onlyGelatoRelay + returns (bytes32) + { _validateDesChainId(relayParams.desChainId); _validateGaslessFeature(); @@ -105,7 +111,14 @@ contract MultichainOrderRouter is MultichainRouter { initialCollateralDeltaAmount - deductFromOrder ); orderVault.transferOut(relayParams.fee.feeToken, address(multichainVault), deductFromOrder); - MultichainUtils.recordTransferIn(dataStore, eventEmitter, multichainVault, relayParams.fee.feeToken, account, srcChainId); + MultichainUtils.recordTransferIn( + dataStore, + eventEmitter, + multichainVault, + relayParams.fee.feeToken, + account, + srcChainId + ); if (unpaidAmount == 0) { return; @@ -128,14 +141,23 @@ contract MultichainOrderRouter is MultichainRouter { // instead of being refunded to the user, to prevent gaming by using the execution fee // to reduce collateral and such that negative pnl or other costs cannot be fully paid dataStore.setBool(Keys.wasPositionCollateralUsedForExecutionFeeKey(key), true); - OrderEventUtils.emitPositionCollateralUsedForExecutionFee(eventEmitter, key, relayParams.fee.feeToken, unpaidAmount); + OrderEventUtils.emitPositionCollateralUsedForExecutionFee( + eventEmitter, + key, + relayParams.fee.feeToken, + unpaidAmount + ); position.setCollateralAmount(positionCollateralAmount - unpaidAmount); - dataStore.setUint( - keccak256(abi.encode(key, PositionStoreUtils.COLLATERAL_AMOUNT)), - positionCollateralAmount - ); + dataStore.setUint(keccak256(abi.encode(key, PositionStoreUtils.COLLATERAL_AMOUNT)), positionCollateralAmount); orderVault.transferOut(relayParams.fee.feeToken, address(multichainVault), unpaidAmount); - MultichainUtils.recordTransferIn(dataStore, eventEmitter, multichainVault, relayParams.fee.feeToken, account, srcChainId); + MultichainUtils.recordTransferIn( + dataStore, + eventEmitter, + multichainVault, + relayParams.fee.feeToken, + account, + srcChainId + ); } } diff --git a/contracts/multichain/MultichainRouter.sol b/contracts/multichain/MultichainRouter.sol index 7fdbea553..362f52205 100644 --- a/contracts/multichain/MultichainRouter.sol +++ b/contracts/multichain/MultichainRouter.sol @@ -7,7 +7,6 @@ import "../router/relay/BaseGelatoRelayRouter.sol"; import "./MultichainUtils.sol"; abstract contract MultichainRouter is BaseGelatoRelayRouter { - struct BaseConstructorParams { Router router; RoleStore roleStore; @@ -24,18 +23,15 @@ abstract contract MultichainRouter is BaseGelatoRelayRouter { constructor( BaseConstructorParams memory params - ) - BaseGelatoRelayRouter( - params.oracle, - params.orderHandler, - params.orderVault, - params.externalHandler - ) - { + ) BaseGelatoRelayRouter(params.oracle, params.orderHandler, params.orderVault, params.externalHandler) { multichainVault = params.multichainVault; } - function _processTransferRequests(address account, RelayUtils.TransferRequests calldata transferRequests, uint256 srcChainId) internal { + function _processTransferRequests( + address account, + RelayUtils.TransferRequests calldata transferRequests, + uint256 srcChainId + ) internal { if ( transferRequests.tokens.length != transferRequests.receivers.length || transferRequests.tokens.length != transferRequests.amounts.length @@ -54,16 +50,37 @@ abstract contract MultichainRouter is BaseGelatoRelayRouter { } } - function _sendTokens(address account, address token, address receiver, uint256 amount, uint256 srcChainId) internal override { + function _sendTokens( + address account, + address token, + address receiver, + uint256 amount, + uint256 srcChainId + ) internal override { AccountUtils.validateReceiver(receiver); if (srcChainId == 0) { router.pluginTransfer(token, account, receiver, amount); } else { - MultichainUtils.transferOut(dataStore, eventEmitter, multichainVault, token, account, receiver, amount, srcChainId); + MultichainUtils.transferOut( + dataStore, + eventEmitter, + multichainVault, + token, + account, + receiver, + amount, + srcChainId + ); } } - function _transferResidualFee(address wnt, address residualFeeReceiver, uint256 residualFee, address account, uint256 srcChainId) internal override { + function _transferResidualFee( + address wnt, + address residualFeeReceiver, + uint256 residualFee, + address account, + uint256 srcChainId + ) internal override { TokenUtils.transfer(dataStore, wnt, residualFeeReceiver, residualFee); if (residualFeeReceiver == address(multichainVault)) { MultichainUtils.recordTransferIn(dataStore, eventEmitter, multichainVault, wnt, account, srcChainId); diff --git a/contracts/multichain/MultichainTransferRouter.sol b/contracts/multichain/MultichainTransferRouter.sol index a62c447ef..4a292c132 100644 --- a/contracts/multichain/MultichainTransferRouter.sol +++ b/contracts/multichain/MultichainTransferRouter.sol @@ -33,14 +33,7 @@ contract MultichainTransferRouter is MultichainRouter { bytes32 structHash = RelayUtils.getBridgeInStructHash(relayParams, params); _validateCall(relayParams, account, structHash, srcChainId); - MultichainUtils.recordTransferIn( - dataStore, - eventEmitter, - multichainVault, - params.token, - account, - srcChainId - ); + MultichainUtils.recordTransferIn(dataStore, eventEmitter, multichainVault, params.token, account, srcChainId); } function bridgeOut( @@ -58,15 +51,15 @@ contract MultichainTransferRouter is MultichainRouter { bytes32 structHash = RelayUtils.getBridgeOutStructHash(relayParams, params); _validateCall(relayParams, account, structHash, srcChainId); - multichainProvider.bridgeOut( + multichainProvider.bridgeOut(provider, account, params.token, params.amount, srcChainId, data); + MultichainEventUtils.emitMultichainBridgeOut( + eventEmitter, provider, - account, params.token, + account, params.amount, - srcChainId, - data + srcChainId ); - MultichainEventUtils.emitMultichainBridgeOut(eventEmitter, provider, params.token, account, params.amount, srcChainId); } function _validateMultichainProvider(DataStore dataStore, address provider) internal view { diff --git a/contracts/multichain/MultichainUtils.sol b/contracts/multichain/MultichainUtils.sol index 9e80dab94..f701d5aa0 100644 --- a/contracts/multichain/MultichainUtils.sol +++ b/contracts/multichain/MultichainUtils.sol @@ -29,7 +29,14 @@ library MultichainUtils { uint256 srcChainId ) external { uint256 amount = recordTransferIn(dataStore, eventEmitter, multichainVault, token, account, srcChainId); - MultichainEventUtils.emitMultichainBridgeIn(eventEmitter, address(provider), token, account, amount, srcChainId); + MultichainEventUtils.emitMultichainBridgeIn( + eventEmitter, + address(provider), + token, + account, + amount, + srcChainId + ); } /** From 651555c550024fd9562537ca2d48ba85125478cc Mon Sep 17 00:00:00 2001 From: Solar Date: Mon, 3 Mar 2025 00:33:39 +0300 Subject: [PATCH 203/205] update scripts --- contracts/config/TimelockConfig.sol | 12 +++ scripts/updateAtomicOracleProviders.ts | 8 +- scripts/updateOracleConfigForTokens.ts | 67 ++++++++---- scripts/updateOracleProviders.ts | 11 +- scripts/updatePriceFeeds.ts | 49 ++++----- scripts/updateRoles.ts | 25 +++-- test/config/Timelock.ts | 138 +++++++++++++++++-------- utils/timelock.ts | 105 +++++++++++++++++++ 8 files changed, 309 insertions(+), 106 deletions(-) diff --git a/contracts/config/TimelockConfig.sol b/contracts/config/TimelockConfig.sol index acad23ac4..4f26f78b1 100644 --- a/contracts/config/TimelockConfig.sol +++ b/contracts/config/TimelockConfig.sol @@ -319,4 +319,16 @@ contract TimelockConfig is RoleModule, BasicMulticall { function executeBatch(address[] calldata targets, uint256[] calldata values, bytes[] calldata payloads) external onlyTimelockAdmin { timelockController.executeBatch(targets, values, payloads, 0, 0); } + + function getHash(address target, bytes calldata payload) external view returns (bytes32) { + return timelockController.hashOperation(target, 0, payload, 0, 0); + } + + function getHashBatch(address[] calldata targets, bytes[] calldata payloads, uint256[] calldata values) external view returns (bytes32) { + return timelockController.hashOperationBatch(targets, values, payloads, 0, 0); + } + + function cancelAction(bytes32 id) external onlyTimelockAdmin { + timelockController.cancel(id); + } } diff --git a/scripts/updateAtomicOracleProviders.ts b/scripts/updateAtomicOracleProviders.ts index c0ed7f7c7..e41a24f8d 100644 --- a/scripts/updateAtomicOracleProviders.ts +++ b/scripts/updateAtomicOracleProviders.ts @@ -1,5 +1,5 @@ import hre from "hardhat"; -import { timelockWriteMulticall } from "../utils/timelock"; +import { setAtomicOracleProviderPayload, timelockWriteMulticall } from "../utils/timelock"; const expectedTimelockMethods = ["signalSetAtomicOracleProvider", "setAtomicOracleProviderAfterSignal"]; @@ -27,9 +27,11 @@ async function main() { // in case a revert is needed if (timelockMethod === "signalSetAtomicOracleProvider") { multicallWriteParams.push(timelock.interface.encodeFunctionData(timelockMethod, [provider, false])); + multicallWriteParams.push(timelock.interface.encodeFunctionData(timelockMethod, [provider, true])); + } else { + const { target, payload } = await setAtomicOracleProviderPayload(provider, true); + multicallWriteParams.push(timelock.interface.encodeFunctionData("execute", [target, payload])); } - - multicallWriteParams.push(timelock.interface.encodeFunctionData(timelockMethod, [provider, true])); } console.log(`sending ${multicallWriteParams.length} updates`); diff --git a/scripts/updateOracleConfigForTokens.ts b/scripts/updateOracleConfigForTokens.ts index afff3ad05..d68b382f0 100644 --- a/scripts/updateOracleConfigForTokens.ts +++ b/scripts/updateOracleConfigForTokens.ts @@ -1,7 +1,12 @@ import { getFullKey } from "../utils/config"; import { encodeData } from "../utils/hash"; import { bigNumberify, expandDecimals } from "../utils/math"; -import { timelockWriteMulticall } from "../utils/timelock"; +import { + setDataStreamPayload, + setOracleProviderForTokenPayload, + setPriceFeedPayload, + timelockWriteMulticall, +} from "../utils/timelock"; import * as keys from "../utils/keys"; import { getOracleProviderAddress, getOracleProviderKey } from "../utils/oracle"; @@ -147,17 +152,26 @@ export async function updateOracleConfigForTokens() { }, ${stablePrice.toString()})` ); - const method = phase === "signal" ? "signalSetPriceFeed" : "setPriceFeedAfterSignal"; - - multicallWriteParams.push( - timelock.interface.encodeFunctionData(method, [ + if (phase === "signal") { + multicallWriteParams.push( + timelock.interface.encodeFunctionData("signalSetPriceFeed", [ + token.address, + priceFeed.address, + priceFeedMultiplier, + priceFeed.heartbeatDuration, + stablePrice, + ]) + ); + } else { + const { targets, values, payloads } = await setPriceFeedPayload( token.address, priceFeed.address, priceFeedMultiplier, priceFeed.heartbeatDuration, - stablePrice, - ]) - ); + stablePrice + ); + multicallWriteParams.push(timelock.interface.encodeFunctionData("executeBatch", [targets, values, payloads])); + } } if (token.dataStreamFeedId && onchainConfig.dataStreamId !== token.dataStreamFeedId) { @@ -176,16 +190,24 @@ export async function updateOracleConfigForTokens() { }, ${dataStreamMultiplier.toString()}, ${dataStreamSpreadReductionFactor.toString()})` ); - const method = phase === "signal" ? "signalSetDataStream" : "setDataStreamAfterSignal"; - - multicallWriteParams.push( - timelock.interface.encodeFunctionData(method, [ + if (phase === "signal") { + multicallWriteParams.push( + timelock.interface.encodeFunctionData("signalSetDataStream", [ + token.address, + token.dataStreamFeedId, + dataStreamMultiplier, + dataStreamSpreadReductionFactor, + ]) + ); + } else { + const { targets, values, payloads } = await setDataStreamPayload( token.address, token.dataStreamFeedId, dataStreamMultiplier, - dataStreamSpreadReductionFactor, - ]) - ); + dataStreamSpreadReductionFactor + ); + multicallWriteParams.push(timelock.interface.encodeFunctionData("executeBatch", [targets, values, payloads])); + } } const oracleProviderAddress = await getOracleProviderAddress(token.oracleProvider); @@ -193,20 +215,25 @@ export async function updateOracleConfigForTokens() { const oracleProviderKey = await getOracleProviderKey(oracleProviderAddress); console.log(`setOracleProviderForToken(${tokenSymbol} ${oracleProviderKey} ${oracleProviderAddress})`); - const method = phase === "signal" ? "signalSetOracleProviderForToken" : "setOracleProviderForTokenAfterSignal"; - // signalSetOracleProviderForToken back to the current oracle provider in case // the oracle provider change needs to be rolled back - if (method === "signalSetOracleProviderForToken") { + if (phase === "signal") { multicallWriteParams.push( timelock.interface.encodeFunctionData("signalSetOracleProviderForToken", [ token.address, onchainConfig.oracleProviderForToken, ]) ); + multicallWriteParams.push( + timelock.interface.encodeFunctionData("signalSetOracleProviderForToken", [ + token.address, + oracleProviderAddress, + ]) + ); + } else { + const { target, payload } = await setOracleProviderForTokenPayload(token.address, oracleProviderAddress); + multicallWriteParams.push(timelock.interface.encodeFunctionData("execute", [target, payload])); } - - multicallWriteParams.push(timelock.interface.encodeFunctionData(method, [token.address, oracleProviderAddress])); } } diff --git a/scripts/updateOracleProviders.ts b/scripts/updateOracleProviders.ts index 0cac6445b..df3c639a1 100644 --- a/scripts/updateOracleProviders.ts +++ b/scripts/updateOracleProviders.ts @@ -1,10 +1,10 @@ import hre from "hardhat"; -import { timelockWriteMulticall } from "../utils/timelock"; +import { setOracleProviderEnabledPayload, timelockWriteMulticall } from "../utils/timelock"; const expectedTimelockMethods = ["signalSetOracleProviderEnabled", "setOracleProviderEnabledAfterSignal"]; async function main() { - const timelock = await hre.ethers.getContract("Timelock"); + const timelock = await hre.ethers.getContract("TimelockConfig"); const providersToAdd = { arbitrum: [ @@ -31,7 +31,12 @@ async function main() { } for (const provider of providersToAdd[hre.network.name]) { - multicallWriteParams.push(timelock.interface.encodeFunctionData(timelockMethod, [provider, true])); + if (timelockMethod === "signalSetOracleProviderEnabled") { + multicallWriteParams.push(timelock.interface.encodeFunctionData(timelockMethod, [provider, true])); + } else { + const { target, payload } = await setOracleProviderEnabledPayload(provider, true); + multicallWriteParams.push(timelock.interface.encodeFunctionData("execute", [target, payload])); + } } console.log(`updating ${multicallWriteParams.length} providers`); diff --git a/scripts/updatePriceFeeds.ts b/scripts/updatePriceFeeds.ts index df17b4bd0..099da580a 100644 --- a/scripts/updatePriceFeeds.ts +++ b/scripts/updatePriceFeeds.ts @@ -1,13 +1,13 @@ import hre from "hardhat"; import { expandDecimals } from "../utils/math"; -import { timelockWriteMulticall } from "../utils/timelock"; +import { setPriceFeedPayload, timelockWriteMulticall } from "../utils/timelock"; import * as keys from "../utils/keys"; const expectedPhases = ["signal", "finalize"]; async function main() { const dataStore = await hre.ethers.getContract("DataStore"); - const timelock = await hre.ethers.getContract("Timelock"); + const timelock = await hre.ethers.getContract("TimelockConfig"); console.log("timelock", timelock.address); const tokenConfigs = await hre.gmx.getTokens(); @@ -36,27 +36,13 @@ async function main() { throw new Error(`Empty oracle config for ${token}`); } - const { decimals, realtimeFeedId, realtimeFeedDecimals } = tokenConfig; + const { decimals } = tokenConfig; const { priceFeed } = oracleConfig; const priceFeedMultiplier = expandDecimals(1, 60 - decimals - priceFeed.decimals); - const realtimeFeedMultiplier = expandDecimals(1, 60 - decimals - realtimeFeedDecimals); - - const priceFeedMethod = phase === "signal" ? "signalSetPriceFeed" : "setPriceFeedAfterSignal"; - const realtimeFeedMethod = phase === "signal" ? "signalSetRealtimeFeed" : "setRealtimeFeedAfterSignal"; const stablePrice = priceFeed.stablePrice ? priceFeed.stablePrice : 0; - const currentRealtimeFeedId = await dataStore.getBytes32(keys.realtimeFeedIdKey(tokenConfig.address)); - if (currentRealtimeFeedId !== ethers.constants.HashZero) { - throw new Error(`realtimeFeedId already exists for ${token}`); - } - - const currentRealtimeFeedMultiplier = await dataStore.getUint(keys.realtimeFeedMultiplierKey(tokenConfig.address)); - if (!currentRealtimeFeedMultiplier.eq(0)) { - throw new Error(`realtimeFeedMultiplier already exists for ${token}`); - } - const currentPriceFeed = await dataStore.getAddress(keys.priceFeedKey(tokenConfig.address)); if (currentPriceFeed !== ethers.constants.AddressZero) { throw new Error(`priceFeed already exists for ${token}`); @@ -67,23 +53,26 @@ async function main() { throw new Error(`realtimeFeedMultiplier already exists for ${token}`); } - multicallWriteParams.push( - timelock.interface.encodeFunctionData(priceFeedMethod, [ + if (phase === "signal") { + multicallWriteParams.push( + timelock.interface.encodeFunctionData("signalSetPriceFeed", [ + tokenConfig.address, + priceFeed.address, + priceFeedMultiplier, + priceFeed.heartbeatDuration, + stablePrice, + ]) + ); + } else { + const { targets, values, payloads } = await setPriceFeedPayload( tokenConfig.address, priceFeed.address, priceFeedMultiplier, priceFeed.heartbeatDuration, - stablePrice, - ]) - ); - - multicallWriteParams.push( - timelock.interface.encodeFunctionData(realtimeFeedMethod, [ - tokenConfig.address, - realtimeFeedId, - realtimeFeedMultiplier, - ]) - ); + stablePrice + ); + multicallWriteParams.push(timelock.interface.encodeFunctionData("executeBatch", [targets, values, payloads])); + } } console.log(`updating ${multicallWriteParams.length} feeds`); diff --git a/scripts/updateRoles.ts b/scripts/updateRoles.ts index fbc1043d2..7959805f6 100644 --- a/scripts/updateRoles.ts +++ b/scripts/updateRoles.ts @@ -1,6 +1,7 @@ import hre from "hardhat"; import { hashString } from "../utils/hash"; -import { timelockWriteMulticall } from "../utils/timelock"; +import { cancelActionById, getGrantRolePayload, getRevokeRolePayload, timelockWriteMulticall } from "../utils/timelock"; +import { TimelockConfig } from "../typechain-types"; const expectedTimelockMethods = [ "signalGrantRole", @@ -10,15 +11,17 @@ const expectedTimelockMethods = [ "cancelGrantRole", ]; -async function getTimelock() { +async function getTimelock(): Promise { const network = hre.network.name; if (network === "arbitrum") { - return await ethers.getContractAt("Timelock", "0x7A967D114B8676874FA2cFC1C14F3095C88418Eb"); + throw new Error("Contract not deployed yet"); + // return await ethers.getContractAt("TimelockConfig", "0x..."); } if (network === "avalanche") { - return await ethers.getContractAt("Timelock", "0xdF23692341538340db0ff04C65017F51b69a29f6"); + throw new Error("Contract not deployed yet"); + // return await ethers.getContractAt("TimelockConfig", "0x..."); } throw new Error("Unsupported network"); @@ -145,12 +148,19 @@ async function main() { throw new Error(`Unexpected TIMELOCK_METHOD: ${timelockMethod}`); } - if (["signalGrantRole", "grantRoleAfterSignal"].includes(timelockMethod)) { + if (timelockMethod === "signalGrantRole") { for (const { member, role } of rolesToAdd[hre.network.name]) { multicallWriteParams.push(timelock.interface.encodeFunctionData(timelockMethod, [member, hashString(role)])); } } + if (timelockMethod === "grantRoleAfterSignal") { + for (const { member, role } of rolesToAdd[hre.network.name]) { + const { target, payload } = await getGrantRolePayload(member, hashString(role)); + multicallWriteParams.push(timelock.interface.encodeFunctionData("execute", [target, payload])); + } + } + if (timelockMethod === "signalRevokeRole") { for (const { member, role } of rolesToRemove[hre.network.name]) { multicallWriteParams.push(timelock.interface.encodeFunctionData(timelockMethod, [member, hashString(role)])); @@ -161,14 +171,15 @@ async function main() { if (timelockMethod === "revokeRoleAfterSignal") { for (const { member, role } of rolesToRemove[hre.network.name]) { - multicallWriteParams.push(timelock.interface.encodeFunctionData(timelockMethod, [member, hashString(role)])); + const { target, payload } = await getRevokeRolePayload(member, hashString(role)); + multicallWriteParams.push(timelock.interface.encodeFunctionData("execute", [target, payload])); } } if (timelockMethod === "cancelGrantRole") { const actionKeys = await getGrantRoleActionKeysToCancel({ timelock }); for (const actionKey of actionKeys) { - multicallWriteParams.push(timelock.interface.encodeFunctionData("cancelAction", [actionKey])); + multicallWriteParams.push(cancelActionById(timelock, actionKey)); } } diff --git a/test/config/Timelock.ts b/test/config/Timelock.ts index ae6cf5e25..4e72c34ae 100644 --- a/test/config/Timelock.ts +++ b/test/config/Timelock.ts @@ -7,6 +7,15 @@ import { hashString } from "../../utils/hash"; import { decimalToFloat, expandDecimals, percentageToFloat } from "../../utils/math"; import { errorsContract } from "../../utils/error"; import * as keys from "../../utils/keys"; +import { + getGrantRolePayload, + getRevokeRolePayload, + setPriceFeedPayload, + setDataStreamPayload, + setOracleProviderEnabledPayload, + setOracleProviderForTokenPayload, + setAtomicOracleProviderPayload, +} from "../../utils/timelock"; describe("Timelock", () => { let fixture; @@ -32,8 +41,8 @@ describe("Timelock", () => { await timelockConfig.connect(timelockAdmin).signalGrantRole(user3.address, orderKeeperRole); await time.increase(1 * 24 * 60 * 60 + 10); - const grantRolePayload = roleStore.interface.encodeFunctionData("grantRole", [user3.address, orderKeeperRole]); - await timelockConfig.connect(timelockAdmin).execute(roleStore.address, grantRolePayload); + const { target, payload } = await getGrantRolePayload(user3.address, orderKeeperRole); + await timelockConfig.connect(timelockAdmin).execute(target, payload); expect(await roleStore.hasRole(user3.address, orderKeeperRole)).eq(true); @@ -157,12 +166,12 @@ describe("Timelock", () => { await timelockConfig.connect(timelockAdmin).signalGrantRole(user3.address, orderKeeperRole); - const payload = roleStore.interface.encodeFunctionData("grantRole", [user3.address, orderKeeperRole]); - await expect(timelockConfig.connect(user2).execute(roleStore.address, payload)) + const { target, payload } = await getGrantRolePayload(user3.address, orderKeeperRole); + await expect(timelockConfig.connect(user2).execute(target, payload)) .to.be.revertedWithCustomError(errorsContract, "Unauthorized") .withArgs(user2.address, "TIMELOCK_ADMIN"); - await expect(timelockConfig.connect(timelockAdmin).execute(roleStore.address, payload)).to.be.revertedWith( + await expect(timelockConfig.connect(timelockAdmin).execute(target, payload)).to.be.revertedWith( "TimelockController: operation is not ready" ); @@ -170,7 +179,7 @@ describe("Timelock", () => { expect(await roleStore.hasRole(user3.address, orderKeeperRole)).eq(false); - await timelockConfig.connect(timelockAdmin).execute(roleStore.address, payload); + await timelockConfig.connect(timelockAdmin).execute(target, payload); expect(await roleStore.hasRole(user3.address, orderKeeperRole)).eq(true); }); @@ -179,12 +188,18 @@ describe("Timelock", () => { const orderKeeperRole = hashString("ORDER_KEEPER"); expect(await roleStore.hasRole(user3.address, orderKeeperRole)).eq(false); - const payloadGrantRole = roleStore.interface.encodeFunctionData("grantRole", [user3.address, orderKeeperRole]); - const payloadRevokeRole = roleStore.interface.encodeFunctionData("revokeRole", [user3.address, orderKeeperRole]); + const { target: grantTarget, payload: payloadGrantRole } = await getGrantRolePayload( + user3.address, + orderKeeperRole + ); + const { target: revokeTarget, payload: payloadRevokeRole } = await getRevokeRolePayload( + user3.address, + orderKeeperRole + ); await timelockConfig.connect(timelockAdmin).signalGrantRole(user3.address, orderKeeperRole); await time.increase(1 * 24 * 60 * 60 + 10); - await timelockConfig.connect(timelockAdmin).execute(roleStore.address, payloadGrantRole); + await timelockConfig.connect(timelockAdmin).execute(grantTarget, payloadGrantRole); expect(await roleStore.hasRole(user3.address, orderKeeperRole)).eq(true); @@ -194,19 +209,19 @@ describe("Timelock", () => { await timelockConfig.connect(timelockAdmin).signalRevokeRole(user3.address, orderKeeperRole); - await expect(timelockConfig.connect(user2).execute(roleStore.address, payloadRevokeRole)) + await expect(timelockConfig.connect(user2).execute(revokeTarget, payloadRevokeRole)) .to.be.revertedWithCustomError(errorsContract, "Unauthorized") .withArgs(user2.address, "TIMELOCK_ADMIN"); - await expect( - timelockConfig.connect(timelockAdmin).execute(roleStore.address, payloadRevokeRole) - ).to.be.revertedWith("TimelockController: operation is not ready"); + await expect(timelockConfig.connect(timelockAdmin).execute(revokeTarget, payloadRevokeRole)).to.be.revertedWith( + "TimelockController: operation is not ready" + ); await time.increase(1 * 24 * 60 * 60 + 10); expect(await roleStore.hasRole(user3.address, orderKeeperRole)).eq(true); - await timelockConfig.connect(timelockAdmin).execute(roleStore.address, payloadRevokeRole); + await timelockConfig.connect(timelockAdmin).execute(revokeTarget, payloadRevokeRole); expect(await roleStore.hasRole(user3.address, orderKeeperRole)).eq(false); }); @@ -218,15 +233,12 @@ describe("Timelock", () => { await timelockConfig.connect(timelockAdmin).signalSetOracleProviderForToken(wnt.address, user3.address); - const payload = dataStore.interface.encodeFunctionData("setAddress", [ - keys.oracleProviderForTokenKey(wnt.address), - user3.address, - ]); - await expect(timelockConfig.connect(user2).execute(dataStore.address, payload)) + const { target, payload } = await setOracleProviderForTokenPayload(wnt.address, user3.address); + await expect(timelockConfig.connect(user2).execute(target, payload)) .to.be.revertedWithCustomError(errorsContract, "Unauthorized") .withArgs(user2.address, "TIMELOCK_ADMIN"); - await expect(timelockConfig.connect(timelockAdmin).execute(dataStore.address, payload)).to.be.revertedWith( + await expect(timelockConfig.connect(timelockAdmin).execute(target, payload)).to.be.revertedWith( "TimelockController: operation is not ready" ); @@ -236,11 +248,65 @@ describe("Timelock", () => { fixture.contracts.gmOracleProvider.address ); - await timelockConfig.connect(timelockAdmin).execute(dataStore.address, payload); + await timelockConfig.connect(timelockAdmin).execute(target, payload); expect(await dataStore.getAddress(keys.oracleProviderForTokenKey(wnt.address))).eq(user3.address); }); + it("setOracleProviderEnabled", async () => { + expect(await dataStore.getBool(keys.isOracleProviderEnabledKey(user3.address))).eq(false); + + await expect(timelockConfig.connect(user2).signalSetOracleProviderEnabled(user3.address, true)) + .to.be.revertedWithCustomError(errorsContract, "Unauthorized") + .withArgs(user2.address, "TIMELOCK_ADMIN"); + + await timelockConfig.connect(timelockAdmin).signalSetOracleProviderEnabled(user3.address, true); + + const { target, payload } = await setOracleProviderEnabledPayload(user3.address, true); + await expect(timelockConfig.connect(user2).execute(target, payload)) + .to.be.revertedWithCustomError(errorsContract, "Unauthorized") + .withArgs(user2.address, "TIMELOCK_ADMIN"); + + await expect(timelockConfig.connect(timelockAdmin).execute(dataStore.address, payload)).to.be.revertedWith( + "TimelockController: operation is not ready" + ); + + await time.increase(1 * 24 * 60 * 60 + 10); + + expect(await dataStore.getBool(keys.isOracleProviderEnabledKey(user3.address))).eq(false); + + await timelockConfig.connect(timelockAdmin).execute(target, payload); + + expect(await dataStore.getBool(keys.isOracleProviderEnabledKey(user3.address))).eq(true); + }); + + it("SetAtomicOracleProvider", async () => { + expect(await dataStore.getBool(keys.isAtomicOracleProviderKey(user3.address))).eq(false); + + await expect(timelockConfig.connect(user2).signalSetAtomicOracleProvider(user3.address, true)) + .to.be.revertedWithCustomError(errorsContract, "Unauthorized") + .withArgs(user2.address, "TIMELOCK_ADMIN"); + + await timelockConfig.connect(timelockAdmin).signalSetAtomicOracleProvider(user3.address, true); + + const { target, payload } = await setAtomicOracleProviderPayload(user3.address, true); + await expect(timelockConfig.connect(user2).execute(target, payload)) + .to.be.revertedWithCustomError(errorsContract, "Unauthorized") + .withArgs(user2.address, "TIMELOCK_ADMIN"); + + await expect(timelockConfig.connect(timelockAdmin).execute(dataStore.address, payload)).to.be.revertedWith( + "TimelockController: operation is not ready" + ); + + await time.increase(1 * 24 * 60 * 60 + 10); + + expect(await dataStore.getBool(keys.isAtomicOracleProviderKey(user3.address))).eq(false); + + await timelockConfig.connect(timelockAdmin).execute(target, payload); + + expect(await dataStore.getBool(keys.isAtomicOracleProviderKey(user3.address))).eq(true); + }); + it("setPriceFeed", async () => { await dataStore.setAddress(keys.priceFeedKey(wnt.address), ethers.constants.AddressZero); await dataStore.setUint(keys.priceFeedMultiplierKey(wnt.address), 0); @@ -257,17 +323,13 @@ describe("Timelock", () => { .connect(timelockAdmin) .signalSetPriceFeed(wnt.address, user3.address, 1000, 24 * 60 * 60, decimalToFloat(5000)); - const targets = [dataStore.address, dataStore.address, dataStore.address, dataStore.address]; - const values = [0, 0, 0, 0]; - const payloads = [ - dataStore.interface.encodeFunctionData("setAddress", [keys.priceFeedKey(wnt.address), user3.address]), - dataStore.interface.encodeFunctionData("setUint", [keys.priceFeedMultiplierKey(wnt.address), 1000]), - dataStore.interface.encodeFunctionData("setUint", [ - keys.priceFeedHeartbeatDurationKey(wnt.address), - 24 * 60 * 60, - ]), - dataStore.interface.encodeFunctionData("setUint", [keys.stablePriceKey(wnt.address), decimalToFloat(5000)]), - ]; + const { targets, values, payloads } = await setPriceFeedPayload( + wnt.address, + user3.address, + 1000, + 24 * 60 * 60, + decimalToFloat(5000) + ); await expect(timelockConfig.connect(user2).executeBatch(targets, values, payloads)) .to.be.revertedWithCustomError(errorsContract, "Unauthorized") @@ -310,17 +372,7 @@ describe("Timelock", () => { .connect(timelockAdmin) .signalSetDataStream(wnt.address, hashString("WNT"), expandDecimals(1, 34), p99); - const targets = [dataStore.address, dataStore.address, dataStore.address]; - const values = [0, 0, 0]; - const payloads = [ - dataStore.interface.encodeFunctionData("setBytes32", [keys.dataStreamIdKey(wnt.address), hashString("WNT")]), - dataStore.interface.encodeFunctionData("setUint", [ - keys.dataStreamMultiplierKey(wnt.address), - expandDecimals(1, 34), - ]), - dataStore.interface.encodeFunctionData("setUint", [keys.dataStreamSpreadReductionFactorKey(wnt.address), p99]), - ]; - + const { targets, values, payloads } = await setDataStreamPayload(wnt.address, "WNT", expandDecimals(1, 34), p99); await expect(timelockConfig.connect(user2).executeBatch(targets, values, payloads)) .to.be.revertedWithCustomError(errorsContract, "Unauthorized") .withArgs(user2.address, "TIMELOCK_ADMIN"); diff --git a/utils/timelock.ts b/utils/timelock.ts index 4d2b1131a..3189c80c6 100644 --- a/utils/timelock.ts +++ b/utils/timelock.ts @@ -1,6 +1,9 @@ import prompts from "prompts"; import { signExternally } from "./signer"; +import { hashString } from "./hash"; +import { TimelockConfig } from "../typechain-types"; +import * as keys from "./keys"; export async function timelockWriteMulticall({ timelock, multicallWriteParams }) { console.info("multicallWriteParams", multicallWriteParams); @@ -38,3 +41,105 @@ export async function timelockWriteMulticall({ timelock, multicallWriteParams }) console.info("NOTE: executed in read-only mode, no transactions were sent, simulation was successful"); } } + +export async function cancelAction(timelock: TimelockConfig, target: string, payload: string) { + const id = await timelock.getHash(target, payload); + await cancelActionById(timelock, id); +} + +export async function cancelActionById(timelock: TimelockConfig, id: string) { + await timelock.cancelAction(id); +} + +export async function setPriceFeedPayload( + token: string, + priceFeedAddress: string, + priceFeedMultiplier: any, + priceFeedHeartbeatDuration: any, + stablePrice: any +) { + const dataStore = await hre.ethers.getContract("DataStore"); + const targets = [dataStore.address, dataStore.address, dataStore.address, dataStore.address]; + const values = [0, 0, 0, 0]; + const payloads = [ + dataStore.interface.encodeFunctionData("setAddress", [keys.priceFeedKey(token), priceFeedAddress]), + dataStore.interface.encodeFunctionData("setUint", [keys.priceFeedMultiplierKey(token), priceFeedMultiplier]), + dataStore.interface.encodeFunctionData("setUint", [ + keys.priceFeedHeartbeatDurationKey(token), + priceFeedHeartbeatDuration, + ]), + dataStore.interface.encodeFunctionData("setUint", [keys.stablePriceKey(token), stablePrice]), + ]; + return { targets, values, payloads }; +} + +export async function setDataStreamPayload( + token: string, + feedId: string, + dataStreamMultiplier: any, + dataStreamSpreadReductionFactor: any +) { + const dataStore = await hre.ethers.getContract("DataStore"); + + const targets = [dataStore.address, dataStore.address, dataStore.address]; + const values = [0, 0, 0]; + const payloads = [ + dataStore.interface.encodeFunctionData("setBytes32", [keys.dataStreamIdKey(token), hashString(feedId)]), + dataStore.interface.encodeFunctionData("setUint", [keys.dataStreamMultiplierKey(token), dataStreamMultiplier]), + dataStore.interface.encodeFunctionData("setUint", [ + keys.dataStreamSpreadReductionFactorKey(token), + dataStreamSpreadReductionFactor, + ]), + ]; + + return { targets, values, payloads }; +} + +export async function getGrantRolePayload(address: string, roleKey: string) { + const roleStore = await hre.ethers.getContract("RoleStore"); + return { + target: roleStore.address, + payload: roleStore.interface.encodeFunctionData("grantRole", [address, roleKey]), + }; +} + +export async function getRevokeRolePayload(address: string, roleKey: string) { + const roleStore = await hre.ethers.getContract("RoleStore"); + return { + target: roleStore.address, + payload: roleStore.interface.encodeFunctionData("revokeRole", [address, roleKey]), + }; +} + +export async function setOracleProviderEnabledPayload(providerAddress: string, value: boolean) { + const dataStore = await hre.ethers.getContract("DataStore"); + return { + target: dataStore.address, + payload: dataStore.interface.encodeFunctionData("setBool", [ + keys.isOracleProviderEnabledKey(providerAddress), + value, + ]), + }; +} + +export async function setOracleProviderForTokenPayload(tokenAddress: string, providerAddress: string) { + const dataStore = await hre.ethers.getContract("DataStore"); + return { + target: dataStore.address, + payload: dataStore.interface.encodeFunctionData("setAddress", [ + keys.oracleProviderForTokenKey(tokenAddress), + providerAddress, + ]), + }; +} + +export async function setAtomicOracleProviderPayload(providerAddress: string, value: boolean) { + const dataStore = await hre.ethers.getContract("DataStore"); + return { + target: dataStore.address, + payload: dataStore.interface.encodeFunctionData("setBool", [ + keys.isAtomicOracleProviderKey(providerAddress), + value, + ]), + }; +} From 67afac2798cae4cc493ed822c3bcca9e1a0d705d Mon Sep 17 00:00:00 2001 From: Solar Date: Mon, 3 Mar 2025 07:36:40 +0300 Subject: [PATCH 204/205] update CI node version --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 79f648e92..aa6a3fd86 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,7 +12,7 @@ jobs: - name: Install packages uses: actions/setup-node@v3 with: - node-version: '18.x' + node-version: '20.x' - run: yarn --ignore-scripts shell: bash - name: Run Tests From 64b62e52ca459425a2c7f9068c11eae7c84e59f6 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 3 Mar 2025 11:03:22 +0200 Subject: [PATCH 205/205] Fix multichain shift (the withdawal part should go into the shiftVault instead of the multicahinVault) --- contracts/shift/ShiftUtils.sol | 2 +- test/multichain/MultichainRouter.ts | 24 ++++++++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/contracts/shift/ShiftUtils.sol b/contracts/shift/ShiftUtils.sol index c33faeeaa..f6de69794 100644 --- a/contracts/shift/ShiftUtils.sol +++ b/contracts/shift/ShiftUtils.sol @@ -207,7 +207,7 @@ library ShiftUtils { shift.updatedAtTime(), 0, // executionFee 0, // callbackGasLimit - shift.srcChainId() + 0 // srcChainId ), Withdrawal.Flags( false diff --git a/test/multichain/MultichainRouter.ts b/test/multichain/MultichainRouter.ts index 36b1f4321..85d00552e 100644 --- a/test/multichain/MultichainRouter.ts +++ b/test/multichain/MultichainRouter.ts @@ -420,19 +420,35 @@ describe("MultichainRouter", () => { await executeDeposit(fixture, { gasUsageLabel: "executeDeposit" }); expect(await getBalanceOf(ethUsdMarket.marketToken, multichainVault.address)).eq(expandDecimals(95_000, 18)); + expect(await getBalanceOf(solUsdMarket.marketToken, multichainVault.address)).eq(0); expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, ethUsdMarket.marketToken))).to.eq( expandDecimals(95_000, 18) ); + await sendCreateShift(createShiftParams); const shiftKeys = await getShiftKeys(dataStore, 0, 1); - const shift = await reader.getShift(dataStore.address, shiftKeys[0]); + let shift = await reader.getShift(dataStore.address, shiftKeys[0]); expect(shift.addresses.account).eq(user1.address); expect(await getShiftCount(dataStore)).eq(1); - // await executeShift(fixture, { gasUsageLabel: "executeShift" }); - // shift = await reader.getShift(dataStore.address, shiftKeys[0]); - // expect(shift.addresses.account).eq(ethers.constants.AddressZero); + await executeShift(fixture, { gasUsageLabel: "executeShift" }); + + expect(await getShiftCount(dataStore)).eq(0); + shift = await reader.getShift(dataStore.address, shiftKeys[0]); + expect(shift.addresses.account).eq(ethers.constants.AddressZero); + + expect(await getBalanceOf(ethUsdMarket.marketToken, multichainVault.address)).eq(expandDecimals(45_000, 18)); // 95k - 50k + expect(await dataStore.getUint(keys.multichainBalanceKey(user1.address, ethUsdMarket.marketToken))).to.eq( + expandDecimals(45_000, 18) + ); // 95k - 50k + expect(await getBalanceOf(solUsdMarket.marketToken, multichainVault.address)).to.approximately( + expandDecimals(50_000, 18), + expandDecimals(1, 12) + ); // ~50k + expect( + await dataStore.getUint(keys.multichainBalanceKey(user1.address, solUsdMarket.marketToken)) + ).to.approximately(expandDecimals(50_000, 18), expandDecimals(1, 12)); // ~50k }); });