From 892a18415b6831786bd4bff22c67b78800dc9a33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mati=CC=81as?= Date: Thu, 18 Jan 2024 17:12:20 -0300 Subject: [PATCH] add ccip from core-modules to protocol/synthetix --- protocol/synthetix/cannonfile.test.toml | 5 +- protocol/synthetix/cannonfile.toml | 4 + .../contracts/interfaces/IUtilsModule.sol | 32 +--- .../external/IAny2EVMMessageReceiver.sol | 16 -- .../interfaces/external/ICcipRouterClient.sol | 45 ------ .../contracts/mocks/CcipRouterMock.sol | 3 +- .../modules/core/CcipReceiverModule.sol | 20 +-- .../modules/core/CrossChainModule.sol | 12 ++ .../modules/core/CrossChainUSDModule.sol | 3 +- .../contracts/modules/core/UtilsModule.sol | 80 ++-------- .../contracts/modules/usd/USDTokenModule.sol | 2 +- .../contracts/storage/CrossChain.sol | 147 ------------------ .../synthetix/contracts/utils/CcipClient.sol | 41 ----- protocol/synthetix/storage.dump.sol | 90 +++++------ .../modules/core/CcipReceiverModule.test.ts | 7 +- .../modules/core/CrossChainUSDModule.test.ts | 8 +- .../modules/core/USDTokenModule.test.ts | 6 +- .../modules/core/UtilsModule.test.ts | 47 ------ 18 files changed, 108 insertions(+), 460 deletions(-) delete mode 100644 protocol/synthetix/contracts/interfaces/external/IAny2EVMMessageReceiver.sol delete mode 100644 protocol/synthetix/contracts/interfaces/external/ICcipRouterClient.sol create mode 100644 protocol/synthetix/contracts/modules/core/CrossChainModule.sol delete mode 100644 protocol/synthetix/contracts/storage/CrossChain.sol delete mode 100644 protocol/synthetix/contracts/utils/CcipClient.sol diff --git a/protocol/synthetix/cannonfile.test.toml b/protocol/synthetix/cannonfile.test.toml index 080d24e1a1..1404f06772 100644 --- a/protocol/synthetix/cannonfile.test.toml +++ b/protocol/synthetix/cannonfile.test.toml @@ -22,9 +22,6 @@ artifact = "contracts/generated/test/TestableCollateralConfigurationStorage.sol: [contract.TestableCollateralLockStorage] artifact = "contracts/generated/test/TestableCollateralLockStorage.sol:TestableCollateralLockStorage" -[contract.TestableCrossChainStorage] -artifact = "contracts/generated/test/TestableCrossChainStorage.sol:TestableCrossChainStorage" - [contract.TestableDistributionStorage] artifact = "contracts/generated/test/TestableDistributionStorage.sol:TestableDistributionStorage" @@ -72,6 +69,7 @@ contracts = [ "CcipReceiverModule", "CollateralModule", "CollateralConfigurationModule", + "CrossChainModule", "CrossChainUSDModule", "IssueUSDModule", "LiquidationModule", @@ -87,7 +85,6 @@ contracts = [ "TestableCollateralStorage", "TestableCollateralConfigurationStorage", "TestableCollateralLockStorage", - "TestableCrossChainStorage", "TestableDistributionStorage", "TestableDistributionActorStorage", "TestableMarketStorage", diff --git a/protocol/synthetix/cannonfile.toml b/protocol/synthetix/cannonfile.toml index 85b4e62c9e..10cde882c7 100644 --- a/protocol/synthetix/cannonfile.toml +++ b/protocol/synthetix/cannonfile.toml @@ -49,6 +49,9 @@ artifact = "contracts/modules/core/CollateralModule.sol:CollateralModule" [contract.CollateralConfigurationModule] artifact = "contracts/modules/core/CollateralConfigurationModule.sol:CollateralConfigurationModule" +[contract.CrossChainModule] +artifact = "contracts/modules/core/CrossChainModule.sol:CrossChainModule" + [contract.CrossChainUSDModule] artifact = "contracts/modules/core/CrossChainUSDModule.sol:CrossChainUSDModule" @@ -96,6 +99,7 @@ contracts = [ "CcipReceiverModule", "CollateralModule", "CollateralConfigurationModule", + "CrossChainModule", "CrossChainUSDModule", "IssueUSDModule", "LiquidationModule", diff --git a/protocol/synthetix/contracts/interfaces/IUtilsModule.sol b/protocol/synthetix/contracts/interfaces/IUtilsModule.sol index f39f29c69f..0235c5c072 100644 --- a/protocol/synthetix/contracts/interfaces/IUtilsModule.sol +++ b/protocol/synthetix/contracts/interfaces/IUtilsModule.sol @@ -7,31 +7,6 @@ import {IERC165} from "@synthetixio/core-contracts/contracts/interfaces/IERC165. * @title Module with assorted utility functions. */ interface IUtilsModule is IERC165 { - /** - * @notice Emitted when a new cross chain network becomes supported by the protocol - */ - event NewSupportedCrossChainNetwork(uint64 newChainId); - - /** - * @notice Configure CCIP addresses on the stablecoin. - * @param ccipRouter The address on this chain to which CCIP messages will be sent or received. - * @param ccipTokenPool The address where CCIP fees will be sent to when sending and receiving cross chain messages. - */ - function configureChainlinkCrossChain(address ccipRouter, address ccipTokenPool) external; - - /** - * @notice Used to add new cross chain networks to the protocol - * Ignores a network if it matches the current chain id - * Ignores a network if it has already been added - * @param supportedNetworks array of all networks that are supported by the protocol - * @param ccipSelectors the ccip "selector" which maps to the chain id on the same index. must be same length as `supportedNetworks` - * @return numRegistered the number of networks that were actually registered - */ - function setSupportedCrossChainNetworks( - uint64[] memory supportedNetworks, - uint64[] memory ccipSelectors - ) external returns (uint256 numRegistered); - /** * @notice Configure the system's single oracle manager address. * @param oracleManagerAddress The address of the oracle manager. @@ -66,6 +41,13 @@ interface IUtilsModule is IERC165 { */ function getConfigAddress(bytes32 k) external view returns (address v); + /** + * @notice Configure CCIP addresses on the stablecoin. + * @param ccipRouter The address on this chain to which CCIP messages will be sent or received. + * @param ccipTokenPool The address where CCIP fees will be sent to when sending and receiving cross chain messages. + */ + function configureUsdTokenChainlink(address ccipRouter, address ccipTokenPool) external; + /** * @notice Checks if the address is the trusted forwarder * @param forwarder The address to check diff --git a/protocol/synthetix/contracts/interfaces/external/IAny2EVMMessageReceiver.sol b/protocol/synthetix/contracts/interfaces/external/IAny2EVMMessageReceiver.sol deleted file mode 100644 index cfe879cce6..0000000000 --- a/protocol/synthetix/contracts/interfaces/external/IAny2EVMMessageReceiver.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "../../utils/CcipClient.sol"; - -/// @notice Application contracts that intend to receive messages from -/// the router should implement this interface. -interface IAny2EVMMessageReceiver { - /// @notice Router calls this to deliver a message. - /// If this reverts, any token transfers also revert. The message - /// will move to a FAILED state and become available for manual execution - /// as a retry. Fees already paid are NOT currently refunded (may change). - /// @param message CCIP Message - /// @dev Note ensure you check the msg.sender is the router - function ccipReceive(CcipClient.Any2EVMMessage calldata message) external; -} diff --git a/protocol/synthetix/contracts/interfaces/external/ICcipRouterClient.sol b/protocol/synthetix/contracts/interfaces/external/ICcipRouterClient.sol deleted file mode 100644 index cfc2275b2b..0000000000 --- a/protocol/synthetix/contracts/interfaces/external/ICcipRouterClient.sol +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "../../utils/CcipClient.sol"; - -interface ICcipRouterClient { - error UnsupportedDestinationChain(uint64 destinationChainId); - /// @dev Sender is not whitelisted - error SenderNotAllowed(address sender); - error InsufficientFeeTokenAmount(); - /// @dev Sent msg.value with a non-empty feeToken - error InvalidMsgValue(); - - /// @notice Checks if the given chain ID is supported for sending/receiving. - /// @param chainId The chain to check - /// @return supported is true if it is supported, false if not - function isChainSupported(uint64 chainId) external view returns (bool supported); - - /// @notice Gets a list of all supported tokens which can be sent or received - /// to/from a given chain id. - /// @param chainId The chainId. - /// @return tokens The addresses of all tokens that are supported. - function getSupportedTokens(uint64 chainId) external view returns (address[] memory tokens); - - /// @param destinationChainId The destination chain ID - /// @param message The cross-chain CCIP message including data and/or tokens - /// @return fee returns execution fee for the specified message - /// delivery to destination chain - /// @dev returns 0 fee on invalid message. - function getFee( - uint64 destinationChainId, - CcipClient.EVM2AnyMessage memory message - ) external view returns (uint256 fee); - - /// @notice Request a message to be sent to the destination chain - /// @param destinationChainId The destination chain ID - /// @param message The cross-chain CCIP message including data and/or tokens - /// @return messageId The message ID - /// @dev Note if msg.value is larger than the required fee (from getFee) we accept - /// the overpayment with no refund. - function ccipSend( - uint64 destinationChainId, - CcipClient.EVM2AnyMessage calldata message - ) external payable returns (bytes32 messageId); -} diff --git a/protocol/synthetix/contracts/mocks/CcipRouterMock.sol b/protocol/synthetix/contracts/mocks/CcipRouterMock.sol index e42d2de5d2..def0d71603 100644 --- a/protocol/synthetix/contracts/mocks/CcipRouterMock.sol +++ b/protocol/synthetix/contracts/mocks/CcipRouterMock.sol @@ -1,7 +1,6 @@ //SPDX-License-Identifier: MIT pragma solidity >=0.8.4; - -import "../interfaces/external/ICcipRouterClient.sol"; +import "@synthetixio/core-modules/contracts/interfaces/external/ICcipRouterClient.sol"; contract CcipRouterMock { // solhint-disable no-empty-blocks diff --git a/protocol/synthetix/contracts/modules/core/CcipReceiverModule.sol b/protocol/synthetix/contracts/modules/core/CcipReceiverModule.sol index 7126a304fa..f2ed096950 100644 --- a/protocol/synthetix/contracts/modules/core/CcipReceiverModule.sol +++ b/protocol/synthetix/contracts/modules/core/CcipReceiverModule.sol @@ -1,22 +1,12 @@ //SPDX-License-Identifier: MIT pragma solidity >=0.8.11 <0.9.0; -import "@synthetixio/core-modules/contracts/interfaces/IAssociatedSystemsModule.sol"; -import "@synthetixio/core-modules/contracts/storage/AssociatedSystem.sol"; -import "@synthetixio/core-contracts/contracts/ownership/OwnableStorage.sol"; - -import "../../interfaces/external/IAny2EVMMessageReceiver.sol"; - -import "../../storage/OracleManager.sol"; -import "../../storage/Config.sol"; -import "../../storage/CrossChain.sol"; +import {CcipReceiverModule as BaseCcipReceiverModule} from "@synthetixio/core-modules/contracts/modules/CcipReceiverModule.sol"; /** - * @title Module with assorted utility functions. - * @dev See IUtilsModule. + * @title Module that handles receiving ccip messages. */ -contract CcipReceiverModule is IAny2EVMMessageReceiver { - function ccipReceive(CcipClient.Any2EVMMessage memory message) external { - CrossChain.processCcipReceive(CrossChain.load(), message); - } +// solhint-disable-next-line no-empty-blocks +contract CcipReceiverModule is BaseCcipReceiverModule { + } diff --git a/protocol/synthetix/contracts/modules/core/CrossChainModule.sol b/protocol/synthetix/contracts/modules/core/CrossChainModule.sol new file mode 100644 index 0000000000..df36c8c708 --- /dev/null +++ b/protocol/synthetix/contracts/modules/core/CrossChainModule.sol @@ -0,0 +1,12 @@ +//SPDX-License-Identifier: MIT +pragma solidity >=0.8.11 <0.9.0; + +import {CrossChainModule as BaseCrossChainModule} from "@synthetixio/core-modules/contracts/modules/CrossChainModule.sol"; + +/** + * @title Module that handles anything related to cross-chain. + */ +// solhint-disable-next-line no-empty-blocks +contract CrossChainModule is BaseCrossChainModule { + +} diff --git a/protocol/synthetix/contracts/modules/core/CrossChainUSDModule.sol b/protocol/synthetix/contracts/modules/core/CrossChainUSDModule.sol index 1883920d3c..2732bedb5f 100644 --- a/protocol/synthetix/contracts/modules/core/CrossChainUSDModule.sol +++ b/protocol/synthetix/contracts/modules/core/CrossChainUSDModule.sol @@ -5,10 +5,9 @@ import "../../interfaces/ICrossChainUSDModule.sol"; import "@synthetixio/core-modules/contracts/interfaces/ITokenModule.sol"; import "@synthetixio/core-contracts/contracts/utils/ERC2771Context.sol"; -import "../../storage/CrossChain.sol"; - import "@synthetixio/core-modules/contracts/storage/AssociatedSystem.sol"; import "@synthetixio/core-modules/contracts/storage/FeatureFlag.sol"; +import "@synthetixio/core-modules/contracts/storage/CrossChain.sol"; /** * @title Module for the cross-chain transfers of stablecoins. diff --git a/protocol/synthetix/contracts/modules/core/UtilsModule.sol b/protocol/synthetix/contracts/modules/core/UtilsModule.sol index faeeca7102..fc8e751e22 100644 --- a/protocol/synthetix/contracts/modules/core/UtilsModule.sol +++ b/protocol/synthetix/contracts/modules/core/UtilsModule.sol @@ -4,86 +4,25 @@ pragma solidity >=0.8.11 <0.9.0; import "@synthetixio/core-modules/contracts/interfaces/IAssociatedSystemsModule.sol"; import "@synthetixio/core-modules/contracts/storage/AssociatedSystem.sol"; import "@synthetixio/core-contracts/contracts/ownership/OwnableStorage.sol"; -import "@synthetixio/core-contracts/contracts/errors/ParameterError.sol"; -import "@synthetixio/core-contracts/contracts/utils/SafeCast.sol"; import "@synthetixio/core-contracts/contracts/utils/ERC2771Context.sol"; import "../../interfaces/IUtilsModule.sol"; -import "../../storage/CrossChain.sol"; import "../../storage/OracleManager.sol"; import "../../storage/Config.sol"; -import "../../interfaces/external/IAny2EVMMessageReceiver.sol"; - /** * @title Module with assorted utility functions. * @dev See IUtilsModule. */ contract UtilsModule is IUtilsModule { using AssociatedSystem for AssociatedSystem.Data; - using SetUtil for SetUtil.UintSet; - using SafeCastU256 for uint256; bytes32 private constant _USD_TOKEN = "USDToken"; bytes32 private constant _CCIP_CHAINLINK_SEND = "ccipChainlinkSend"; bytes32 private constant _CCIP_CHAINLINK_RECV = "ccipChainlinkRecv"; bytes32 private constant _CCIP_CHAINLINK_TOKEN_POOL = "ccipChainlinkTokenPool"; - /** - * @inheritdoc IUtilsModule - */ - function configureChainlinkCrossChain( - address ccipRouter, - address ccipTokenPool - ) external override { - OwnableStorage.onlyOwner(); - - CrossChain.Data storage cc = CrossChain.load(); - - cc.ccipRouter = ICcipRouterClient(ccipRouter); - - IAssociatedSystemsModule usdToken = IAssociatedSystemsModule( - AssociatedSystem.load(_USD_TOKEN).proxy - ); - - usdToken.registerUnmanagedSystem(_CCIP_CHAINLINK_SEND, ccipRouter); - usdToken.registerUnmanagedSystem(_CCIP_CHAINLINK_RECV, ccipRouter); - usdToken.registerUnmanagedSystem(_CCIP_CHAINLINK_TOKEN_POOL, ccipTokenPool); - } - - /** - * @inheritdoc IUtilsModule - */ - function setSupportedCrossChainNetworks( - uint64[] memory supportedNetworks, - uint64[] memory ccipSelectors - ) external returns (uint256 numRegistered) { - OwnableStorage.onlyOwner(); - - uint64 myChainId = block.chainid.to64(); - - if (ccipSelectors.length != supportedNetworks.length) { - revert ParameterError.InvalidParameter("ccipSelectors", "must match length"); - } - - CrossChain.Data storage cc = CrossChain.load(); - for (uint i = 0; i < supportedNetworks.length; i++) { - if (supportedNetworks[i] == myChainId) continue; - if ( - supportedNetworks[i] != myChainId && - !cc.supportedNetworks.contains(supportedNetworks[i]) - ) { - numRegistered++; - cc.supportedNetworks.add(supportedNetworks[i]); - emit NewSupportedCrossChainNetwork(supportedNetworks[i]); - } - - cc.ccipChainIdToSelector[supportedNetworks[i]] = ccipSelectors[i]; - cc.ccipSelectorToChainId[ccipSelectors[i]] = supportedNetworks[i]; - } - } - /** * @inheritdoc IUtilsModule */ @@ -128,8 +67,21 @@ contract UtilsModule is IUtilsModule { function supportsInterface( bytes4 interfaceId ) public view virtual override(IERC165) returns (bool) { - return - interfaceId == type(IAny2EVMMessageReceiver).interfaceId || - interfaceId == this.supportsInterface.selector; + return interfaceId == this.supportsInterface.selector; + } + + function configureUsdTokenChainlink( + address ccipRouter, + address ccipTokenPool + ) external override { + OwnableStorage.onlyOwner(); + + IAssociatedSystemsModule usdToken = IAssociatedSystemsModule( + AssociatedSystem.load(_USD_TOKEN).getAddress() + ); + + usdToken.registerUnmanagedSystem(_CCIP_CHAINLINK_SEND, ccipRouter); + usdToken.registerUnmanagedSystem(_CCIP_CHAINLINK_RECV, ccipRouter); + usdToken.registerUnmanagedSystem(_CCIP_CHAINLINK_TOKEN_POOL, ccipTokenPool); } } diff --git a/protocol/synthetix/contracts/modules/usd/USDTokenModule.sol b/protocol/synthetix/contracts/modules/usd/USDTokenModule.sol index 25303914e6..e6be5d5614 100644 --- a/protocol/synthetix/contracts/modules/usd/USDTokenModule.sol +++ b/protocol/synthetix/contracts/modules/usd/USDTokenModule.sol @@ -2,10 +2,10 @@ pragma solidity ^0.8.7; import "../../interfaces/IUSDTokenModule.sol"; -import "../../storage/CrossChain.sol"; import "@synthetixio/core-contracts/contracts/utils/ERC2771Context.sol"; import "@synthetixio/core-modules/contracts/storage/AssociatedSystem.sol"; +import "@synthetixio/core-modules/contracts/storage/CrossChain.sol"; import "@synthetixio/core-contracts/contracts/token/ERC20.sol"; import "@synthetixio/core-modules/contracts/storage/FeatureFlag.sol"; import "@synthetixio/core-contracts/contracts/initializable/InitializableMixin.sol"; diff --git a/protocol/synthetix/contracts/storage/CrossChain.sol b/protocol/synthetix/contracts/storage/CrossChain.sol deleted file mode 100644 index fa3bb8a1d6..0000000000 --- a/protocol/synthetix/contracts/storage/CrossChain.sol +++ /dev/null @@ -1,147 +0,0 @@ -//SPDX-License-Identifier: MIT -pragma solidity >=0.8.11 <0.9.0; - -import {SetUtil} from "@synthetixio/core-contracts/contracts/utils/SetUtil.sol"; -import {AccessError} from "@synthetixio/core-contracts/contracts/errors/AccessError.sol"; - -import "@synthetixio/core-contracts/contracts/utils/ERC2771Context.sol"; -import "@synthetixio/core-contracts/contracts/interfaces/IERC20.sol"; -import "../interfaces/external/ICcipRouterClient.sol"; - -/** - * @title System wide configuration for anything - */ -library CrossChain { - using SetUtil for SetUtil.UintSet; - - event ProcessedCcipMessage(bytes payload, bytes result); - - error NotCcipRouter(address); - error UnsupportedNetwork(uint64); - error InsufficientCcipFee(uint256 requiredAmount, uint256 availableAmount); - error InvalidMessage(); - - bytes32 private constant _SLOT_CROSS_CHAIN = - keccak256(abi.encode("io.synthetix.synthetix.CrossChain")); - - struct Data { - ICcipRouterClient ccipRouter; - SetUtil.UintSet supportedNetworks; - mapping(uint64 => uint64) ccipChainIdToSelector; - mapping(uint64 => uint64) ccipSelectorToChainId; - } - - function load() internal pure returns (Data storage crossChain) { - bytes32 s = _SLOT_CROSS_CHAIN; - assembly { - crossChain.slot := s - } - } - - function processCcipReceive(Data storage self, CcipClient.Any2EVMMessage memory data) internal { - if ( - address(self.ccipRouter) == address(0) || - ERC2771Context._msgSender() != address(self.ccipRouter) - ) { - revert NotCcipRouter(ERC2771Context._msgSender()); - } - - uint64 sourceChainId = self.ccipSelectorToChainId[data.sourceChainSelector]; - - if (!self.supportedNetworks.contains(sourceChainId)) { - revert UnsupportedNetwork(sourceChainId); - } - - address sender = abi.decode(data.sender, (address)); - if (sender != address(this)) { - revert AccessError.Unauthorized(sender); - } - - address caller; - bytes memory payload; - - if (data.tokenAmounts.length == 1) { - address to = abi.decode(data.data, (address)); - - caller = data.tokenAmounts[0].token; - payload = abi.encodeWithSelector( - IERC20.transfer.selector, - to, - data.tokenAmounts[0].amount - ); - } else { - revert InvalidMessage(); - } - - // at this point, everything should be good to send the message to ourselves. - // the below `onlyCrossChain` function will verify that the caller is self - (bool success, bytes memory result) = caller.call(payload); - - if (!success) { - uint len = result.length; - assembly { - revert(add(result, 0x20), len) - } - } - - emit ProcessedCcipMessage(payload, result); - } - - function onlyCrossChain() internal view { - if (ERC2771Context._msgSender() != address(this)) { - revert AccessError.Unauthorized(ERC2771Context._msgSender()); - } - } - - /** - * @dev Transfers tokens to a destination chain. - */ - function teleport( - Data storage self, - uint64 destChainId, - address token, - uint256 amount, - uint256 gasLimit - ) internal returns (uint256 gasTokenUsed) { - ICcipRouterClient router = self.ccipRouter; - - CcipClient.EVMTokenAmount[] memory tokenAmounts = new CcipClient.EVMTokenAmount[](1); - tokenAmounts[0] = CcipClient.EVMTokenAmount(token, amount); - - bytes memory data = abi.encode(ERC2771Context._msgSender()); - CcipClient.EVM2AnyMessage memory sentMsg = CcipClient.EVM2AnyMessage( - abi.encode(address(this)), // abi.encode(receiver address) for dest EVM chains - data, - tokenAmounts, - address(0), // Address of feeToken. address(0) means you will send msg.value. - CcipClient._argsToBytes(CcipClient.EVMExtraArgsV1(gasLimit, false)) - ); - - uint64 chainSelector = self.ccipChainIdToSelector[destChainId]; - uint256 fee = router.getFee(chainSelector, sentMsg); - - // need to check sufficient fee here or else the error is very confusing - if (address(this).balance < fee) { - revert InsufficientCcipFee(fee, address(this).balance); - } - - router.ccipSend{value: fee}(chainSelector, sentMsg); - - return fee; - } - - function refundLeftoverGas(uint256 gasTokenUsed) internal returns (uint256 amountRefunded) { - amountRefunded = msg.value - gasTokenUsed; - - (bool success, bytes memory result) = ERC2771Context._msgSender().call{ - value: amountRefunded - }(""); - - if (!success) { - uint256 len = result.length; - assembly { - revert(result, len) - } - } - } -} diff --git a/protocol/synthetix/contracts/utils/CcipClient.sol b/protocol/synthetix/contracts/utils/CcipClient.sol deleted file mode 100644 index 7e58879874..0000000000 --- a/protocol/synthetix/contracts/utils/CcipClient.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -library CcipClient { - struct EVMTokenAmount { - address token; // token address on the local chain - uint256 amount; - } - - struct Any2EVMMessage { - bytes32 messageId; // MessageId corresponding to ccipSend on source - uint64 sourceChainSelector; - bytes sender; // abi.decode(sender) if coming from an EVM chain - bytes data; // payload sent in original message - EVMTokenAmount[] tokenAmounts; - } - - // If extraArgs is empty bytes, the default is - // 200k gas limit and strict = false. - struct EVM2AnyMessage { - bytes receiver; // abi.encode(receiver address) for dest EVM chains - bytes data; // Data payload - EVMTokenAmount[] tokenAmounts; // Token transfers - address feeToken; // Address of feeToken. address(0) means you will send msg.value. - bytes extraArgs; // Populate this with _argsToBytes(EVMExtraArgsV1) - } - - // extraArgs will evolve to support new features - // bytes4(keccak256("CCIP EVMExtraArgsV1")); - bytes4 public constant EVM_EXTRA_ARGS_V1_TAG = 0x97a657c9; - struct EVMExtraArgsV1 { - uint256 gasLimit; // ATTENTION!!! MAX GAS LIMIT 4M FOR ALPHA TESTING - bool strict; // See strict sequencing details below. - } - - function _argsToBytes( - EVMExtraArgsV1 memory extraArgs - ) internal pure returns (bytes memory bts) { - return abi.encodeWithSelector(EVM_EXTRA_ARGS_V1_TAG, extraArgs); - } -} diff --git a/protocol/synthetix/storage.dump.sol b/protocol/synthetix/storage.dump.sol index fae28b4090..266c3a1120 100644 --- a/protocol/synthetix/storage.dump.sol +++ b/protocol/synthetix/storage.dump.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.4; +pragma solidity >=0.8.11<0.9.0; // @custom:artifact @synthetixio/core-contracts/contracts/ownership/OwnableStorage.sol:OwnableStorage library OwnableStorage { @@ -155,6 +155,23 @@ library AssociatedSystem { } } +// @custom:artifact @synthetixio/core-modules/contracts/storage/CrossChain.sol:CrossChain +library CrossChain { + bytes32 private constant _SLOT_CROSS_CHAIN = keccak256(abi.encode("io.synthetix.core-modules.CrossChain")); + struct Data { + address ccipRouter; + SetUtil.UintSet supportedNetworks; + mapping(uint64 => uint64) ccipChainIdToSelector; + mapping(uint64 => uint64) ccipSelectorToChainId; + } + function load() internal pure returns (Data storage crossChain) { + bytes32 s = _SLOT_CROSS_CHAIN; + assembly { + crossChain.slot := s + } + } +} + // @custom:artifact @synthetixio/core-modules/contracts/storage/FeatureFlag.sol:FeatureFlag library FeatureFlag { struct Data { @@ -185,6 +202,33 @@ library Initialized { } } +// @custom:artifact @synthetixio/core-modules/contracts/utils/CcipClient.sol:CcipClient +library CcipClient { + bytes4 public constant EVM_EXTRA_ARGS_V1_TAG = 0x97a657c9; + struct EVMTokenAmount { + address token; + uint256 amount; + } + struct Any2EVMMessage { + bytes32 messageId; + uint64 sourceChainSelector; + bytes sender; + bytes data; + EVMTokenAmount[] tokenAmounts; + } + struct EVM2AnyMessage { + bytes receiver; + bytes data; + EVMTokenAmount[] tokenAmounts; + address feeToken; + bytes extraArgs; + } + struct EVMExtraArgsV1 { + uint256 gasLimit; + bool strict; + } +} + // @custom:artifact @synthetixio/oracle-manager/contracts/interfaces/external/IPyth.sol:PythStructs contract PythStructs { struct Price { @@ -474,23 +518,6 @@ library Config { } } -// @custom:artifact contracts/storage/CrossChain.sol:CrossChain -library CrossChain { - bytes32 private constant _SLOT_CROSS_CHAIN = keccak256(abi.encode("io.synthetix.synthetix.CrossChain")); - struct Data { - address ccipRouter; - SetUtil.UintSet supportedNetworks; - mapping(uint64 => uint64) ccipChainIdToSelector; - mapping(uint64 => uint64) ccipSelectorToChainId; - } - function load() internal pure returns (Data storage crossChain) { - bytes32 s = _SLOT_CROSS_CHAIN; - assembly { - crossChain.slot := s - } - } -} - // @custom:artifact contracts/storage/Distribution.sol:Distribution library Distribution { struct Data { @@ -709,30 +736,3 @@ library VaultEpoch { mapping(uint128 => uint64) lastDelegationTime; } } - -// @custom:artifact contracts/utils/CcipClient.sol:CcipClient -library CcipClient { - bytes4 public constant EVM_EXTRA_ARGS_V1_TAG = 0x97a657c9; - struct EVMTokenAmount { - address token; - uint256 amount; - } - struct Any2EVMMessage { - bytes32 messageId; - uint64 sourceChainSelector; - bytes sender; - bytes data; - EVMTokenAmount[] tokenAmounts; - } - struct EVM2AnyMessage { - bytes receiver; - bytes data; - EVMTokenAmount[] tokenAmounts; - address feeToken; - bytes extraArgs; - } - struct EVMExtraArgsV1 { - uint256 gasLimit; - bool strict; - } -} diff --git a/protocol/synthetix/test/integration/modules/core/CcipReceiverModule.test.ts b/protocol/synthetix/test/integration/modules/core/CcipReceiverModule.test.ts index edaf89d2f8..a830012c20 100644 --- a/protocol/synthetix/test/integration/modules/core/CcipReceiverModule.test.ts +++ b/protocol/synthetix/test/integration/modules/core/CcipReceiverModule.test.ts @@ -2,7 +2,6 @@ import assertBn from '@synthetixio/core-utils/utils/assertions/assert-bignumber' import assertEvent from '@synthetixio/core-utils/utils/assertions/assert-event'; import assertRevert from '@synthetixio/core-utils/utils/assertions/assert-revert'; import { ethers } from 'ethers'; - import { bn, bootstrapWithStakedPool } from '../../bootstrap'; describe('CcipReceiverModule', function () { @@ -24,7 +23,11 @@ describe('CcipReceiverModule', function () { before('set ccip settings', async () => { await systems() .Core.connect(owner()) - .configureChainlinkCrossChain(await FakeCcip.getAddress(), ethers.constants.AddressZero); + .configureChainlinkCrossChain(await FakeCcip.getAddress()); + + await systems() + .Core.connect(owner()) + .configureUsdTokenChainlink(await FakeCcip.getAddress(), ethers.constants.AddressZero); await systems() .Core.connect(owner()) diff --git a/protocol/synthetix/test/integration/modules/core/CrossChainUSDModule.test.ts b/protocol/synthetix/test/integration/modules/core/CrossChainUSDModule.test.ts index 17abd4b2da..7e46a8e5cb 100644 --- a/protocol/synthetix/test/integration/modules/core/CrossChainUSDModule.test.ts +++ b/protocol/synthetix/test/integration/modules/core/CrossChainUSDModule.test.ts @@ -1,10 +1,10 @@ import assertBn from '@synthetixio/core-utils/src/utils/assertions/assert-bignumber'; import assertEvent from '@synthetixio/core-utils/utils/assertions/assert-event'; import assertRevert from '@synthetixio/core-utils/utils/assertions/assert-revert'; -import hre from 'hardhat'; import { ethers } from 'ethers'; -import { verifyUsesFeatureFlag } from '../../verifications'; +import hre from 'hardhat'; import { bn, bootstrapWithStakedPool } from '../../bootstrap'; +import { verifyUsesFeatureFlag } from '../../verifications'; describe('CrossChainUSDModule', function () { const { owner, systems, staker, accountId, poolId, collateralAddress } = @@ -27,9 +27,11 @@ describe('CrossChainUSDModule', function () { }); before('configure CCIP', async () => { + await systems().Core.connect(owner()).configureChainlinkCrossChain(CcipRouterMock.address); + await systems() .Core.connect(owner()) - .configureChainlinkCrossChain(CcipRouterMock.address, ethers.constants.AddressZero); + .configureUsdTokenChainlink(CcipRouterMock.address, ethers.constants.AddressZero); }); before('mint some sUSD', async () => { diff --git a/protocol/synthetix/test/integration/modules/core/USDTokenModule.test.ts b/protocol/synthetix/test/integration/modules/core/USDTokenModule.test.ts index 9eafa928e2..4e6d7c9756 100644 --- a/protocol/synthetix/test/integration/modules/core/USDTokenModule.test.ts +++ b/protocol/synthetix/test/integration/modules/core/USDTokenModule.test.ts @@ -43,7 +43,11 @@ describe('USDTokenModule', function () { describe('burn(uint256)', () => { before('configure CCIP', async () => { - await systems().Core.connect(owner()).configureChainlinkCrossChain( + await systems() + .Core.connect(owner()) + .configureChainlinkCrossChain(ethers.constants.AddressZero); + + await systems().Core.connect(owner()).configureUsdTokenChainlink( ethers.constants.AddressZero, stakerAddress // fake CCIP token pool address ); diff --git a/protocol/synthetix/test/integration/modules/core/UtilsModule.test.ts b/protocol/synthetix/test/integration/modules/core/UtilsModule.test.ts index 84c5e09105..18e07342a5 100644 --- a/protocol/synthetix/test/integration/modules/core/UtilsModule.test.ts +++ b/protocol/synthetix/test/integration/modules/core/UtilsModule.test.ts @@ -12,53 +12,6 @@ describe('UtilsModule', function () { [owner, user1] = signers(); }); - describe('registerCcip()', () => { - it('is only owner', async () => { - await assertRevert( - systems() - .Core.connect(user1) - .configureChainlinkCrossChain(ethers.constants.AddressZero, ethers.constants.AddressZero), - `Unauthorized("${await user1.getAddress()}")`, - systems().Core - ); - }); - - describe('on success', () => { - before('call', async () => { - await systems() - .Core.connect(owner) - .configureChainlinkCrossChain(user1.getAddress(), user1.getAddress()); - }); - - it('sets ccip values in usd token', async () => { - assert.equal( - ( - await systems().USD.getAssociatedSystem( - ethers.utils.formatBytes32String('ccipChainlinkSend') - ) - )[0], - await user1.getAddress() - ); - assert.equal( - ( - await systems().USD.getAssociatedSystem( - ethers.utils.formatBytes32String('ccipChainlinkRecv') - ) - )[0], - await user1.getAddress() - ); - assert.equal( - ( - await systems().USD.getAssociatedSystem( - ethers.utils.formatBytes32String('ccipChainlinkTokenPool') - ) - )[0], - await user1.getAddress() - ); - }); - }); - }); - describe('configureOracleManager()', () => { it('is only owner', async () => { await assertRevert(