From eee08536dd3a56217522435cb5b3545abc615a4e Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 21 Jun 2024 14:56:47 +0200 Subject: [PATCH 01/41] cross-chain prototype v1 --- contracts/crosschain/GenericGatewayAxelar.sol | 198 ++++++++ contracts/crosschain/IGenericGateway.sol | 56 +++ .../axelar/interfaces/IAxelarGasService.sol | 436 ++++++++++++++++++ .../axelar/interfaces/IAxelarGateway.sol | 192 ++++++++ .../axelar/interfaces/IContractIdentifier.sol | 13 + .../vendor/axelar/interfaces/IGovernable.sol | 41 ++ .../axelar/interfaces/IImplementation.sol | 11 + .../interfaces/IInterchainGasEstimation.sol | 45 ++ .../vendor/axelar/interfaces/IOwnable.sol | 50 ++ .../vendor/axelar/interfaces/IUpgradable.sol | 23 + .../axelar/types/GasEstimationTypes.sol | 35 ++ contracts/utils/Set.sol | 32 ++ 12 files changed, 1132 insertions(+) create mode 100644 contracts/crosschain/GenericGatewayAxelar.sol create mode 100644 contracts/crosschain/IGenericGateway.sol create mode 100644 contracts/crosschain/vendor/axelar/interfaces/IAxelarGasService.sol create mode 100644 contracts/crosschain/vendor/axelar/interfaces/IAxelarGateway.sol create mode 100644 contracts/crosschain/vendor/axelar/interfaces/IContractIdentifier.sol create mode 100644 contracts/crosschain/vendor/axelar/interfaces/IGovernable.sol create mode 100644 contracts/crosschain/vendor/axelar/interfaces/IImplementation.sol create mode 100644 contracts/crosschain/vendor/axelar/interfaces/IInterchainGasEstimation.sol create mode 100644 contracts/crosschain/vendor/axelar/interfaces/IOwnable.sol create mode 100644 contracts/crosschain/vendor/axelar/interfaces/IUpgradable.sol create mode 100644 contracts/crosschain/vendor/axelar/types/GasEstimationTypes.sol create mode 100644 contracts/utils/Set.sol diff --git a/contracts/crosschain/GenericGatewayAxelar.sol b/contracts/crosschain/GenericGatewayAxelar.sol new file mode 100644 index 00000000..4de8c0dd --- /dev/null +++ b/contracts/crosschain/GenericGatewayAxelar.sol @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {IGenericGateway} from "./IGenericGateway.sol"; +import {Set} from "../utils/Set.sol"; + +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {Address} from "@openzeppelin/contracts/utils/Address.sol"; +import {Math} from "@openzeppelin/contracts@master/utils/math/Math.sol"; + +import {IAxelarGateway} from "./vendor/axelar/interfaces/IAxelarGateway.sol"; +import {IAxelarGasService} from "./vendor/axelar/interfaces/IAxelarGasService.sol"; + +contract GenericGatewayAxelar is IGenericGateway, Ownable { + using Set for Set.Bytes32Set; + + event RequestCreated(bytes32 id, Request req); + event RequestForwarded(bytes32 id); + event RequestExecuted(bytes32 id); + + IAxelarGateway public immutable gateway; + IAxelarGasService public immutable gasService; + Set.Bytes32Set private _outBox; + + struct ChainDetails { string name; string remote; } + mapping(uint256 chainId => ChainDetails chainName) public chainDetails; + + constructor(IAxelarGateway _gateway, address _initialOwner) Ownable(_initialOwner) { + gateway = _gateway; + } + + function registerForeignChainDetails(uint256 chainId, ChainDetails memory details) public onlyOwner() { + require(chainId != block.chainid); + require(bytes(chainDetails[chainId].name).length == 0); + chainDetails[chainId] = details; + } + + // =============================================== cost estimation =============================================== + + function defaultCost(Message memory /*message*/) public pure returns (address, uint256) { + return (address(0), 0); + } + + function estimateCost(Message memory /*message*/, address asset) public pure returns (uint256) { + return Math.ternary(asset == address(0), 0, type(uint256).max); + } + + // ================================================= 1 step mode ================================================= + + function sendRequest(uint256 chain, address target, bytes memory data, bytes32 salt) public payable returns (bytes32) { + Request memory req = _generateRequest(chain, target, msg.sender, 0, data, salt); + (address feeAsset, uint256 feeValue) = defaultCost(req.message); + return _sendRequest(req, feeAsset, feeValue); + } + + function sendRequest(uint256 chain, address target, bytes memory data, bytes32 salt, address feeAsset, uint256 feeValue) public payable returns (bytes32) { + Request memory req = _generateRequest(chain, target, msg.sender, 0, data, salt); + return _sendRequest(req, feeAsset, feeValue); + } + + function _sendRequest(Request memory req, address feeAsset, uint256 feeValue) internal returns (bytes32) { + // retrieve chain details for the destination chain + ChainDetails storage details = chainDetails[req.message.destination.chain]; + require(bytes(details.name).length > 0, "Remote chain not registered"); + + // rebuild request hash + bytes memory payload = abi.encode(req); + bytes32 id = keccak256(payload); + + // If value is provided, forward it to the gasService + require(feeAsset == address(0) && feeValue == msg.value); // Axelar only support ether + if (msg.value > 0) { + gasService.payNativeGasForContractCall{ value: msg.value }(address(this), details.name, details.remote, payload, msg.sender); + } + + // send cross-chain signal + gateway.callContract(details.name, details.remote, payload); + + // TODO: event + + return id; + } + + // ================================================= 2 step mode ================================================= + + function createRequest(uint256 chain, address target, bytes memory data, bytes32 salt) public payable returns (bytes32) { + require(msg.value == 0); // Axelar doesn't support value + + Request memory req = _generateRequest(chain, target, msg.sender, 0, data, salt); + return _createRequest(req); + } + + function _createRequest(Request memory req) internal returns (bytes32) { + // retrieve chain details for the destination chain + ChainDetails storage details = chainDetails[req.message.destination.chain]; + require(bytes(details.name).length > 0, "Remote chain not registered"); + + // compute the request hash + bytes memory payload = abi.encode(req); + bytes32 id = keccak256(payload); + + // register the request hash + require(_outBox.insert(id), "Ticket already scheduled"); + + // emit notice + emit RequestCreated(id, req); + + return id; + + } + + function forwardRequest(Request memory req) public payable { + (address feeAsset, uint256 feeValue) = defaultCost(req.message); + _forwardRequest(req, feeAsset, feeValue); + } + + function forwardRequest(Request memory req, address feeAsset, uint256 feeValue) public payable { + _forwardRequest(req, feeAsset, feeValue); + } + + function _forwardRequest(Request memory req, address feeAsset, uint256 feeValue) internal { + ChainDetails storage details = chainDetails[req.message.destination.chain]; + // Not needed, was verified during request creation + // require(bytes(details.name).length > 0, "Remote chain not registered"); + + // compute the request hash + bytes memory payload = abi.encode(req); + bytes32 id = keccak256(payload); + + // consume request hash + require(_outBox.remove(id), "Ticket not scheduled"); + + // If value is provided, forward it to the gasService + require(feeAsset == address(0) && feeValue == msg.value); + if (msg.value > 0) { + gasService.payNativeGasForContractCall{ value: msg.value }(address(this), details.name, details.remote, payload, msg.sender); + } + + // send cross-chain signal + gateway.callContract(details.name, details.remote, payload); + + // emit notice + emit RequestForwarded(id); + } + + // =========================================== receive end (specific) ============================================ + function executeRequest(Request memory req, bytes32 commandId) public payable { + // compute the request hash + bytes memory payload = abi.encode(req); + bytes32 id = keccak256(payload); + + // retrieve chain details for the source chain + ChainDetails storage details = chainDetails[req.source.chain]; + require(bytes(details.name).length > 0, "Remote chain not registered"); + + // validate operation (includes replay protection) + require(gateway.validateContractCall(commandId, details.name, details.remote, id)); + + // perform call + _executeRequest(req.message); + + // emit notice + emit RequestExecuted(id); + } + + // =================================================== helpers =================================================== + function _generateRequest( + uint256 chain, + address target, + address sender, + uint256 value, + bytes memory data, + bytes32 salt + ) internal view returns (Request memory) { + return Request({ + source: Account({ + chain: block.chainid, + instance: sender + }), + message: Message({ + destination: Account({ + chain: chain, + instance: target + }), + value: value, + data: data + }), + salt: salt + }); + } + + function _executeRequest(Message memory message) internal { + require(message.destination.chain == block.chainid); + (bool success, bytes memory returndata) = message.destination.instance.call{value: message.value}(message.data); + Address.verifyCallResult(success, returndata); + } +} diff --git a/contracts/crosschain/IGenericGateway.sol b/contracts/crosschain/IGenericGateway.sol new file mode 100644 index 00000000..471919a1 --- /dev/null +++ b/contracts/crosschain/IGenericGateway.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +interface IGenericGateway { + struct Account { + uint256 chain; + address instance; + } + + // TODO: (address,uint256)[] tokens? + // TODO: put value in tokens? + struct Message { + Account destination; + uint256 value; + bytes data; + } + + struct Request { + Account source; + Message message; + bytes32 salt; + } + + // =============================================== cost estimation =============================================== + + function defaultCost(Message memory message) external view returns (address, uint256); + + /// @dev Returns the (minimum) cost (in a given asset) of performing a cross-chain operation. If asset is not supported for payment, returns type(uint256).max + function estimateCost(Message memory message, address asset) external view returns (uint256); + + // ================================================= 1 step mode ================================================= + + /// @dev Perform a cross-chain call using the canonical payment method for this bridge. The provided value is + /// passed along the request, minus anything that would be part of the canonical payment method. + function sendRequest(uint256 chain, address target, bytes memory data, bytes32 salt) external payable returns (bytes32); + + /// @dev Perform a cross-chain call using the specified payment method. If feeAsset is 0, then feeValue will be + /// deduced from the provided value to cover costs. The rest of the value is passed along the request. + function sendRequest(uint256 chain, address target, bytes memory data, bytes32 salt, address feeAsset, uint256 feeValue) external payable returns (bytes32); + + // ================================================= 2 step mode ================================================= + + /// @dev Register a cross-chain call that will later be forwarded using {forwardRequest}. Any value passed here + /// will be escrowed. It will then be passed along the request be forwarding happens. + function createRequest(uint256 chain, address target, bytes memory data, bytes32 salt) external payable returns (bytes32); + + /// @dev Forwards a cross-chain request using the canonical payment method for this bridge. Any value provided + /// here will be used for the payment. It will not be forwarded with the cross-chain call. + function forwardRequest(Request memory req) external payable; + + /// @dev Forwards a cross-chain request using using the specified payment method. Any value provided here will be + /// used for the payment. It will not be forwarded with the cross-chain call. This means that value should only be + /// used with `feeAsset = address(0)` and with `feeValue = msg.value`. + function forwardRequest(Request memory req, address feeAsset, uint256 feeValue) external payable; +} diff --git a/contracts/crosschain/vendor/axelar/interfaces/IAxelarGasService.sol b/contracts/crosschain/vendor/axelar/interfaces/IAxelarGasService.sol new file mode 100644 index 00000000..78ea0f1d --- /dev/null +++ b/contracts/crosschain/vendor/axelar/interfaces/IAxelarGasService.sol @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import { GasInfo } from '../types/GasEstimationTypes.sol'; +import { IInterchainGasEstimation } from './IInterchainGasEstimation.sol'; +import { IUpgradable } from './IUpgradable.sol'; + +/** + * @title IAxelarGasService Interface + * @notice This is an interface for the AxelarGasService contract which manages gas payments + * and refunds for cross-chain communication on the Axelar network. + * @dev This interface inherits IUpgradable + */ +interface IAxelarGasService is IInterchainGasEstimation, IUpgradable { + error InvalidAddress(); + error NotCollector(); + error InvalidAmounts(); + error InvalidGasUpdates(); + error InvalidParams(); + error InsufficientGasPayment(uint256 required, uint256 provided); + + event GasPaidForContractCall( + address indexed sourceAddress, + string destinationChain, + string destinationAddress, + bytes32 indexed payloadHash, + address gasToken, + uint256 gasFeeAmount, + address refundAddress + ); + + event GasPaidForContractCallWithToken( + address indexed sourceAddress, + string destinationChain, + string destinationAddress, + bytes32 indexed payloadHash, + string symbol, + uint256 amount, + address gasToken, + uint256 gasFeeAmount, + address refundAddress + ); + + event NativeGasPaidForContractCall( + address indexed sourceAddress, + string destinationChain, + string destinationAddress, + bytes32 indexed payloadHash, + uint256 gasFeeAmount, + address refundAddress + ); + + event NativeGasPaidForContractCallWithToken( + address indexed sourceAddress, + string destinationChain, + string destinationAddress, + bytes32 indexed payloadHash, + string symbol, + uint256 amount, + uint256 gasFeeAmount, + address refundAddress + ); + + event GasPaidForExpressCall( + address indexed sourceAddress, + string destinationChain, + string destinationAddress, + bytes32 indexed payloadHash, + address gasToken, + uint256 gasFeeAmount, + address refundAddress + ); + + event GasPaidForExpressCallWithToken( + address indexed sourceAddress, + string destinationChain, + string destinationAddress, + bytes32 indexed payloadHash, + string symbol, + uint256 amount, + address gasToken, + uint256 gasFeeAmount, + address refundAddress + ); + + event NativeGasPaidForExpressCall( + address indexed sourceAddress, + string destinationChain, + string destinationAddress, + bytes32 indexed payloadHash, + uint256 gasFeeAmount, + address refundAddress + ); + + event NativeGasPaidForExpressCallWithToken( + address indexed sourceAddress, + string destinationChain, + string destinationAddress, + bytes32 indexed payloadHash, + string symbol, + uint256 amount, + uint256 gasFeeAmount, + address refundAddress + ); + + event GasAdded( + bytes32 indexed txHash, + uint256 indexed logIndex, + address gasToken, + uint256 gasFeeAmount, + address refundAddress + ); + + event NativeGasAdded(bytes32 indexed txHash, uint256 indexed logIndex, uint256 gasFeeAmount, address refundAddress); + + event ExpressGasAdded( + bytes32 indexed txHash, + uint256 indexed logIndex, + address gasToken, + uint256 gasFeeAmount, + address refundAddress + ); + + event NativeExpressGasAdded( + bytes32 indexed txHash, + uint256 indexed logIndex, + uint256 gasFeeAmount, + address refundAddress + ); + + event Refunded( + bytes32 indexed txHash, + uint256 indexed logIndex, + address payable receiver, + address token, + uint256 amount + ); + + /** + * @notice Pay for gas for any type of contract execution on a destination chain. + * @dev This function is called on the source chain before calling the gateway to execute a remote contract. + * @dev If estimateOnChain is true, the function will estimate the gas cost and revert if the payment is insufficient. + * @param sender The address making the payment + * @param destinationChain The target chain where the contract call will be made + * @param destinationAddress The target address on the destination chain + * @param payload Data payload for the contract call + * @param executionGasLimit The gas limit for the contract call + * @param estimateOnChain Flag to enable on-chain gas estimation + * @param refundAddress The address where refunds, if any, should be sent + * @param params Additional parameters for gas payment. This can be left empty for normal contract call payments. + */ + function payGas( + address sender, + string calldata destinationChain, + string calldata destinationAddress, + bytes calldata payload, + uint256 executionGasLimit, + bool estimateOnChain, + address refundAddress, + bytes calldata params + ) external payable; + + /** + * @notice Pay for gas using ERC20 tokens for a contract call on a destination chain. + * @dev This function is called on the source chain before calling the gateway to execute a remote contract. + * @param sender The address making the payment + * @param destinationChain The target chain where the contract call will be made + * @param destinationAddress The target address on the destination chain + * @param payload Data payload for the contract call + * @param gasToken The address of the ERC20 token used to pay for gas + * @param gasFeeAmount The amount of tokens to pay for gas + * @param refundAddress The address where refunds, if any, should be sent + */ + function payGasForContractCall( + address sender, + string calldata destinationChain, + string calldata destinationAddress, + bytes calldata payload, + address gasToken, + uint256 gasFeeAmount, + address refundAddress + ) external; + + /** + * @notice Pay for gas using ERC20 tokens for a contract call with tokens on a destination chain. + * @dev This function is called on the source chain before calling the gateway to execute a remote contract. + * @param sender The address making the payment + * @param destinationChain The target chain where the contract call with tokens will be made + * @param destinationAddress The target address on the destination chain + * @param payload Data payload for the contract call with tokens + * @param symbol The symbol of the token to be sent with the call + * @param amount The amount of tokens to be sent with the call + * @param gasToken The address of the ERC20 token used to pay for gas + * @param gasFeeAmount The amount of tokens to pay for gas + * @param refundAddress The address where refunds, if any, should be sent + */ + function payGasForContractCallWithToken( + address sender, + string calldata destinationChain, + string calldata destinationAddress, + bytes calldata payload, + string calldata symbol, + uint256 amount, + address gasToken, + uint256 gasFeeAmount, + address refundAddress + ) external; + + /** + * @notice Pay for gas using native currency for a contract call on a destination chain. + * @dev This function is called on the source chain before calling the gateway to execute a remote contract. + * @param sender The address making the payment + * @param destinationChain The target chain where the contract call will be made + * @param destinationAddress The target address on the destination chain + * @param payload Data payload for the contract call + * @param refundAddress The address where refunds, if any, should be sent + */ + function payNativeGasForContractCall( + address sender, + string calldata destinationChain, + string calldata destinationAddress, + bytes calldata payload, + address refundAddress + ) external payable; + + /** + * @notice Pay for gas using native currency for a contract call with tokens on a destination chain. + * @dev This function is called on the source chain before calling the gateway to execute a remote contract. + * @param sender The address making the payment + * @param destinationChain The target chain where the contract call with tokens will be made + * @param destinationAddress The target address on the destination chain + * @param payload Data payload for the contract call with tokens + * @param symbol The symbol of the token to be sent with the call + * @param amount The amount of tokens to be sent with the call + * @param refundAddress The address where refunds, if any, should be sent + */ + function payNativeGasForContractCallWithToken( + address sender, + string calldata destinationChain, + string calldata destinationAddress, + bytes calldata payload, + string calldata symbol, + uint256 amount, + address refundAddress + ) external payable; + + /** + * @notice Pay for gas using ERC20 tokens for an express contract call on a destination chain. + * @dev This function is called on the source chain before calling the gateway to express execute a remote contract. + * @param sender The address making the payment + * @param destinationChain The target chain where the contract call will be made + * @param destinationAddress The target address on the destination chain + * @param payload Data payload for the contract call + * @param gasToken The address of the ERC20 token used to pay for gas + * @param gasFeeAmount The amount of tokens to pay for gas + * @param refundAddress The address where refunds, if any, should be sent + */ + function payGasForExpressCall( + address sender, + string calldata destinationChain, + string calldata destinationAddress, + bytes calldata payload, + address gasToken, + uint256 gasFeeAmount, + address refundAddress + ) external; + + /** + * @notice Pay for gas using ERC20 tokens for an express contract call with tokens on a destination chain. + * @dev This function is called on the source chain before calling the gateway to express execute a remote contract. + * @param sender The address making the payment + * @param destinationChain The target chain where the contract call with tokens will be made + * @param destinationAddress The target address on the destination chain + * @param payload Data payload for the contract call with tokens + * @param symbol The symbol of the token to be sent with the call + * @param amount The amount of tokens to be sent with the call + * @param gasToken The address of the ERC20 token used to pay for gas + * @param gasFeeAmount The amount of tokens to pay for gas + * @param refundAddress The address where refunds, if any, should be sent + */ + function payGasForExpressCallWithToken( + address sender, + string calldata destinationChain, + string calldata destinationAddress, + bytes calldata payload, + string calldata symbol, + uint256 amount, + address gasToken, + uint256 gasFeeAmount, + address refundAddress + ) external; + + /** + * @notice Pay for gas using native currency for an express contract call on a destination chain. + * @dev This function is called on the source chain before calling the gateway to execute a remote contract. + * @param sender The address making the payment + * @param destinationChain The target chain where the contract call will be made + * @param destinationAddress The target address on the destination chain + * @param payload Data payload for the contract call + * @param refundAddress The address where refunds, if any, should be sent + */ + function payNativeGasForExpressCall( + address sender, + string calldata destinationChain, + string calldata destinationAddress, + bytes calldata payload, + address refundAddress + ) external payable; + + /** + * @notice Pay for gas using native currency for an express contract call with tokens on a destination chain. + * @dev This function is called on the source chain before calling the gateway to execute a remote contract. + * @param sender The address making the payment + * @param destinationChain The target chain where the contract call with tokens will be made + * @param destinationAddress The target address on the destination chain + * @param payload Data payload for the contract call with tokens + * @param symbol The symbol of the token to be sent with the call + * @param amount The amount of tokens to be sent with the call + * @param refundAddress The address where refunds, if any, should be sent + */ + function payNativeGasForExpressCallWithToken( + address sender, + string calldata destinationChain, + string calldata destinationAddress, + bytes calldata payload, + string calldata symbol, + uint256 amount, + address refundAddress + ) external payable; + + /** + * @notice Add additional gas payment using ERC20 tokens after initiating a cross-chain call. + * @dev This function can be called on the source chain after calling the gateway to execute a remote contract. + * @param txHash The transaction hash of the cross-chain call + * @param logIndex The log index for the cross-chain call + * @param gasToken The ERC20 token address used to add gas + * @param gasFeeAmount The amount of tokens to add as gas + * @param refundAddress The address where refunds, if any, should be sent + */ + function addGas( + bytes32 txHash, + uint256 logIndex, + address gasToken, + uint256 gasFeeAmount, + address refundAddress + ) external; + + /** + * @notice Add additional gas payment using native currency after initiating a cross-chain call. + * @dev This function can be called on the source chain after calling the gateway to execute a remote contract. + * @param txHash The transaction hash of the cross-chain call + * @param logIndex The log index for the cross-chain call + * @param refundAddress The address where refunds, if any, should be sent + */ + function addNativeGas( + bytes32 txHash, + uint256 logIndex, + address refundAddress + ) external payable; + + /** + * @notice Add additional gas payment using ERC20 tokens after initiating an express cross-chain call. + * @dev This function can be called on the source chain after calling the gateway to express execute a remote contract. + * @param txHash The transaction hash of the cross-chain call + * @param logIndex The log index for the cross-chain call + * @param gasToken The ERC20 token address used to add gas + * @param gasFeeAmount The amount of tokens to add as gas + * @param refundAddress The address where refunds, if any, should be sent + */ + function addExpressGas( + bytes32 txHash, + uint256 logIndex, + address gasToken, + uint256 gasFeeAmount, + address refundAddress + ) external; + + /** + * @notice Add additional gas payment using native currency after initiating an express cross-chain call. + * @dev This function can be called on the source chain after calling the gateway to express execute a remote contract. + * @param txHash The transaction hash of the cross-chain call + * @param logIndex The log index for the cross-chain call + * @param refundAddress The address where refunds, if any, should be sent + */ + function addNativeExpressGas( + bytes32 txHash, + uint256 logIndex, + address refundAddress + ) external payable; + + /** + * @notice Updates the gas price for a specific chain. + * @dev This function is called by the gas oracle to update the gas prices for a specific chains. + * @param chains Array of chain names + * @param gasUpdates Array of gas updates + */ + function updateGasInfo(string[] calldata chains, GasInfo[] calldata gasUpdates) external; + + /** + * @notice Allows the gasCollector to collect accumulated fees from the contract. + * @dev Use address(0) as the token address for native currency. + * @param receiver The address to receive the collected fees + * @param tokens Array of token addresses to be collected + * @param amounts Array of amounts to be collected for each respective token address + */ + function collectFees( + address payable receiver, + address[] calldata tokens, + uint256[] calldata amounts + ) external; + + /** + * @notice Refunds gas payment to the receiver in relation to a specific cross-chain transaction. + * @dev Only callable by the gasCollector. + * @dev Use address(0) as the token address to refund native currency. + * @param txHash The transaction hash of the cross-chain call + * @param logIndex The log index for the cross-chain call + * @param receiver The address to receive the refund + * @param token The token address to be refunded + * @param amount The amount to refund + */ + function refund( + bytes32 txHash, + uint256 logIndex, + address payable receiver, + address token, + uint256 amount + ) external; + + /** + * @notice Returns the address of the designated gas collector. + * @return address of the gas collector + */ + function gasCollector() external returns (address); +} diff --git a/contracts/crosschain/vendor/axelar/interfaces/IAxelarGateway.sol b/contracts/crosschain/vendor/axelar/interfaces/IAxelarGateway.sol new file mode 100644 index 00000000..92f891cc --- /dev/null +++ b/contracts/crosschain/vendor/axelar/interfaces/IAxelarGateway.sol @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import { IGovernable } from './IGovernable.sol'; +import { IImplementation } from './IImplementation.sol'; + +interface IAxelarGateway is IImplementation, IGovernable { + /**********\ + |* Errors *| + \**********/ + + error NotSelf(); + error InvalidCodeHash(); + error SetupFailed(); + error InvalidAuthModule(); + error InvalidTokenDeployer(); + error InvalidAmount(); + error InvalidChainId(); + error InvalidCommands(); + error TokenDoesNotExist(string symbol); + error TokenAlreadyExists(string symbol); + error TokenDeployFailed(string symbol); + error TokenContractDoesNotExist(address token); + error BurnFailed(string symbol); + error MintFailed(string symbol); + error InvalidSetMintLimitsParams(); + error ExceedMintLimit(string symbol); + + /**********\ + |* Events *| + \**********/ + + event TokenSent( + address indexed sender, + string destinationChain, + string destinationAddress, + string symbol, + uint256 amount + ); + + event ContractCall( + address indexed sender, + string destinationChain, + string destinationContractAddress, + bytes32 indexed payloadHash, + bytes payload + ); + + event ContractCallWithToken( + address indexed sender, + string destinationChain, + string destinationContractAddress, + bytes32 indexed payloadHash, + bytes payload, + string symbol, + uint256 amount + ); + + event Executed(bytes32 indexed commandId); + + event TokenDeployed(string symbol, address tokenAddresses); + + event ContractCallApproved( + bytes32 indexed commandId, + string sourceChain, + string sourceAddress, + address indexed contractAddress, + bytes32 indexed payloadHash, + bytes32 sourceTxHash, + uint256 sourceEventIndex + ); + + event ContractCallApprovedWithMint( + bytes32 indexed commandId, + string sourceChain, + string sourceAddress, + address indexed contractAddress, + bytes32 indexed payloadHash, + string symbol, + uint256 amount, + bytes32 sourceTxHash, + uint256 sourceEventIndex + ); + + event ContractCallExecuted(bytes32 indexed commandId); + + event TokenMintLimitUpdated(string symbol, uint256 limit); + + event OperatorshipTransferred(bytes newOperatorsData); + + event Upgraded(address indexed implementation); + + /********************\ + |* Public Functions *| + \********************/ + + function sendToken( + string calldata destinationChain, + string calldata destinationAddress, + string calldata symbol, + uint256 amount + ) external; + + function callContract( + string calldata destinationChain, + string calldata contractAddress, + bytes calldata payload + ) external; + + function callContractWithToken( + string calldata destinationChain, + string calldata contractAddress, + bytes calldata payload, + string calldata symbol, + uint256 amount + ) external; + + function isContractCallApproved( + bytes32 commandId, + string calldata sourceChain, + string calldata sourceAddress, + address contractAddress, + bytes32 payloadHash + ) external view returns (bool); + + function isContractCallAndMintApproved( + bytes32 commandId, + string calldata sourceChain, + string calldata sourceAddress, + address contractAddress, + bytes32 payloadHash, + string calldata symbol, + uint256 amount + ) external view returns (bool); + + function validateContractCall( + bytes32 commandId, + string calldata sourceChain, + string calldata sourceAddress, + bytes32 payloadHash + ) external returns (bool); + + function validateContractCallAndMint( + bytes32 commandId, + string calldata sourceChain, + string calldata sourceAddress, + bytes32 payloadHash, + string calldata symbol, + uint256 amount + ) external returns (bool); + + /***********\ + |* Getters *| + \***********/ + + function authModule() external view returns (address); + + function tokenDeployer() external view returns (address); + + function tokenMintLimit(string memory symbol) external view returns (uint256); + + function tokenMintAmount(string memory symbol) external view returns (uint256); + + function allTokensFrozen() external view returns (bool); + + function implementation() external view returns (address); + + function tokenAddresses(string memory symbol) external view returns (address); + + function tokenFrozen(string memory symbol) external view returns (bool); + + function isCommandExecuted(bytes32 commandId) external view returns (bool); + + /************************\ + |* Governance Functions *| + \************************/ + + function setTokenMintLimits(string[] calldata symbols, uint256[] calldata limits) external; + + function upgrade( + address newImplementation, + bytes32 newImplementationCodeHash, + bytes calldata setupParams + ) external; + + /**********************\ + |* External Functions *| + \**********************/ + + function execute(bytes calldata input) external; +} \ No newline at end of file diff --git a/contracts/crosschain/vendor/axelar/interfaces/IContractIdentifier.sol b/contracts/crosschain/vendor/axelar/interfaces/IContractIdentifier.sol new file mode 100644 index 00000000..bf32f96c --- /dev/null +++ b/contracts/crosschain/vendor/axelar/interfaces/IContractIdentifier.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +// General interface for upgradable contracts +interface IContractIdentifier { + /** + * @notice Returns the contract ID. It can be used as a check during upgrades. + * @dev Meant to be overridden in derived contracts. + * @return bytes32 The contract ID + */ + function contractId() external pure returns (bytes32); +} diff --git a/contracts/crosschain/vendor/axelar/interfaces/IGovernable.sol b/contracts/crosschain/vendor/axelar/interfaces/IGovernable.sol new file mode 100644 index 00000000..c08f4afc --- /dev/null +++ b/contracts/crosschain/vendor/axelar/interfaces/IGovernable.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +/** + * @title IGovernable Interface + * @notice This is an interface used by the AxelarGateway contract to manage governance and mint limiter roles. + */ +interface IGovernable { + error NotGovernance(); + error NotMintLimiter(); + error InvalidGovernance(); + error InvalidMintLimiter(); + + event GovernanceTransferred(address indexed previousGovernance, address indexed newGovernance); + event MintLimiterTransferred(address indexed previousGovernance, address indexed newGovernance); + + /** + * @notice Returns the governance address. + * @return address of the governance + */ + function governance() external view returns (address); + + /** + * @notice Returns the mint limiter address. + * @return address of the mint limiter + */ + function mintLimiter() external view returns (address); + + /** + * @notice Transfer the governance role to another address. + * @param newGovernance The new governance address + */ + function transferGovernance(address newGovernance) external; + + /** + * @notice Transfer the mint limiter role to another address. + * @param newGovernance The new mint limiter address + */ + function transferMintLimiter(address newGovernance) external; +} diff --git a/contracts/crosschain/vendor/axelar/interfaces/IImplementation.sol b/contracts/crosschain/vendor/axelar/interfaces/IImplementation.sol new file mode 100644 index 00000000..037b3ce8 --- /dev/null +++ b/contracts/crosschain/vendor/axelar/interfaces/IImplementation.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import { IContractIdentifier } from './IContractIdentifier.sol'; + +interface IImplementation is IContractIdentifier { + error NotProxy(); + + function setup(bytes calldata data) external; +} diff --git a/contracts/crosschain/vendor/axelar/interfaces/IInterchainGasEstimation.sol b/contracts/crosschain/vendor/axelar/interfaces/IInterchainGasEstimation.sol new file mode 100644 index 00000000..cab0ba6b --- /dev/null +++ b/contracts/crosschain/vendor/axelar/interfaces/IInterchainGasEstimation.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import { GasEstimationType, GasInfo } from '../types/GasEstimationTypes.sol'; + +/** + * @title IInterchainGasEstimation Interface + * @notice This is an interface for the InterchainGasEstimation contract + * which allows for estimating gas fees for cross-chain communication on the Axelar network. + */ +interface IInterchainGasEstimation { + error UnsupportedEstimationType(GasEstimationType gasEstimationType); + + /** + * @notice Event emitted when the gas price for a specific chain is updated. + * @param chain The name of the chain + * @param info The gas info for the chain + */ + event GasInfoUpdated(string chain, GasInfo info); + + /** + * @notice Returns the gas price for a specific chain. + * @param chain The name of the chain + * @return gasInfo The gas info for the chain + */ + function getGasInfo(string calldata chain) external view returns (GasInfo memory); + + /** + * @notice Estimates the gas fee for a cross-chain contract call. + * @param destinationChain Axelar registered name of the destination chain + * @param destinationAddress Destination contract address being called + * @param executionGasLimit The gas limit to be used for the destination contract execution, + * e.g. pass in 200k if your app consumes needs upto 200k for this contract call + * @param params Additional parameters for the gas estimation + * @return gasEstimate The cross-chain gas estimate, in terms of source chain's native gas token that should be forwarded to the gas service. + */ + function estimateGasFee( + string calldata destinationChain, + string calldata destinationAddress, + bytes calldata payload, + uint256 executionGasLimit, + bytes calldata params + ) external view returns (uint256 gasEstimate); +} diff --git a/contracts/crosschain/vendor/axelar/interfaces/IOwnable.sol b/contracts/crosschain/vendor/axelar/interfaces/IOwnable.sol new file mode 100644 index 00000000..725bcb36 --- /dev/null +++ b/contracts/crosschain/vendor/axelar/interfaces/IOwnable.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +/** + * @title IOwnable Interface + * @notice IOwnable is an interface that abstracts the implementation of a + * contract with ownership control features. It's commonly used in upgradable + * contracts and includes the functionality to get current owner, transfer + * ownership, and propose and accept ownership. + */ +interface IOwnable { + error NotOwner(); + error InvalidOwner(); + error InvalidOwnerAddress(); + + event OwnershipTransferStarted(address indexed newOwner); + event OwnershipTransferred(address indexed newOwner); + + /** + * @notice Returns the current owner of the contract. + * @return address The address of the current owner + */ + function owner() external view returns (address); + + /** + * @notice Returns the address of the pending owner of the contract. + * @return address The address of the pending owner + */ + function pendingOwner() external view returns (address); + + /** + * @notice Transfers ownership of the contract to a new address + * @param newOwner The address to transfer ownership to + */ + function transferOwnership(address newOwner) external; + + /** + * @notice Proposes to transfer the contract's ownership to a new address. + * The new owner needs to accept the ownership explicitly. + * @param newOwner The address to transfer ownership to + */ + function proposeOwnership(address newOwner) external; + + /** + * @notice Transfers ownership to the pending owner. + * @dev Can only be called by the pending owner + */ + function acceptOwnership() external; +} diff --git a/contracts/crosschain/vendor/axelar/interfaces/IUpgradable.sol b/contracts/crosschain/vendor/axelar/interfaces/IUpgradable.sol new file mode 100644 index 00000000..0ef082fb --- /dev/null +++ b/contracts/crosschain/vendor/axelar/interfaces/IUpgradable.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import { IOwnable } from './IOwnable.sol'; +import { IImplementation } from './IImplementation.sol'; + +// General interface for upgradable contracts +interface IUpgradable is IOwnable, IImplementation { + error InvalidCodeHash(); + error InvalidImplementation(); + error SetupFailed(); + + event Upgraded(address indexed newImplementation); + + function implementation() external view returns (address); + + function upgrade( + address newImplementation, + bytes32 newImplementationCodeHash, + bytes calldata params + ) external; +} diff --git a/contracts/crosschain/vendor/axelar/types/GasEstimationTypes.sol b/contracts/crosschain/vendor/axelar/types/GasEstimationTypes.sol new file mode 100644 index 00000000..7a3aa14d --- /dev/null +++ b/contracts/crosschain/vendor/axelar/types/GasEstimationTypes.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +/** + * @title GasEstimationType + * @notice This enum represents the gas estimation types for different chains. + */ +enum GasEstimationType { + Default, + OptimismEcotone, + OptimismBedrock, + Arbitrum, + Scroll +} + +/** + * @title GasInfo + * @notice This struct represents the gas pricing information for a specific chain. + * @dev Smaller uint types are used for efficient struct packing to save storage costs. + */ +struct GasInfo { + /// @dev Custom gas pricing rule, such as L1 data fee on L2s + uint64 gasEstimationType; + /// @dev Scalar value needed for specific gas estimation types, expected to be less than 1e10 + uint64 l1FeeScalar; + /// @dev Axelar base fee for cross-chain message approval on destination, in terms of source native gas token + uint128 axelarBaseFee; + /// @dev Gas price of destination chain, in terms of the source chain token, i.e dest_gas_price * dest_token_market_price / src_token_market_price + uint128 relativeGasPrice; + /// @dev Needed for specific gas estimation types. Blob base fee of destination chain, in terms of the source chain token, i.e dest_blob_base_fee * dest_token_market_price / src_token_market_price + uint128 relativeBlobBaseFee; + /// @dev Axelar express fee for express execution, in terms of source chain token + uint128 expressFee; +} diff --git a/contracts/utils/Set.sol b/contracts/utils/Set.sol new file mode 100644 index 00000000..5ad58f30 --- /dev/null +++ b/contracts/utils/Set.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +/// @dev non-iterable variant of OpenZeppelin's EnumerableSet library. +library Set { + struct Bytes32Set { + mapping(bytes32 value => bool) _data; + } + + function insert(Bytes32Set storage self, bytes32 value) internal returns (bool) { + if (!self._data[value]) { + self._data[value] = true; + return true; + } else { + return false; + } + } + + function remove(Bytes32Set storage self, bytes32 value) internal returns (bool) { + if (self._data[value]) { + self._data[value] = false; + return true; + } else { + return false; + } + } + + function contains(Bytes32Set storage self, bytes32 value) internal view returns (bool) { + return self._data[value]; + } +} \ No newline at end of file From 50ced4acdd465f7e952ad12e9b341a8dc50d5112 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Fri, 21 Jun 2024 15:45:42 +0200 Subject: [PATCH 02/41] split common <> axelar --- contracts/crosschain/GenericGatewayAxelar.sol | 187 ++++-------------- contracts/crosschain/GenericGatewayCommon.sol | 139 +++++++++++++ contracts/crosschain/IGenericGateway.sol | 25 ++- .../axelar/interfaces/IAxelarGasService.sol | 32 +-- .../axelar/interfaces/IAxelarGateway.sol | 12 +- .../axelar/interfaces/IImplementation.sol | 2 +- .../interfaces/IInterchainGasEstimation.sol | 2 +- .../vendor/axelar/interfaces/IUpgradable.sol | 10 +- contracts/utils/Set.sol | 2 +- 9 files changed, 215 insertions(+), 196 deletions(-) create mode 100644 contracts/crosschain/GenericGatewayCommon.sol diff --git a/contracts/crosschain/GenericGatewayAxelar.sol b/contracts/crosschain/GenericGatewayAxelar.sol index 4de8c0dd..ed7d7ac7 100644 --- a/contracts/crosschain/GenericGatewayAxelar.sol +++ b/contracts/crosschain/GenericGatewayAxelar.sol @@ -2,197 +2,84 @@ pragma solidity ^0.8.0; -import {IGenericGateway} from "./IGenericGateway.sol"; -import {Set} from "../utils/Set.sol"; - -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -import {Address} from "@openzeppelin/contracts/utils/Address.sol"; -import {Math} from "@openzeppelin/contracts@master/utils/math/Math.sol"; - import {IAxelarGateway} from "./vendor/axelar/interfaces/IAxelarGateway.sol"; import {IAxelarGasService} from "./vendor/axelar/interfaces/IAxelarGasService.sol"; -contract GenericGatewayAxelar is IGenericGateway, Ownable { - using Set for Set.Bytes32Set; +import {GenericGatewayCommon} from "./GenericGatewayCommon.sol"; - event RequestCreated(bytes32 id, Request req); - event RequestForwarded(bytes32 id); - event RequestExecuted(bytes32 id); +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {Math} from "@openzeppelin/contracts@master/utils/math/Math.sol"; +contract GenericGatewayAxelar is GenericGatewayCommon, Ownable { IAxelarGateway public immutable gateway; IAxelarGasService public immutable gasService; - Set.Bytes32Set private _outBox; - struct ChainDetails { string name; string remote; } + struct ChainDetails { + string name; + string remote; + } mapping(uint256 chainId => ChainDetails chainName) public chainDetails; constructor(IAxelarGateway _gateway, address _initialOwner) Ownable(_initialOwner) { gateway = _gateway; } - function registerForeignChainDetails(uint256 chainId, ChainDetails memory details) public onlyOwner() { + function registerForeignChainDetails(uint256 chainId, ChainDetails memory details) public onlyOwner { require(chainId != block.chainid); require(bytes(chainDetails[chainId].name).length == 0); chainDetails[chainId] = details; } - // =============================================== cost estimation =============================================== - - function defaultCost(Message memory /*message*/) public pure returns (address, uint256) { + function defaultCost(Message memory /*message*/) public pure virtual override returns (address, uint256) { return (address(0), 0); } - function estimateCost(Message memory /*message*/, address asset) public pure returns (uint256) { + function estimateCost(Message memory /*message*/, address asset) public pure virtual returns (uint256) { return Math.ternary(asset == address(0), 0, type(uint256).max); } - // ================================================= 1 step mode ================================================= - - function sendRequest(uint256 chain, address target, bytes memory data, bytes32 salt) public payable returns (bytes32) { - Request memory req = _generateRequest(chain, target, msg.sender, 0, data, salt); - (address feeAsset, uint256 feeValue) = defaultCost(req.message); - return _sendRequest(req, feeAsset, feeValue); - } - - function sendRequest(uint256 chain, address target, bytes memory data, bytes32 salt, address feeAsset, uint256 feeValue) public payable returns (bytes32) { - Request memory req = _generateRequest(chain, target, msg.sender, 0, data, salt); - return _sendRequest(req, feeAsset, feeValue); - } + /// @dev Override that the target blockchain is registered and that 0 value is passed when creating a request. + function createRequest( + uint256 chain, + address target, + bytes memory data, + bytes32 salt + ) public payable virtual override returns (bytes32) { + require(msg.value == 0, "Axelar does not support native currency bridging"); - function _sendRequest(Request memory req, address feeAsset, uint256 feeValue) internal returns (bytes32) { // retrieve chain details for the destination chain - ChainDetails storage details = chainDetails[req.message.destination.chain]; + ChainDetails storage details = chainDetails[chain]; require(bytes(details.name).length > 0, "Remote chain not registered"); - // rebuild request hash - bytes memory payload = abi.encode(req); - bytes32 id = keccak256(payload); - - // If value is provided, forward it to the gasService - require(feeAsset == address(0) && feeValue == msg.value); // Axelar only support ether - if (msg.value > 0) { - gasService.payNativeGasForContractCall{ value: msg.value }(address(this), details.name, details.remote, payload, msg.sender); - } - - // send cross-chain signal - gateway.callContract(details.name, details.remote, payload); - - // TODO: event - - return id; + return super.createRequest(chain, target, data, salt); } - // ================================================= 2 step mode ================================================= + function _processRequest( + bytes32 /*id*/, + Request memory req, + address feeAsset, + uint256 feeValue + ) internal virtual override { + require(feeAsset == address(0), "Axelar only supports fees in native currency"); + require(req.message.value == 0, "Axelar does not support native currency bridging"); - function createRequest(uint256 chain, address target, bytes memory data, bytes32 salt) public payable returns (bytes32) { - require(msg.value == 0); // Axelar doesn't support value - - Request memory req = _generateRequest(chain, target, msg.sender, 0, data, salt); - return _createRequest(req); - } - - function _createRequest(Request memory req) internal returns (bytes32) { - // retrieve chain details for the destination chain ChainDetails storage details = chainDetails[req.message.destination.chain]; require(bytes(details.name).length > 0, "Remote chain not registered"); - // compute the request hash bytes memory payload = abi.encode(req); - bytes32 id = keccak256(payload); - - // register the request hash - require(_outBox.insert(id), "Ticket already scheduled"); - - // emit notice - emit RequestCreated(id, req); - - return id; - - } - - function forwardRequest(Request memory req) public payable { - (address feeAsset, uint256 feeValue) = defaultCost(req.message); - _forwardRequest(req, feeAsset, feeValue); - } - - function forwardRequest(Request memory req, address feeAsset, uint256 feeValue) public payable { - _forwardRequest(req, feeAsset, feeValue); - } - - function _forwardRequest(Request memory req, address feeAsset, uint256 feeValue) internal { - ChainDetails storage details = chainDetails[req.message.destination.chain]; - // Not needed, was verified during request creation - // require(bytes(details.name).length > 0, "Remote chain not registered"); - - // compute the request hash - bytes memory payload = abi.encode(req); - bytes32 id = keccak256(payload); - - // consume request hash - require(_outBox.remove(id), "Ticket not scheduled"); // If value is provided, forward it to the gasService - require(feeAsset == address(0) && feeValue == msg.value); - if (msg.value > 0) { - gasService.payNativeGasForContractCall{ value: msg.value }(address(this), details.name, details.remote, payload, msg.sender); + if (feeValue > 0) { + gasService.payNativeGasForContractCall{value: feeValue}( + address(this), + details.name, + details.remote, + payload, + msg.sender + ); } // send cross-chain signal gateway.callContract(details.name, details.remote, payload); - - // emit notice - emit RequestForwarded(id); - } - - // =========================================== receive end (specific) ============================================ - function executeRequest(Request memory req, bytes32 commandId) public payable { - // compute the request hash - bytes memory payload = abi.encode(req); - bytes32 id = keccak256(payload); - - // retrieve chain details for the source chain - ChainDetails storage details = chainDetails[req.source.chain]; - require(bytes(details.name).length > 0, "Remote chain not registered"); - - // validate operation (includes replay protection) - require(gateway.validateContractCall(commandId, details.name, details.remote, id)); - - // perform call - _executeRequest(req.message); - - // emit notice - emit RequestExecuted(id); - } - - // =================================================== helpers =================================================== - function _generateRequest( - uint256 chain, - address target, - address sender, - uint256 value, - bytes memory data, - bytes32 salt - ) internal view returns (Request memory) { - return Request({ - source: Account({ - chain: block.chainid, - instance: sender - }), - message: Message({ - destination: Account({ - chain: chain, - instance: target - }), - value: value, - data: data - }), - salt: salt - }); - } - - function _executeRequest(Message memory message) internal { - require(message.destination.chain == block.chainid); - (bool success, bytes memory returndata) = message.destination.instance.call{value: message.value}(message.data); - Address.verifyCallResult(success, returndata); } } diff --git a/contracts/crosschain/GenericGatewayCommon.sol b/contracts/crosschain/GenericGatewayCommon.sol new file mode 100644 index 00000000..e1a5b312 --- /dev/null +++ b/contracts/crosschain/GenericGatewayCommon.sol @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {IGenericGateway} from "./IGenericGateway.sol"; +import {Set} from "../utils/Set.sol"; + +import {Address} from "@openzeppelin/contracts/utils/Address.sol"; +import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +abstract contract GenericGatewayCommon is IGenericGateway { + using Set for Set.Bytes32Set; + + event RequestCreated(bytes32 id, Request req); + event RequestForwarded(bytes32 id); + event RequestExecuted(bytes32 id); + + Set.Bytes32Set private _outBox; + + // This must be redeclared as public so that other function can call it + function defaultCost(Message memory /*message*/) public pure virtual returns (address, uint256); + + function sendRequest( + uint256 chain, + address target, + uint256 value, + bytes memory data, + bytes32 salt + ) public payable virtual returns (bytes32) { + (address feeAsset, uint256 feeValue) = defaultCost( + Message({destination: Account({chain: chain, instance: target}), value: value, data: data}) + ); + return sendRequest(chain, target, value, data, salt, feeAsset, feeValue); + } + + function sendRequest( + uint256 chain, + address target, + uint256 value, + bytes memory data, + bytes32 salt, + address feeAsset, + uint256 feeValue + ) public payable virtual returns (bytes32) { + // build request, payload and hash + Request memory req = _generateRequest(chain, target, msg.sender, value, data, salt); + bytes32 id = keccak256(abi.encode(req)); + + if (feeAsset == address(0)) { + uint256 totalValue = value + feeValue; + require(msg.value >= totalValue, "invalid value provided"); + if (msg.value > totalValue) Address.sendValue(payable(msg.sender), msg.value - totalValue); + } else { + require(msg.value >= value, "invalid value provided"); + if (feeValue > 0) SafeERC20.safeTransferFrom(IERC20(feeAsset), msg.sender, address(this), feeValue); + if (msg.value > value) Address.sendValue(payable(msg.sender), msg.value - value); + } + + _processRequest(id, req, feeAsset, feeValue); + + // TODO: event + + return id; + } + + // ================================================= 2 step mode ================================================= + + function createRequest( + uint256 chain, + address target, + bytes memory data, + bytes32 salt + ) public payable virtual returns (bytes32) { + // build request, payload and hash + Request memory req = _generateRequest(chain, target, msg.sender, msg.value, data, salt); + bytes32 id = keccak256(abi.encode(req)); + + // register the request hash + require(_outBox.insert(id), "Ticket already scheduled"); + + // emit notice + emit RequestCreated(id, req); + + return id; + } + + function forwardRequest(Request memory req) public payable virtual { + (address feeAsset, uint256 feeValue) = defaultCost(req.message); + forwardRequest(req, feeAsset, feeValue); + } + + function forwardRequest(Request memory req, address feeAsset, uint256 feeValue) public payable virtual { + // compute the request hash + bytes32 id = keccak256(abi.encode(req)); + + if (feeAsset == address(0)) { + require(msg.value >= feeValue, "invalid value provided"); + if (msg.value > feeValue) Address.sendValue(payable(msg.sender), msg.value - feeValue); + } else { + if (feeValue > 0) SafeERC20.safeTransferFrom(IERC20(feeAsset), msg.sender, address(this), feeValue); + if (msg.value > 0) Address.sendValue(payable(msg.sender), msg.value); + } + + // consume request hash + require(_outBox.remove(id), "Ticket not scheduled"); + + _processRequest(id, req, feeAsset, feeValue); + + // emit notice + emit RequestForwarded(id); + } + + // =============================================== specialisation ================================================ + + function _processRequest(bytes32 id, Request memory req, address feeAsset, uint256 feeValue) internal virtual; + + // =================================================== helpers =================================================== + function _generateRequest( + uint256 chain, + address target, + address sender, + uint256 value, + bytes memory data, + bytes32 salt + ) internal view returns (Request memory) { + return + Request({ + source: Account({chain: block.chainid, instance: sender}), + message: Message({destination: Account({chain: chain, instance: target}), value: value, data: data}), + salt: salt + }); + } + + function _executeRequest(Message memory message) internal { + require(message.destination.chain == block.chainid); + (bool success, bytes memory returndata) = message.destination.instance.call{value: message.value}(message.data); + Address.verifyCallResult(success, returndata); + } +} diff --git a/contracts/crosschain/IGenericGateway.sol b/contracts/crosschain/IGenericGateway.sol index 471919a1..fe824276 100644 --- a/contracts/crosschain/IGenericGateway.sol +++ b/contracts/crosschain/IGenericGateway.sol @@ -33,17 +33,36 @@ interface IGenericGateway { /// @dev Perform a cross-chain call using the canonical payment method for this bridge. The provided value is /// passed along the request, minus anything that would be part of the canonical payment method. - function sendRequest(uint256 chain, address target, bytes memory data, bytes32 salt) external payable returns (bytes32); + function sendRequest( + uint256 chain, + address target, + uint256 value, + bytes memory data, + bytes32 salt + ) external payable returns (bytes32); /// @dev Perform a cross-chain call using the specified payment method. If feeAsset is 0, then feeValue will be /// deduced from the provided value to cover costs. The rest of the value is passed along the request. - function sendRequest(uint256 chain, address target, bytes memory data, bytes32 salt, address feeAsset, uint256 feeValue) external payable returns (bytes32); + function sendRequest( + uint256 chain, + address target, + uint256 value, + bytes memory data, + bytes32 salt, + address feeAsset, + uint256 feeValue + ) external payable returns (bytes32); // ================================================= 2 step mode ================================================= /// @dev Register a cross-chain call that will later be forwarded using {forwardRequest}. Any value passed here /// will be escrowed. It will then be passed along the request be forwarding happens. - function createRequest(uint256 chain, address target, bytes memory data, bytes32 salt) external payable returns (bytes32); + function createRequest( + uint256 chain, + address target, + bytes memory data, + bytes32 salt + ) external payable returns (bytes32); /// @dev Forwards a cross-chain request using the canonical payment method for this bridge. Any value provided /// here will be used for the payment. It will not be forwarded with the cross-chain call. diff --git a/contracts/crosschain/vendor/axelar/interfaces/IAxelarGasService.sol b/contracts/crosschain/vendor/axelar/interfaces/IAxelarGasService.sol index 78ea0f1d..60e363c2 100644 --- a/contracts/crosschain/vendor/axelar/interfaces/IAxelarGasService.sol +++ b/contracts/crosschain/vendor/axelar/interfaces/IAxelarGasService.sol @@ -2,9 +2,9 @@ pragma solidity ^0.8.0; -import { GasInfo } from '../types/GasEstimationTypes.sol'; -import { IInterchainGasEstimation } from './IInterchainGasEstimation.sol'; -import { IUpgradable } from './IUpgradable.sol'; +import {GasInfo} from "../types/GasEstimationTypes.sol"; +import {IInterchainGasEstimation} from "./IInterchainGasEstimation.sol"; +import {IUpgradable} from "./IUpgradable.sol"; /** * @title IAxelarGasService Interface @@ -353,11 +353,7 @@ interface IAxelarGasService is IInterchainGasEstimation, IUpgradable { * @param logIndex The log index for the cross-chain call * @param refundAddress The address where refunds, if any, should be sent */ - function addNativeGas( - bytes32 txHash, - uint256 logIndex, - address refundAddress - ) external payable; + function addNativeGas(bytes32 txHash, uint256 logIndex, address refundAddress) external payable; /** * @notice Add additional gas payment using ERC20 tokens after initiating an express cross-chain call. @@ -383,11 +379,7 @@ interface IAxelarGasService is IInterchainGasEstimation, IUpgradable { * @param logIndex The log index for the cross-chain call * @param refundAddress The address where refunds, if any, should be sent */ - function addNativeExpressGas( - bytes32 txHash, - uint256 logIndex, - address refundAddress - ) external payable; + function addNativeExpressGas(bytes32 txHash, uint256 logIndex, address refundAddress) external payable; /** * @notice Updates the gas price for a specific chain. @@ -404,11 +396,7 @@ interface IAxelarGasService is IInterchainGasEstimation, IUpgradable { * @param tokens Array of token addresses to be collected * @param amounts Array of amounts to be collected for each respective token address */ - function collectFees( - address payable receiver, - address[] calldata tokens, - uint256[] calldata amounts - ) external; + function collectFees(address payable receiver, address[] calldata tokens, uint256[] calldata amounts) external; /** * @notice Refunds gas payment to the receiver in relation to a specific cross-chain transaction. @@ -420,13 +408,7 @@ interface IAxelarGasService is IInterchainGasEstimation, IUpgradable { * @param token The token address to be refunded * @param amount The amount to refund */ - function refund( - bytes32 txHash, - uint256 logIndex, - address payable receiver, - address token, - uint256 amount - ) external; + function refund(bytes32 txHash, uint256 logIndex, address payable receiver, address token, uint256 amount) external; /** * @notice Returns the address of the designated gas collector. diff --git a/contracts/crosschain/vendor/axelar/interfaces/IAxelarGateway.sol b/contracts/crosschain/vendor/axelar/interfaces/IAxelarGateway.sol index 92f891cc..bb12c951 100644 --- a/contracts/crosschain/vendor/axelar/interfaces/IAxelarGateway.sol +++ b/contracts/crosschain/vendor/axelar/interfaces/IAxelarGateway.sol @@ -2,8 +2,8 @@ pragma solidity ^0.8.0; -import { IGovernable } from './IGovernable.sol'; -import { IImplementation } from './IImplementation.sol'; +import {IGovernable} from "./IGovernable.sol"; +import {IImplementation} from "./IImplementation.sol"; interface IAxelarGateway is IImplementation, IGovernable { /**********\ @@ -178,15 +178,11 @@ interface IAxelarGateway is IImplementation, IGovernable { function setTokenMintLimits(string[] calldata symbols, uint256[] calldata limits) external; - function upgrade( - address newImplementation, - bytes32 newImplementationCodeHash, - bytes calldata setupParams - ) external; + function upgrade(address newImplementation, bytes32 newImplementationCodeHash, bytes calldata setupParams) external; /**********************\ |* External Functions *| \**********************/ function execute(bytes calldata input) external; -} \ No newline at end of file +} diff --git a/contracts/crosschain/vendor/axelar/interfaces/IImplementation.sol b/contracts/crosschain/vendor/axelar/interfaces/IImplementation.sol index 037b3ce8..ef2631d4 100644 --- a/contracts/crosschain/vendor/axelar/interfaces/IImplementation.sol +++ b/contracts/crosschain/vendor/axelar/interfaces/IImplementation.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; -import { IContractIdentifier } from './IContractIdentifier.sol'; +import {IContractIdentifier} from "./IContractIdentifier.sol"; interface IImplementation is IContractIdentifier { error NotProxy(); diff --git a/contracts/crosschain/vendor/axelar/interfaces/IInterchainGasEstimation.sol b/contracts/crosschain/vendor/axelar/interfaces/IInterchainGasEstimation.sol index cab0ba6b..74d4cce8 100644 --- a/contracts/crosschain/vendor/axelar/interfaces/IInterchainGasEstimation.sol +++ b/contracts/crosschain/vendor/axelar/interfaces/IInterchainGasEstimation.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; -import { GasEstimationType, GasInfo } from '../types/GasEstimationTypes.sol'; +import {GasEstimationType, GasInfo} from "../types/GasEstimationTypes.sol"; /** * @title IInterchainGasEstimation Interface diff --git a/contracts/crosschain/vendor/axelar/interfaces/IUpgradable.sol b/contracts/crosschain/vendor/axelar/interfaces/IUpgradable.sol index 0ef082fb..c7a14709 100644 --- a/contracts/crosschain/vendor/axelar/interfaces/IUpgradable.sol +++ b/contracts/crosschain/vendor/axelar/interfaces/IUpgradable.sol @@ -2,8 +2,8 @@ pragma solidity ^0.8.0; -import { IOwnable } from './IOwnable.sol'; -import { IImplementation } from './IImplementation.sol'; +import {IOwnable} from "./IOwnable.sol"; +import {IImplementation} from "./IImplementation.sol"; // General interface for upgradable contracts interface IUpgradable is IOwnable, IImplementation { @@ -15,9 +15,5 @@ interface IUpgradable is IOwnable, IImplementation { function implementation() external view returns (address); - function upgrade( - address newImplementation, - bytes32 newImplementationCodeHash, - bytes calldata params - ) external; + function upgrade(address newImplementation, bytes32 newImplementationCodeHash, bytes calldata params) external; } diff --git a/contracts/utils/Set.sol b/contracts/utils/Set.sol index 5ad58f30..1a952162 100644 --- a/contracts/utils/Set.sol +++ b/contracts/utils/Set.sol @@ -29,4 +29,4 @@ library Set { function contains(Bytes32Set storage self, bytes32 value) internal view returns (bool) { return self._data[value]; } -} \ No newline at end of file +} From 28e88cdba3be5e59e2e765449ff66cedae05204f Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Sun, 23 Jun 2024 14:23:45 +0200 Subject: [PATCH 03/41] add relay observability --- contracts/crosschain/GenericGatewayAxelar.sol | 1 + contracts/crosschain/GenericGatewayCommon.sol | 30 +++++++++++++++++-- contracts/crosschain/IGenericGateway.sol | 5 ++++ lib/@openzeppelin-contracts | 2 +- 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/contracts/crosschain/GenericGatewayAxelar.sol b/contracts/crosschain/GenericGatewayAxelar.sol index ed7d7ac7..eebeb5fb 100644 --- a/contracts/crosschain/GenericGatewayAxelar.sol +++ b/contracts/crosschain/GenericGatewayAxelar.sol @@ -18,6 +18,7 @@ contract GenericGatewayAxelar is GenericGatewayCommon, Ownable { string name; string remote; } + mapping(uint256 chainId => ChainDetails chainName) public chainDetails; constructor(IAxelarGateway _gateway, address _initialOwner) Ownable(_initialOwner) { diff --git a/contracts/crosschain/GenericGatewayCommon.sol b/contracts/crosschain/GenericGatewayCommon.sol index e1a5b312..6fedfbfc 100644 --- a/contracts/crosschain/GenericGatewayCommon.sol +++ b/contracts/crosschain/GenericGatewayCommon.sol @@ -7,8 +7,12 @@ import {Set} from "../utils/Set.sol"; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {SlotDerivation} from "@openzeppelin/contracts@master/utils/SlotDerivation.sol"; +import {StorageSlot} from "@openzeppelin/contracts@master/utils/StorageSlot.sol"; abstract contract GenericGatewayCommon is IGenericGateway { + using SlotDerivation for *; + using StorageSlot for *; using Set for Set.Bytes32Set; event RequestCreated(bytes32 id, Request req); @@ -17,6 +21,17 @@ abstract contract GenericGatewayCommon is IGenericGateway { Set.Bytes32Set private _outBox; + // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.GenericGatewayCommon")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant GENERIC_GATEWAY_COMMON_STORAGE = + 0x71ab59e9fe1edd5f3d56389a2c715a90ddbb606dacc41e1c5360c10e3fe15b00; + + function crossChainSender() public view returns (uint256 chainId, address sender) { + return ( + GENERIC_GATEWAY_COMMON_STORAGE.offset(0).asUint256().tload(), + GENERIC_GATEWAY_COMMON_STORAGE.offset(1).asAddress().tload() + ); + } + // This must be redeclared as public so that other function can call it function defaultCost(Message memory /*message*/) public pure virtual returns (address, uint256); @@ -131,9 +146,18 @@ abstract contract GenericGatewayCommon is IGenericGateway { }); } - function _executeRequest(Message memory message) internal { - require(message.destination.chain == block.chainid); - (bool success, bytes memory returndata) = message.destination.instance.call{value: message.value}(message.data); + function _executeRequest(Request memory req) internal { + require(req.message.destination.chain == block.chainid); + + GENERIC_GATEWAY_COMMON_STORAGE.offset(0).asUint256().tstore(req.source.chain); + GENERIC_GATEWAY_COMMON_STORAGE.offset(1).asAddress().tstore(req.source.instance); + + (bool success, bytes memory returndata) = req.message.destination.instance.call{value: req.message.value}( + req.message.data + ); Address.verifyCallResult(success, returndata); + + GENERIC_GATEWAY_COMMON_STORAGE.offset(0).asUint256().tstore(0); + GENERIC_GATEWAY_COMMON_STORAGE.offset(1).asAddress().tstore(address(0)); } } diff --git a/contracts/crosschain/IGenericGateway.sol b/contracts/crosschain/IGenericGateway.sol index fe824276..84d58e2b 100644 --- a/contracts/crosschain/IGenericGateway.sol +++ b/contracts/crosschain/IGenericGateway.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.0; interface IGenericGateway { + // TODO: use uint96 chain id so that Account fit in a single word ? struct Account { uint256 chain; address instance; @@ -22,6 +23,10 @@ interface IGenericGateway { bytes32 salt; } + // ============================================ relay instrumentation ============================================ + + function crossChainSender() external view returns (uint256 chainId, address sender); + // =============================================== cost estimation =============================================== function defaultCost(Message memory message) external view returns (address, uint256); diff --git a/lib/@openzeppelin-contracts b/lib/@openzeppelin-contracts index 52c36d41..c3f8b760 160000 --- a/lib/@openzeppelin-contracts +++ b/lib/@openzeppelin-contracts @@ -1 +1 @@ -Subproject commit 52c36d412e8681053975396223d0ea39687fe33b +Subproject commit c3f8b760ad9d0431f33e8303658ccbdcc1610f24 From 948dab4b4e1889844ffb1a406ec075cfca745187 Mon Sep 17 00:00:00 2001 From: ernestognw Date: Tue, 30 Jul 2024 11:14:16 -0600 Subject: [PATCH 04/41] Update oz to master --- lib/@openzeppelin-contracts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/@openzeppelin-contracts b/lib/@openzeppelin-contracts index c3f8b760..5480641e 160000 --- a/lib/@openzeppelin-contracts +++ b/lib/@openzeppelin-contracts @@ -1 +1 @@ -Subproject commit c3f8b760ad9d0431f33e8303658ccbdcc1610f24 +Subproject commit 5480641e5c572fc7f9d68d59003f4b6417168cdd From e350890c98501d0a6da59233a39e5d9bf1ca79d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernesto=20Garc=C3=ADa?= Date: Tue, 13 Aug 2024 00:20:35 -0600 Subject: [PATCH 05/41] Iterate --- contracts/crosschain/Gateway.sol | 110 ++++++++++++++++++ contracts/crosschain/GenericGatewayCommon.sol | 3 + contracts/crosschain/IGateway.sol | 95 +++++++++++++++ contracts/crosschain/IGenericGateway.sol | 80 ------------- lib/@openzeppelin-contracts | 2 +- 5 files changed, 209 insertions(+), 81 deletions(-) create mode 100644 contracts/crosschain/Gateway.sol create mode 100644 contracts/crosschain/IGateway.sol delete mode 100644 contracts/crosschain/IGenericGateway.sol diff --git a/contracts/crosschain/Gateway.sol b/contracts/crosschain/Gateway.sol new file mode 100644 index 00000000..aca74ba5 --- /dev/null +++ b/contracts/crosschain/Gateway.sol @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {IGatewayDestination, IGatewaySource, IGateway} from "./IGateway.sol"; +import {Set} from "../utils/Set.sol"; + +/// @dev Generic implementation of a Gateway contract on the source chain according to ERC-XXXX definitions. +abstract contract GatewaySource is IGatewaySource { + using Set for Set.Bytes32Set; + Set.Bytes32Set private _createdBox; + Set.Bytes32Set private _sentBox; + + /// @inheritdoc IGateway + function messageId(Message memory message) public pure virtual returns (bytes32) { + return keccak256(abi.encode(message)); + } + + /// @inheritdoc IGatewaySource + function sendingStatus(bytes32 id) public view virtual returns (MessageSourceStatus) { + if (_sentBox.contains(id)) return MessageSourceStatus.Sent; + if (_createdBox.contains(id)) return MessageSourceStatus.Created; + return MessageSourceStatus.Unknown; + } + + /// @inheritdoc IGatewaySource + function sendRequest(Message memory message) external payable virtual returns (bytes32) { + return _sendRequest(messageId(message), message); + } + + /// @inheritdoc IGatewaySource + function createRequest(Message memory message) external payable virtual returns (bytes32) { + bytes32 id = messageId(message); + + // Check if the message was already sent or created. + if (sendingStatus(id) == MessageSourceStatus.Unknown) revert DuplicatedSourceMessage(id); + + emit MessageCreated(id, message); + return id; + } + + /// @inheritdoc IGatewaySource + function forwardRequest(Message memory message) external payable virtual returns (bytes32) { + bytes32 id = messageId(message); + if (!_createdBox.contains(id)) revert UnknownMessage(id); + return _sendRequest(id, message); + } + + function _sendRequest(bytes32 id, Message memory message) internal virtual returns (bytes32) { + // Check if the message was already sent. + if (_createdBox.insert(id)) revert DuplicatedSourceMessage(id); + + _processSend(id, message); + emit MessageSent(id, message); + return id; + } + + /// @dev Process a cross-chain message sent. Its up to the gateway to implement the logic. + function _processSend(bytes32 id, Message memory message) internal virtual; +} + +abstract contract GatewayDestination is IGatewayDestination { + using Set for Set.Bytes32Set; + Set.Bytes32Set private _executedBox; + Set.Bytes32Set private _deliveredBox; + + /// @inheritdoc IGateway + function messageId(Message memory message) public pure virtual returns (bytes32) { + return keccak256(abi.encode(message)); + } + + /// @inheritdoc IGatewayDestination + function destinationStatus(bytes32 id) public view virtual returns (MessageDestinationStatus) { + if (_executedBox.contains(id)) return MessageDestinationStatus.Executed; + if (_deliveredBox.contains(id)) return MessageDestinationStatus.Delivered; + return MessageDestinationStatus.Unknown; + } + + /// @inheritdoc IGatewayDestination + function setRequestDelivered(Message memory message) external virtual returns (bytes32) { + bytes32 id = messageId(message); + + // Check if the message was already delivered or executed. + if (destinationStatus(id) == MessageDestinationStatus.Unknown) revert DuplicatedDestinationMessage(id); + + _processDelivery(id, message); + emit MessageDelivered(id, message); + return id; + } + + /// @inheritdoc IGatewayDestination + function setRequestExecuted(Message memory message) external virtual returns (bytes32) { + bytes32 id = messageId(message); + if (!_executedBox.insert(id)) revert DuplicatedDestinationMessage(id); + emit MessageExecuted(id, message); + return id; + } + + /// @dev Process a cross-chain message delivery. Its up to the gateway to implement the logic. + function _processDelivery(bytes32 id, Message memory message) internal virtual; +} + +/// @dev Generic implementation of a Gateway contract according to ERC-XXXX definitions. +abstract contract Gateway is GatewayDestination, GatewaySource { + function messageId( + Message memory message + ) public pure override(GatewayDestination, GatewaySource) returns (bytes32) { + return super.messageId(message); + } +} diff --git a/contracts/crosschain/GenericGatewayCommon.sol b/contracts/crosschain/GenericGatewayCommon.sol index 6fedfbfc..b0ebd145 100644 --- a/contracts/crosschain/GenericGatewayCommon.sol +++ b/contracts/crosschain/GenericGatewayCommon.sol @@ -10,6 +10,9 @@ import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeE import {SlotDerivation} from "@openzeppelin/contracts@master/utils/SlotDerivation.sol"; import {StorageSlot} from "@openzeppelin/contracts@master/utils/StorageSlot.sol"; +/** + * @dev Generic implementation of a Gateway contract according to ERC-XXXX definitions. + */ abstract contract GenericGatewayCommon is IGenericGateway { using SlotDerivation for *; using StorageSlot for *; diff --git a/contracts/crosschain/IGateway.sol b/contracts/crosschain/IGateway.sol new file mode 100644 index 00000000..c3b36e19 --- /dev/null +++ b/contracts/crosschain/IGateway.sol @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +/// @dev Interface for a generic cross-chain gateway. +/// The gateway is responsible for sending and receiving messages between different blockchains. +interface IGateway { + /// @dev Represents an account on a specific chain. Might be a contract. + struct Account { + uint256 chain; + address instance; + } + + /// @dev Represents a cross-chain message. + struct Message { + Account source; + Account destination; + // Native token value to be sent with the message. + uint256 value; + // To dissambiguate between duplicated messages. + bytes32 salt; + // Arbitrary data to be sent with the message. + bytes payload; + // Extra parameters to be used by the gateway specialization. + bytes extraParams; + } + + /// @dev Uniquely identifies a message. + function messageId(Message memory message) external pure returns (bytes32); +} + +/// @dev Interface for a cross-chain gateway that sends messages. +/// Allows for 2 sending modes: {sendRequest} and {createRequest} + {forwardRequest}, +/// where the latter allows to hook logic before sending the message. +interface IGatewaySource is IGateway { + enum MessageSourceStatus { + Unknown, + Created, + Sent + } + + event MessageCreated(bytes32 indexed id, Message message); + event MessageSent(bytes32 indexed id, Message message); + + /// @dev Emitted when a message is created with the same id as a previous one (either created or sent). + error DuplicatedSourceMessage(bytes32 id); + + /// @dev Emitted when trying to forward a message that was not created. + error UnknownMessage(bytes32 id); + + /// @dev Returns the status of a sent cross-chain request. + function sendingStatus(bytes32 id) external view returns (MessageSourceStatus); + + /// @dev Send a cross-chain message to the target chain. + /// MessageSourceStatus.Unknown -> MessageSourceStatus.Sent + function sendRequest(Message memory message) external payable returns (bytes32); + + /// @dev Create a cross-chain message to the target chain. See {forwardRequest} to send it. + /// MessageSourceStatus.Unknown -> MessageSourceStatus.Created + function createRequest(Message memory message) external payable returns (bytes32); + + /// @dev Forwards a previously created cross-chain message to the target chain. See {createRequest} to create it. + /// MessageSourceStatus.Created -> MessageSourceStatus.Sent + function forwardRequest(Message memory message) external payable returns (bytes32); +} + +/// @dev Interface for a cross-chain gateway that receives messages. +/// Allows to check the status of a message and to mark it as delivered or executed. +interface IGatewayDestination is IGateway { + enum MessageDestinationStatus { + Unknown, + Delivered, + Executed + } + + event MessageDelivered(bytes32 indexed id, Message message); + event MessageExecuted(bytes32 indexed id, Message message); + + /// @dev Emitted when a message is delivered with the same id as a previous one (either delivered or executed). + error DuplicatedDestinationMessage(bytes32 id); + + /// @dev Returns the status of a received cross-chain request. + function destinationStatus(bytes32 id) external view returns (MessageDestinationStatus); + + /// @dev Sets a cross-chain request as delivered, ready for execution. + /// MessageDestinationStatus.Unknown -> MessageDestinationStatus.Delivered + /// NOTE: Should only be called by an authorized gateway operator. + function setRequestDelivered(Message memory message) external returns (bytes32); + + /// @dev Marks a cross-chain request as executed. + /// MessageDestinationStatus.Unknown -> MessageDestinationStatus.Executed + /// MessageDestinationStatus.Delivered -> MessageDestinationStatus.Executed + /// NOTE: Should only be called by the destination account. + function setRequestExecuted(Message memory message) external returns (bytes32); +} diff --git a/contracts/crosschain/IGenericGateway.sol b/contracts/crosschain/IGenericGateway.sol deleted file mode 100644 index 84d58e2b..00000000 --- a/contracts/crosschain/IGenericGateway.sol +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -interface IGenericGateway { - // TODO: use uint96 chain id so that Account fit in a single word ? - struct Account { - uint256 chain; - address instance; - } - - // TODO: (address,uint256)[] tokens? - // TODO: put value in tokens? - struct Message { - Account destination; - uint256 value; - bytes data; - } - - struct Request { - Account source; - Message message; - bytes32 salt; - } - - // ============================================ relay instrumentation ============================================ - - function crossChainSender() external view returns (uint256 chainId, address sender); - - // =============================================== cost estimation =============================================== - - function defaultCost(Message memory message) external view returns (address, uint256); - - /// @dev Returns the (minimum) cost (in a given asset) of performing a cross-chain operation. If asset is not supported for payment, returns type(uint256).max - function estimateCost(Message memory message, address asset) external view returns (uint256); - - // ================================================= 1 step mode ================================================= - - /// @dev Perform a cross-chain call using the canonical payment method for this bridge. The provided value is - /// passed along the request, minus anything that would be part of the canonical payment method. - function sendRequest( - uint256 chain, - address target, - uint256 value, - bytes memory data, - bytes32 salt - ) external payable returns (bytes32); - - /// @dev Perform a cross-chain call using the specified payment method. If feeAsset is 0, then feeValue will be - /// deduced from the provided value to cover costs. The rest of the value is passed along the request. - function sendRequest( - uint256 chain, - address target, - uint256 value, - bytes memory data, - bytes32 salt, - address feeAsset, - uint256 feeValue - ) external payable returns (bytes32); - - // ================================================= 2 step mode ================================================= - - /// @dev Register a cross-chain call that will later be forwarded using {forwardRequest}. Any value passed here - /// will be escrowed. It will then be passed along the request be forwarding happens. - function createRequest( - uint256 chain, - address target, - bytes memory data, - bytes32 salt - ) external payable returns (bytes32); - - /// @dev Forwards a cross-chain request using the canonical payment method for this bridge. Any value provided - /// here will be used for the payment. It will not be forwarded with the cross-chain call. - function forwardRequest(Request memory req) external payable; - - /// @dev Forwards a cross-chain request using using the specified payment method. Any value provided here will be - /// used for the payment. It will not be forwarded with the cross-chain call. This means that value should only be - /// used with `feeAsset = address(0)` and with `feeValue = msg.value`. - function forwardRequest(Request memory req, address feeAsset, uint256 feeValue) external payable; -} diff --git a/lib/@openzeppelin-contracts b/lib/@openzeppelin-contracts index 5480641e..52c36d41 160000 --- a/lib/@openzeppelin-contracts +++ b/lib/@openzeppelin-contracts @@ -1 +1 @@ -Subproject commit 5480641e5c572fc7f9d68d59003f4b6417168cdd +Subproject commit 52c36d412e8681053975396223d0ea39687fe33b From de9da705bb788ef2b07cbfe43a247d811abbb2bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernesto=20Garc=C3=ADa?= Date: Tue, 13 Aug 2024 09:25:58 -0600 Subject: [PATCH 06/41] Remove salt --- contracts/crosschain/IGateway.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/contracts/crosschain/IGateway.sol b/contracts/crosschain/IGateway.sol index c3b36e19..5d81a8e2 100644 --- a/contracts/crosschain/IGateway.sol +++ b/contracts/crosschain/IGateway.sol @@ -17,8 +17,6 @@ interface IGateway { Account destination; // Native token value to be sent with the message. uint256 value; - // To dissambiguate between duplicated messages. - bytes32 salt; // Arbitrary data to be sent with the message. bytes payload; // Extra parameters to be used by the gateway specialization. From d76074284699466eecb13931233ffa5ab04cf2a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernesto=20Garc=C3=ADa?= Date: Tue, 13 Aug 2024 18:10:16 -0600 Subject: [PATCH 07/41] Iterate --- contracts/crosschain/Gateway.sol | 206 +++++++++++++++++++++++------- contracts/crosschain/IGateway.sol | 81 +++++++----- contracts/utils/CAIP-10.sol | 62 +++++++++ contracts/utils/CAIP-2.sol | 64 ++++++++++ 4 files changed, 334 insertions(+), 79 deletions(-) create mode 100644 contracts/utils/CAIP-10.sol create mode 100644 contracts/utils/CAIP-2.sol diff --git a/contracts/crosschain/Gateway.sol b/contracts/crosschain/Gateway.sol index aca74ba5..3a5921c9 100644 --- a/contracts/crosschain/Gateway.sol +++ b/contracts/crosschain/Gateway.sol @@ -2,18 +2,30 @@ pragma solidity ^0.8.0; -import {IGatewayDestination, IGatewaySource, IGateway} from "./IGateway.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {IGatewayDestination, IGatewaySource, IGatewayBase} from "./IGateway.sol"; import {Set} from "../utils/Set.sol"; +import {CAIP10} from "../utils/CAIP-10.sol"; +import {CAIP2} from "../utils/CAIP-2.sol"; -/// @dev Generic implementation of a Gateway contract on the source chain according to ERC-XXXX definitions. +/// @dev Gateway contract on the source chain according to ERC-XXXX definitions. +/// +/// This is a generic implementation of an asynchronous message-passing system between accounts on different chains. abstract contract GatewaySource is IGatewaySource { using Set for Set.Bytes32Set; + using CAIP2 for CAIP2.ChainId; + using CAIP10 for CAIP10.Account; + Set.Bytes32Set private _createdBox; Set.Bytes32Set private _sentBox; - /// @inheritdoc IGateway - function messageId(Message memory message) public pure virtual returns (bytes32) { - return keccak256(abi.encode(message)); + /// @inheritdoc IGatewayBase + function messageId( + string memory source, + string memory destination, + Message memory message + ) public pure virtual returns (bytes32) { + return keccak256(abi.encode(source, destination, message)); } /// @inheritdoc IGatewaySource @@ -24,39 +36,95 @@ abstract contract GatewaySource is IGatewaySource { } /// @inheritdoc IGatewaySource - function sendRequest(Message memory message) external payable virtual returns (bytes32) { - return _sendRequest(messageId(message), message); + function sendMessage( + string memory source, + string memory destination, + Message memory message + ) external payable virtual returns (bytes32) { + _authorizeSendingMessage(source, source, message); + bytes32 id = messageId(source, destination, message); + return _sendMessage(id, sendingStatus(id), message); } /// @inheritdoc IGatewaySource - function createRequest(Message memory message) external payable virtual returns (bytes32) { - bytes32 id = messageId(message); + function createMessage( + string memory source, + string memory destination, + Message memory message + ) external payable virtual returns (bytes32) { + _authorizeSendingMessage(source, message); + bytes32 id = messageId(source, destination, message); + return _createMessage(id, sendingStatus(id), message); + } + + /// @inheritdoc IGatewaySource + function forwardMessage( + string memory source, + string memory destination, + Message memory message + ) external payable virtual returns (bytes32) { + bytes32 id = messageId(source, destination, message); + return _forwardMessage(id, sendingStatus(id), message); + } - // Check if the message was already sent or created. - if (sendingStatus(id) == MessageSourceStatus.Unknown) revert DuplicatedSourceMessage(id); + function _authorizeSendingMessage(string memory source, Message memory message) internal virtual { + CAIP10.Account memory sourceAccount = CAIP10.fromString(source); - emit MessageCreated(id, message); - return id; + // Sender must match the source account + bool isSelf = Strings.equal(Strings.toHexString(msg.sender), sourceAccount._accountId); + + if (!isSelf || !_EVMValidity()) { + revert UnauthorizedSourceMessage(source, msg.sender, message); + } } - /// @inheritdoc IGatewaySource - function forwardRequest(Message memory message) external payable virtual returns (bytes32) { - bytes32 id = messageId(message); - if (!_createdBox.contains(id)) revert UnknownMessage(id); - return _sendRequest(id, message); + function _EVMValidity(CAIP10.Account memory sourceAccount) private pure { + return + sourceAccount._chainId._namespace == _chainId() && // Chain ID must match the current chain + sourceAccount._chainId._reference == bytes32(bytes(string("eip155"))); // EIP-155 for EVM chains } - function _sendRequest(bytes32 id, Message memory message) internal virtual returns (bytes32) { - // Check if the message was already sent. - if (_createdBox.insert(id)) revert DuplicatedSourceMessage(id); + function _createMessage(bytes32 id, MessageSourceStatus status, Message memory message) private returns (bytes32) { + // Check if the message was not created or sent before. NOOP otherwise. + if (status == MessageSourceStatus.Unknown) { + _createdBox.insert(id); + emit MessageCreated(id, message); + } + return id; + } + + function _sendMessage(bytes32 id, MessageSourceStatus status, Message memory message) private returns (bytes32) { + /// Check if the message hwas not sent before. NOOP otherwise. + if (status != MessageSourceStatus.Sent) { + _sentBox.insert(id); + emit MessageSent(id, message); + } + return id; + } - _processSend(id, message); - emit MessageSent(id, message); + function _forwardMessage(bytes32 id, MessageSourceStatus status, Message memory message) private returns (bytes32) { + // Check if the message was created first. NOOP otherwise. + if (status == MessageSourceStatus.Created) { + _sendMessage(id, status, message); + } return id; } - /// @dev Process a cross-chain message sent. Its up to the gateway to implement the logic. - function _processSend(bytes32 id, Message memory message) internal virtual; + /// @dev Returns the chain ID of the current chain. + /// Assumes block.chainId < type(uint64).max + function _chainId() private view returns (bytes8 chainId) { + unchecked { + uint256 id = block.chainid; + while (true) { + chainId--; + assembly ("memory-safe") { + mstore8(chainId, byte(mod(id, 10), HEX_DIGITS)) + } + id /= 10; + if (id == 0) break; + } + } + } } abstract contract GatewayDestination is IGatewayDestination { @@ -64,9 +132,13 @@ abstract contract GatewayDestination is IGatewayDestination { Set.Bytes32Set private _executedBox; Set.Bytes32Set private _deliveredBox; - /// @inheritdoc IGateway - function messageId(Message memory message) public pure virtual returns (bytes32) { - return keccak256(abi.encode(message)); + /// @inheritdoc IGatewayBase + function messageId( + string memory source, + string memory destination, + Message memory message + ) public pure virtual returns (bytes32) { + return keccak256(abi.encode(source, destination, message)); } /// @inheritdoc IGatewayDestination @@ -77,34 +149,80 @@ abstract contract GatewayDestination is IGatewayDestination { } /// @inheritdoc IGatewayDestination - function setRequestDelivered(Message memory message) external virtual returns (bytes32) { - bytes32 id = messageId(message); + function deliverMessage( + string memory source, + string memory destination, + Message memory message + ) external virtual returns (bytes32) { + bytes32 id = messageId(source, destination, message); + _authorizeDeliveringMessage(destination, message); + MessageDestinationStatus status = destinationStatus(id); + return _deliverMessage(id, status, message); + } - // Check if the message was already delivered or executed. - if (destinationStatus(id) == MessageDestinationStatus.Unknown) revert DuplicatedDestinationMessage(id); + /// @inheritdoc IGatewayDestination + function setMessageExecuted( + string memory source, + string memory destination, + Message memory message + ) external virtual returns (bytes32) { + bytes32 id = messageId(source, destination, message); + MessageDestinationStatus status = destinationStatus(id); + return _setMessageExecuted(id, status, message); + } - _processDelivery(id, message); - emit MessageDelivered(id, message); - return id; + /// @dev Authorizes the delivery of a message to the destination chain. + function _authorizeDeliveringMessage(string memory destination, Message memory message) internal virtual { + CAIP10.Account memory destinationAccount = CAIP10.fromString(destination); + + if (_validateMessage(message) && !_EVMValidity(destinationAccount)) { + revert UnauthorizedDestinationMessage(destination, msg.sender, message); + } } - /// @inheritdoc IGatewayDestination - function setRequestExecuted(Message memory message) external virtual returns (bytes32) { - bytes32 id = messageId(message); - if (!_executedBox.insert(id)) revert DuplicatedDestinationMessage(id); - emit MessageExecuted(id, message); + /// @dev Validates the message before delivering it. Left unimplemented to allow for custom access control. + function _validateMessage(Message memory message) internal virtual; + + function _deliverMessage( + bytes32 id, + MessageDestinationStatus status, + Message memory message + ) private returns (bytes32) { + // Check if the message was not delivered or executed before. NOOP otherwise. + if (status == MessageDestinationStatus.Unknown) { + _deliveredBox.insert(id); + emit MessageDelivered(id, message); + } return id; } - /// @dev Process a cross-chain message delivery. Its up to the gateway to implement the logic. - function _processDelivery(bytes32 id, Message memory message) internal virtual; + function _EVMValidity(CAIP10.Account memory destinationAccount) private pure { + return + destinationAccount._chainId._namespace == _chainId() && // Chain ID must match the current chain + destinationAccount._chainId._reference == bytes32(bytes(string("eip155"))); // EIP-155 for EVM chains + } + + function _setMessageExecuted( + bytes32 id, + MessageDestinationStatus status, + Message memory message + ) private returns (bytes32) { + // Check if the message was not executed already. NOOP otherwise. + if (status != MessageDestinationStatus.Executed) { + _executedBox.insert(id); + emit MessageExecuted(id, message); + } + return id; + } } /// @dev Generic implementation of a Gateway contract according to ERC-XXXX definitions. abstract contract Gateway is GatewayDestination, GatewaySource { function messageId( + string memory source, + string memory destination, Message memory message - ) public pure override(GatewayDestination, GatewaySource) returns (bytes32) { - return super.messageId(message); + ) public pure override(GatewaySource, GatewayDestination) returns (bytes32) { + return super.messageId(source, destination, message); } } diff --git a/contracts/crosschain/IGateway.sol b/contracts/crosschain/IGateway.sol index 5d81a8e2..3fb6cadd 100644 --- a/contracts/crosschain/IGateway.sol +++ b/contracts/crosschain/IGateway.sol @@ -4,17 +4,9 @@ pragma solidity ^0.8.0; /// @dev Interface for a generic cross-chain gateway. /// The gateway is responsible for sending and receiving messages between different blockchains. -interface IGateway { - /// @dev Represents an account on a specific chain. Might be a contract. - struct Account { - uint256 chain; - address instance; - } - +interface IGatewayBase { /// @dev Represents a cross-chain message. struct Message { - Account source; - Account destination; // Native token value to be sent with the message. uint256 value; // Arbitrary data to be sent with the message. @@ -24,13 +16,19 @@ interface IGateway { } /// @dev Uniquely identifies a message. - function messageId(Message memory message) external pure returns (bytes32); + /// @param source CAIP-10 account ID of the source chain. + /// @param destination CAIP-10 account ID of the destination chain. + function messageId( + string memory source, + string memory destination, + Message memory message + ) external pure returns (bytes32); } /// @dev Interface for a cross-chain gateway that sends messages. -/// Allows for 2 sending modes: {sendRequest} and {createRequest} + {forwardRequest}, +/// Allows for 2 sending modes: {sendMessage} and {createMessage} + {forwardMessage}, /// where the latter allows to hook logic before sending the message. -interface IGatewaySource is IGateway { +interface IGatewaySource is IGatewayBase { enum MessageSourceStatus { Unknown, Created, @@ -40,31 +38,39 @@ interface IGatewaySource is IGateway { event MessageCreated(bytes32 indexed id, Message message); event MessageSent(bytes32 indexed id, Message message); - /// @dev Emitted when a message is created with the same id as a previous one (either created or sent). - error DuplicatedSourceMessage(bytes32 id); - - /// @dev Emitted when trying to forward a message that was not created. - error UnknownMessage(bytes32 id); + error UnauthorizedSourceMessage(string source, address sender, Message message); - /// @dev Returns the status of a sent cross-chain request. + /// @dev Returns the status of a sent cross-chain message. function sendingStatus(bytes32 id) external view returns (MessageSourceStatus); /// @dev Send a cross-chain message to the target chain. /// MessageSourceStatus.Unknown -> MessageSourceStatus.Sent - function sendRequest(Message memory message) external payable returns (bytes32); + function sendMessage( + string memory source, + string memory destination, + Message memory message + ) external payable returns (bytes32); - /// @dev Create a cross-chain message to the target chain. See {forwardRequest} to send it. + /// @dev Create a cross-chain message to the target chain. See {forwardMessage} to send it. /// MessageSourceStatus.Unknown -> MessageSourceStatus.Created - function createRequest(Message memory message) external payable returns (bytes32); + function createMessage( + string memory source, + string memory destination, + Message memory message + ) external payable returns (bytes32); - /// @dev Forwards a previously created cross-chain message to the target chain. See {createRequest} to create it. + /// @dev Forwards a previously created cross-chain message to the target chain. See {createMessage} to create it. /// MessageSourceStatus.Created -> MessageSourceStatus.Sent - function forwardRequest(Message memory message) external payable returns (bytes32); + function forwardMessage( + string memory source, + string memory destination, + Message memory message + ) external payable returns (bytes32); } /// @dev Interface for a cross-chain gateway that receives messages. /// Allows to check the status of a message and to mark it as delivered or executed. -interface IGatewayDestination is IGateway { +interface IGatewayDestination is IGatewayBase { enum MessageDestinationStatus { Unknown, Delivered, @@ -74,20 +80,25 @@ interface IGatewayDestination is IGateway { event MessageDelivered(bytes32 indexed id, Message message); event MessageExecuted(bytes32 indexed id, Message message); - /// @dev Emitted when a message is delivered with the same id as a previous one (either delivered or executed). - error DuplicatedDestinationMessage(bytes32 id); - - /// @dev Returns the status of a received cross-chain request. + /// @dev Returns the status of a received cross-chain message. function destinationStatus(bytes32 id) external view returns (MessageDestinationStatus); - /// @dev Sets a cross-chain request as delivered, ready for execution. + /// @dev Sets a cross-chain message as delivered, ready for execution. /// MessageDestinationStatus.Unknown -> MessageDestinationStatus.Delivered /// NOTE: Should only be called by an authorized gateway operator. - function setRequestDelivered(Message memory message) external returns (bytes32); - - /// @dev Marks a cross-chain request as executed. - /// MessageDestinationStatus.Unknown -> MessageDestinationStatus.Executed - /// MessageDestinationStatus.Delivered -> MessageDestinationStatus.Executed + function deliverMessage( + string memory source, + string memory destination, + Message memory message + ) external returns (bytes32); + + /// @dev Marks a cross-chain message as executed. + /// MessageDestinationStatus.Unknown -> MessageDestinationStatus.Executed. + /// MessageDestinationStatus.Delivered -> MessageDestinationStatus.Executed. /// NOTE: Should only be called by the destination account. - function setRequestExecuted(Message memory message) external returns (bytes32); + function setMessageExecuted( + string memory source, + string memory destination, + Message memory message + ) external returns (bytes32); } diff --git a/contracts/utils/CAIP-10.sol b/contracts/utils/CAIP-10.sol new file mode 100644 index 00000000..687d54cf --- /dev/null +++ b/contracts/utils/CAIP-10.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {CAIP2} from "./CAIP-2.sol"; + +library CAIP10 { + using CAIP2 for CAIP2.ChainId; + + // account_id: chain_id + ":" + account_address + // chain_id: [-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32} (See [CAIP-2][]) + // account_address: [-.%a-zA-Z0-9]{1,128} + struct Account { + CAIP2.ChainId _chainId; + string _accountId; // Often referred to as address + } + + function toString(Account memory account) internal pure returns (string memory) { + return string(abi.encodePacked(account._chainId.toString(), CAIP2.SEMICOLON, account._accountId)); + } + + function fromString(string memory accountStr) internal pure returns (Account memory account) { + bytes memory accountBuffer = bytes(accountStr); + uint256 lastSeparatorIndex = _findLastSeparatorIndex(accountBuffer); + account._chainId = extractChainId(accountBuffer, lastSeparatorIndex); + account._accountId = extractAccountId(accountBuffer, lastSeparatorIndex); + return account; + } + + function extractChainId( + bytes memory accountBuffer, + uint256 lastSeparatorIndex + ) internal pure returns (CAIP2.ChainId memory chainId) { + bytes memory _chainId = new bytes(lastSeparatorIndex); + for (uint256 i = 0; i < lastSeparatorIndex; i++) { + _chainId[i] = accountBuffer[i]; + } + return CAIP2.fromString(string(_chainId)); + } + + function extractAccountId( + bytes memory accountBuffer, + uint256 lastSeparatorIndex + ) internal pure returns (string memory) { + uint256 length = accountBuffer.length; + uint256 offset = lastSeparatorIndex - 1; + bytes memory _accountId = new bytes(length - offset); // Will overflow if no separator is found + for (uint256 i = lastSeparatorIndex + 1; i < length; i++) { + _accountId[i - offset] = accountBuffer[i]; + } + return string(_accountId); + } + + function _findLastSeparatorIndex(bytes memory accountBuffer) private pure returns (uint256) { + for (uint256 i = accountBuffer.length - 1; i >= 0; i--) { + if (accountBuffer[i] == CAIP2.SEMICOLON) { + return i; + } + } + return 0; + } +} diff --git a/contracts/utils/CAIP-2.sol b/contracts/utils/CAIP-2.sol new file mode 100644 index 00000000..61e5faee --- /dev/null +++ b/contracts/utils/CAIP-2.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {Arrays} from "@openzeppelin/contracts/utils/Arrays.sol"; +import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; + +library CAIP2 { + // chain_id: namespace + ":" + reference + // namespace: [-a-z0-9]{3,8} + // reference: [-_a-zA-Z0-9]{1,32} + struct ChainId { + bytes8 _namespace; + bytes32 _reference; + } + + bytes1 constant SEMICOLON = ":"; + + error TooLongChainId(); + + function toString(ChainId memory chainId) internal pure returns (string memory) { + return string(abi.encodePacked(chainId._namespace, SEMICOLON, chainId._reference)); + } + + /// @dev Parses a chain ID from a string by splitting it at the first semicolon. + /// The function parses both sides as `bytes8` and `bytes32` respectively wiothout any validation. + function fromString(string memory chainStr) internal pure returns (ChainId memory chainId) { + bytes memory chainBuffer = bytes(chainStr); + uint8 semicolonIndex = _findSemicolonIndex(chainBuffer); + chainId._namespace = _extractNamespace(chainBuffer, semicolonIndex); + chainId._reference = _unsafeExtractReference(chainBuffer, semicolonIndex); + return chainId; + } + + /// @dev Extracts the first `semicolonIndex` bytes from the chain buffer as a bytes8 namespace. + function _extractNamespace(bytes memory chainBuffer, uint8 semicolonIndex) private pure returns (bytes8 namespace) { + assembly ("memory-safe") { + let shift := sub(256, mul(semicolonIndex, 8)) + namespace := shl(shift, shr(shift, mload(add(chainBuffer, 0x20)))) + } + } + + /// @dev Extracts the reference from the chain buffer after the semicolon located at `offset`. + /// + /// IMPORTANT: The caller must make sure that the semicolon index is within the chain buffer length + /// and that there are 32 bytes available after the semicolon. Otherwise dirty memory could be read. + function _unsafeExtractReference(bytes memory chainBuffer, uint8 offset) private pure returns (bytes32 ref) { + assembly ("memory-safe") { + ref := mload(add(chainBuffer, add(0x20, offset))) + } + } + + /// @dev Looks for the first semicolon in the chain buffer. This is the optimal way since + /// the namespace is shorter than the reference. + function _findSemicolonIndex(bytes memory chainBuffer) private pure returns (uint8) { + uint8 length = SafeCast.toUint8(chainBuffer.length); + for (uint8 i = 0; i < length; i++) { + if (chainBuffer[i] == SEMICOLON) { + return i; + } + } + return length; + } +} From 348ad8d63d0f73f2652b4e01e576285872973e3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernesto=20Garc=C3=ADa?= Date: Tue, 13 Aug 2024 19:09:11 -0600 Subject: [PATCH 08/41] Add GatewayAxelar specialization --- contracts/crosschain/Gateway.sol | 64 ++++++++++++------- contracts/crosschain/GatewayAxelar.sol | 53 +++++++++++++++ contracts/crosschain/GatewayAxelarCAIP2.sol | 33 ++++++++++ .../crosschain/ICAIP2GatewayEquivalence.sol | 13 ++++ 4 files changed, 140 insertions(+), 23 deletions(-) create mode 100644 contracts/crosschain/GatewayAxelar.sol create mode 100644 contracts/crosschain/GatewayAxelarCAIP2.sol create mode 100644 contracts/crosschain/ICAIP2GatewayEquivalence.sol diff --git a/contracts/crosschain/Gateway.sol b/contracts/crosschain/Gateway.sol index 3a5921c9..4fe6e557 100644 --- a/contracts/crosschain/Gateway.sol +++ b/contracts/crosschain/Gateway.sol @@ -41,7 +41,7 @@ abstract contract GatewaySource is IGatewaySource { string memory destination, Message memory message ) external payable virtual returns (bytes32) { - _authorizeSendingMessage(source, source, message); + _authorizeCreatingMessage(source, source, message); bytes32 id = messageId(source, destination, message); return _sendMessage(id, sendingStatus(id), message); } @@ -52,7 +52,7 @@ abstract contract GatewaySource is IGatewaySource { string memory destination, Message memory message ) external payable virtual returns (bytes32) { - _authorizeSendingMessage(source, message); + _authorizeCreatingMessage(source, message); bytes32 id = messageId(source, destination, message); return _createMessage(id, sendingStatus(id), message); } @@ -67,24 +67,13 @@ abstract contract GatewaySource is IGatewaySource { return _forwardMessage(id, sendingStatus(id), message); } - function _authorizeSendingMessage(string memory source, Message memory message) internal virtual { - CAIP10.Account memory sourceAccount = CAIP10.fromString(source); - - // Sender must match the source account - bool isSelf = Strings.equal(Strings.toHexString(msg.sender), sourceAccount._accountId); - - if (!isSelf || !_EVMValidity()) { - revert UnauthorizedSourceMessage(source, msg.sender, message); - } - } - - function _EVMValidity(CAIP10.Account memory sourceAccount) private pure { - return - sourceAccount._chainId._namespace == _chainId() && // Chain ID must match the current chain - sourceAccount._chainId._reference == bytes32(bytes(string("eip155"))); // EIP-155 for EVM chains - } - - function _createMessage(bytes32 id, MessageSourceStatus status, Message memory message) private returns (bytes32) { + function _createMessage( + bytes32 id, + string memory source, + string memory destination, + MessageSourceStatus status, + Message memory message + ) internal virtual returns (bytes32) { // Check if the message was not created or sent before. NOOP otherwise. if (status == MessageSourceStatus.Unknown) { _createdBox.insert(id); @@ -93,7 +82,13 @@ abstract contract GatewaySource is IGatewaySource { return id; } - function _sendMessage(bytes32 id, MessageSourceStatus status, Message memory message) private returns (bytes32) { + function _sendMessage( + bytes32 id, + string memory source, + string memory destination, + MessageSourceStatus status, + Message memory message + ) internal virtual returns (bytes32) { /// Check if the message hwas not sent before. NOOP otherwise. if (status != MessageSourceStatus.Sent) { _sentBox.insert(id); @@ -102,7 +97,13 @@ abstract contract GatewaySource is IGatewaySource { return id; } - function _forwardMessage(bytes32 id, MessageSourceStatus status, Message memory message) private returns (bytes32) { + function _forwardMessage( + bytes32 id, + string memory source, + string memory destination, + MessageSourceStatus status, + Message memory message + ) internal virtual returns (bytes32) { // Check if the message was created first. NOOP otherwise. if (status == MessageSourceStatus.Created) { _sendMessage(id, status, message); @@ -110,6 +111,23 @@ abstract contract GatewaySource is IGatewaySource { return id; } + function _authorizeCreatingMessage(string memory source, Message memory message) internal virtual { + CAIP10.Account memory sourceAccount = CAIP10.fromString(source); + + // Sender must match the source account + bool isSelf = Strings.equal(Strings.toHexString(msg.sender), sourceAccount._accountId); + + if (!isSelf || !_EVMValidity()) { + revert UnauthorizedSourceMessage(source, msg.sender, message); + } + } + + function _EVMValidity(CAIP10.Account memory sourceAccount) private pure { + return + sourceAccount._chainId._namespace == _chainId() && // Chain ID must match the current chain + sourceAccount._chainId._reference == bytes32(bytes(string("eip155"))); // EIP-155 for EVM chains + } + /// @dev Returns the chain ID of the current chain. /// Assumes block.chainId < type(uint64).max function _chainId() private view returns (bytes8 chainId) { @@ -187,7 +205,7 @@ abstract contract GatewayDestination is IGatewayDestination { bytes32 id, MessageDestinationStatus status, Message memory message - ) private returns (bytes32) { + ) internal virtual returns (bytes32) { // Check if the message was not delivered or executed before. NOOP otherwise. if (status == MessageDestinationStatus.Unknown) { _deliveredBox.insert(id); diff --git a/contracts/crosschain/GatewayAxelar.sol b/contracts/crosschain/GatewayAxelar.sol new file mode 100644 index 00000000..fc8fc547 --- /dev/null +++ b/contracts/crosschain/GatewayAxelar.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {IAxelarGateway} from "./vendor/axelar/interfaces/IAxelarGateway.sol"; +import {IAxelarGasService} from "./vendor/axelar/interfaces/IAxelarGasService.sol"; + +import {GatewaySource} from "./Gateway.sol"; +import {GatewayAxelarCAIP2} from "./GatewayAxelarCAIP2.sol"; +import {CAIP10} from "../utils/CAIP-10.sol"; + +contract GatewayAxelar is GatewaySource, GatewayAxelarCAIP2 { + IAxelarGateway public immutable axelarGateway; + IAxelarGasService public immutable gasService; + + error UnsupportedNativeCurrency(); + + constructor(IAxelarGateway _axelarGateway, address _initialOwner) Ownable(_initialOwner) { + axelarGateway = _axelarGateway; + } + + function _authorizeCreatingMessage( + string memory source, + Message memory message + ) internal override returns (bytes32) { + if (message.value > 0) { + revert UnsupportedNativeCurrency(); + } + + if (!isRegisteredCAIP2(CAIP10.fromString(destination)._chainId)) { + revert UnsupportedChain(chain); + } + + return super._authorizeCreatingMessage(source, message); + } + + function _sendMessage( + bytes32 id, + string memory /* source */, + string memory destination, + MessageSourceStatus status, + Message memory message + ) internal override returns (bytes32) { + super._sendMessage(id, status, message); + + if (status != MessageSourceStatus.Sent) { + AxelarChain memory details = chainDetails[CAIP10.fromString(destination)._chainId]; + gateway.callContract(details.name, details.remote, payload); + } + + return id; + } +} diff --git a/contracts/crosschain/GatewayAxelarCAIP2.sol b/contracts/crosschain/GatewayAxelarCAIP2.sol new file mode 100644 index 00000000..ccf20005 --- /dev/null +++ b/contracts/crosschain/GatewayAxelarCAIP2.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {IGatewayCAIP2Equivalence} from "./ICAIP2GatewayEquivalence.sol"; +import {CAIP2} from "../utils/CAIP-2.sol"; + +abstract contract GatewayAxelarCAIP2 is IGatewayCAIP2Equivalence { + error AlreadyRegisteredChain(CAIP2.ChainId chain); + error UnsupportedChain(CAIP2.ChainId chain); + + mapping(CAIP2.Chain chain => AxelarChain chainName) public chainDetails; + + struct AxelarChain { + string destinationChain; + string contractAddress; + } + + function isRegisteredCAIP2(CAIP2.ChainId memory chain) public view override returns (bool) { + return bytes(chainDetails[chain].destinationChain).length != 0; + } + + function fromCAIP2(CAIP2.ChainId memory chain) public pure returns (bytes memory) { + return abi.encode(AxelarChain(chain.destinationChain, chain.contractAddress)); + } + + function registerCAIP2Equivalence(CAIP2.ChainId memory chain, bytes memory custom) public onlyOwner { + if (isRegisteredCAIP2(chain)) { + revert AlreadyRegisteredChain(chain); + } + chainDetails[chain] = abi.decode(custom, (AxelarChain)); + } +} diff --git a/contracts/crosschain/ICAIP2GatewayEquivalence.sol b/contracts/crosschain/ICAIP2GatewayEquivalence.sol new file mode 100644 index 00000000..53015c93 --- /dev/null +++ b/contracts/crosschain/ICAIP2GatewayEquivalence.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {IAxelarGateway} from "./vendor/axelar/interfaces/IAxelarGateway.sol"; +import {IAxelarGasService} from "./vendor/axelar/interfaces/IAxelarGasService.sol"; +import {CAIP2} from "../utils/CAIP-2.sol"; + +interface IGatewayCAIP2Equivalence { + function registerCAIP2Equivalence(CAIP2.ChainId memory chain, bytes memory custom) public; + function isRegisteredCAIP2(CAIP2.ChainId memory chain) public view returns (bool); + function fromCAIP2(CAIP2.ChainId memory chain) public pure returns (bytes memory); +} From edfd3edc95572942c77f1b094bf55e33eeaeaf65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernesto=20Garc=C3=ADa?= Date: Wed, 14 Aug 2024 13:32:34 -0600 Subject: [PATCH 09/41] Iterate --- contracts/crosschain/Gateway.sol | 246 ------------------ contracts/crosschain/GatewayAxelar.sol | 53 ---- contracts/crosschain/GatewayDestination.sol | 106 ++++++++ contracts/crosschain/GatewaySource.sol | 145 +++++++++++ contracts/crosschain/GenericGatewayAxelar.sol | 86 ------ contracts/crosschain/GenericGatewayCommon.sol | 166 ------------ contracts/crosschain/ICAIP2Equivalence.sol | 21 ++ .../crosschain/ICAIP2GatewayEquivalence.sol | 13 - contracts/crosschain/IGateway.sol | 104 -------- contracts/crosschain/IGatewayCommon.sol | 26 ++ contracts/crosschain/IGatewayDestination.sol | 44 ++++ contracts/crosschain/IGatewaySource.sol | 52 ++++ .../{ => axelar}/GatewayAxelarCAIP2.sol | 6 +- .../crosschain/axelar/GatewayAxelarSource.sol | 63 +++++ .../generic/GatewayDestinationGeneric.sol | 58 +++++ .../generic/GatewaySourceGeneric.sol | 56 ++++ contracts/utils/CAIP-10.sol | 4 + contracts/utils/CAIP-2.sol | 24 ++ contracts/utils/LinkedList.sol | 28 ++ 19 files changed, 630 insertions(+), 671 deletions(-) delete mode 100644 contracts/crosschain/Gateway.sol delete mode 100644 contracts/crosschain/GatewayAxelar.sol create mode 100644 contracts/crosschain/GatewayDestination.sol create mode 100644 contracts/crosschain/GatewaySource.sol delete mode 100644 contracts/crosschain/GenericGatewayAxelar.sol delete mode 100644 contracts/crosschain/GenericGatewayCommon.sol create mode 100644 contracts/crosschain/ICAIP2Equivalence.sol delete mode 100644 contracts/crosschain/ICAIP2GatewayEquivalence.sol delete mode 100644 contracts/crosschain/IGateway.sol create mode 100644 contracts/crosschain/IGatewayCommon.sol create mode 100644 contracts/crosschain/IGatewayDestination.sol create mode 100644 contracts/crosschain/IGatewaySource.sol rename contracts/crosschain/{ => axelar}/GatewayAxelarCAIP2.sol (84%) create mode 100644 contracts/crosschain/axelar/GatewayAxelarSource.sol create mode 100644 contracts/crosschain/generic/GatewayDestinationGeneric.sol create mode 100644 contracts/crosschain/generic/GatewaySourceGeneric.sol create mode 100644 contracts/utils/LinkedList.sol diff --git a/contracts/crosschain/Gateway.sol b/contracts/crosschain/Gateway.sol deleted file mode 100644 index 4fe6e557..00000000 --- a/contracts/crosschain/Gateway.sol +++ /dev/null @@ -1,246 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; -import {IGatewayDestination, IGatewaySource, IGatewayBase} from "./IGateway.sol"; -import {Set} from "../utils/Set.sol"; -import {CAIP10} from "../utils/CAIP-10.sol"; -import {CAIP2} from "../utils/CAIP-2.sol"; - -/// @dev Gateway contract on the source chain according to ERC-XXXX definitions. -/// -/// This is a generic implementation of an asynchronous message-passing system between accounts on different chains. -abstract contract GatewaySource is IGatewaySource { - using Set for Set.Bytes32Set; - using CAIP2 for CAIP2.ChainId; - using CAIP10 for CAIP10.Account; - - Set.Bytes32Set private _createdBox; - Set.Bytes32Set private _sentBox; - - /// @inheritdoc IGatewayBase - function messageId( - string memory source, - string memory destination, - Message memory message - ) public pure virtual returns (bytes32) { - return keccak256(abi.encode(source, destination, message)); - } - - /// @inheritdoc IGatewaySource - function sendingStatus(bytes32 id) public view virtual returns (MessageSourceStatus) { - if (_sentBox.contains(id)) return MessageSourceStatus.Sent; - if (_createdBox.contains(id)) return MessageSourceStatus.Created; - return MessageSourceStatus.Unknown; - } - - /// @inheritdoc IGatewaySource - function sendMessage( - string memory source, - string memory destination, - Message memory message - ) external payable virtual returns (bytes32) { - _authorizeCreatingMessage(source, source, message); - bytes32 id = messageId(source, destination, message); - return _sendMessage(id, sendingStatus(id), message); - } - - /// @inheritdoc IGatewaySource - function createMessage( - string memory source, - string memory destination, - Message memory message - ) external payable virtual returns (bytes32) { - _authorizeCreatingMessage(source, message); - bytes32 id = messageId(source, destination, message); - return _createMessage(id, sendingStatus(id), message); - } - - /// @inheritdoc IGatewaySource - function forwardMessage( - string memory source, - string memory destination, - Message memory message - ) external payable virtual returns (bytes32) { - bytes32 id = messageId(source, destination, message); - return _forwardMessage(id, sendingStatus(id), message); - } - - function _createMessage( - bytes32 id, - string memory source, - string memory destination, - MessageSourceStatus status, - Message memory message - ) internal virtual returns (bytes32) { - // Check if the message was not created or sent before. NOOP otherwise. - if (status == MessageSourceStatus.Unknown) { - _createdBox.insert(id); - emit MessageCreated(id, message); - } - return id; - } - - function _sendMessage( - bytes32 id, - string memory source, - string memory destination, - MessageSourceStatus status, - Message memory message - ) internal virtual returns (bytes32) { - /// Check if the message hwas not sent before. NOOP otherwise. - if (status != MessageSourceStatus.Sent) { - _sentBox.insert(id); - emit MessageSent(id, message); - } - return id; - } - - function _forwardMessage( - bytes32 id, - string memory source, - string memory destination, - MessageSourceStatus status, - Message memory message - ) internal virtual returns (bytes32) { - // Check if the message was created first. NOOP otherwise. - if (status == MessageSourceStatus.Created) { - _sendMessage(id, status, message); - } - return id; - } - - function _authorizeCreatingMessage(string memory source, Message memory message) internal virtual { - CAIP10.Account memory sourceAccount = CAIP10.fromString(source); - - // Sender must match the source account - bool isSelf = Strings.equal(Strings.toHexString(msg.sender), sourceAccount._accountId); - - if (!isSelf || !_EVMValidity()) { - revert UnauthorizedSourceMessage(source, msg.sender, message); - } - } - - function _EVMValidity(CAIP10.Account memory sourceAccount) private pure { - return - sourceAccount._chainId._namespace == _chainId() && // Chain ID must match the current chain - sourceAccount._chainId._reference == bytes32(bytes(string("eip155"))); // EIP-155 for EVM chains - } - - /// @dev Returns the chain ID of the current chain. - /// Assumes block.chainId < type(uint64).max - function _chainId() private view returns (bytes8 chainId) { - unchecked { - uint256 id = block.chainid; - while (true) { - chainId--; - assembly ("memory-safe") { - mstore8(chainId, byte(mod(id, 10), HEX_DIGITS)) - } - id /= 10; - if (id == 0) break; - } - } - } -} - -abstract contract GatewayDestination is IGatewayDestination { - using Set for Set.Bytes32Set; - Set.Bytes32Set private _executedBox; - Set.Bytes32Set private _deliveredBox; - - /// @inheritdoc IGatewayBase - function messageId( - string memory source, - string memory destination, - Message memory message - ) public pure virtual returns (bytes32) { - return keccak256(abi.encode(source, destination, message)); - } - - /// @inheritdoc IGatewayDestination - function destinationStatus(bytes32 id) public view virtual returns (MessageDestinationStatus) { - if (_executedBox.contains(id)) return MessageDestinationStatus.Executed; - if (_deliveredBox.contains(id)) return MessageDestinationStatus.Delivered; - return MessageDestinationStatus.Unknown; - } - - /// @inheritdoc IGatewayDestination - function deliverMessage( - string memory source, - string memory destination, - Message memory message - ) external virtual returns (bytes32) { - bytes32 id = messageId(source, destination, message); - _authorizeDeliveringMessage(destination, message); - MessageDestinationStatus status = destinationStatus(id); - return _deliverMessage(id, status, message); - } - - /// @inheritdoc IGatewayDestination - function setMessageExecuted( - string memory source, - string memory destination, - Message memory message - ) external virtual returns (bytes32) { - bytes32 id = messageId(source, destination, message); - MessageDestinationStatus status = destinationStatus(id); - return _setMessageExecuted(id, status, message); - } - - /// @dev Authorizes the delivery of a message to the destination chain. - function _authorizeDeliveringMessage(string memory destination, Message memory message) internal virtual { - CAIP10.Account memory destinationAccount = CAIP10.fromString(destination); - - if (_validateMessage(message) && !_EVMValidity(destinationAccount)) { - revert UnauthorizedDestinationMessage(destination, msg.sender, message); - } - } - - /// @dev Validates the message before delivering it. Left unimplemented to allow for custom access control. - function _validateMessage(Message memory message) internal virtual; - - function _deliverMessage( - bytes32 id, - MessageDestinationStatus status, - Message memory message - ) internal virtual returns (bytes32) { - // Check if the message was not delivered or executed before. NOOP otherwise. - if (status == MessageDestinationStatus.Unknown) { - _deliveredBox.insert(id); - emit MessageDelivered(id, message); - } - return id; - } - - function _EVMValidity(CAIP10.Account memory destinationAccount) private pure { - return - destinationAccount._chainId._namespace == _chainId() && // Chain ID must match the current chain - destinationAccount._chainId._reference == bytes32(bytes(string("eip155"))); // EIP-155 for EVM chains - } - - function _setMessageExecuted( - bytes32 id, - MessageDestinationStatus status, - Message memory message - ) private returns (bytes32) { - // Check if the message was not executed already. NOOP otherwise. - if (status != MessageDestinationStatus.Executed) { - _executedBox.insert(id); - emit MessageExecuted(id, message); - } - return id; - } -} - -/// @dev Generic implementation of a Gateway contract according to ERC-XXXX definitions. -abstract contract Gateway is GatewayDestination, GatewaySource { - function messageId( - string memory source, - string memory destination, - Message memory message - ) public pure override(GatewaySource, GatewayDestination) returns (bytes32) { - return super.messageId(source, destination, message); - } -} diff --git a/contracts/crosschain/GatewayAxelar.sol b/contracts/crosschain/GatewayAxelar.sol deleted file mode 100644 index fc8fc547..00000000 --- a/contracts/crosschain/GatewayAxelar.sol +++ /dev/null @@ -1,53 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {IAxelarGateway} from "./vendor/axelar/interfaces/IAxelarGateway.sol"; -import {IAxelarGasService} from "./vendor/axelar/interfaces/IAxelarGasService.sol"; - -import {GatewaySource} from "./Gateway.sol"; -import {GatewayAxelarCAIP2} from "./GatewayAxelarCAIP2.sol"; -import {CAIP10} from "../utils/CAIP-10.sol"; - -contract GatewayAxelar is GatewaySource, GatewayAxelarCAIP2 { - IAxelarGateway public immutable axelarGateway; - IAxelarGasService public immutable gasService; - - error UnsupportedNativeCurrency(); - - constructor(IAxelarGateway _axelarGateway, address _initialOwner) Ownable(_initialOwner) { - axelarGateway = _axelarGateway; - } - - function _authorizeCreatingMessage( - string memory source, - Message memory message - ) internal override returns (bytes32) { - if (message.value > 0) { - revert UnsupportedNativeCurrency(); - } - - if (!isRegisteredCAIP2(CAIP10.fromString(destination)._chainId)) { - revert UnsupportedChain(chain); - } - - return super._authorizeCreatingMessage(source, message); - } - - function _sendMessage( - bytes32 id, - string memory /* source */, - string memory destination, - MessageSourceStatus status, - Message memory message - ) internal override returns (bytes32) { - super._sendMessage(id, status, message); - - if (status != MessageSourceStatus.Sent) { - AxelarChain memory details = chainDetails[CAIP10.fromString(destination)._chainId]; - gateway.callContract(details.name, details.remote, payload); - } - - return id; - } -} diff --git a/contracts/crosschain/GatewayDestination.sol b/contracts/crosschain/GatewayDestination.sol new file mode 100644 index 00000000..778dc283 --- /dev/null +++ b/contracts/crosschain/GatewayDestination.sol @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {CAIP10} from "../utils/CAIP-10.sol"; +import {CAIP2} from "../utils/CAIP-2.sol"; +import {IGatewayDestination, IGatewayCommon} from "./IGatewayDestination.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {Set} from "../utils/Set.sol"; + +/// @dev A gateway destination that receives messages from a source chain. +/// +/// This contract allows for 2 main operations: +/// - Message delivery (i.e. making it available for execution) +/// - Message execution (i.e. marking it as executed) +/// +/// Message delivery is permissioned through {_authorizeMessageDelivered} and it's usually set +/// to an authority that can validate the message and make it available for execution. +/// +/// Message execution is permissioned through {_authorizeMessageExecuted} and it checks if the +/// destination account is the one marking the message as executed. +abstract contract GatewayDestination is IGatewayDestination { + /// @inheritdoc IGatewayCommon + function messageId( + string memory source, + string memory destination, + Message memory message + ) public pure virtual override returns (bytes32); + + /// @inheritdoc IGatewayDestination + function destinationStatus(bytes32 id) public view virtual override returns (MessageDestinationStatus); + + /// @inheritdoc IGatewayDestination + function deliverMessage( + string memory source, + string memory destination, + Message memory message + ) external returns (bytes32) { + bytes32 id = messageId(source, destination, message); + MessageDestinationStatus status = destinationStatus(id); + _validateCurrentChain(destination); + _authorizeMessageDelivered(destination, message); + return _deliverMessage(id, status, message); + } + + /// @inheritdoc IGatewayDestination + function setMessageExecuted( + string memory source, + string memory destination, + Message memory message + ) external returns (bytes32) { + bytes32 id = messageId(source, destination, message); + MessageDestinationStatus status = destinationStatus(id); + _authorizeMessageExecuted(destination, message); + return _setMessageExecuted(id, status, message); + } + + /// @dev Internal version of {deliverMessage} without access control. + function _deliverMessage( + bytes32 id, + MessageDestinationStatus status, + Message memory message + ) internal virtual returns (bytes32) { + // Check if the message was not delivered or executed before. NOOP otherwise. + if (status == MessageDestinationStatus.Unknown) { + emit MessageDelivered(id, message); + } + return id; + } + + /// @dev Internal version of {setMessageExecuted} without access control. + function _setMessageExecuted( + bytes32 id, + MessageDestinationStatus status, + Message memory message + ) internal virtual returns (bytes32) { + // Check if the message was not executed already. NOOP otherwise. + if (status != MessageDestinationStatus.Executed) { + emit MessageExecuted(id, message); + } + return id; + } + + /// @dev Authorizes the delivery of a message to the destination chain. + function _authorizeMessageDelivered(string memory destination, Message memory message) internal virtual; + + /// @dev Validates a message submitted as executed. + /// + /// Requirements: + /// - The destination must be the `msg.sender` + function _authorizeMessageExecuted(string memory destination, Message memory message) internal virtual { + CAIP10.Account memory destinationAccount = CAIP10.fromString(destination); + + if (CAIP10.getAddress(destinationAccount) != msg.sender) { + revert UnauthorizedDestinationMessage(destination, msg.sender, message); + } + } + + /// @dev Validates that the destination chain is the current chain. + function _validateCurrentChain(string memory destination) private view { + CAIP10.Account memory destinationAccount = CAIP10.fromString(destination); + if (!CAIP2.isCurrentEVMChain(destinationAccount._chainId)) { + revert MismatchedDestinationChain(destination); + } + } +} diff --git a/contracts/crosschain/GatewaySource.sol b/contracts/crosschain/GatewaySource.sol new file mode 100644 index 00000000..63dd08e7 --- /dev/null +++ b/contracts/crosschain/GatewaySource.sol @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {CAIP10} from "../utils/CAIP-10.sol"; +import {CAIP2} from "../utils/CAIP-2.sol"; +import {IGatewaySource, IGatewayCommon} from "./IGatewaySource.sol"; +import {Set} from "../utils/Set.sol"; + +/// @dev Gateway contract on the source chain according to ERC-XXXX definitions. +/// +/// This is a generic implementation of an asynchronous message-passing system between accounts on different chains. +abstract contract GatewaySource is IGatewaySource { + /// @inheritdoc IGatewayCommon + function messageId( + string memory source, + string memory destination, + Message memory message + ) public pure virtual returns (bytes32); + + /// @inheritdoc IGatewaySource + function sendingStatus(bytes32 id) public view virtual returns (MessageSourceStatus); + + /// @inheritdoc IGatewaySource + function sendMessage( + string memory source, + string memory destination, + Message memory message + ) external payable virtual returns (bytes32) { + bytes32 id = messageId(source, destination, message); + _validateCurrentChain(source); + _authorizeMessageCreated(source, message); + return _sendMessage(id, sendingStatus(id), message); + } + + /// @inheritdoc IGatewaySource + function createMessage( + string memory source, + string memory destination, + Message memory message + ) external payable virtual returns (bytes32) { + bytes32 id = messageId(source, destination, message); + _validateCurrentChain(source); + _authorizeMessageCreated(source, message); + return _createMessage(id, sendingStatus(id), message); + } + + /// @inheritdoc IGatewaySource + function forwardMessage( + string memory source, + string memory destination, + Message memory message + ) external payable virtual returns (bytes32) { + bytes32 id = messageId(source, destination, message); + _authorizeMessageForwarded(destination, message); + return _forwardMessage(id, sendingStatus(id), message); + } + + /// @dev Internal version of {createMessage} without access control. + function _createMessage( + bytes32 id, + MessageSourceStatus status, + Message memory message + ) internal virtual returns (bytes32) { + // Check if the message was not created or sent before. NOOP otherwise. + if (status == MessageSourceStatus.Unknown) { + emit MessageCreated(id, message); + } + return id; + } + + /// @dev Internal version of {sendMessage} without access control. + function _sendMessage( + bytes32 id, + MessageSourceStatus status, + Message memory message + ) internal virtual returns (bytes32) { + /// Check if the message hwas not sent before. NOOP otherwise. + if (status != MessageSourceStatus.Sent) { + emit MessageSent(id, message); + } + return id; + } + + /// @dev Internal version of {forwardMessage} without access control. + function _forwardMessage( + bytes32 id, + MessageSourceStatus status, + Message memory message + ) internal virtual returns (bytes32) { + // Check if the message was created first. NOOP otherwise. + if (status == MessageSourceStatus.Created) { + _sendMessage(id, status, message); + } + return id; + } + + /// @dev Authorizes the creation of a message on the source chain. + /// + /// Requirements: + /// - The source chain must match `msg.sender` + function _authorizeMessageCreated(string memory source, Message memory message) internal virtual { + CAIP10.Account memory sourceAccount = CAIP10.fromString(source); + + if (CAIP10.getAddress(sourceAccount) != msg.sender) { + revert UnauthorizedSourceMessage(source, msg.sender, message); + } + } + + /// @dev Authorizes the forwarding of a message to the destination chain. + function _authorizeMessageForwarded(string memory destination, Message memory message) internal virtual; + + /// @dev Validates that the source chain is the current chain. + function _validateCurrentChain(string memory source) private view { + CAIP10.Account memory sourceAccount = CAIP10.fromString(source); + if (!CAIP2.isCurrentEVMChain(sourceAccount._chainId)) { + revert MismatchedSourceChain(source); + } + } +} + +abstract contract GatewaySourceGeneric is GatewaySource { + using Set for Set.Bytes32Set; + using CAIP10 for CAIP10.Account; + + Set.Bytes32Set private _createdBox; + Set.Bytes32Set private _sentBox; + + /// @inheritdoc IGatewayCommon + function messageId( + string memory source, + string memory destination, + Message memory message + ) public pure override returns (bytes32) { + return keccak256(abi.encode(source, destination, message)); + } + + /// @inheritdoc IGatewaySource + function sendingStatus(bytes32 id) public view virtual override returns (MessageSourceStatus) { + if (_sentBox.contains(id)) return MessageSourceStatus.Sent; + if (_createdBox.contains(id)) return MessageSourceStatus.Created; + return MessageSourceStatus.Unknown; + } +} diff --git a/contracts/crosschain/GenericGatewayAxelar.sol b/contracts/crosschain/GenericGatewayAxelar.sol deleted file mode 100644 index eebeb5fb..00000000 --- a/contracts/crosschain/GenericGatewayAxelar.sol +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {IAxelarGateway} from "./vendor/axelar/interfaces/IAxelarGateway.sol"; -import {IAxelarGasService} from "./vendor/axelar/interfaces/IAxelarGasService.sol"; - -import {GenericGatewayCommon} from "./GenericGatewayCommon.sol"; - -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -import {Math} from "@openzeppelin/contracts@master/utils/math/Math.sol"; - -contract GenericGatewayAxelar is GenericGatewayCommon, Ownable { - IAxelarGateway public immutable gateway; - IAxelarGasService public immutable gasService; - - struct ChainDetails { - string name; - string remote; - } - - mapping(uint256 chainId => ChainDetails chainName) public chainDetails; - - constructor(IAxelarGateway _gateway, address _initialOwner) Ownable(_initialOwner) { - gateway = _gateway; - } - - function registerForeignChainDetails(uint256 chainId, ChainDetails memory details) public onlyOwner { - require(chainId != block.chainid); - require(bytes(chainDetails[chainId].name).length == 0); - chainDetails[chainId] = details; - } - - function defaultCost(Message memory /*message*/) public pure virtual override returns (address, uint256) { - return (address(0), 0); - } - - function estimateCost(Message memory /*message*/, address asset) public pure virtual returns (uint256) { - return Math.ternary(asset == address(0), 0, type(uint256).max); - } - - /// @dev Override that the target blockchain is registered and that 0 value is passed when creating a request. - function createRequest( - uint256 chain, - address target, - bytes memory data, - bytes32 salt - ) public payable virtual override returns (bytes32) { - require(msg.value == 0, "Axelar does not support native currency bridging"); - - // retrieve chain details for the destination chain - ChainDetails storage details = chainDetails[chain]; - require(bytes(details.name).length > 0, "Remote chain not registered"); - - return super.createRequest(chain, target, data, salt); - } - - function _processRequest( - bytes32 /*id*/, - Request memory req, - address feeAsset, - uint256 feeValue - ) internal virtual override { - require(feeAsset == address(0), "Axelar only supports fees in native currency"); - require(req.message.value == 0, "Axelar does not support native currency bridging"); - - ChainDetails storage details = chainDetails[req.message.destination.chain]; - require(bytes(details.name).length > 0, "Remote chain not registered"); - - bytes memory payload = abi.encode(req); - - // If value is provided, forward it to the gasService - if (feeValue > 0) { - gasService.payNativeGasForContractCall{value: feeValue}( - address(this), - details.name, - details.remote, - payload, - msg.sender - ); - } - - // send cross-chain signal - gateway.callContract(details.name, details.remote, payload); - } -} diff --git a/contracts/crosschain/GenericGatewayCommon.sol b/contracts/crosschain/GenericGatewayCommon.sol deleted file mode 100644 index b0ebd145..00000000 --- a/contracts/crosschain/GenericGatewayCommon.sol +++ /dev/null @@ -1,166 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {IGenericGateway} from "./IGenericGateway.sol"; -import {Set} from "../utils/Set.sol"; - -import {Address} from "@openzeppelin/contracts/utils/Address.sol"; -import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import {SlotDerivation} from "@openzeppelin/contracts@master/utils/SlotDerivation.sol"; -import {StorageSlot} from "@openzeppelin/contracts@master/utils/StorageSlot.sol"; - -/** - * @dev Generic implementation of a Gateway contract according to ERC-XXXX definitions. - */ -abstract contract GenericGatewayCommon is IGenericGateway { - using SlotDerivation for *; - using StorageSlot for *; - using Set for Set.Bytes32Set; - - event RequestCreated(bytes32 id, Request req); - event RequestForwarded(bytes32 id); - event RequestExecuted(bytes32 id); - - Set.Bytes32Set private _outBox; - - // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.GenericGatewayCommon")) - 1)) & ~bytes32(uint256(0xff)) - bytes32 private constant GENERIC_GATEWAY_COMMON_STORAGE = - 0x71ab59e9fe1edd5f3d56389a2c715a90ddbb606dacc41e1c5360c10e3fe15b00; - - function crossChainSender() public view returns (uint256 chainId, address sender) { - return ( - GENERIC_GATEWAY_COMMON_STORAGE.offset(0).asUint256().tload(), - GENERIC_GATEWAY_COMMON_STORAGE.offset(1).asAddress().tload() - ); - } - - // This must be redeclared as public so that other function can call it - function defaultCost(Message memory /*message*/) public pure virtual returns (address, uint256); - - function sendRequest( - uint256 chain, - address target, - uint256 value, - bytes memory data, - bytes32 salt - ) public payable virtual returns (bytes32) { - (address feeAsset, uint256 feeValue) = defaultCost( - Message({destination: Account({chain: chain, instance: target}), value: value, data: data}) - ); - return sendRequest(chain, target, value, data, salt, feeAsset, feeValue); - } - - function sendRequest( - uint256 chain, - address target, - uint256 value, - bytes memory data, - bytes32 salt, - address feeAsset, - uint256 feeValue - ) public payable virtual returns (bytes32) { - // build request, payload and hash - Request memory req = _generateRequest(chain, target, msg.sender, value, data, salt); - bytes32 id = keccak256(abi.encode(req)); - - if (feeAsset == address(0)) { - uint256 totalValue = value + feeValue; - require(msg.value >= totalValue, "invalid value provided"); - if (msg.value > totalValue) Address.sendValue(payable(msg.sender), msg.value - totalValue); - } else { - require(msg.value >= value, "invalid value provided"); - if (feeValue > 0) SafeERC20.safeTransferFrom(IERC20(feeAsset), msg.sender, address(this), feeValue); - if (msg.value > value) Address.sendValue(payable(msg.sender), msg.value - value); - } - - _processRequest(id, req, feeAsset, feeValue); - - // TODO: event - - return id; - } - - // ================================================= 2 step mode ================================================= - - function createRequest( - uint256 chain, - address target, - bytes memory data, - bytes32 salt - ) public payable virtual returns (bytes32) { - // build request, payload and hash - Request memory req = _generateRequest(chain, target, msg.sender, msg.value, data, salt); - bytes32 id = keccak256(abi.encode(req)); - - // register the request hash - require(_outBox.insert(id), "Ticket already scheduled"); - - // emit notice - emit RequestCreated(id, req); - - return id; - } - - function forwardRequest(Request memory req) public payable virtual { - (address feeAsset, uint256 feeValue) = defaultCost(req.message); - forwardRequest(req, feeAsset, feeValue); - } - - function forwardRequest(Request memory req, address feeAsset, uint256 feeValue) public payable virtual { - // compute the request hash - bytes32 id = keccak256(abi.encode(req)); - - if (feeAsset == address(0)) { - require(msg.value >= feeValue, "invalid value provided"); - if (msg.value > feeValue) Address.sendValue(payable(msg.sender), msg.value - feeValue); - } else { - if (feeValue > 0) SafeERC20.safeTransferFrom(IERC20(feeAsset), msg.sender, address(this), feeValue); - if (msg.value > 0) Address.sendValue(payable(msg.sender), msg.value); - } - - // consume request hash - require(_outBox.remove(id), "Ticket not scheduled"); - - _processRequest(id, req, feeAsset, feeValue); - - // emit notice - emit RequestForwarded(id); - } - - // =============================================== specialisation ================================================ - - function _processRequest(bytes32 id, Request memory req, address feeAsset, uint256 feeValue) internal virtual; - - // =================================================== helpers =================================================== - function _generateRequest( - uint256 chain, - address target, - address sender, - uint256 value, - bytes memory data, - bytes32 salt - ) internal view returns (Request memory) { - return - Request({ - source: Account({chain: block.chainid, instance: sender}), - message: Message({destination: Account({chain: chain, instance: target}), value: value, data: data}), - salt: salt - }); - } - - function _executeRequest(Request memory req) internal { - require(req.message.destination.chain == block.chainid); - - GENERIC_GATEWAY_COMMON_STORAGE.offset(0).asUint256().tstore(req.source.chain); - GENERIC_GATEWAY_COMMON_STORAGE.offset(1).asAddress().tstore(req.source.instance); - - (bool success, bytes memory returndata) = req.message.destination.instance.call{value: req.message.value}( - req.message.data - ); - Address.verifyCallResult(success, returndata); - - GENERIC_GATEWAY_COMMON_STORAGE.offset(0).asUint256().tstore(0); - GENERIC_GATEWAY_COMMON_STORAGE.offset(1).asAddress().tstore(address(0)); - } -} diff --git a/contracts/crosschain/ICAIP2Equivalence.sol b/contracts/crosschain/ICAIP2Equivalence.sol new file mode 100644 index 00000000..11e696e5 --- /dev/null +++ b/contracts/crosschain/ICAIP2Equivalence.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {IAxelarGateway} from "./vendor/axelar/interfaces/IAxelarGateway.sol"; +import {IAxelarGasService} from "./vendor/axelar/interfaces/IAxelarGasService.sol"; +import {CAIP2} from "../utils/CAIP-2.sol"; + +/// @dev Equivalence interface between CAIP-2 chain identifiers and Gateway-specific chain identifiers. +/// +/// See https://chainagnostic.org/CAIPs/caip-2[CAIP2]. +interface ICAIP2Equivalence { + /// @dev Sets a CAIP-2 chain identifier as equivalent to a Gateway-specific chain identifier. + function setCAIP2Equivalence(CAIP2.ChainId memory chain, bytes memory custom) external; + + /// @dev Checks if a CAIP-2 chain identifier is registered as equivalent to a Gateway-specific chain identifier. + function exists(CAIP2.ChainId memory chain) external view returns (bool); + + /// @dev Retrieves the Gateway-specific chain identifier equivalent to a CAIP-2 chain identifier. + function fromCAIP2(CAIP2.ChainId memory chain) external pure returns (bytes memory); +} diff --git a/contracts/crosschain/ICAIP2GatewayEquivalence.sol b/contracts/crosschain/ICAIP2GatewayEquivalence.sol deleted file mode 100644 index 53015c93..00000000 --- a/contracts/crosschain/ICAIP2GatewayEquivalence.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {IAxelarGateway} from "./vendor/axelar/interfaces/IAxelarGateway.sol"; -import {IAxelarGasService} from "./vendor/axelar/interfaces/IAxelarGasService.sol"; -import {CAIP2} from "../utils/CAIP-2.sol"; - -interface IGatewayCAIP2Equivalence { - function registerCAIP2Equivalence(CAIP2.ChainId memory chain, bytes memory custom) public; - function isRegisteredCAIP2(CAIP2.ChainId memory chain) public view returns (bool); - function fromCAIP2(CAIP2.ChainId memory chain) public pure returns (bytes memory); -} diff --git a/contracts/crosschain/IGateway.sol b/contracts/crosschain/IGateway.sol deleted file mode 100644 index 3fb6cadd..00000000 --- a/contracts/crosschain/IGateway.sol +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -/// @dev Interface for a generic cross-chain gateway. -/// The gateway is responsible for sending and receiving messages between different blockchains. -interface IGatewayBase { - /// @dev Represents a cross-chain message. - struct Message { - // Native token value to be sent with the message. - uint256 value; - // Arbitrary data to be sent with the message. - bytes payload; - // Extra parameters to be used by the gateway specialization. - bytes extraParams; - } - - /// @dev Uniquely identifies a message. - /// @param source CAIP-10 account ID of the source chain. - /// @param destination CAIP-10 account ID of the destination chain. - function messageId( - string memory source, - string memory destination, - Message memory message - ) external pure returns (bytes32); -} - -/// @dev Interface for a cross-chain gateway that sends messages. -/// Allows for 2 sending modes: {sendMessage} and {createMessage} + {forwardMessage}, -/// where the latter allows to hook logic before sending the message. -interface IGatewaySource is IGatewayBase { - enum MessageSourceStatus { - Unknown, - Created, - Sent - } - - event MessageCreated(bytes32 indexed id, Message message); - event MessageSent(bytes32 indexed id, Message message); - - error UnauthorizedSourceMessage(string source, address sender, Message message); - - /// @dev Returns the status of a sent cross-chain message. - function sendingStatus(bytes32 id) external view returns (MessageSourceStatus); - - /// @dev Send a cross-chain message to the target chain. - /// MessageSourceStatus.Unknown -> MessageSourceStatus.Sent - function sendMessage( - string memory source, - string memory destination, - Message memory message - ) external payable returns (bytes32); - - /// @dev Create a cross-chain message to the target chain. See {forwardMessage} to send it. - /// MessageSourceStatus.Unknown -> MessageSourceStatus.Created - function createMessage( - string memory source, - string memory destination, - Message memory message - ) external payable returns (bytes32); - - /// @dev Forwards a previously created cross-chain message to the target chain. See {createMessage} to create it. - /// MessageSourceStatus.Created -> MessageSourceStatus.Sent - function forwardMessage( - string memory source, - string memory destination, - Message memory message - ) external payable returns (bytes32); -} - -/// @dev Interface for a cross-chain gateway that receives messages. -/// Allows to check the status of a message and to mark it as delivered or executed. -interface IGatewayDestination is IGatewayBase { - enum MessageDestinationStatus { - Unknown, - Delivered, - Executed - } - - event MessageDelivered(bytes32 indexed id, Message message); - event MessageExecuted(bytes32 indexed id, Message message); - - /// @dev Returns the status of a received cross-chain message. - function destinationStatus(bytes32 id) external view returns (MessageDestinationStatus); - - /// @dev Sets a cross-chain message as delivered, ready for execution. - /// MessageDestinationStatus.Unknown -> MessageDestinationStatus.Delivered - /// NOTE: Should only be called by an authorized gateway operator. - function deliverMessage( - string memory source, - string memory destination, - Message memory message - ) external returns (bytes32); - - /// @dev Marks a cross-chain message as executed. - /// MessageDestinationStatus.Unknown -> MessageDestinationStatus.Executed. - /// MessageDestinationStatus.Delivered -> MessageDestinationStatus.Executed. - /// NOTE: Should only be called by the destination account. - function setMessageExecuted( - string memory source, - string memory destination, - Message memory message - ) external returns (bytes32); -} diff --git a/contracts/crosschain/IGatewayCommon.sol b/contracts/crosschain/IGatewayCommon.sol new file mode 100644 index 00000000..2d198ad3 --- /dev/null +++ b/contracts/crosschain/IGatewayCommon.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +/// @dev Common for a generic cross-chain gateway. +/// +/// Gateways are split in two parts: source and destination. Respectively, they are responsible for +/// sending and receiving messages between different blockchains. +interface IGatewayCommon { + /// @dev Represents a cross-chain message. + struct Message { + // Arbitrary data to be sent with the message. + bytes payload; + // Extra parameters to be used by the gateway specialization. + bytes extraParams; + } + + /// @dev Uniquely identifies a message. + /// @param source CAIP-10 account ID of the source chain. + /// @param destination CAIP-10 account ID of the destination chain. + function messageId( + string memory source, + string memory destination, + Message memory message + ) external pure returns (bytes32); +} diff --git a/contracts/crosschain/IGatewayDestination.sol b/contracts/crosschain/IGatewayDestination.sol new file mode 100644 index 00000000..02224cd9 --- /dev/null +++ b/contracts/crosschain/IGatewayDestination.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {IGatewayCommon} from "./IGatewayCommon.sol"; + +/// @dev Interface for a cross-chain gateway that receives messages. +/// +/// Allows to check the status of a message and to mark it as delivered or executed. +interface IGatewayDestination is IGatewayCommon { + enum MessageDestinationStatus { + Unknown, + Delivered, + Executed + } + + event MessageDelivered(bytes32 indexed id, Message message); + event MessageExecuted(bytes32 indexed id, Message message); + + error UnauthorizedDestinationMessage(string destination, address sender, Message message); + error MismatchedDestinationChain(string destination); + + /// @dev Returns the status of a received cross-chain message. + function destinationStatus(bytes32 id) external view returns (MessageDestinationStatus); + + /// @dev Sets a cross-chain message as delivered, ready for execution. + /// MessageDestinationStatus.Unknown -> MessageDestinationStatus.Delivered + /// NOTE: Should only be called by an authorized gateway operator and validate that destination is the current chain. + function deliverMessage( + string memory source, + string memory destination, + Message memory message + ) external returns (bytes32); + + /// @dev Sets a cross-chain message as executed. + /// MessageDestinationStatus.Unknown -> MessageDestinationStatus.Executed. + /// MessageDestinationStatus.Delivered -> MessageDestinationStatus.Executed. + /// NOTE: Should only be called by the destination account. + function setMessageExecuted( + string memory source, + string memory destination, + Message memory message + ) external returns (bytes32); +} diff --git a/contracts/crosschain/IGatewaySource.sol b/contracts/crosschain/IGatewaySource.sol new file mode 100644 index 00000000..ba4c12b1 --- /dev/null +++ b/contracts/crosschain/IGatewaySource.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {IGatewayCommon} from "./IGatewayCommon.sol"; + +/// @dev Interface for a cross-chain gateway that sends messages. +/// +/// Allows for 2 sending modes: {sendMessage} and {createMessage} + {forwardMessage}, +/// where the latter allows to hook logic before sending the message. +interface IGatewaySource is IGatewayCommon { + enum MessageSourceStatus { + Unknown, + Created, + Sent + } + + event MessageCreated(bytes32 indexed id, Message message); + event MessageSent(bytes32 indexed id, Message message); + + error UnauthorizedSourceMessage(string source, address sender, Message message); + error MismatchedSourceChain(string source); + + /// @dev Returns the status of a sent cross-chain message. + function sendingStatus(bytes32 id) external view returns (MessageSourceStatus); + + /// @dev Send a cross-chain message to the target chain. + /// MessageSourceStatus.Unknown -> MessageSourceStatus.Sent + /// NOTE: Must validate that source is the current chain. + function sendMessage( + string memory source, + string memory destination, + Message memory message + ) external payable returns (bytes32); + + /// @dev Create a cross-chain message to the target chain. See {forwardMessage} to send it. + /// MessageSourceStatus.Unknown -> MessageSourceStatus.Created + /// NOTE: Must validate that source is the current chain. + function createMessage( + string memory source, + string memory destination, + Message memory message + ) external payable returns (bytes32); + + /// @dev Forwards a previously created cross-chain message to the target chain. See {createMessage} to create it. + /// MessageSourceStatus.Created -> MessageSourceStatus.Sent + function forwardMessage( + string memory source, + string memory destination, + Message memory message + ) external payable returns (bytes32); +} diff --git a/contracts/crosschain/GatewayAxelarCAIP2.sol b/contracts/crosschain/axelar/GatewayAxelarCAIP2.sol similarity index 84% rename from contracts/crosschain/GatewayAxelarCAIP2.sol rename to contracts/crosschain/axelar/GatewayAxelarCAIP2.sol index ccf20005..6fa778fb 100644 --- a/contracts/crosschain/GatewayAxelarCAIP2.sol +++ b/contracts/crosschain/axelar/GatewayAxelarCAIP2.sol @@ -2,10 +2,10 @@ pragma solidity ^0.8.0; -import {IGatewayCAIP2Equivalence} from "./ICAIP2GatewayEquivalence.sol"; -import {CAIP2} from "../utils/CAIP-2.sol"; +import {ICAIP2Equivalence} from "../ICAIP2Equivalence.sol"; +import {CAIP2} from "../../utils/CAIP-2.sol"; -abstract contract GatewayAxelarCAIP2 is IGatewayCAIP2Equivalence { +abstract contract GatewayAxelarCAIP2 is ICAIP2Equivalence { error AlreadyRegisteredChain(CAIP2.ChainId chain); error UnsupportedChain(CAIP2.ChainId chain); diff --git a/contracts/crosschain/axelar/GatewayAxelarSource.sol b/contracts/crosschain/axelar/GatewayAxelarSource.sol new file mode 100644 index 00000000..3136e706 --- /dev/null +++ b/contracts/crosschain/axelar/GatewayAxelarSource.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {IAxelarGateway} from "../vendor/axelar/interfaces/IAxelarGateway.sol"; +import {IAxelarGasService} from "../vendor/axelar/interfaces/IAxelarGasService.sol"; + +import {GatewaySource} from "../GatewaySource.sol"; +import {GatewayAxelarCAIP2} from "./GatewayAxelarCAIP2.sol"; +import {CAIP10} from "../../utils/CAIP-10.sol"; + +contract GatewayAxelarSource is GatewaySource, GatewayAxelarCAIP2 { + IAxelarGateway public immutable axelarGateway; + IAxelarGasService public immutable gasService; + + constructor(IAxelarGateway _axelarGateway, address _initialOwner) Ownable(_initialOwner) { + axelarGateway = _axelarGateway; + } + + /// @inheritdoc GatewayDestination + function messageId( + string memory source, + string memory destination, + Message memory message + ) public pure override returns (bytes32) { + return keccak256(abi.encode(source, destination, message)); + } + + /// @inheritdoc GatewayDestination + function destinationStatus(bytes32 id) public view override returns (MessageDestinationStatus) { + if (axelarGateway.isCommandExecuted(commandId)) return MessageDestinationStatus.Executed; + if (_deliveredBox.contains(id)) return MessageDestinationStatus.Delivered; + return MessageDestinationStatus.Unknown; + } + + function _authorizeMessageCreated( + string memory source, + Message memory message + ) internal override returns (bytes32) { + if (!isRegisteredCAIP2(CAIP10.fromString(destination)._chainId)) { + revert UnsupportedChain(chain); + } + + return super._authorizeMessageCreated(source, message); + } + + function _sendMessage( + bytes32 id, + string memory /* source */, + string memory destination, + MessageSourceStatus status, + Message memory message + ) internal override returns (bytes32) { + super._sendMessage(id, status, message); + + if (status != MessageSourceStatus.Sent) { + AxelarChain memory details = chainDetails[CAIP10.fromString(destination)._chainId]; + axelarGateway.callContract(details.name, details.remote, payload); + } + + return id; + } +} diff --git a/contracts/crosschain/generic/GatewayDestinationGeneric.sol b/contracts/crosschain/generic/GatewayDestinationGeneric.sol new file mode 100644 index 00000000..9e39cc85 --- /dev/null +++ b/contracts/crosschain/generic/GatewayDestinationGeneric.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {CAIP10} from "../../utils/CAIP-10.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {GatewayDestination} from "../GatewayDestination.sol"; +import {Set} from "../../utils/Set.sol"; + +contract GatewayDestinationGeneric is GatewayDestination, Ownable { + using Set for Set.Bytes32Set; + Set.Bytes32Set private _executedBox; + Set.Bytes32Set private _deliveredBox; + + constructor() Ownable(msg.sender) {} + + /// @inheritdoc GatewayDestination + function messageId( + string memory source, + string memory destination, + Message memory message + ) public pure override returns (bytes32) { + return keccak256(abi.encode(source, destination, message)); + } + + /// @inheritdoc GatewayDestination + function destinationStatus(bytes32 id) public view override returns (MessageDestinationStatus) { + if (_executedBox.contains(id)) return MessageDestinationStatus.Executed; + if (_deliveredBox.contains(id)) return MessageDestinationStatus.Delivered; + return MessageDestinationStatus.Unknown; + } + + /// @inheritdoc GatewayDestination + function _authorizeMessageDelivered( + string memory destination, + Message memory message + ) internal virtual override onlyOwner {} + + /// @inheritdoc GatewayDestination + function _deliverMessage( + bytes32 id, + MessageDestinationStatus, + Message memory + ) internal virtual override returns (bytes32) { + _deliveredBox.insert(id); // Idempotent + return id; + } + + /// @inheritdoc GatewayDestination + function _setMessageExecuted( + bytes32 id, + MessageDestinationStatus, + Message memory + ) internal virtual override returns (bytes32) { + _executedBox.insert(id); // Idempotent + return id; + } +} diff --git a/contracts/crosschain/generic/GatewaySourceGeneric.sol b/contracts/crosschain/generic/GatewaySourceGeneric.sol new file mode 100644 index 00000000..49bcaae2 --- /dev/null +++ b/contracts/crosschain/generic/GatewaySourceGeneric.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {CAIP10} from "../../utils/CAIP-10.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {GatewaySource} from "../GatewaySource.sol"; +import {Set} from "../../utils/Set.sol"; + +contract GatewaySourceGeneric is GatewaySource, Ownable { + using Set for Set.Bytes32Set; + using CAIP10 for CAIP10.Account; + + Set.Bytes32Set private _createdBox; + Set.Bytes32Set private _sentBox; + + constructor() Ownable(msg.sender) {} + + /// @inheritdoc GatewaySource + function messageId( + string memory source, + string memory destination, + Message memory message + ) public pure override returns (bytes32) { + return keccak256(abi.encode(source, destination, message)); + } + + /// @inheritdoc GatewaySource + function sendingStatus(bytes32 id) public view virtual override returns (MessageSourceStatus) { + if (_sentBox.contains(id)) return MessageSourceStatus.Sent; + if (_createdBox.contains(id)) return MessageSourceStatus.Created; + return MessageSourceStatus.Unknown; + } + + /// @inheritdoc GatewaySource + function _authorizeMessageForwarded( + string memory destination, + Message memory message + ) internal virtual override onlyOwner {} + + /// @inheritdoc GatewaySource + function _createMessage( + bytes32 id, + MessageSourceStatus, + Message memory + ) internal virtual override returns (bytes32) { + _createdBox.insert(id); + return id; + } + + /// @inheritdoc GatewaySource + function _sendMessage(bytes32 id, MessageSourceStatus, Message memory) internal virtual override returns (bytes32) { + _sentBox.insert(id); + return id; + } +} diff --git a/contracts/utils/CAIP-10.sol b/contracts/utils/CAIP-10.sol index 687d54cf..a9c26c2f 100644 --- a/contracts/utils/CAIP-10.sol +++ b/contracts/utils/CAIP-10.sol @@ -51,6 +51,10 @@ library CAIP10 { return string(_accountId); } + function getAddress(Account memory account) internal pure returns (address) { + return address(bytes20(bytes(account._accountId))); + } + function _findLastSeparatorIndex(bytes memory accountBuffer) private pure returns (uint256) { for (uint256 i = accountBuffer.length - 1; i >= 0; i--) { if (accountBuffer[i] == CAIP2.SEMICOLON) { diff --git a/contracts/utils/CAIP-2.sol b/contracts/utils/CAIP-2.sol index 61e5faee..9a7ae9a8 100644 --- a/contracts/utils/CAIP-2.sol +++ b/contracts/utils/CAIP-2.sol @@ -6,6 +6,8 @@ import {Arrays} from "@openzeppelin/contracts/utils/Arrays.sol"; import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; library CAIP2 { + bytes16 private constant HEX_DIGITS = "0123456789abcdef"; + // chain_id: namespace + ":" + reference // namespace: [-a-z0-9]{3,8} // reference: [-_a-zA-Z0-9]{1,32} @@ -61,4 +63,26 @@ library CAIP2 { } return length; } + + function isCurrentEVMChain(ChainId memory chain) internal view returns (bool) { + return + chain._namespace == currentChainId() && // Chain ID must match the current chain + chain._reference == bytes32(bytes(string("eip155"))); // EIP-155 for EVM chains + } + + /// @dev Returns the chain ID of the current chain. + /// Assumes block.chainId < type(uint64).max + function currentChainId() internal view returns (bytes8 _chainId) { + unchecked { + uint256 id = block.chainid; + while (true) { + _chainId = bytes8(uint64(_chainId) - 1); + assembly ("memory-safe") { + mstore8(_chainId, byte(mod(id, 10), HEX_DIGITS)) + } + id /= 10; + if (id == 0) break; + } + } + } } diff --git a/contracts/utils/LinkedList.sol b/contracts/utils/LinkedList.sol new file mode 100644 index 00000000..fe2d2932 --- /dev/null +++ b/contracts/utils/LinkedList.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {Panic} from "@openzeppelin/contracts@master/utils/Panic.sol"; + +/** + * @dev A linked list implemented in an array. + */ +library LinkedList { + struct Node { + uint256 next; + uint256 prev; + bytes32 _value; + } + + struct List { + Node[] nodes; + } + + function insert(Node[] storage self, uint256 index, uint256 value) internal { + if (index > self.length) revert Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS); + + self.push(self[index]); + self[index].prev = self.length - 1; + self[index].next = value; + } +} From 645794b2f74b12bae20fc14c3fef96c6e87682ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernesto=20Garc=C3=ADa?= Date: Tue, 20 Aug 2024 09:54:06 -0600 Subject: [PATCH 10/41] Fix GatewayAxelarSource --- contracts/crosschain/axelar/GatewayAxelarSource.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/crosschain/axelar/GatewayAxelarSource.sol b/contracts/crosschain/axelar/GatewayAxelarSource.sol index 3136e706..b5347669 100644 --- a/contracts/crosschain/axelar/GatewayAxelarSource.sol +++ b/contracts/crosschain/axelar/GatewayAxelarSource.sol @@ -55,7 +55,7 @@ contract GatewayAxelarSource is GatewaySource, GatewayAxelarCAIP2 { if (status != MessageSourceStatus.Sent) { AxelarChain memory details = chainDetails[CAIP10.fromString(destination)._chainId]; - axelarGateway.callContract(details.name, details.remote, payload); + axelarGateway.callContract(details.destinationChain, details.contractAddress, payload); } return id; From 270c5bdbb821e5d40c1d73021fb35c125d45414b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernesto=20Garc=C3=ADa?= Date: Mon, 26 Aug 2024 11:25:38 -0600 Subject: [PATCH 11/41] Remove unnecessary contract --- contracts/utils/LinkedList.sol | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 contracts/utils/LinkedList.sol diff --git a/contracts/utils/LinkedList.sol b/contracts/utils/LinkedList.sol deleted file mode 100644 index fe2d2932..00000000 --- a/contracts/utils/LinkedList.sol +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {Panic} from "@openzeppelin/contracts@master/utils/Panic.sol"; - -/** - * @dev A linked list implemented in an array. - */ -library LinkedList { - struct Node { - uint256 next; - uint256 prev; - bytes32 _value; - } - - struct List { - Node[] nodes; - } - - function insert(Node[] storage self, uint256 index, uint256 value) internal { - if (index > self.length) revert Panic.panic(Panic.ARRAY_OUT_OF_BOUNDS); - - self.push(self[index]); - self[index].prev = self.length - 1; - self[index].next = value; - } -} From 4605a57ab97231542d0b97ebca5cec26618d5ad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernesto=20Garc=C3=ADa?= Date: Mon, 26 Aug 2024 14:08:57 -0600 Subject: [PATCH 12/41] Iteration --- contracts/crosschain/GatewayDestination.sol | 106 ------------- contracts/crosschain/GatewaySource.sol | 145 ------------------ contracts/crosschain/ICAIP2Equivalence.sol | 14 +- contracts/crosschain/IGatewayCommon.sol | 26 ---- contracts/crosschain/IGatewayDestination.sol | 44 ------ contracts/crosschain/IGatewayIncoming.sol | 17 ++ .../crosschain/IGatewayIncomingPassive.sol | 17 ++ contracts/crosschain/IGatewayOutgoing.sol | 22 +++ contracts/crosschain/IGatewayReceiver.sol | 13 ++ contracts/crosschain/IGatewaySource.sol | 52 ------- .../axelar/AxelarCAIP2Equivalence.sol | 88 +++++++++++ .../axelar/AxelarGatewayOutgoing.sol | 26 ++++ .../crosschain/axelar/GatewayAxelarCAIP2.sol | 33 ---- .../crosschain/axelar/GatewayAxelarSource.sol | 63 -------- .../generic/GatewayDestinationGeneric.sol | 58 ------- .../generic/GatewaySourceGeneric.sol | 56 ------- contracts/utils/CAIP-10.sol | 41 ++--- contracts/utils/CAIP-2.sol | 69 ++++----- 18 files changed, 233 insertions(+), 657 deletions(-) delete mode 100644 contracts/crosschain/GatewayDestination.sol delete mode 100644 contracts/crosschain/GatewaySource.sol delete mode 100644 contracts/crosschain/IGatewayCommon.sol delete mode 100644 contracts/crosschain/IGatewayDestination.sol create mode 100644 contracts/crosschain/IGatewayIncoming.sol create mode 100644 contracts/crosschain/IGatewayIncomingPassive.sol create mode 100644 contracts/crosschain/IGatewayOutgoing.sol create mode 100644 contracts/crosschain/IGatewayReceiver.sol delete mode 100644 contracts/crosschain/IGatewaySource.sol create mode 100644 contracts/crosschain/axelar/AxelarCAIP2Equivalence.sol create mode 100644 contracts/crosschain/axelar/AxelarGatewayOutgoing.sol delete mode 100644 contracts/crosschain/axelar/GatewayAxelarCAIP2.sol delete mode 100644 contracts/crosschain/axelar/GatewayAxelarSource.sol delete mode 100644 contracts/crosschain/generic/GatewayDestinationGeneric.sol delete mode 100644 contracts/crosschain/generic/GatewaySourceGeneric.sol diff --git a/contracts/crosschain/GatewayDestination.sol b/contracts/crosschain/GatewayDestination.sol deleted file mode 100644 index 778dc283..00000000 --- a/contracts/crosschain/GatewayDestination.sol +++ /dev/null @@ -1,106 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {CAIP10} from "../utils/CAIP-10.sol"; -import {CAIP2} from "../utils/CAIP-2.sol"; -import {IGatewayDestination, IGatewayCommon} from "./IGatewayDestination.sol"; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -import {Set} from "../utils/Set.sol"; - -/// @dev A gateway destination that receives messages from a source chain. -/// -/// This contract allows for 2 main operations: -/// - Message delivery (i.e. making it available for execution) -/// - Message execution (i.e. marking it as executed) -/// -/// Message delivery is permissioned through {_authorizeMessageDelivered} and it's usually set -/// to an authority that can validate the message and make it available for execution. -/// -/// Message execution is permissioned through {_authorizeMessageExecuted} and it checks if the -/// destination account is the one marking the message as executed. -abstract contract GatewayDestination is IGatewayDestination { - /// @inheritdoc IGatewayCommon - function messageId( - string memory source, - string memory destination, - Message memory message - ) public pure virtual override returns (bytes32); - - /// @inheritdoc IGatewayDestination - function destinationStatus(bytes32 id) public view virtual override returns (MessageDestinationStatus); - - /// @inheritdoc IGatewayDestination - function deliverMessage( - string memory source, - string memory destination, - Message memory message - ) external returns (bytes32) { - bytes32 id = messageId(source, destination, message); - MessageDestinationStatus status = destinationStatus(id); - _validateCurrentChain(destination); - _authorizeMessageDelivered(destination, message); - return _deliverMessage(id, status, message); - } - - /// @inheritdoc IGatewayDestination - function setMessageExecuted( - string memory source, - string memory destination, - Message memory message - ) external returns (bytes32) { - bytes32 id = messageId(source, destination, message); - MessageDestinationStatus status = destinationStatus(id); - _authorizeMessageExecuted(destination, message); - return _setMessageExecuted(id, status, message); - } - - /// @dev Internal version of {deliverMessage} without access control. - function _deliverMessage( - bytes32 id, - MessageDestinationStatus status, - Message memory message - ) internal virtual returns (bytes32) { - // Check if the message was not delivered or executed before. NOOP otherwise. - if (status == MessageDestinationStatus.Unknown) { - emit MessageDelivered(id, message); - } - return id; - } - - /// @dev Internal version of {setMessageExecuted} without access control. - function _setMessageExecuted( - bytes32 id, - MessageDestinationStatus status, - Message memory message - ) internal virtual returns (bytes32) { - // Check if the message was not executed already. NOOP otherwise. - if (status != MessageDestinationStatus.Executed) { - emit MessageExecuted(id, message); - } - return id; - } - - /// @dev Authorizes the delivery of a message to the destination chain. - function _authorizeMessageDelivered(string memory destination, Message memory message) internal virtual; - - /// @dev Validates a message submitted as executed. - /// - /// Requirements: - /// - The destination must be the `msg.sender` - function _authorizeMessageExecuted(string memory destination, Message memory message) internal virtual { - CAIP10.Account memory destinationAccount = CAIP10.fromString(destination); - - if (CAIP10.getAddress(destinationAccount) != msg.sender) { - revert UnauthorizedDestinationMessage(destination, msg.sender, message); - } - } - - /// @dev Validates that the destination chain is the current chain. - function _validateCurrentChain(string memory destination) private view { - CAIP10.Account memory destinationAccount = CAIP10.fromString(destination); - if (!CAIP2.isCurrentEVMChain(destinationAccount._chainId)) { - revert MismatchedDestinationChain(destination); - } - } -} diff --git a/contracts/crosschain/GatewaySource.sol b/contracts/crosschain/GatewaySource.sol deleted file mode 100644 index 63dd08e7..00000000 --- a/contracts/crosschain/GatewaySource.sol +++ /dev/null @@ -1,145 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; -import {CAIP10} from "../utils/CAIP-10.sol"; -import {CAIP2} from "../utils/CAIP-2.sol"; -import {IGatewaySource, IGatewayCommon} from "./IGatewaySource.sol"; -import {Set} from "../utils/Set.sol"; - -/// @dev Gateway contract on the source chain according to ERC-XXXX definitions. -/// -/// This is a generic implementation of an asynchronous message-passing system between accounts on different chains. -abstract contract GatewaySource is IGatewaySource { - /// @inheritdoc IGatewayCommon - function messageId( - string memory source, - string memory destination, - Message memory message - ) public pure virtual returns (bytes32); - - /// @inheritdoc IGatewaySource - function sendingStatus(bytes32 id) public view virtual returns (MessageSourceStatus); - - /// @inheritdoc IGatewaySource - function sendMessage( - string memory source, - string memory destination, - Message memory message - ) external payable virtual returns (bytes32) { - bytes32 id = messageId(source, destination, message); - _validateCurrentChain(source); - _authorizeMessageCreated(source, message); - return _sendMessage(id, sendingStatus(id), message); - } - - /// @inheritdoc IGatewaySource - function createMessage( - string memory source, - string memory destination, - Message memory message - ) external payable virtual returns (bytes32) { - bytes32 id = messageId(source, destination, message); - _validateCurrentChain(source); - _authorizeMessageCreated(source, message); - return _createMessage(id, sendingStatus(id), message); - } - - /// @inheritdoc IGatewaySource - function forwardMessage( - string memory source, - string memory destination, - Message memory message - ) external payable virtual returns (bytes32) { - bytes32 id = messageId(source, destination, message); - _authorizeMessageForwarded(destination, message); - return _forwardMessage(id, sendingStatus(id), message); - } - - /// @dev Internal version of {createMessage} without access control. - function _createMessage( - bytes32 id, - MessageSourceStatus status, - Message memory message - ) internal virtual returns (bytes32) { - // Check if the message was not created or sent before. NOOP otherwise. - if (status == MessageSourceStatus.Unknown) { - emit MessageCreated(id, message); - } - return id; - } - - /// @dev Internal version of {sendMessage} without access control. - function _sendMessage( - bytes32 id, - MessageSourceStatus status, - Message memory message - ) internal virtual returns (bytes32) { - /// Check if the message hwas not sent before. NOOP otherwise. - if (status != MessageSourceStatus.Sent) { - emit MessageSent(id, message); - } - return id; - } - - /// @dev Internal version of {forwardMessage} without access control. - function _forwardMessage( - bytes32 id, - MessageSourceStatus status, - Message memory message - ) internal virtual returns (bytes32) { - // Check if the message was created first. NOOP otherwise. - if (status == MessageSourceStatus.Created) { - _sendMessage(id, status, message); - } - return id; - } - - /// @dev Authorizes the creation of a message on the source chain. - /// - /// Requirements: - /// - The source chain must match `msg.sender` - function _authorizeMessageCreated(string memory source, Message memory message) internal virtual { - CAIP10.Account memory sourceAccount = CAIP10.fromString(source); - - if (CAIP10.getAddress(sourceAccount) != msg.sender) { - revert UnauthorizedSourceMessage(source, msg.sender, message); - } - } - - /// @dev Authorizes the forwarding of a message to the destination chain. - function _authorizeMessageForwarded(string memory destination, Message memory message) internal virtual; - - /// @dev Validates that the source chain is the current chain. - function _validateCurrentChain(string memory source) private view { - CAIP10.Account memory sourceAccount = CAIP10.fromString(source); - if (!CAIP2.isCurrentEVMChain(sourceAccount._chainId)) { - revert MismatchedSourceChain(source); - } - } -} - -abstract contract GatewaySourceGeneric is GatewaySource { - using Set for Set.Bytes32Set; - using CAIP10 for CAIP10.Account; - - Set.Bytes32Set private _createdBox; - Set.Bytes32Set private _sentBox; - - /// @inheritdoc IGatewayCommon - function messageId( - string memory source, - string memory destination, - Message memory message - ) public pure override returns (bytes32) { - return keccak256(abi.encode(source, destination, message)); - } - - /// @inheritdoc IGatewaySource - function sendingStatus(bytes32 id) public view virtual override returns (MessageSourceStatus) { - if (_sentBox.contains(id)) return MessageSourceStatus.Sent; - if (_createdBox.contains(id)) return MessageSourceStatus.Created; - return MessageSourceStatus.Unknown; - } -} diff --git a/contracts/crosschain/ICAIP2Equivalence.sol b/contracts/crosschain/ICAIP2Equivalence.sol index 11e696e5..68ac10af 100644 --- a/contracts/crosschain/ICAIP2Equivalence.sol +++ b/contracts/crosschain/ICAIP2Equivalence.sol @@ -4,18 +4,16 @@ pragma solidity ^0.8.0; import {IAxelarGateway} from "./vendor/axelar/interfaces/IAxelarGateway.sol"; import {IAxelarGasService} from "./vendor/axelar/interfaces/IAxelarGasService.sol"; -import {CAIP2} from "../utils/CAIP-2.sol"; -/// @dev Equivalence interface between CAIP-2 chain identifiers and Gateway-specific chain identifiers. +/// @dev Equivalence interface between CAIP-2 chain identifiers and protocol-specific chain identifiers. /// /// See https://chainagnostic.org/CAIPs/caip-2[CAIP2]. interface ICAIP2Equivalence { - /// @dev Sets a CAIP-2 chain identifier as equivalent to a Gateway-specific chain identifier. - function setCAIP2Equivalence(CAIP2.ChainId memory chain, bytes memory custom) external; + error UnsupportedChain(string caip2); - /// @dev Checks if a CAIP-2 chain identifier is registered as equivalent to a Gateway-specific chain identifier. - function exists(CAIP2.ChainId memory chain) external view returns (bool); + /// @dev Checks if a CAIP-2 chain identifier is registered as equivalent to a protocol-specific chain identifier. + function supported(string memory caip2) external view returns (bool); - /// @dev Retrieves the Gateway-specific chain identifier equivalent to a CAIP-2 chain identifier. - function fromCAIP2(CAIP2.ChainId memory chain) external pure returns (bytes memory); + /// @dev Retrieves the protocol-specific chain identifier equivalent to a CAIP-2 chain identifier. + function fromCAIP2(string memory caip2) external view returns (bytes memory); } diff --git a/contracts/crosschain/IGatewayCommon.sol b/contracts/crosschain/IGatewayCommon.sol deleted file mode 100644 index 2d198ad3..00000000 --- a/contracts/crosschain/IGatewayCommon.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -/// @dev Common for a generic cross-chain gateway. -/// -/// Gateways are split in two parts: source and destination. Respectively, they are responsible for -/// sending and receiving messages between different blockchains. -interface IGatewayCommon { - /// @dev Represents a cross-chain message. - struct Message { - // Arbitrary data to be sent with the message. - bytes payload; - // Extra parameters to be used by the gateway specialization. - bytes extraParams; - } - - /// @dev Uniquely identifies a message. - /// @param source CAIP-10 account ID of the source chain. - /// @param destination CAIP-10 account ID of the destination chain. - function messageId( - string memory source, - string memory destination, - Message memory message - ) external pure returns (bytes32); -} diff --git a/contracts/crosschain/IGatewayDestination.sol b/contracts/crosschain/IGatewayDestination.sol deleted file mode 100644 index 02224cd9..00000000 --- a/contracts/crosschain/IGatewayDestination.sol +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {IGatewayCommon} from "./IGatewayCommon.sol"; - -/// @dev Interface for a cross-chain gateway that receives messages. -/// -/// Allows to check the status of a message and to mark it as delivered or executed. -interface IGatewayDestination is IGatewayCommon { - enum MessageDestinationStatus { - Unknown, - Delivered, - Executed - } - - event MessageDelivered(bytes32 indexed id, Message message); - event MessageExecuted(bytes32 indexed id, Message message); - - error UnauthorizedDestinationMessage(string destination, address sender, Message message); - error MismatchedDestinationChain(string destination); - - /// @dev Returns the status of a received cross-chain message. - function destinationStatus(bytes32 id) external view returns (MessageDestinationStatus); - - /// @dev Sets a cross-chain message as delivered, ready for execution. - /// MessageDestinationStatus.Unknown -> MessageDestinationStatus.Delivered - /// NOTE: Should only be called by an authorized gateway operator and validate that destination is the current chain. - function deliverMessage( - string memory source, - string memory destination, - Message memory message - ) external returns (bytes32); - - /// @dev Sets a cross-chain message as executed. - /// MessageDestinationStatus.Unknown -> MessageDestinationStatus.Executed. - /// MessageDestinationStatus.Delivered -> MessageDestinationStatus.Executed. - /// NOTE: Should only be called by the destination account. - function setMessageExecuted( - string memory source, - string memory destination, - Message memory message - ) external returns (bytes32); -} diff --git a/contracts/crosschain/IGatewayIncoming.sol b/contracts/crosschain/IGatewayIncoming.sol new file mode 100644 index 00000000..cf01f8b6 --- /dev/null +++ b/contracts/crosschain/IGatewayIncoming.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +interface IGatewayIncoming { + event MessageExecuted(bytes32 indexed id); +} + +interface IGatewayIncomingPassive { + function validateReceivedMessage( + bytes32 messageId, + string calldata srcChain, + string calldata srcAccount, + bytes calldata payload, + bytes calldata attributes + ) external; +} diff --git a/contracts/crosschain/IGatewayIncomingPassive.sol b/contracts/crosschain/IGatewayIncomingPassive.sol new file mode 100644 index 00000000..cf01f8b6 --- /dev/null +++ b/contracts/crosschain/IGatewayIncomingPassive.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +interface IGatewayIncoming { + event MessageExecuted(bytes32 indexed id); +} + +interface IGatewayIncomingPassive { + function validateReceivedMessage( + bytes32 messageId, + string calldata srcChain, + string calldata srcAccount, + bytes calldata payload, + bytes calldata attributes + ) external; +} diff --git a/contracts/crosschain/IGatewayOutgoing.sol b/contracts/crosschain/IGatewayOutgoing.sol new file mode 100644 index 00000000..69079ac9 --- /dev/null +++ b/contracts/crosschain/IGatewayOutgoing.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +interface IGatewayOutgoing { + struct Message { + string source; // CAIP-10 account ID + string destination; // CAIP-10 account ID + bytes payload; + bytes attributes; + } + + event MessageCreated(bytes32 indexed id, Message message); + event MessageSent(bytes32 indexed id); + + function sendMessage( + string calldata destChain, // CAIP-2 chain ID + string calldata destAccount, // i.e. address + bytes calldata payload, + bytes calldata attributes + ) external payable returns (bytes32 messageId); +} diff --git a/contracts/crosschain/IGatewayReceiver.sol b/contracts/crosschain/IGatewayReceiver.sol new file mode 100644 index 00000000..6035ebfe --- /dev/null +++ b/contracts/crosschain/IGatewayReceiver.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +interface IGatewayReceiver { + function receiveMessage( + bytes32 messageId, + string calldata srcChain, + string calldata srcAccount, + bytes calldata payload, + bytes calldata attributes + ) external payable; +} diff --git a/contracts/crosschain/IGatewaySource.sol b/contracts/crosschain/IGatewaySource.sol deleted file mode 100644 index ba4c12b1..00000000 --- a/contracts/crosschain/IGatewaySource.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {IGatewayCommon} from "./IGatewayCommon.sol"; - -/// @dev Interface for a cross-chain gateway that sends messages. -/// -/// Allows for 2 sending modes: {sendMessage} and {createMessage} + {forwardMessage}, -/// where the latter allows to hook logic before sending the message. -interface IGatewaySource is IGatewayCommon { - enum MessageSourceStatus { - Unknown, - Created, - Sent - } - - event MessageCreated(bytes32 indexed id, Message message); - event MessageSent(bytes32 indexed id, Message message); - - error UnauthorizedSourceMessage(string source, address sender, Message message); - error MismatchedSourceChain(string source); - - /// @dev Returns the status of a sent cross-chain message. - function sendingStatus(bytes32 id) external view returns (MessageSourceStatus); - - /// @dev Send a cross-chain message to the target chain. - /// MessageSourceStatus.Unknown -> MessageSourceStatus.Sent - /// NOTE: Must validate that source is the current chain. - function sendMessage( - string memory source, - string memory destination, - Message memory message - ) external payable returns (bytes32); - - /// @dev Create a cross-chain message to the target chain. See {forwardMessage} to send it. - /// MessageSourceStatus.Unknown -> MessageSourceStatus.Created - /// NOTE: Must validate that source is the current chain. - function createMessage( - string memory source, - string memory destination, - Message memory message - ) external payable returns (bytes32); - - /// @dev Forwards a previously created cross-chain message to the target chain. See {createMessage} to create it. - /// MessageSourceStatus.Created -> MessageSourceStatus.Sent - function forwardMessage( - string memory source, - string memory destination, - Message memory message - ) external payable returns (bytes32); -} diff --git a/contracts/crosschain/axelar/AxelarCAIP2Equivalence.sol b/contracts/crosschain/axelar/AxelarCAIP2Equivalence.sol new file mode 100644 index 00000000..aa17c3c3 --- /dev/null +++ b/contracts/crosschain/axelar/AxelarCAIP2Equivalence.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {ICAIP2Equivalence} from "../ICAIP2Equivalence.sol"; + +abstract contract AxelarCAIP2Equivalence is ICAIP2Equivalence { + mapping(string caip2 => string destinationChain) private _equivalence; + + function supported(string memory caip2) public view returns (bool) { + return bytes(_equivalence[caip2]).length != 0; + } + + function fromCAIP2(string memory caip2) public view returns (bytes memory) { + return bytes(_equivalence[caip2]); + } +} + +// EVM (https://axelarscan.io/resources/chains?type=evm) +// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("1"))] = "Ethereum"; +// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("56"))] = "binance"; +// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("137"))] = "Polygon"; +// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("43114"))] = "Avalanche"; +// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("250"))] = "Fantom"; +// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("1284"))] = "Moonbeam"; +// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("1313161554"))] = "aurora"; +// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("42161"))] = "arbitrum"; +// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("10"))] = "optimism"; +// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("8453"))] = "base"; +// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("5000"))] = "mantle"; +// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("42220"))] = "celo"; +// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("2222"))] = "kava"; +// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("314"))] = "filecoin"; +// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("59144"))] = "linea"; +// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("2031"))] = "centrifuge"; +// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("534352"))] = "scroll"; +// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("13371"))] = "immutable"; +// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("252"))] = "fraxtal"; +// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("81457"))] = "blast"; + +// Cosmos (https://axelarscan.io/resources/chains?type=cosmos) +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('axelar-dojo-1'))] = 'Axelarnet'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('osmosis-1'))] = 'osmosis'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('cosmoshub-4'))] = 'cosmoshub'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('juno-1'))] = 'juno'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('emoney-3'))] = 'e-money'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('injective-1'))] = 'injective'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('crescent-1'))] = 'crescent'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('kaiyo-1'))] = 'kujira'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('secret-4'))] = 'secret-snip'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('secret-4'))] = 'secret'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('pacific-1'))] = 'sei'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('stargaze-1'))] = 'stargaze'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('mantle-1'))] = 'assetmantle'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('fetchhub-4'))] = 'fetch'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('kichain-2'))] = 'ki'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('evmos_9001-2'))] = 'evmos'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('xstaxy-1'))] = 'aura'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('comdex-1'))] = 'comdex'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('core-1'))] = 'persistence'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('regen-1'))] = 'regen'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('umee-1'))] = 'umee'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('agoric-3'))] = 'agoric'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('dimension_37-1'))] = 'xpla'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('acre_9052-1'))] = 'acre'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('stride-1'))] = 'stride'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('carbon-1'))] = 'carbon'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('sommelier-3'))] = 'sommelier'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('neutron-1'))] = 'neutron'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('reb_1111-1'))] = 'rebus'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('archway-1'))] = 'archway'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('pio-mainnet-1'))] = 'provenance'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('ixo-5'))] = 'ixo'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('migaloo-1'))] = 'migaloo'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('teritori-1'))] = 'teritori'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('haqq_11235-1'))] = 'haqq'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('celestia'))] = 'celestia'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('agamotto'))] = 'ojo'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('chihuahua-1'))] = 'chihuahua'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('ssc-1'))] = 'saga'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('dymension_1100-1'))] = 'dymension'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('fxcore'))] = 'fxcore'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('perun-1'))] = 'c4e'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('bitsong-2b'))] = 'bitsong'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('pirin-1'))] = 'nolus'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('lava-mainnet-1'))] = 'lava'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('phoenix-1'))] = 'terra-2'; +// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('columbus-5'))] = 'terra';" diff --git a/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol b/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol new file mode 100644 index 00000000..3865e55d --- /dev/null +++ b/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {IAxelarGateway} from "../vendor/axelar/interfaces/IAxelarGateway.sol"; +import {IAxelarGasService} from "../vendor/axelar/interfaces/IAxelarGasService.sol"; +import {IGatewayOutgoing} from "../IGatewayOutgoing.sol"; +import {AxelarCAIP2Equivalence} from "./AxelarCAIP2Equivalence.sol"; +import {CAIP2} from "../../utils/CAIP-2.sol"; +import {CAIP10} from "../../utils/CAIP-10.sol"; + +abstract contract AxelarGatewayOutgoing is IGatewayOutgoing, AxelarCAIP2Equivalence { + IAxelarGateway public immutable axelarGateway; + + function sendMessage( + string calldata destChain, // CAIP-2 chain ID + string calldata destAccount, // i.e. address + bytes calldata payload, + bytes calldata /* attributes */ + ) external payable override returns (bytes32 messageId) { + // TODO: Handle ether (payable) + if (!supported(destChain)) revert UnsupportedChain(destChain); + axelarGateway.callContract(string(fromCAIP2(destChain)), destAccount, payload); + return keccak256(payload); + } +} diff --git a/contracts/crosschain/axelar/GatewayAxelarCAIP2.sol b/contracts/crosschain/axelar/GatewayAxelarCAIP2.sol deleted file mode 100644 index 6fa778fb..00000000 --- a/contracts/crosschain/axelar/GatewayAxelarCAIP2.sol +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {ICAIP2Equivalence} from "../ICAIP2Equivalence.sol"; -import {CAIP2} from "../../utils/CAIP-2.sol"; - -abstract contract GatewayAxelarCAIP2 is ICAIP2Equivalence { - error AlreadyRegisteredChain(CAIP2.ChainId chain); - error UnsupportedChain(CAIP2.ChainId chain); - - mapping(CAIP2.Chain chain => AxelarChain chainName) public chainDetails; - - struct AxelarChain { - string destinationChain; - string contractAddress; - } - - function isRegisteredCAIP2(CAIP2.ChainId memory chain) public view override returns (bool) { - return bytes(chainDetails[chain].destinationChain).length != 0; - } - - function fromCAIP2(CAIP2.ChainId memory chain) public pure returns (bytes memory) { - return abi.encode(AxelarChain(chain.destinationChain, chain.contractAddress)); - } - - function registerCAIP2Equivalence(CAIP2.ChainId memory chain, bytes memory custom) public onlyOwner { - if (isRegisteredCAIP2(chain)) { - revert AlreadyRegisteredChain(chain); - } - chainDetails[chain] = abi.decode(custom, (AxelarChain)); - } -} diff --git a/contracts/crosschain/axelar/GatewayAxelarSource.sol b/contracts/crosschain/axelar/GatewayAxelarSource.sol deleted file mode 100644 index b5347669..00000000 --- a/contracts/crosschain/axelar/GatewayAxelarSource.sol +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {IAxelarGateway} from "../vendor/axelar/interfaces/IAxelarGateway.sol"; -import {IAxelarGasService} from "../vendor/axelar/interfaces/IAxelarGasService.sol"; - -import {GatewaySource} from "../GatewaySource.sol"; -import {GatewayAxelarCAIP2} from "./GatewayAxelarCAIP2.sol"; -import {CAIP10} from "../../utils/CAIP-10.sol"; - -contract GatewayAxelarSource is GatewaySource, GatewayAxelarCAIP2 { - IAxelarGateway public immutable axelarGateway; - IAxelarGasService public immutable gasService; - - constructor(IAxelarGateway _axelarGateway, address _initialOwner) Ownable(_initialOwner) { - axelarGateway = _axelarGateway; - } - - /// @inheritdoc GatewayDestination - function messageId( - string memory source, - string memory destination, - Message memory message - ) public pure override returns (bytes32) { - return keccak256(abi.encode(source, destination, message)); - } - - /// @inheritdoc GatewayDestination - function destinationStatus(bytes32 id) public view override returns (MessageDestinationStatus) { - if (axelarGateway.isCommandExecuted(commandId)) return MessageDestinationStatus.Executed; - if (_deliveredBox.contains(id)) return MessageDestinationStatus.Delivered; - return MessageDestinationStatus.Unknown; - } - - function _authorizeMessageCreated( - string memory source, - Message memory message - ) internal override returns (bytes32) { - if (!isRegisteredCAIP2(CAIP10.fromString(destination)._chainId)) { - revert UnsupportedChain(chain); - } - - return super._authorizeMessageCreated(source, message); - } - - function _sendMessage( - bytes32 id, - string memory /* source */, - string memory destination, - MessageSourceStatus status, - Message memory message - ) internal override returns (bytes32) { - super._sendMessage(id, status, message); - - if (status != MessageSourceStatus.Sent) { - AxelarChain memory details = chainDetails[CAIP10.fromString(destination)._chainId]; - axelarGateway.callContract(details.destinationChain, details.contractAddress, payload); - } - - return id; - } -} diff --git a/contracts/crosschain/generic/GatewayDestinationGeneric.sol b/contracts/crosschain/generic/GatewayDestinationGeneric.sol deleted file mode 100644 index 9e39cc85..00000000 --- a/contracts/crosschain/generic/GatewayDestinationGeneric.sol +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {CAIP10} from "../../utils/CAIP-10.sol"; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -import {GatewayDestination} from "../GatewayDestination.sol"; -import {Set} from "../../utils/Set.sol"; - -contract GatewayDestinationGeneric is GatewayDestination, Ownable { - using Set for Set.Bytes32Set; - Set.Bytes32Set private _executedBox; - Set.Bytes32Set private _deliveredBox; - - constructor() Ownable(msg.sender) {} - - /// @inheritdoc GatewayDestination - function messageId( - string memory source, - string memory destination, - Message memory message - ) public pure override returns (bytes32) { - return keccak256(abi.encode(source, destination, message)); - } - - /// @inheritdoc GatewayDestination - function destinationStatus(bytes32 id) public view override returns (MessageDestinationStatus) { - if (_executedBox.contains(id)) return MessageDestinationStatus.Executed; - if (_deliveredBox.contains(id)) return MessageDestinationStatus.Delivered; - return MessageDestinationStatus.Unknown; - } - - /// @inheritdoc GatewayDestination - function _authorizeMessageDelivered( - string memory destination, - Message memory message - ) internal virtual override onlyOwner {} - - /// @inheritdoc GatewayDestination - function _deliverMessage( - bytes32 id, - MessageDestinationStatus, - Message memory - ) internal virtual override returns (bytes32) { - _deliveredBox.insert(id); // Idempotent - return id; - } - - /// @inheritdoc GatewayDestination - function _setMessageExecuted( - bytes32 id, - MessageDestinationStatus, - Message memory - ) internal virtual override returns (bytes32) { - _executedBox.insert(id); // Idempotent - return id; - } -} diff --git a/contracts/crosschain/generic/GatewaySourceGeneric.sol b/contracts/crosschain/generic/GatewaySourceGeneric.sol deleted file mode 100644 index 49bcaae2..00000000 --- a/contracts/crosschain/generic/GatewaySourceGeneric.sol +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {CAIP10} from "../../utils/CAIP-10.sol"; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -import {GatewaySource} from "../GatewaySource.sol"; -import {Set} from "../../utils/Set.sol"; - -contract GatewaySourceGeneric is GatewaySource, Ownable { - using Set for Set.Bytes32Set; - using CAIP10 for CAIP10.Account; - - Set.Bytes32Set private _createdBox; - Set.Bytes32Set private _sentBox; - - constructor() Ownable(msg.sender) {} - - /// @inheritdoc GatewaySource - function messageId( - string memory source, - string memory destination, - Message memory message - ) public pure override returns (bytes32) { - return keccak256(abi.encode(source, destination, message)); - } - - /// @inheritdoc GatewaySource - function sendingStatus(bytes32 id) public view virtual override returns (MessageSourceStatus) { - if (_sentBox.contains(id)) return MessageSourceStatus.Sent; - if (_createdBox.contains(id)) return MessageSourceStatus.Created; - return MessageSourceStatus.Unknown; - } - - /// @inheritdoc GatewaySource - function _authorizeMessageForwarded( - string memory destination, - Message memory message - ) internal virtual override onlyOwner {} - - /// @inheritdoc GatewaySource - function _createMessage( - bytes32 id, - MessageSourceStatus, - Message memory - ) internal virtual override returns (bytes32) { - _createdBox.insert(id); - return id; - } - - /// @inheritdoc GatewaySource - function _sendMessage(bytes32 id, MessageSourceStatus, Message memory) internal virtual override returns (bytes32) { - _sentBox.insert(id); - return id; - } -} diff --git a/contracts/utils/CAIP-10.sol b/contracts/utils/CAIP-10.sol index a9c26c2f..d190d95f 100644 --- a/contracts/utils/CAIP-10.sol +++ b/contracts/utils/CAIP-10.sol @@ -2,46 +2,37 @@ pragma solidity ^0.8.0; -import {CAIP2} from "./CAIP-2.sol"; - +// account_id: chain_id + ":" + account_address +// chain_id: [-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32} (See [CAIP-2][]) +// account_address: [-.%a-zA-Z0-9]{1,128} library CAIP10 { - using CAIP2 for CAIP2.ChainId; - - // account_id: chain_id + ":" + account_address - // chain_id: [-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32} (See [CAIP-2][]) - // account_address: [-.%a-zA-Z0-9]{1,128} - struct Account { - CAIP2.ChainId _chainId; - string _accountId; // Often referred to as address - } + bytes1 constant SEMICOLON = ":"; - function toString(Account memory account) internal pure returns (string memory) { - return string(abi.encodePacked(account._chainId.toString(), CAIP2.SEMICOLON, account._accountId)); + function toString(bytes32 chainId, string memory accountId) internal pure returns (string memory) { + return string(abi.encodePacked(chainId, SEMICOLON, accountId)); } - function fromString(string memory accountStr) internal pure returns (Account memory account) { + function fromString(string memory accountStr) internal pure returns (string memory caip2, string memory accountId) { bytes memory accountBuffer = bytes(accountStr); uint256 lastSeparatorIndex = _findLastSeparatorIndex(accountBuffer); - account._chainId = extractChainId(accountBuffer, lastSeparatorIndex); - account._accountId = extractAccountId(accountBuffer, lastSeparatorIndex); - return account; + return (_extractCAIP2(accountBuffer, lastSeparatorIndex), _extractAccountId(accountBuffer, lastSeparatorIndex)); } - function extractChainId( + function _extractCAIP2( bytes memory accountBuffer, uint256 lastSeparatorIndex - ) internal pure returns (CAIP2.ChainId memory chainId) { + ) private pure returns (string memory chainId) { bytes memory _chainId = new bytes(lastSeparatorIndex); for (uint256 i = 0; i < lastSeparatorIndex; i++) { _chainId[i] = accountBuffer[i]; } - return CAIP2.fromString(string(_chainId)); + return string(_chainId); } - function extractAccountId( + function _extractAccountId( bytes memory accountBuffer, uint256 lastSeparatorIndex - ) internal pure returns (string memory) { + ) private pure returns (string memory) { uint256 length = accountBuffer.length; uint256 offset = lastSeparatorIndex - 1; bytes memory _accountId = new bytes(length - offset); // Will overflow if no separator is found @@ -51,13 +42,9 @@ library CAIP10 { return string(_accountId); } - function getAddress(Account memory account) internal pure returns (address) { - return address(bytes20(bytes(account._accountId))); - } - function _findLastSeparatorIndex(bytes memory accountBuffer) private pure returns (uint256) { for (uint256 i = accountBuffer.length - 1; i >= 0; i--) { - if (accountBuffer[i] == CAIP2.SEMICOLON) { + if (accountBuffer[i] == SEMICOLON) { return i; } } diff --git a/contracts/utils/CAIP-2.sol b/contracts/utils/CAIP-2.sol index 9a7ae9a8..2b921ad4 100644 --- a/contracts/utils/CAIP-2.sol +++ b/contracts/utils/CAIP-2.sol @@ -5,33 +5,46 @@ pragma solidity ^0.8.0; import {Arrays} from "@openzeppelin/contracts/utils/Arrays.sol"; import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; +// chain_id: namespace + ":" + reference +// namespace: [-a-z0-9]{3,8} +// reference: [-_a-zA-Z0-9]{1,32} library CAIP2 { bytes16 private constant HEX_DIGITS = "0123456789abcdef"; - - // chain_id: namespace + ":" + reference - // namespace: [-a-z0-9]{3,8} - // reference: [-_a-zA-Z0-9]{1,32} - struct ChainId { - bytes8 _namespace; - bytes32 _reference; - } - bytes1 constant SEMICOLON = ":"; - error TooLongChainId(); - - function toString(ChainId memory chainId) internal pure returns (string memory) { - return string(abi.encodePacked(chainId._namespace, SEMICOLON, chainId._reference)); + function toString(bytes8 namespace, bytes32 ref) internal pure returns (string memory) { + return string(abi.encodePacked(namespace, SEMICOLON, ref)); } /// @dev Parses a chain ID from a string by splitting it at the first semicolon. /// The function parses both sides as `bytes8` and `bytes32` respectively wiothout any validation. - function fromString(string memory chainStr) internal pure returns (ChainId memory chainId) { + function fromString(string memory chainStr) internal pure returns (bytes8 namespace, bytes32 ref) { bytes memory chainBuffer = bytes(chainStr); uint8 semicolonIndex = _findSemicolonIndex(chainBuffer); - chainId._namespace = _extractNamespace(chainBuffer, semicolonIndex); - chainId._reference = _unsafeExtractReference(chainBuffer, semicolonIndex); - return chainId; + return (_extractNamespace(chainBuffer, semicolonIndex), _unsafeExtractReference(chainBuffer, semicolonIndex)); + } + + function isCurrentEVMChain(string memory chainStr) internal view returns (bool) { + (bytes8 namespace, bytes32 ref) = fromString(chainStr); + return + namespace == currentChainId() && // Chain ID must match the current chain + ref == bytes32(bytes(string("eip155"))); // EIP-155 for EVM chains + } + + /// @dev Returns the chain ID of the current chain. + /// Assumes block.chainId < type(uint64).max + function currentChainId() internal view returns (bytes8 _chainId) { + unchecked { + uint256 id = block.chainid; + while (true) { + _chainId = bytes8(uint64(_chainId) - 1); + assembly ("memory-safe") { + mstore8(_chainId, byte(mod(id, 10), HEX_DIGITS)) + } + id /= 10; + if (id == 0) break; + } + } } /// @dev Extracts the first `semicolonIndex` bytes from the chain buffer as a bytes8 namespace. @@ -63,26 +76,4 @@ library CAIP2 { } return length; } - - function isCurrentEVMChain(ChainId memory chain) internal view returns (bool) { - return - chain._namespace == currentChainId() && // Chain ID must match the current chain - chain._reference == bytes32(bytes(string("eip155"))); // EIP-155 for EVM chains - } - - /// @dev Returns the chain ID of the current chain. - /// Assumes block.chainId < type(uint64).max - function currentChainId() internal view returns (bytes8 _chainId) { - unchecked { - uint256 id = block.chainid; - while (true) { - _chainId = bytes8(uint64(_chainId) - 1); - assembly ("memory-safe") { - mstore8(_chainId, byte(mod(id, 10), HEX_DIGITS)) - } - id /= 10; - if (id == 0) break; - } - } - } } From 597bfc248b0c1add0485bfe37f3cc1a214b600d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernesto=20Garc=C3=ADa?= Date: Mon, 26 Aug 2024 14:10:37 -0600 Subject: [PATCH 13/41] Remove interfaces --- contracts/crosschain/IGatewayIncoming.sol | 10 ---------- contracts/crosschain/IGatewayIncomingPassive.sol | 4 ---- 2 files changed, 14 deletions(-) diff --git a/contracts/crosschain/IGatewayIncoming.sol b/contracts/crosschain/IGatewayIncoming.sol index cf01f8b6..b67eb536 100644 --- a/contracts/crosschain/IGatewayIncoming.sol +++ b/contracts/crosschain/IGatewayIncoming.sol @@ -5,13 +5,3 @@ pragma solidity ^0.8.0; interface IGatewayIncoming { event MessageExecuted(bytes32 indexed id); } - -interface IGatewayIncomingPassive { - function validateReceivedMessage( - bytes32 messageId, - string calldata srcChain, - string calldata srcAccount, - bytes calldata payload, - bytes calldata attributes - ) external; -} diff --git a/contracts/crosschain/IGatewayIncomingPassive.sol b/contracts/crosschain/IGatewayIncomingPassive.sol index cf01f8b6..b8cbca21 100644 --- a/contracts/crosschain/IGatewayIncomingPassive.sol +++ b/contracts/crosschain/IGatewayIncomingPassive.sol @@ -2,10 +2,6 @@ pragma solidity ^0.8.0; -interface IGatewayIncoming { - event MessageExecuted(bytes32 indexed id); -} - interface IGatewayIncomingPassive { function validateReceivedMessage( bytes32 messageId, From 2951fa5d28ae590420aeabb69651ca6bd88cb140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernesto=20Garc=C3=ADa?= Date: Mon, 26 Aug 2024 15:27:22 -0600 Subject: [PATCH 14/41] Checkpoint --- contracts/crosschain/IGatewayOutgoing.sol | 4 +- .../axelar/AxelarGatewayOutgoing.sol | 29 ++++++++++++--- contracts/utils/CAIP-10.sol | 13 +++++-- contracts/utils/CAIP-2.sol | 37 ++++++++++++------- 4 files changed, 60 insertions(+), 23 deletions(-) diff --git a/contracts/crosschain/IGatewayOutgoing.sol b/contracts/crosschain/IGatewayOutgoing.sol index 69079ac9..ab90553c 100644 --- a/contracts/crosschain/IGatewayOutgoing.sol +++ b/contracts/crosschain/IGatewayOutgoing.sol @@ -6,7 +6,7 @@ interface IGatewayOutgoing { struct Message { string source; // CAIP-10 account ID string destination; // CAIP-10 account ID - bytes payload; + bytes data; bytes attributes; } @@ -16,7 +16,7 @@ interface IGatewayOutgoing { function sendMessage( string calldata destChain, // CAIP-2 chain ID string calldata destAccount, // i.e. address - bytes calldata payload, + bytes calldata data, bytes calldata attributes ) external payable returns (bytes32 messageId); } diff --git a/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol b/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol index 3865e55d..c384d4d8 100644 --- a/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol +++ b/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.0; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; import {IAxelarGateway} from "../vendor/axelar/interfaces/IAxelarGateway.sol"; import {IAxelarGasService} from "../vendor/axelar/interfaces/IAxelarGasService.sol"; import {IGatewayOutgoing} from "../IGatewayOutgoing.sol"; @@ -15,12 +16,30 @@ abstract contract AxelarGatewayOutgoing is IGatewayOutgoing, AxelarCAIP2Equivale function sendMessage( string calldata destChain, // CAIP-2 chain ID string calldata destAccount, // i.e. address - bytes calldata payload, - bytes calldata /* attributes */ + bytes calldata data, + bytes calldata attributes ) external payable override returns (bytes32 messageId) { // TODO: Handle ether (payable) - if (!supported(destChain)) revert UnsupportedChain(destChain); - axelarGateway.callContract(string(fromCAIP2(destChain)), destAccount, payload); - return keccak256(payload); + // TODO: Validate attributes + + // Validate there's an equivalent chain identifier supported by the gateway + string memory destinationCAIP2 = CAIP2.toString(destChain); + if (!supported(destinationCAIP2)) revert UnsupportedChain(destinationCAIP2); + + // Create a message + Message memory message = Message( + CAIP10.currentId(Strings.toHexString(msg.sender)), + CAIP10.toString(destinationCAIP2, destAccount), + data, + attributes + ); + bytes32 id = keccak256(message); + emit MessageCreated(id, message); + + // Send the message + axelarGateway.callContract(string(fromCAIP2(destination)), destAccount, data); + emit MessageSent(id); + + return id; } } diff --git a/contracts/utils/CAIP-10.sol b/contracts/utils/CAIP-10.sol index d190d95f..46e50c06 100644 --- a/contracts/utils/CAIP-10.sol +++ b/contracts/utils/CAIP-10.sol @@ -2,14 +2,16 @@ pragma solidity ^0.8.0; +import {CAIP2} from "./CAIP-2.sol"; + // account_id: chain_id + ":" + account_address // chain_id: [-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32} (See [CAIP-2][]) // account_address: [-.%a-zA-Z0-9]{1,128} library CAIP10 { - bytes1 constant SEMICOLON = ":"; + bytes1 private constant SEMICOLON = ":"; - function toString(bytes32 chainId, string memory accountId) internal pure returns (string memory) { - return string(abi.encodePacked(chainId, SEMICOLON, accountId)); + function toString(string memory caip2, string memory accountId) internal pure returns (string memory) { + return string(abi.encodePacked(caip2, SEMICOLON, accountId)); } function fromString(string memory accountStr) internal pure returns (string memory caip2, string memory accountId) { @@ -18,6 +20,11 @@ library CAIP10 { return (_extractCAIP2(accountBuffer, lastSeparatorIndex), _extractAccountId(accountBuffer, lastSeparatorIndex)); } + function currentId(string memory accountId) internal view returns (string memory) { + (bytes8 namespace, bytes32 ref) = CAIP2.currentId(); + return toString(CAIP2.toString(namespace, ref), accountId); + } + function _extractCAIP2( bytes memory accountBuffer, uint256 lastSeparatorIndex diff --git a/contracts/utils/CAIP-2.sol b/contracts/utils/CAIP-2.sol index 2b921ad4..3d0053c4 100644 --- a/contracts/utils/CAIP-2.sol +++ b/contracts/utils/CAIP-2.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.0; -import {Arrays} from "@openzeppelin/contracts/utils/Arrays.sol"; import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; // chain_id: namespace + ":" + reference @@ -10,30 +9,37 @@ import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; // reference: [-_a-zA-Z0-9]{1,32} library CAIP2 { bytes16 private constant HEX_DIGITS = "0123456789abcdef"; - bytes1 constant SEMICOLON = ":"; + bytes1 private constant SEMICOLON = ":"; + bytes32 private constant EVM_REFERENCE = bytes32("eip155"); // EIP-155 for EVM chains + /// @dev Converts a namespace and reference to a CAIP2 chain identifier. function toString(bytes8 namespace, bytes32 ref) internal pure returns (string memory) { return string(abi.encodePacked(namespace, SEMICOLON, ref)); } - /// @dev Parses a chain ID from a string by splitting it at the first semicolon. - /// The function parses both sides as `bytes8` and `bytes32` respectively wiothout any validation. - function fromString(string memory chainStr) internal pure returns (bytes8 namespace, bytes32 ref) { - bytes memory chainBuffer = bytes(chainStr); + /// @dev Parses a CAIP2 identifier from a string by splitting it at the first semicolon. + /// The function parses both sides as `bytes8` and `bytes32` respectively without any validation. + function fromString(string memory caip2) internal pure returns (bytes8 namespace, bytes32 ref) { + bytes memory chainBuffer = bytes(caip2); uint8 semicolonIndex = _findSemicolonIndex(chainBuffer); return (_extractNamespace(chainBuffer, semicolonIndex), _unsafeExtractReference(chainBuffer, semicolonIndex)); } - function isCurrentEVMChain(string memory chainStr) internal view returns (bool) { - (bytes8 namespace, bytes32 ref) = fromString(chainStr); - return - namespace == currentChainId() && // Chain ID must match the current chain - ref == bytes32(bytes(string("eip155"))); // EIP-155 for EVM chains + /// @dev Checks if the given CAIP2 identifier is the current chain. + function isCurrentId(string memory caip2) internal view returns (bool) { + (bytes8 namespace, bytes32 ref) = fromString(caip2); + (bytes8 currentNamespace, bytes32 currentRef) = currentId(); + return namespace == currentNamespace && ref == currentRef; } - /// @dev Returns the chain ID of the current chain. + /// @dev Returns the CAIP2 identifier of the current chain. + function currentId() internal view returns (bytes8 namespace, bytes32 ref) { + return (currentNamespace(), currentReference()); + } + + /// @dev Returns the CAIP2 identifier of the current chain. /// Assumes block.chainId < type(uint64).max - function currentChainId() internal view returns (bytes8 _chainId) { + function currentNamespace() internal view returns (bytes8 _chainId) { unchecked { uint256 id = block.chainid; while (true) { @@ -47,6 +53,11 @@ library CAIP2 { } } + /// @dev Returns the reference of the current chain. + function currentReference() internal pure returns (bytes32) { + return EVM_REFERENCE; + } + /// @dev Extracts the first `semicolonIndex` bytes from the chain buffer as a bytes8 namespace. function _extractNamespace(bytes memory chainBuffer, uint8 semicolonIndex) private pure returns (bytes8 namespace) { assembly ("memory-safe") { From a7bd1309cfcfdbd10b3b39ab0d5c03753c679858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernesto=20Garc=C3=ADa?= Date: Mon, 26 Aug 2024 16:38:20 -0600 Subject: [PATCH 15/41] Add incoming dual mode --- .../crosschain/IGatewayIncomingPassive.sol | 2 + contracts/crosschain/IGatewayOutgoing.sol | 4 +- .../axelar/AxelarCAIP2Equivalence.sol | 134 +++++++++--------- .../axelar/AxelarGatewayIncoming.sol | 56 ++++++++ .../axelar/AxelarGatewayOutgoing.sol | 13 +- 5 files changed, 133 insertions(+), 76 deletions(-) create mode 100644 contracts/crosschain/axelar/AxelarGatewayIncoming.sol diff --git a/contracts/crosschain/IGatewayIncomingPassive.sol b/contracts/crosschain/IGatewayIncomingPassive.sol index b8cbca21..d999e1e6 100644 --- a/contracts/crosschain/IGatewayIncomingPassive.sol +++ b/contracts/crosschain/IGatewayIncomingPassive.sol @@ -3,6 +3,8 @@ pragma solidity ^0.8.0; interface IGatewayIncomingPassive { + error GatewayIncomingPassiveInvalidMessage(bytes32 messageId); + function validateReceivedMessage( bytes32 messageId, string calldata srcChain, diff --git a/contracts/crosschain/IGatewayOutgoing.sol b/contracts/crosschain/IGatewayOutgoing.sol index ab90553c..69079ac9 100644 --- a/contracts/crosschain/IGatewayOutgoing.sol +++ b/contracts/crosschain/IGatewayOutgoing.sol @@ -6,7 +6,7 @@ interface IGatewayOutgoing { struct Message { string source; // CAIP-10 account ID string destination; // CAIP-10 account ID - bytes data; + bytes payload; bytes attributes; } @@ -16,7 +16,7 @@ interface IGatewayOutgoing { function sendMessage( string calldata destChain, // CAIP-2 chain ID string calldata destAccount, // i.e. address - bytes calldata data, + bytes calldata payload, bytes calldata attributes ) external payable returns (bytes32 messageId); } diff --git a/contracts/crosschain/axelar/AxelarCAIP2Equivalence.sol b/contracts/crosschain/axelar/AxelarCAIP2Equivalence.sol index aa17c3c3..35eb4eb4 100644 --- a/contracts/crosschain/axelar/AxelarCAIP2Equivalence.sol +++ b/contracts/crosschain/axelar/AxelarCAIP2Equivalence.sol @@ -17,72 +17,72 @@ abstract contract AxelarCAIP2Equivalence is ICAIP2Equivalence { } // EVM (https://axelarscan.io/resources/chains?type=evm) -// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("1"))] = "Ethereum"; -// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("56"))] = "binance"; -// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("137"))] = "Polygon"; -// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("43114"))] = "Avalanche"; -// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("250"))] = "Fantom"; -// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("1284"))] = "Moonbeam"; -// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("1313161554"))] = "aurora"; -// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("42161"))] = "arbitrum"; -// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("10"))] = "optimism"; -// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("8453"))] = "base"; -// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("5000"))] = "mantle"; -// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("42220"))] = "celo"; -// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("2222"))] = "kava"; -// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("314"))] = "filecoin"; -// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("59144"))] = "linea"; -// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("2031"))] = "centrifuge"; -// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("534352"))] = "scroll"; -// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("13371"))] = "immutable"; -// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("252"))] = "fraxtal"; -// _equivalence[CAIP2.ChainId(bytes8("eip155"), bytes32("81457"))] = "blast"; +// _equivalence[CAIP2.toString(bytes8("eip155"), bytes32("1"))] = "Ethereum"; +// _equivalence[CAIP2.toString(bytes8("eip155"), bytes32("56"))] = "binance"; +// _equivalence[CAIP2.toString(bytes8("eip155"), bytes32("137"))] = "Polygon"; +// _equivalence[CAIP2.toString(bytes8("eip155"), bytes32("43114"))] = "Avalanche"; +// _equivalence[CAIP2.toString(bytes8("eip155"), bytes32("250"))] = "Fantom"; +// _equivalence[CAIP2.toString(bytes8("eip155"), bytes32("1284"))] = "Moonbeam"; +// _equivalence[CAIP2.toString(bytes8("eip155"), bytes32("1313161554"))] = "aurora"; +// _equivalence[CAIP2.toString(bytes8("eip155"), bytes32("42161"))] = "arbitrum"; +// _equivalence[CAIP2.toString(bytes8("eip155"), bytes32("10"))] = "optimism"; +// _equivalence[CAIP2.toString(bytes8("eip155"), bytes32("8453"))] = "base"; +// _equivalence[CAIP2.toString(bytes8("eip155"), bytes32("5000"))] = "mantle"; +// _equivalence[CAIP2.toString(bytes8("eip155"), bytes32("42220"))] = "celo"; +// _equivalence[CAIP2.toString(bytes8("eip155"), bytes32("2222"))] = "kava"; +// _equivalence[CAIP2.toString(bytes8("eip155"), bytes32("314"))] = "filecoin"; +// _equivalence[CAIP2.toString(bytes8("eip155"), bytes32("59144"))] = "linea"; +// _equivalence[CAIP2.toString(bytes8("eip155"), bytes32("2031"))] = "centrifuge"; +// _equivalence[CAIP2.toString(bytes8("eip155"), bytes32("534352"))] = "scroll"; +// _equivalence[CAIP2.toString(bytes8("eip155"), bytes32("13371"))] = "immutable"; +// _equivalence[CAIP2.toString(bytes8("eip155"), bytes32("252"))] = "fraxtal"; +// _equivalence[CAIP2.toString(bytes8("eip155"), bytes32("81457"))] = "blast"; // Cosmos (https://axelarscan.io/resources/chains?type=cosmos) -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('axelar-dojo-1'))] = 'Axelarnet'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('osmosis-1'))] = 'osmosis'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('cosmoshub-4'))] = 'cosmoshub'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('juno-1'))] = 'juno'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('emoney-3'))] = 'e-money'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('injective-1'))] = 'injective'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('crescent-1'))] = 'crescent'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('kaiyo-1'))] = 'kujira'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('secret-4'))] = 'secret-snip'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('secret-4'))] = 'secret'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('pacific-1'))] = 'sei'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('stargaze-1'))] = 'stargaze'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('mantle-1'))] = 'assetmantle'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('fetchhub-4'))] = 'fetch'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('kichain-2'))] = 'ki'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('evmos_9001-2'))] = 'evmos'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('xstaxy-1'))] = 'aura'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('comdex-1'))] = 'comdex'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('core-1'))] = 'persistence'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('regen-1'))] = 'regen'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('umee-1'))] = 'umee'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('agoric-3'))] = 'agoric'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('dimension_37-1'))] = 'xpla'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('acre_9052-1'))] = 'acre'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('stride-1'))] = 'stride'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('carbon-1'))] = 'carbon'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('sommelier-3'))] = 'sommelier'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('neutron-1'))] = 'neutron'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('reb_1111-1'))] = 'rebus'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('archway-1'))] = 'archway'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('pio-mainnet-1'))] = 'provenance'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('ixo-5'))] = 'ixo'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('migaloo-1'))] = 'migaloo'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('teritori-1'))] = 'teritori'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('haqq_11235-1'))] = 'haqq'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('celestia'))] = 'celestia'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('agamotto'))] = 'ojo'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('chihuahua-1'))] = 'chihuahua'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('ssc-1'))] = 'saga'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('dymension_1100-1'))] = 'dymension'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('fxcore'))] = 'fxcore'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('perun-1'))] = 'c4e'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('bitsong-2b'))] = 'bitsong'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('pirin-1'))] = 'nolus'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('lava-mainnet-1'))] = 'lava'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('phoenix-1'))] = 'terra-2'; -// _equivalence[CAIP2.ChainId(bytes8('cosmos'), bytes32('columbus-5'))] = 'terra';" +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('axelar-dojo-1'))] = 'Axelarnet'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('osmosis-1'))] = 'osmosis'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('cosmoshub-4'))] = 'cosmoshub'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('juno-1'))] = 'juno'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('emoney-3'))] = 'e-money'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('injective-1'))] = 'injective'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('crescent-1'))] = 'crescent'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('kaiyo-1'))] = 'kujira'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('secret-4'))] = 'secret-snip'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('secret-4'))] = 'secret'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('pacific-1'))] = 'sei'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('stargaze-1'))] = 'stargaze'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('mantle-1'))] = 'assetmantle'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('fetchhub-4'))] = 'fetch'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('kichain-2'))] = 'ki'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('evmos_9001-2'))] = 'evmos'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('xstaxy-1'))] = 'aura'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('comdex-1'))] = 'comdex'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('core-1'))] = 'persistence'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('regen-1'))] = 'regen'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('umee-1'))] = 'umee'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('agoric-3'))] = 'agoric'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('dimension_37-1'))] = 'xpla'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('acre_9052-1'))] = 'acre'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('stride-1'))] = 'stride'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('carbon-1'))] = 'carbon'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('sommelier-3'))] = 'sommelier'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('neutron-1'))] = 'neutron'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('reb_1111-1'))] = 'rebus'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('archway-1'))] = 'archway'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('pio-mainnet-1'))] = 'provenance'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('ixo-5'))] = 'ixo'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('migaloo-1'))] = 'migaloo'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('teritori-1'))] = 'teritori'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('haqq_11235-1'))] = 'haqq'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('celestia'))] = 'celestia'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('agamotto'))] = 'ojo'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('chihuahua-1'))] = 'chihuahua'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('ssc-1'))] = 'saga'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('dymension_1100-1'))] = 'dymension'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('fxcore'))] = 'fxcore'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('perun-1'))] = 'c4e'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('bitsong-2b'))] = 'bitsong'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('pirin-1'))] = 'nolus'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('lava-mainnet-1'))] = 'lava'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('phoenix-1'))] = 'terra-2'; +// _equivalence[CAIP2.toString(bytes8('cosmos'), bytes32('columbus-5'))] = 'terra';" diff --git a/contracts/crosschain/axelar/AxelarGatewayIncoming.sol b/contracts/crosschain/axelar/AxelarGatewayIncoming.sol new file mode 100644 index 00000000..b197a719 --- /dev/null +++ b/contracts/crosschain/axelar/AxelarGatewayIncoming.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {IAxelarGateway} from "../vendor/axelar/interfaces/IAxelarGateway.sol"; +import {IGatewayIncomingPassive} from "../IGatewayIncomingPassive.sol"; +import {IGatewayIncoming} from "../IGatewayIncoming.sol"; +import {IGatewayReceiver} from "../IGatewayReceiver.sol"; + +abstract contract AxelarGatewayIncoming is IGatewayIncoming, IGatewayIncomingPassive, IGatewayReceiver { + IAxelarGateway public immutable gateway; + + function validateReceivedMessage( + bytes32 messageId, + string calldata srcChain, // CAIP-2 chain ID + string calldata srcAccount, // i.e. address + bytes calldata payload, + bytes calldata attributes + ) public virtual { + if (!_isValidReceivedMessage(messageId, srcChain, srcAccount, payload, attributes)) { + revert GatewayIncomingPassiveInvalidMessage(messageId); + } + } + + function receiveMessage( + bytes32 messageId, + string calldata srcChain, + string calldata srcAccount, + bytes calldata payload, + bytes calldata attributes + ) external payable override { + if (msg.sender != address(gateway)) { + validateReceivedMessage(messageId, srcChain, srcAccount, payload, attributes); + } + emit MessageExecuted(messageId); + _execute(messageId, srcChain, srcAccount, payload, attributes); + } + + function _isValidReceivedMessage( + bytes32 messageId, + string calldata srcChain, // CAIP-2 chain ID + string calldata srcAccount, // i.e. address + bytes calldata payload, + bytes calldata /* attributes */ + ) internal returns (bool) { + return gateway.validateContractCall(messageId, srcChain, srcAccount, keccak256(payload)); + } + + function _execute( + bytes32 messageId, + string calldata srcChain, + string calldata srcAccount, + bytes calldata payload, + bytes calldata attributes + ) internal virtual; +} diff --git a/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol b/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol index c384d4d8..3ca52430 100644 --- a/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol +++ b/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol @@ -11,33 +11,32 @@ import {CAIP2} from "../../utils/CAIP-2.sol"; import {CAIP10} from "../../utils/CAIP-10.sol"; abstract contract AxelarGatewayOutgoing is IGatewayOutgoing, AxelarCAIP2Equivalence { - IAxelarGateway public immutable axelarGateway; + IAxelarGateway public immutable gateway; function sendMessage( string calldata destChain, // CAIP-2 chain ID string calldata destAccount, // i.e. address - bytes calldata data, + bytes calldata payload, bytes calldata attributes ) external payable override returns (bytes32 messageId) { // TODO: Handle ether (payable) // TODO: Validate attributes // Validate there's an equivalent chain identifier supported by the gateway - string memory destinationCAIP2 = CAIP2.toString(destChain); - if (!supported(destinationCAIP2)) revert UnsupportedChain(destinationCAIP2); + if (!supported(destChain)) revert UnsupportedChain(destChain); // Create a message Message memory message = Message( CAIP10.currentId(Strings.toHexString(msg.sender)), - CAIP10.toString(destinationCAIP2, destAccount), - data, + CAIP10.toString(destChain, destAccount), + payload, attributes ); bytes32 id = keccak256(message); emit MessageCreated(id, message); // Send the message - axelarGateway.callContract(string(fromCAIP2(destination)), destAccount, data); + gateway.callContract(string(fromCAIP2(destChain)), destAccount, message); emit MessageSent(id); return id; From 2f4588eb1e3e46269b9c983380466ec07ad84d3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernesto=20Garc=C3=ADa?= Date: Mon, 26 Aug 2024 16:50:32 -0600 Subject: [PATCH 16/41] Fix compilation --- contracts/crosschain/axelar/AxelarGatewayOutgoing.sol | 4 ++-- contracts/utils/CAIP-2.sol | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol b/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol index 3ca52430..ca96318e 100644 --- a/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol +++ b/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol @@ -32,11 +32,11 @@ abstract contract AxelarGatewayOutgoing is IGatewayOutgoing, AxelarCAIP2Equivale payload, attributes ); - bytes32 id = keccak256(message); + bytes32 id = keccak256(abi.encode(message)); emit MessageCreated(id, message); // Send the message - gateway.callContract(string(fromCAIP2(destChain)), destAccount, message); + gateway.callContract(string(fromCAIP2(destChain)), destAccount, payload); emit MessageSent(id); return id; diff --git a/contracts/utils/CAIP-2.sol b/contracts/utils/CAIP-2.sol index 3d0053c4..41d3249e 100644 --- a/contracts/utils/CAIP-2.sol +++ b/contracts/utils/CAIP-2.sol @@ -28,8 +28,8 @@ library CAIP2 { /// @dev Checks if the given CAIP2 identifier is the current chain. function isCurrentId(string memory caip2) internal view returns (bool) { (bytes8 namespace, bytes32 ref) = fromString(caip2); - (bytes8 currentNamespace, bytes32 currentRef) = currentId(); - return namespace == currentNamespace && ref == currentRef; + (bytes8 _namespace, bytes32 _ref) = currentId(); + return namespace == _namespace && ref == _ref; } /// @dev Returns the CAIP2 identifier of the current chain. From fd010bd57336628ad3eb47d83bef8e9586f7e41a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernesto=20Garc=C3=ADa?= Date: Mon, 26 Aug 2024 17:02:28 -0600 Subject: [PATCH 17/41] Apply review suggestions --- contracts/utils/Bytes.sol | 14 ++++++++++++++ contracts/utils/CAIP-10.sol | 21 +++++++++------------ contracts/utils/CAIP-2.sol | 22 +++++++--------------- 3 files changed, 30 insertions(+), 27 deletions(-) create mode 100644 contracts/utils/Bytes.sol diff --git a/contracts/utils/Bytes.sol b/contracts/utils/Bytes.sol new file mode 100644 index 00000000..ecd97f34 --- /dev/null +++ b/contracts/utils/Bytes.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +library Bytes { + function find(bytes memory input, bytes1 chr, uint256 cursor) internal pure returns (uint256) { + uint256 length = input.length; + for (uint256 i = cursor; i < length; ++i) { + if (input[i] == chr) { + return i; + } + } + return length; + } +} diff --git a/contracts/utils/CAIP-10.sol b/contracts/utils/CAIP-10.sol index 46e50c06..a30bf955 100644 --- a/contracts/utils/CAIP-10.sol +++ b/contracts/utils/CAIP-10.sol @@ -2,21 +2,27 @@ pragma solidity ^0.8.0; +import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; import {CAIP2} from "./CAIP-2.sol"; +import {Bytes} from "./Bytes.sol"; // account_id: chain_id + ":" + account_address // chain_id: [-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32} (See [CAIP-2][]) // account_address: [-.%a-zA-Z0-9]{1,128} library CAIP10 { + using SafeCast for uint256; + using Bytes for bytes; + bytes1 private constant SEMICOLON = ":"; function toString(string memory caip2, string memory accountId) internal pure returns (string memory) { return string(abi.encodePacked(caip2, SEMICOLON, accountId)); } - function fromString(string memory accountStr) internal pure returns (string memory caip2, string memory accountId) { - bytes memory accountBuffer = bytes(accountStr); - uint256 lastSeparatorIndex = _findLastSeparatorIndex(accountBuffer); + function parse(string memory caip10) internal pure returns (string memory caip2, string memory accountId) { + bytes memory accountBuffer = bytes(caip10); + uint8 firstSeparatorIndex = accountBuffer.find(SEMICOLON, 0).toUint8(); + uint256 lastSeparatorIndex = accountBuffer.find(SEMICOLON, firstSeparatorIndex).toUint8(); return (_extractCAIP2(accountBuffer, lastSeparatorIndex), _extractAccountId(accountBuffer, lastSeparatorIndex)); } @@ -48,13 +54,4 @@ library CAIP10 { } return string(_accountId); } - - function _findLastSeparatorIndex(bytes memory accountBuffer) private pure returns (uint256) { - for (uint256 i = accountBuffer.length - 1; i >= 0; i--) { - if (accountBuffer[i] == SEMICOLON) { - return i; - } - } - return 0; - } } diff --git a/contracts/utils/CAIP-2.sol b/contracts/utils/CAIP-2.sol index 41d3249e..558c6473 100644 --- a/contracts/utils/CAIP-2.sol +++ b/contracts/utils/CAIP-2.sol @@ -3,11 +3,15 @@ pragma solidity ^0.8.0; import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; +import {Bytes} from "./Bytes.sol"; // chain_id: namespace + ":" + reference // namespace: [-a-z0-9]{3,8} // reference: [-_a-zA-Z0-9]{1,32} library CAIP2 { + using SafeCast for uint256; + using Bytes for bytes; + bytes16 private constant HEX_DIGITS = "0123456789abcdef"; bytes1 private constant SEMICOLON = ":"; bytes32 private constant EVM_REFERENCE = bytes32("eip155"); // EIP-155 for EVM chains @@ -19,15 +23,15 @@ library CAIP2 { /// @dev Parses a CAIP2 identifier from a string by splitting it at the first semicolon. /// The function parses both sides as `bytes8` and `bytes32` respectively without any validation. - function fromString(string memory caip2) internal pure returns (bytes8 namespace, bytes32 ref) { + function parse(string memory caip2) internal pure returns (bytes8 namespace, bytes32 ref) { bytes memory chainBuffer = bytes(caip2); - uint8 semicolonIndex = _findSemicolonIndex(chainBuffer); + uint8 semicolonIndex = chainBuffer.find(SEMICOLON, 0).toUint8(); return (_extractNamespace(chainBuffer, semicolonIndex), _unsafeExtractReference(chainBuffer, semicolonIndex)); } /// @dev Checks if the given CAIP2 identifier is the current chain. function isCurrentId(string memory caip2) internal view returns (bool) { - (bytes8 namespace, bytes32 ref) = fromString(caip2); + (bytes8 namespace, bytes32 ref) = parse(caip2); (bytes8 _namespace, bytes32 _ref) = currentId(); return namespace == _namespace && ref == _ref; } @@ -75,16 +79,4 @@ library CAIP2 { ref := mload(add(chainBuffer, add(0x20, offset))) } } - - /// @dev Looks for the first semicolon in the chain buffer. This is the optimal way since - /// the namespace is shorter than the reference. - function _findSemicolonIndex(bytes memory chainBuffer) private pure returns (uint8) { - uint8 length = SafeCast.toUint8(chainBuffer.length); - for (uint8 i = 0; i < length; i++) { - if (chainBuffer[i] == SEMICOLON) { - return i; - } - } - return length; - } } From aa731cc3f9dcc575e86e23f3279f3dee38edb65b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernesto=20Garc=C3=ADa?= Date: Mon, 2 Sep 2024 21:48:46 -0600 Subject: [PATCH 18/41] Install axelar contracts --- contracts/crosschain/ICAIP2Equivalence.sol | 4 +- .../axelar/AxelarGatewayIncoming.sol | 22 +- .../axelar/AxelarGatewayOutgoing.sol | 4 +- .../axelar/interfaces/IAxelarGasService.sol | 418 ------------------ .../axelar/interfaces/IAxelarGateway.sol | 188 -------- .../axelar/interfaces/IContractIdentifier.sol | 13 - .../vendor/axelar/interfaces/IGovernable.sol | 41 -- .../axelar/interfaces/IImplementation.sol | 11 - .../interfaces/IInterchainGasEstimation.sol | 45 -- .../vendor/axelar/interfaces/IOwnable.sol | 50 --- .../vendor/axelar/interfaces/IUpgradable.sol | 19 - .../axelar/types/GasEstimationTypes.sol | 35 -- package-lock.json | 22 + package.json | 1 + remappings.txt | 2 + 15 files changed, 34 insertions(+), 841 deletions(-) delete mode 100644 contracts/crosschain/vendor/axelar/interfaces/IAxelarGasService.sol delete mode 100644 contracts/crosschain/vendor/axelar/interfaces/IAxelarGateway.sol delete mode 100644 contracts/crosschain/vendor/axelar/interfaces/IContractIdentifier.sol delete mode 100644 contracts/crosschain/vendor/axelar/interfaces/IGovernable.sol delete mode 100644 contracts/crosschain/vendor/axelar/interfaces/IImplementation.sol delete mode 100644 contracts/crosschain/vendor/axelar/interfaces/IInterchainGasEstimation.sol delete mode 100644 contracts/crosschain/vendor/axelar/interfaces/IOwnable.sol delete mode 100644 contracts/crosschain/vendor/axelar/interfaces/IUpgradable.sol delete mode 100644 contracts/crosschain/vendor/axelar/types/GasEstimationTypes.sol diff --git a/contracts/crosschain/ICAIP2Equivalence.sol b/contracts/crosschain/ICAIP2Equivalence.sol index 68ac10af..b72dc9cb 100644 --- a/contracts/crosschain/ICAIP2Equivalence.sol +++ b/contracts/crosschain/ICAIP2Equivalence.sol @@ -2,8 +2,8 @@ pragma solidity ^0.8.0; -import {IAxelarGateway} from "./vendor/axelar/interfaces/IAxelarGateway.sol"; -import {IAxelarGasService} from "./vendor/axelar/interfaces/IAxelarGasService.sol"; +import {IAxelarGateway} from "@axelar-network/axelar-cgp-solidity/interfaces/IAxelarGateway.sol"; +import {IAxelarGasService} from "@axelar-network/axelar-cgp-solidity/interfaces/IAxelarGasService.sol"; /// @dev Equivalence interface between CAIP-2 chain identifiers and protocol-specific chain identifiers. /// diff --git a/contracts/crosschain/axelar/AxelarGatewayIncoming.sol b/contracts/crosschain/axelar/AxelarGatewayIncoming.sol index b197a719..71ee631a 100644 --- a/contracts/crosschain/axelar/AxelarGatewayIncoming.sol +++ b/contracts/crosschain/axelar/AxelarGatewayIncoming.sol @@ -2,12 +2,11 @@ pragma solidity ^0.8.0; -import {IAxelarGateway} from "../vendor/axelar/interfaces/IAxelarGateway.sol"; +import {IAxelarGateway} from "@axelar-network/axelar-cgp-solidity/interfaces/IAxelarGateway.sol"; import {IGatewayIncomingPassive} from "../IGatewayIncomingPassive.sol"; import {IGatewayIncoming} from "../IGatewayIncoming.sol"; -import {IGatewayReceiver} from "../IGatewayReceiver.sol"; -abstract contract AxelarGatewayIncoming is IGatewayIncoming, IGatewayIncomingPassive, IGatewayReceiver { +abstract contract AxelarGatewayIncoming is IGatewayIncoming, IGatewayIncomingPassive { IAxelarGateway public immutable gateway; function validateReceivedMessage( @@ -20,20 +19,7 @@ abstract contract AxelarGatewayIncoming is IGatewayIncoming, IGatewayIncomingPas if (!_isValidReceivedMessage(messageId, srcChain, srcAccount, payload, attributes)) { revert GatewayIncomingPassiveInvalidMessage(messageId); } - } - - function receiveMessage( - bytes32 messageId, - string calldata srcChain, - string calldata srcAccount, - bytes calldata payload, - bytes calldata attributes - ) external payable override { - if (msg.sender != address(gateway)) { - validateReceivedMessage(messageId, srcChain, srcAccount, payload, attributes); - } emit MessageExecuted(messageId); - _execute(messageId, srcChain, srcAccount, payload, attributes); } function _isValidReceivedMessage( @@ -52,5 +38,7 @@ abstract contract AxelarGatewayIncoming is IGatewayIncoming, IGatewayIncomingPas string calldata srcAccount, bytes calldata payload, bytes calldata attributes - ) internal virtual; + ) internal { + // messageId + } } diff --git a/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol b/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol index ca96318e..6cb2fb45 100644 --- a/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol +++ b/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol @@ -3,8 +3,8 @@ pragma solidity ^0.8.0; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; -import {IAxelarGateway} from "../vendor/axelar/interfaces/IAxelarGateway.sol"; -import {IAxelarGasService} from "../vendor/axelar/interfaces/IAxelarGasService.sol"; +import {IAxelarGateway} from "@axelar-network/axelar-cgp-solidity/interfaces/IAxelarGateway.sol"; +import {IAxelarGasService} from "@axelar-network/axelar-cgp-solidity/interfaces/IAxelarGasService.sol"; import {IGatewayOutgoing} from "../IGatewayOutgoing.sol"; import {AxelarCAIP2Equivalence} from "./AxelarCAIP2Equivalence.sol"; import {CAIP2} from "../../utils/CAIP-2.sol"; diff --git a/contracts/crosschain/vendor/axelar/interfaces/IAxelarGasService.sol b/contracts/crosschain/vendor/axelar/interfaces/IAxelarGasService.sol deleted file mode 100644 index 60e363c2..00000000 --- a/contracts/crosschain/vendor/axelar/interfaces/IAxelarGasService.sol +++ /dev/null @@ -1,418 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {GasInfo} from "../types/GasEstimationTypes.sol"; -import {IInterchainGasEstimation} from "./IInterchainGasEstimation.sol"; -import {IUpgradable} from "./IUpgradable.sol"; - -/** - * @title IAxelarGasService Interface - * @notice This is an interface for the AxelarGasService contract which manages gas payments - * and refunds for cross-chain communication on the Axelar network. - * @dev This interface inherits IUpgradable - */ -interface IAxelarGasService is IInterchainGasEstimation, IUpgradable { - error InvalidAddress(); - error NotCollector(); - error InvalidAmounts(); - error InvalidGasUpdates(); - error InvalidParams(); - error InsufficientGasPayment(uint256 required, uint256 provided); - - event GasPaidForContractCall( - address indexed sourceAddress, - string destinationChain, - string destinationAddress, - bytes32 indexed payloadHash, - address gasToken, - uint256 gasFeeAmount, - address refundAddress - ); - - event GasPaidForContractCallWithToken( - address indexed sourceAddress, - string destinationChain, - string destinationAddress, - bytes32 indexed payloadHash, - string symbol, - uint256 amount, - address gasToken, - uint256 gasFeeAmount, - address refundAddress - ); - - event NativeGasPaidForContractCall( - address indexed sourceAddress, - string destinationChain, - string destinationAddress, - bytes32 indexed payloadHash, - uint256 gasFeeAmount, - address refundAddress - ); - - event NativeGasPaidForContractCallWithToken( - address indexed sourceAddress, - string destinationChain, - string destinationAddress, - bytes32 indexed payloadHash, - string symbol, - uint256 amount, - uint256 gasFeeAmount, - address refundAddress - ); - - event GasPaidForExpressCall( - address indexed sourceAddress, - string destinationChain, - string destinationAddress, - bytes32 indexed payloadHash, - address gasToken, - uint256 gasFeeAmount, - address refundAddress - ); - - event GasPaidForExpressCallWithToken( - address indexed sourceAddress, - string destinationChain, - string destinationAddress, - bytes32 indexed payloadHash, - string symbol, - uint256 amount, - address gasToken, - uint256 gasFeeAmount, - address refundAddress - ); - - event NativeGasPaidForExpressCall( - address indexed sourceAddress, - string destinationChain, - string destinationAddress, - bytes32 indexed payloadHash, - uint256 gasFeeAmount, - address refundAddress - ); - - event NativeGasPaidForExpressCallWithToken( - address indexed sourceAddress, - string destinationChain, - string destinationAddress, - bytes32 indexed payloadHash, - string symbol, - uint256 amount, - uint256 gasFeeAmount, - address refundAddress - ); - - event GasAdded( - bytes32 indexed txHash, - uint256 indexed logIndex, - address gasToken, - uint256 gasFeeAmount, - address refundAddress - ); - - event NativeGasAdded(bytes32 indexed txHash, uint256 indexed logIndex, uint256 gasFeeAmount, address refundAddress); - - event ExpressGasAdded( - bytes32 indexed txHash, - uint256 indexed logIndex, - address gasToken, - uint256 gasFeeAmount, - address refundAddress - ); - - event NativeExpressGasAdded( - bytes32 indexed txHash, - uint256 indexed logIndex, - uint256 gasFeeAmount, - address refundAddress - ); - - event Refunded( - bytes32 indexed txHash, - uint256 indexed logIndex, - address payable receiver, - address token, - uint256 amount - ); - - /** - * @notice Pay for gas for any type of contract execution on a destination chain. - * @dev This function is called on the source chain before calling the gateway to execute a remote contract. - * @dev If estimateOnChain is true, the function will estimate the gas cost and revert if the payment is insufficient. - * @param sender The address making the payment - * @param destinationChain The target chain where the contract call will be made - * @param destinationAddress The target address on the destination chain - * @param payload Data payload for the contract call - * @param executionGasLimit The gas limit for the contract call - * @param estimateOnChain Flag to enable on-chain gas estimation - * @param refundAddress The address where refunds, if any, should be sent - * @param params Additional parameters for gas payment. This can be left empty for normal contract call payments. - */ - function payGas( - address sender, - string calldata destinationChain, - string calldata destinationAddress, - bytes calldata payload, - uint256 executionGasLimit, - bool estimateOnChain, - address refundAddress, - bytes calldata params - ) external payable; - - /** - * @notice Pay for gas using ERC20 tokens for a contract call on a destination chain. - * @dev This function is called on the source chain before calling the gateway to execute a remote contract. - * @param sender The address making the payment - * @param destinationChain The target chain where the contract call will be made - * @param destinationAddress The target address on the destination chain - * @param payload Data payload for the contract call - * @param gasToken The address of the ERC20 token used to pay for gas - * @param gasFeeAmount The amount of tokens to pay for gas - * @param refundAddress The address where refunds, if any, should be sent - */ - function payGasForContractCall( - address sender, - string calldata destinationChain, - string calldata destinationAddress, - bytes calldata payload, - address gasToken, - uint256 gasFeeAmount, - address refundAddress - ) external; - - /** - * @notice Pay for gas using ERC20 tokens for a contract call with tokens on a destination chain. - * @dev This function is called on the source chain before calling the gateway to execute a remote contract. - * @param sender The address making the payment - * @param destinationChain The target chain where the contract call with tokens will be made - * @param destinationAddress The target address on the destination chain - * @param payload Data payload for the contract call with tokens - * @param symbol The symbol of the token to be sent with the call - * @param amount The amount of tokens to be sent with the call - * @param gasToken The address of the ERC20 token used to pay for gas - * @param gasFeeAmount The amount of tokens to pay for gas - * @param refundAddress The address where refunds, if any, should be sent - */ - function payGasForContractCallWithToken( - address sender, - string calldata destinationChain, - string calldata destinationAddress, - bytes calldata payload, - string calldata symbol, - uint256 amount, - address gasToken, - uint256 gasFeeAmount, - address refundAddress - ) external; - - /** - * @notice Pay for gas using native currency for a contract call on a destination chain. - * @dev This function is called on the source chain before calling the gateway to execute a remote contract. - * @param sender The address making the payment - * @param destinationChain The target chain where the contract call will be made - * @param destinationAddress The target address on the destination chain - * @param payload Data payload for the contract call - * @param refundAddress The address where refunds, if any, should be sent - */ - function payNativeGasForContractCall( - address sender, - string calldata destinationChain, - string calldata destinationAddress, - bytes calldata payload, - address refundAddress - ) external payable; - - /** - * @notice Pay for gas using native currency for a contract call with tokens on a destination chain. - * @dev This function is called on the source chain before calling the gateway to execute a remote contract. - * @param sender The address making the payment - * @param destinationChain The target chain where the contract call with tokens will be made - * @param destinationAddress The target address on the destination chain - * @param payload Data payload for the contract call with tokens - * @param symbol The symbol of the token to be sent with the call - * @param amount The amount of tokens to be sent with the call - * @param refundAddress The address where refunds, if any, should be sent - */ - function payNativeGasForContractCallWithToken( - address sender, - string calldata destinationChain, - string calldata destinationAddress, - bytes calldata payload, - string calldata symbol, - uint256 amount, - address refundAddress - ) external payable; - - /** - * @notice Pay for gas using ERC20 tokens for an express contract call on a destination chain. - * @dev This function is called on the source chain before calling the gateway to express execute a remote contract. - * @param sender The address making the payment - * @param destinationChain The target chain where the contract call will be made - * @param destinationAddress The target address on the destination chain - * @param payload Data payload for the contract call - * @param gasToken The address of the ERC20 token used to pay for gas - * @param gasFeeAmount The amount of tokens to pay for gas - * @param refundAddress The address where refunds, if any, should be sent - */ - function payGasForExpressCall( - address sender, - string calldata destinationChain, - string calldata destinationAddress, - bytes calldata payload, - address gasToken, - uint256 gasFeeAmount, - address refundAddress - ) external; - - /** - * @notice Pay for gas using ERC20 tokens for an express contract call with tokens on a destination chain. - * @dev This function is called on the source chain before calling the gateway to express execute a remote contract. - * @param sender The address making the payment - * @param destinationChain The target chain where the contract call with tokens will be made - * @param destinationAddress The target address on the destination chain - * @param payload Data payload for the contract call with tokens - * @param symbol The symbol of the token to be sent with the call - * @param amount The amount of tokens to be sent with the call - * @param gasToken The address of the ERC20 token used to pay for gas - * @param gasFeeAmount The amount of tokens to pay for gas - * @param refundAddress The address where refunds, if any, should be sent - */ - function payGasForExpressCallWithToken( - address sender, - string calldata destinationChain, - string calldata destinationAddress, - bytes calldata payload, - string calldata symbol, - uint256 amount, - address gasToken, - uint256 gasFeeAmount, - address refundAddress - ) external; - - /** - * @notice Pay for gas using native currency for an express contract call on a destination chain. - * @dev This function is called on the source chain before calling the gateway to execute a remote contract. - * @param sender The address making the payment - * @param destinationChain The target chain where the contract call will be made - * @param destinationAddress The target address on the destination chain - * @param payload Data payload for the contract call - * @param refundAddress The address where refunds, if any, should be sent - */ - function payNativeGasForExpressCall( - address sender, - string calldata destinationChain, - string calldata destinationAddress, - bytes calldata payload, - address refundAddress - ) external payable; - - /** - * @notice Pay for gas using native currency for an express contract call with tokens on a destination chain. - * @dev This function is called on the source chain before calling the gateway to execute a remote contract. - * @param sender The address making the payment - * @param destinationChain The target chain where the contract call with tokens will be made - * @param destinationAddress The target address on the destination chain - * @param payload Data payload for the contract call with tokens - * @param symbol The symbol of the token to be sent with the call - * @param amount The amount of tokens to be sent with the call - * @param refundAddress The address where refunds, if any, should be sent - */ - function payNativeGasForExpressCallWithToken( - address sender, - string calldata destinationChain, - string calldata destinationAddress, - bytes calldata payload, - string calldata symbol, - uint256 amount, - address refundAddress - ) external payable; - - /** - * @notice Add additional gas payment using ERC20 tokens after initiating a cross-chain call. - * @dev This function can be called on the source chain after calling the gateway to execute a remote contract. - * @param txHash The transaction hash of the cross-chain call - * @param logIndex The log index for the cross-chain call - * @param gasToken The ERC20 token address used to add gas - * @param gasFeeAmount The amount of tokens to add as gas - * @param refundAddress The address where refunds, if any, should be sent - */ - function addGas( - bytes32 txHash, - uint256 logIndex, - address gasToken, - uint256 gasFeeAmount, - address refundAddress - ) external; - - /** - * @notice Add additional gas payment using native currency after initiating a cross-chain call. - * @dev This function can be called on the source chain after calling the gateway to execute a remote contract. - * @param txHash The transaction hash of the cross-chain call - * @param logIndex The log index for the cross-chain call - * @param refundAddress The address where refunds, if any, should be sent - */ - function addNativeGas(bytes32 txHash, uint256 logIndex, address refundAddress) external payable; - - /** - * @notice Add additional gas payment using ERC20 tokens after initiating an express cross-chain call. - * @dev This function can be called on the source chain after calling the gateway to express execute a remote contract. - * @param txHash The transaction hash of the cross-chain call - * @param logIndex The log index for the cross-chain call - * @param gasToken The ERC20 token address used to add gas - * @param gasFeeAmount The amount of tokens to add as gas - * @param refundAddress The address where refunds, if any, should be sent - */ - function addExpressGas( - bytes32 txHash, - uint256 logIndex, - address gasToken, - uint256 gasFeeAmount, - address refundAddress - ) external; - - /** - * @notice Add additional gas payment using native currency after initiating an express cross-chain call. - * @dev This function can be called on the source chain after calling the gateway to express execute a remote contract. - * @param txHash The transaction hash of the cross-chain call - * @param logIndex The log index for the cross-chain call - * @param refundAddress The address where refunds, if any, should be sent - */ - function addNativeExpressGas(bytes32 txHash, uint256 logIndex, address refundAddress) external payable; - - /** - * @notice Updates the gas price for a specific chain. - * @dev This function is called by the gas oracle to update the gas prices for a specific chains. - * @param chains Array of chain names - * @param gasUpdates Array of gas updates - */ - function updateGasInfo(string[] calldata chains, GasInfo[] calldata gasUpdates) external; - - /** - * @notice Allows the gasCollector to collect accumulated fees from the contract. - * @dev Use address(0) as the token address for native currency. - * @param receiver The address to receive the collected fees - * @param tokens Array of token addresses to be collected - * @param amounts Array of amounts to be collected for each respective token address - */ - function collectFees(address payable receiver, address[] calldata tokens, uint256[] calldata amounts) external; - - /** - * @notice Refunds gas payment to the receiver in relation to a specific cross-chain transaction. - * @dev Only callable by the gasCollector. - * @dev Use address(0) as the token address to refund native currency. - * @param txHash The transaction hash of the cross-chain call - * @param logIndex The log index for the cross-chain call - * @param receiver The address to receive the refund - * @param token The token address to be refunded - * @param amount The amount to refund - */ - function refund(bytes32 txHash, uint256 logIndex, address payable receiver, address token, uint256 amount) external; - - /** - * @notice Returns the address of the designated gas collector. - * @return address of the gas collector - */ - function gasCollector() external returns (address); -} diff --git a/contracts/crosschain/vendor/axelar/interfaces/IAxelarGateway.sol b/contracts/crosschain/vendor/axelar/interfaces/IAxelarGateway.sol deleted file mode 100644 index bb12c951..00000000 --- a/contracts/crosschain/vendor/axelar/interfaces/IAxelarGateway.sol +++ /dev/null @@ -1,188 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {IGovernable} from "./IGovernable.sol"; -import {IImplementation} from "./IImplementation.sol"; - -interface IAxelarGateway is IImplementation, IGovernable { - /**********\ - |* Errors *| - \**********/ - - error NotSelf(); - error InvalidCodeHash(); - error SetupFailed(); - error InvalidAuthModule(); - error InvalidTokenDeployer(); - error InvalidAmount(); - error InvalidChainId(); - error InvalidCommands(); - error TokenDoesNotExist(string symbol); - error TokenAlreadyExists(string symbol); - error TokenDeployFailed(string symbol); - error TokenContractDoesNotExist(address token); - error BurnFailed(string symbol); - error MintFailed(string symbol); - error InvalidSetMintLimitsParams(); - error ExceedMintLimit(string symbol); - - /**********\ - |* Events *| - \**********/ - - event TokenSent( - address indexed sender, - string destinationChain, - string destinationAddress, - string symbol, - uint256 amount - ); - - event ContractCall( - address indexed sender, - string destinationChain, - string destinationContractAddress, - bytes32 indexed payloadHash, - bytes payload - ); - - event ContractCallWithToken( - address indexed sender, - string destinationChain, - string destinationContractAddress, - bytes32 indexed payloadHash, - bytes payload, - string symbol, - uint256 amount - ); - - event Executed(bytes32 indexed commandId); - - event TokenDeployed(string symbol, address tokenAddresses); - - event ContractCallApproved( - bytes32 indexed commandId, - string sourceChain, - string sourceAddress, - address indexed contractAddress, - bytes32 indexed payloadHash, - bytes32 sourceTxHash, - uint256 sourceEventIndex - ); - - event ContractCallApprovedWithMint( - bytes32 indexed commandId, - string sourceChain, - string sourceAddress, - address indexed contractAddress, - bytes32 indexed payloadHash, - string symbol, - uint256 amount, - bytes32 sourceTxHash, - uint256 sourceEventIndex - ); - - event ContractCallExecuted(bytes32 indexed commandId); - - event TokenMintLimitUpdated(string symbol, uint256 limit); - - event OperatorshipTransferred(bytes newOperatorsData); - - event Upgraded(address indexed implementation); - - /********************\ - |* Public Functions *| - \********************/ - - function sendToken( - string calldata destinationChain, - string calldata destinationAddress, - string calldata symbol, - uint256 amount - ) external; - - function callContract( - string calldata destinationChain, - string calldata contractAddress, - bytes calldata payload - ) external; - - function callContractWithToken( - string calldata destinationChain, - string calldata contractAddress, - bytes calldata payload, - string calldata symbol, - uint256 amount - ) external; - - function isContractCallApproved( - bytes32 commandId, - string calldata sourceChain, - string calldata sourceAddress, - address contractAddress, - bytes32 payloadHash - ) external view returns (bool); - - function isContractCallAndMintApproved( - bytes32 commandId, - string calldata sourceChain, - string calldata sourceAddress, - address contractAddress, - bytes32 payloadHash, - string calldata symbol, - uint256 amount - ) external view returns (bool); - - function validateContractCall( - bytes32 commandId, - string calldata sourceChain, - string calldata sourceAddress, - bytes32 payloadHash - ) external returns (bool); - - function validateContractCallAndMint( - bytes32 commandId, - string calldata sourceChain, - string calldata sourceAddress, - bytes32 payloadHash, - string calldata symbol, - uint256 amount - ) external returns (bool); - - /***********\ - |* Getters *| - \***********/ - - function authModule() external view returns (address); - - function tokenDeployer() external view returns (address); - - function tokenMintLimit(string memory symbol) external view returns (uint256); - - function tokenMintAmount(string memory symbol) external view returns (uint256); - - function allTokensFrozen() external view returns (bool); - - function implementation() external view returns (address); - - function tokenAddresses(string memory symbol) external view returns (address); - - function tokenFrozen(string memory symbol) external view returns (bool); - - function isCommandExecuted(bytes32 commandId) external view returns (bool); - - /************************\ - |* Governance Functions *| - \************************/ - - function setTokenMintLimits(string[] calldata symbols, uint256[] calldata limits) external; - - function upgrade(address newImplementation, bytes32 newImplementationCodeHash, bytes calldata setupParams) external; - - /**********************\ - |* External Functions *| - \**********************/ - - function execute(bytes calldata input) external; -} diff --git a/contracts/crosschain/vendor/axelar/interfaces/IContractIdentifier.sol b/contracts/crosschain/vendor/axelar/interfaces/IContractIdentifier.sol deleted file mode 100644 index bf32f96c..00000000 --- a/contracts/crosschain/vendor/axelar/interfaces/IContractIdentifier.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -// General interface for upgradable contracts -interface IContractIdentifier { - /** - * @notice Returns the contract ID. It can be used as a check during upgrades. - * @dev Meant to be overridden in derived contracts. - * @return bytes32 The contract ID - */ - function contractId() external pure returns (bytes32); -} diff --git a/contracts/crosschain/vendor/axelar/interfaces/IGovernable.sol b/contracts/crosschain/vendor/axelar/interfaces/IGovernable.sol deleted file mode 100644 index c08f4afc..00000000 --- a/contracts/crosschain/vendor/axelar/interfaces/IGovernable.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -/** - * @title IGovernable Interface - * @notice This is an interface used by the AxelarGateway contract to manage governance and mint limiter roles. - */ -interface IGovernable { - error NotGovernance(); - error NotMintLimiter(); - error InvalidGovernance(); - error InvalidMintLimiter(); - - event GovernanceTransferred(address indexed previousGovernance, address indexed newGovernance); - event MintLimiterTransferred(address indexed previousGovernance, address indexed newGovernance); - - /** - * @notice Returns the governance address. - * @return address of the governance - */ - function governance() external view returns (address); - - /** - * @notice Returns the mint limiter address. - * @return address of the mint limiter - */ - function mintLimiter() external view returns (address); - - /** - * @notice Transfer the governance role to another address. - * @param newGovernance The new governance address - */ - function transferGovernance(address newGovernance) external; - - /** - * @notice Transfer the mint limiter role to another address. - * @param newGovernance The new mint limiter address - */ - function transferMintLimiter(address newGovernance) external; -} diff --git a/contracts/crosschain/vendor/axelar/interfaces/IImplementation.sol b/contracts/crosschain/vendor/axelar/interfaces/IImplementation.sol deleted file mode 100644 index ef2631d4..00000000 --- a/contracts/crosschain/vendor/axelar/interfaces/IImplementation.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {IContractIdentifier} from "./IContractIdentifier.sol"; - -interface IImplementation is IContractIdentifier { - error NotProxy(); - - function setup(bytes calldata data) external; -} diff --git a/contracts/crosschain/vendor/axelar/interfaces/IInterchainGasEstimation.sol b/contracts/crosschain/vendor/axelar/interfaces/IInterchainGasEstimation.sol deleted file mode 100644 index 74d4cce8..00000000 --- a/contracts/crosschain/vendor/axelar/interfaces/IInterchainGasEstimation.sol +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {GasEstimationType, GasInfo} from "../types/GasEstimationTypes.sol"; - -/** - * @title IInterchainGasEstimation Interface - * @notice This is an interface for the InterchainGasEstimation contract - * which allows for estimating gas fees for cross-chain communication on the Axelar network. - */ -interface IInterchainGasEstimation { - error UnsupportedEstimationType(GasEstimationType gasEstimationType); - - /** - * @notice Event emitted when the gas price for a specific chain is updated. - * @param chain The name of the chain - * @param info The gas info for the chain - */ - event GasInfoUpdated(string chain, GasInfo info); - - /** - * @notice Returns the gas price for a specific chain. - * @param chain The name of the chain - * @return gasInfo The gas info for the chain - */ - function getGasInfo(string calldata chain) external view returns (GasInfo memory); - - /** - * @notice Estimates the gas fee for a cross-chain contract call. - * @param destinationChain Axelar registered name of the destination chain - * @param destinationAddress Destination contract address being called - * @param executionGasLimit The gas limit to be used for the destination contract execution, - * e.g. pass in 200k if your app consumes needs upto 200k for this contract call - * @param params Additional parameters for the gas estimation - * @return gasEstimate The cross-chain gas estimate, in terms of source chain's native gas token that should be forwarded to the gas service. - */ - function estimateGasFee( - string calldata destinationChain, - string calldata destinationAddress, - bytes calldata payload, - uint256 executionGasLimit, - bytes calldata params - ) external view returns (uint256 gasEstimate); -} diff --git a/contracts/crosschain/vendor/axelar/interfaces/IOwnable.sol b/contracts/crosschain/vendor/axelar/interfaces/IOwnable.sol deleted file mode 100644 index 725bcb36..00000000 --- a/contracts/crosschain/vendor/axelar/interfaces/IOwnable.sol +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -/** - * @title IOwnable Interface - * @notice IOwnable is an interface that abstracts the implementation of a - * contract with ownership control features. It's commonly used in upgradable - * contracts and includes the functionality to get current owner, transfer - * ownership, and propose and accept ownership. - */ -interface IOwnable { - error NotOwner(); - error InvalidOwner(); - error InvalidOwnerAddress(); - - event OwnershipTransferStarted(address indexed newOwner); - event OwnershipTransferred(address indexed newOwner); - - /** - * @notice Returns the current owner of the contract. - * @return address The address of the current owner - */ - function owner() external view returns (address); - - /** - * @notice Returns the address of the pending owner of the contract. - * @return address The address of the pending owner - */ - function pendingOwner() external view returns (address); - - /** - * @notice Transfers ownership of the contract to a new address - * @param newOwner The address to transfer ownership to - */ - function transferOwnership(address newOwner) external; - - /** - * @notice Proposes to transfer the contract's ownership to a new address. - * The new owner needs to accept the ownership explicitly. - * @param newOwner The address to transfer ownership to - */ - function proposeOwnership(address newOwner) external; - - /** - * @notice Transfers ownership to the pending owner. - * @dev Can only be called by the pending owner - */ - function acceptOwnership() external; -} diff --git a/contracts/crosschain/vendor/axelar/interfaces/IUpgradable.sol b/contracts/crosschain/vendor/axelar/interfaces/IUpgradable.sol deleted file mode 100644 index c7a14709..00000000 --- a/contracts/crosschain/vendor/axelar/interfaces/IUpgradable.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {IOwnable} from "./IOwnable.sol"; -import {IImplementation} from "./IImplementation.sol"; - -// General interface for upgradable contracts -interface IUpgradable is IOwnable, IImplementation { - error InvalidCodeHash(); - error InvalidImplementation(); - error SetupFailed(); - - event Upgraded(address indexed newImplementation); - - function implementation() external view returns (address); - - function upgrade(address newImplementation, bytes32 newImplementationCodeHash, bytes calldata params) external; -} diff --git a/contracts/crosschain/vendor/axelar/types/GasEstimationTypes.sol b/contracts/crosschain/vendor/axelar/types/GasEstimationTypes.sol deleted file mode 100644 index 7a3aa14d..00000000 --- a/contracts/crosschain/vendor/axelar/types/GasEstimationTypes.sol +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -/** - * @title GasEstimationType - * @notice This enum represents the gas estimation types for different chains. - */ -enum GasEstimationType { - Default, - OptimismEcotone, - OptimismBedrock, - Arbitrum, - Scroll -} - -/** - * @title GasInfo - * @notice This struct represents the gas pricing information for a specific chain. - * @dev Smaller uint types are used for efficient struct packing to save storage costs. - */ -struct GasInfo { - /// @dev Custom gas pricing rule, such as L1 data fee on L2s - uint64 gasEstimationType; - /// @dev Scalar value needed for specific gas estimation types, expected to be less than 1e10 - uint64 l1FeeScalar; - /// @dev Axelar base fee for cross-chain message approval on destination, in terms of source native gas token - uint128 axelarBaseFee; - /// @dev Gas price of destination chain, in terms of the source chain token, i.e dest_gas_price * dest_token_market_price / src_token_market_price - uint128 relativeGasPrice; - /// @dev Needed for specific gas estimation types. Blob base fee of destination chain, in terms of the source chain token, i.e dest_blob_base_fee * dest_token_market_price / src_token_market_price - uint128 relativeBlobBaseFee; - /// @dev Axelar express fee for express execution, in terms of source chain token - uint128 expressFee; -} diff --git a/package-lock.json b/package-lock.json index 0f2bf1ea..de94f67b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "license": "MIT", "dependencies": { + "@axelar-network/axelar-cgp-solidity": "^6.3.1", "@openzeppelin/contracts": "^5.0.2", "@openzeppelin/contracts-upgradeable": "^5.0.2" }, @@ -29,6 +30,27 @@ "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", "dev": true }, + "node_modules/@axelar-network/axelar-cgp-solidity": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@axelar-network/axelar-cgp-solidity/-/axelar-cgp-solidity-6.3.1.tgz", + "integrity": "sha512-RJmcOQbj2VhQb8uqBtu0beNj4MKRVjiYj74wXu6QI/a4bJ5EwwZK3+uCbTVNM9Qc7LqJqObhtGQfKUnAXEFCHA==", + "license": "MIT", + "dependencies": { + "@axelar-network/axelar-gmp-sdk-solidity": "5.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@axelar-network/axelar-gmp-sdk-solidity": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@axelar-network/axelar-gmp-sdk-solidity/-/axelar-gmp-sdk-solidity-5.8.0.tgz", + "integrity": "sha512-ThiCWK7lhwmsipgjKkw8c0z0ubB9toRMV9X0tRVOXHHSknKp5DCFfatbCwjpSC5GZRa+61ciTSqJNtCc7j9YoQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@ethersproject/abi": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", diff --git a/package.json b/package.json index 1cddf635..e2fea6fb 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "zeppelin" ], "dependencies": { + "@axelar-network/axelar-cgp-solidity": "^6.3.1", "@openzeppelin/contracts": "^5.0.2", "@openzeppelin/contracts-upgradeable": "^5.0.2" }, diff --git a/remappings.txt b/remappings.txt index 0097828b..975a565c 100644 --- a/remappings.txt +++ b/remappings.txt @@ -3,3 +3,5 @@ @openzeppelin/contracts@master/=lib/@openzeppelin-contracts/contracts/ @openzeppelin/contracts-upgradeable@master/=lib/@openzeppelin-contracts-upgradeable/contracts/ + +@axelar-network/axelar-cgp-solidity/=node_modules/@axelar-network/axelar-cgp-solidity/contracts/ From e39ff3a675e0beff6fbfcc995cb8e32d83694e7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernesto=20Garc=C3=ADa?= Date: Mon, 2 Sep 2024 21:48:58 -0600 Subject: [PATCH 19/41] Apply review sugggestion --- contracts/utils/CAIP-10.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/utils/CAIP-10.sol b/contracts/utils/CAIP-10.sol index a30bf955..763195e3 100644 --- a/contracts/utils/CAIP-10.sol +++ b/contracts/utils/CAIP-10.sol @@ -13,16 +13,16 @@ library CAIP10 { using SafeCast for uint256; using Bytes for bytes; - bytes1 private constant SEMICOLON = ":"; + bytes1 private constant COLON = ":"; function toString(string memory caip2, string memory accountId) internal pure returns (string memory) { - return string(abi.encodePacked(caip2, SEMICOLON, accountId)); + return string(abi.encodePacked(caip2, COLON, accountId)); } function parse(string memory caip10) internal pure returns (string memory caip2, string memory accountId) { bytes memory accountBuffer = bytes(caip10); - uint8 firstSeparatorIndex = accountBuffer.find(SEMICOLON, 0).toUint8(); - uint256 lastSeparatorIndex = accountBuffer.find(SEMICOLON, firstSeparatorIndex).toUint8(); + uint8 firstSeparatorIndex = accountBuffer.find(COLON, 0).toUint8(); + uint256 lastSeparatorIndex = accountBuffer.find(COLON, firstSeparatorIndex).toUint8(); return (_extractCAIP2(accountBuffer, lastSeparatorIndex), _extractAccountId(accountBuffer, lastSeparatorIndex)); } From 0e7d04049a77ce0fcbc3de5e4266ee0921b91122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernesto=20Garc=C3=ADa?= Date: Mon, 2 Sep 2024 22:14:32 -0600 Subject: [PATCH 20/41] Apply suggestions --- .../axelar/AxelarGatewayIncoming.sol | 26 ++++++++++--------- package-lock.json | 14 +++++++++- package.json | 1 + remappings.txt | 1 + 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/contracts/crosschain/axelar/AxelarGatewayIncoming.sol b/contracts/crosschain/axelar/AxelarGatewayIncoming.sol index 71ee631a..cb96d990 100644 --- a/contracts/crosschain/axelar/AxelarGatewayIncoming.sol +++ b/contracts/crosschain/axelar/AxelarGatewayIncoming.sol @@ -5,10 +5,16 @@ pragma solidity ^0.8.0; import {IAxelarGateway} from "@axelar-network/axelar-cgp-solidity/interfaces/IAxelarGateway.sol"; import {IGatewayIncomingPassive} from "../IGatewayIncomingPassive.sol"; import {IGatewayIncoming} from "../IGatewayIncoming.sol"; +import {IGatewayReceiver} from "../IGatewayReceiver.sol"; +import {AxelarCAIP2Equivalence} from "./AxelarCAIP2Equivalence.sol"; +import {AxelarExecutable} from "@axelar-network/axelar-gmp-sdk-solidity/executable/AxelarExecutable.sol"; -abstract contract AxelarGatewayIncoming is IGatewayIncoming, IGatewayIncomingPassive { - IAxelarGateway public immutable gateway; - +abstract contract AxelarGatewayIncoming is + AxelarExecutable, + AxelarCAIP2Equivalence, + IGatewayIncoming, + IGatewayIncomingPassive +{ function validateReceivedMessage( bytes32 messageId, string calldata srcChain, // CAIP-2 chain ID @@ -19,7 +25,7 @@ abstract contract AxelarGatewayIncoming is IGatewayIncoming, IGatewayIncomingPas if (!_isValidReceivedMessage(messageId, srcChain, srcAccount, payload, attributes)) { revert GatewayIncomingPassiveInvalidMessage(messageId); } - emit MessageExecuted(messageId); + _execute(string(fromCAIP2(destChain)), srcAccount, payload); } function _isValidReceivedMessage( @@ -32,13 +38,9 @@ abstract contract AxelarGatewayIncoming is IGatewayIncoming, IGatewayIncomingPas return gateway.validateContractCall(messageId, srcChain, srcAccount, keccak256(payload)); } - function _execute( - bytes32 messageId, - string calldata srcChain, - string calldata srcAccount, - bytes calldata payload, - bytes calldata attributes - ) internal { - // messageId + function _execute(string calldata srcChain, string calldata srcAccount, bytes calldata payload) internal virtual { + (address destination, bytes memory data) = abi.decode(payload, (address, bytes)); + IGatewayReceiver(destination).receiveMessage(keccak256(data), sourceChain, sourceAddress, data, ""); + emit MessageExecuted(keccak256(data)); // What to use if we can't reconstruct the message? } } diff --git a/package-lock.json b/package-lock.json index b8e72f4a..0e196841 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "MIT", "dependencies": { "@axelar-network/axelar-cgp-solidity": "^6.3.1", + "@axelar-network/axelar-gmp-sdk-solidity": "^5.10.0", "@openzeppelin/contracts": "^5.0.2", "@openzeppelin/contracts-upgradeable": "^5.0.2" }, @@ -42,8 +43,19 @@ "node": ">=18" } }, - "node_modules/@axelar-network/axelar-gmp-sdk-solidity": { + "node_modules/@axelar-network/axelar-cgp-solidity/node_modules/@axelar-network/axelar-gmp-sdk-solidity": { "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@axelar-network/axelar-gmp-sdk-solidity/-/axelar-gmp-sdk-solidity-5.8.0.tgz", + "integrity": "sha512-ThiCWK7lhwmsipgjKkw8c0z0ubB9toRMV9X0tRVOXHHSknKp5DCFfatbCwjpSC5GZRa+61ciTSqJNtCc7j9YoQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@axelar-network/axelar-gmp-sdk-solidity": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/@axelar-network/axelar-gmp-sdk-solidity/-/axelar-gmp-sdk-solidity-5.10.0.tgz", + "integrity": "sha512-s8SImALvYB+5AeiT3tbfWNBI2Mhqw1x91i/zM3DNpVUCnAR2HKtsB9T84KnUn/OJjOVgb4h0lv7q9smeYniRPw==", "license": "MIT", "engines": { "node": ">=18" diff --git a/package.json b/package.json index 3d7a26f4..02b8f1aa 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ ], "dependencies": { "@axelar-network/axelar-cgp-solidity": "^6.3.1", + "@axelar-network/axelar-gmp-sdk-solidity": "^5.10.0", "@openzeppelin/contracts": "^5.0.2", "@openzeppelin/contracts-upgradeable": "^5.0.2" }, diff --git a/remappings.txt b/remappings.txt index 71bac809..138ddb98 100644 --- a/remappings.txt +++ b/remappings.txt @@ -7,3 +7,4 @@ @openzeppelin/community-contracts/=contracts/ @axelar-network/axelar-cgp-solidity/=node_modules/@axelar-network/axelar-cgp-solidity/contracts/ +@axelar-network/axelar-gmp-sdk-solidity/=node_modules/@axelar-network/axelar-gmp-sdk-solidity/contracts/ From 24029264ee1618ab57dc43318b08290540833fe7 Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Tue, 3 Sep 2024 12:43:54 -0300 Subject: [PATCH 21/41] wip fixes --- .../axelar/AxelarGatewayIncoming.sol | 22 +++++++++++-------- .../axelar/AxelarGatewayOutgoing.sol | 12 +++++----- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/contracts/crosschain/axelar/AxelarGatewayIncoming.sol b/contracts/crosschain/axelar/AxelarGatewayIncoming.sol index cb96d990..d0c8ec74 100644 --- a/contracts/crosschain/axelar/AxelarGatewayIncoming.sol +++ b/contracts/crosschain/axelar/AxelarGatewayIncoming.sol @@ -8,6 +8,7 @@ import {IGatewayIncoming} from "../IGatewayIncoming.sol"; import {IGatewayReceiver} from "../IGatewayReceiver.sol"; import {AxelarCAIP2Equivalence} from "./AxelarCAIP2Equivalence.sol"; import {AxelarExecutable} from "@axelar-network/axelar-gmp-sdk-solidity/executable/AxelarExecutable.sol"; +import {CAIP10} from "../../utils/CAIP-10.sol"; abstract contract AxelarGatewayIncoming is AxelarExecutable, @@ -22,25 +23,28 @@ abstract contract AxelarGatewayIncoming is bytes calldata payload, bytes calldata attributes ) public virtual { - if (!_isValidReceivedMessage(messageId, srcChain, srcAccount, payload, attributes)) { + address dstAccount = CAIP10.toString(msg.sender); + if (!_isValidReceivedMessage(messageId, srcChain, srcAccount, dstAccount, msg.sender, paylod, attributes)) { revert GatewayIncomingPassiveInvalidMessage(messageId); } - _execute(string(fromCAIP2(destChain)), srcAccount, payload); + _execute(string(fromCAIP2(destChain)), srcAccount, wrappedPayload); } function _isValidReceivedMessage( bytes32 messageId, string calldata srcChain, // CAIP-2 chain ID string calldata srcAccount, // i.e. address - bytes calldata payload, - bytes calldata /* attributes */ + string calldata dstAccount, + bytes calldata paylod, + bytes calldata attributes ) internal returns (bool) { - return gateway.validateContractCall(messageId, srcChain, srcAccount, keccak256(payload)); + bytes wrappedPayload = abi.encode(messageId, dstAccount, payload, attributes); + return gateway.validateContractCall(messageId, srcChain, srcAccount, keccak256(wrappedPayload)); } - function _execute(string calldata srcChain, string calldata srcAccount, bytes calldata payload) internal virtual { - (address destination, bytes memory data) = abi.decode(payload, (address, bytes)); - IGatewayReceiver(destination).receiveMessage(keccak256(data), sourceChain, sourceAddress, data, ""); - emit MessageExecuted(keccak256(data)); // What to use if we can't reconstruct the message? + function _execute(string calldata srcChain, string calldata srcAccount, bytes calldata wrappedPayload) internal virtual { + (bytes32 messageId, string destAccount, bytes payload, bytes attributes) = abi.decode(wrappedPayload, (bytes32, string, bytes, bytes)); + IGatewayReceiver(destination).receiveMessage(messageId, srcChain, srcAccount, payload, attributes); + emit MessageExecuted(messageId); } } diff --git a/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol b/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol index 6cb2fb45..02531683 100644 --- a/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol +++ b/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol @@ -32,13 +32,15 @@ abstract contract AxelarGatewayOutgoing is IGatewayOutgoing, AxelarCAIP2Equivale payload, attributes ); - bytes32 id = keccak256(abi.encode(message)); + messageId = keccak256(abi.encode(message)); emit MessageCreated(id, message); - // Send the message - gateway.callContract(string(fromCAIP2(destChain)), destAccount, payload); - emit MessageSent(id); + // Wrap the message + bytes wrappedPayload = abi.encode(messageId, destAccount, payload, attributes); - return id; + // Send the message + address destGateway = address(0); // TODO + gateway.callContract(string(fromCAIP2(destChain)), destGateway, wrappedPayload); + emit MessageSent(messageId); } } From 27dcd99e4179cd56190332f593b251871d4d3aaf Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 5 Sep 2024 15:53:42 +0200 Subject: [PATCH 22/41] trying to get crosschain to compile --- contracts/crosschain/ICAIP2Equivalence.sol | 5 +- contracts/crosschain/IGatewayDestination.sol | 7 ++ ...ive.sol => IGatewayDestinationPassive.sol} | 6 +- contracts/crosschain/IGatewayIncoming.sol | 7 -- ...GatewayOutgoing.sol => IGatewaySource.sol} | 6 +- ...2Equivalence.sol => AxelarGatewayBase.sol} | 25 +++++- .../axelar/AxelarGatewayDestination.sol | 90 +++++++++++++++++++ .../axelar/AxelarGatewayIncoming.sol | 50 ----------- .../axelar/AxelarGatewayOutgoing.sol | 46 ---------- .../crosschain/axelar/AxelarGatewaySource.sol | 44 +++++++++ hardhat.config.js | 5 +- lib/@openzeppelin-contracts | 2 +- remappings.txt | 4 +- 13 files changed, 177 insertions(+), 120 deletions(-) create mode 100644 contracts/crosschain/IGatewayDestination.sol rename contracts/crosschain/{IGatewayIncomingPassive.sol => IGatewayDestinationPassive.sol} (61%) delete mode 100644 contracts/crosschain/IGatewayIncoming.sol rename contracts/crosschain/{IGatewayOutgoing.sol => IGatewaySource.sol} (76%) rename contracts/crosschain/axelar/{AxelarCAIP2Equivalence.sol => AxelarGatewayBase.sol} (86%) create mode 100644 contracts/crosschain/axelar/AxelarGatewayDestination.sol delete mode 100644 contracts/crosschain/axelar/AxelarGatewayIncoming.sol delete mode 100644 contracts/crosschain/axelar/AxelarGatewayOutgoing.sol create mode 100644 contracts/crosschain/axelar/AxelarGatewaySource.sol diff --git a/contracts/crosschain/ICAIP2Equivalence.sol b/contracts/crosschain/ICAIP2Equivalence.sol index b72dc9cb..45b47bb6 100644 --- a/contracts/crosschain/ICAIP2Equivalence.sol +++ b/contracts/crosschain/ICAIP2Equivalence.sol @@ -2,9 +2,6 @@ pragma solidity ^0.8.0; -import {IAxelarGateway} from "@axelar-network/axelar-cgp-solidity/interfaces/IAxelarGateway.sol"; -import {IAxelarGasService} from "@axelar-network/axelar-cgp-solidity/interfaces/IAxelarGasService.sol"; - /// @dev Equivalence interface between CAIP-2 chain identifiers and protocol-specific chain identifiers. /// /// See https://chainagnostic.org/CAIPs/caip-2[CAIP2]. @@ -15,5 +12,5 @@ interface ICAIP2Equivalence { function supported(string memory caip2) external view returns (bool); /// @dev Retrieves the protocol-specific chain identifier equivalent to a CAIP-2 chain identifier. - function fromCAIP2(string memory caip2) external view returns (bytes memory); + function fromCAIP2(string memory caip2) external view returns (string memory); } diff --git a/contracts/crosschain/IGatewayDestination.sol b/contracts/crosschain/IGatewayDestination.sol new file mode 100644 index 00000000..74d62c5e --- /dev/null +++ b/contracts/crosschain/IGatewayDestination.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +interface IGatewayDestination { + event MessageExecuted(bytes32 indexed messageId); +} diff --git a/contracts/crosschain/IGatewayIncomingPassive.sol b/contracts/crosschain/IGatewayDestinationPassive.sol similarity index 61% rename from contracts/crosschain/IGatewayIncomingPassive.sol rename to contracts/crosschain/IGatewayDestinationPassive.sol index d999e1e6..7732da17 100644 --- a/contracts/crosschain/IGatewayIncomingPassive.sol +++ b/contracts/crosschain/IGatewayDestinationPassive.sol @@ -2,11 +2,11 @@ pragma solidity ^0.8.0; -interface IGatewayIncomingPassive { - error GatewayIncomingPassiveInvalidMessage(bytes32 messageId); +interface IGatewayDestinationPassive { + error GatewayDestinationPassiveInvalidMessage(bytes32 messageDestinationId); function validateReceivedMessage( - bytes32 messageId, + bytes32 messageDestinationId, string calldata srcChain, string calldata srcAccount, bytes calldata payload, diff --git a/contracts/crosschain/IGatewayIncoming.sol b/contracts/crosschain/IGatewayIncoming.sol deleted file mode 100644 index b67eb536..00000000 --- a/contracts/crosschain/IGatewayIncoming.sol +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -interface IGatewayIncoming { - event MessageExecuted(bytes32 indexed id); -} diff --git a/contracts/crosschain/IGatewayOutgoing.sol b/contracts/crosschain/IGatewaySource.sol similarity index 76% rename from contracts/crosschain/IGatewayOutgoing.sol rename to contracts/crosschain/IGatewaySource.sol index 69079ac9..0c2f022a 100644 --- a/contracts/crosschain/IGatewayOutgoing.sol +++ b/contracts/crosschain/IGatewaySource.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; -interface IGatewayOutgoing { +interface IGatewaySource { struct Message { string source; // CAIP-10 account ID string destination; // CAIP-10 account ID @@ -10,8 +10,8 @@ interface IGatewayOutgoing { bytes attributes; } - event MessageCreated(bytes32 indexed id, Message message); - event MessageSent(bytes32 indexed id); + event MessageCreated(bytes32 indexed messageId, Message message); + event MessageSent(bytes32 indexed messageId); function sendMessage( string calldata destChain, // CAIP-2 chain ID diff --git a/contracts/crosschain/axelar/AxelarCAIP2Equivalence.sol b/contracts/crosschain/axelar/AxelarGatewayBase.sol similarity index 86% rename from contracts/crosschain/axelar/AxelarCAIP2Equivalence.sol rename to contracts/crosschain/axelar/AxelarGatewayBase.sol index 35eb4eb4..83fc6fdb 100644 --- a/contracts/crosschain/axelar/AxelarCAIP2Equivalence.sol +++ b/contracts/crosschain/axelar/AxelarGatewayBase.sol @@ -2,17 +2,36 @@ pragma solidity ^0.8.0; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {IAxelarGateway} from "@axelar-network/axelar-cgp-solidity/contracts/interfaces/IAxelarGateway.sol"; import {ICAIP2Equivalence} from "../ICAIP2Equivalence.sol"; -abstract contract AxelarCAIP2Equivalence is ICAIP2Equivalence { +abstract contract AxelarGatewayBase is ICAIP2Equivalence, Ownable { + IAxelarGateway public immutable localGateway; + + mapping(string caip2 => string foreignGateway) private _foreignGateways; mapping(string caip2 => string destinationChain) private _equivalence; + constructor(IAxelarGateway _gateway) { + localGateway = _gateway; + } + function supported(string memory caip2) public view returns (bool) { return bytes(_equivalence[caip2]).length != 0; } - function fromCAIP2(string memory caip2) public view returns (bytes memory) { - return bytes(_equivalence[caip2]); + function fromCAIP2(string memory caip2) public view returns (string memory) { + return _equivalence[caip2]; + } + + function registerForeignGateway(string calldata caip2, string calldata foreignGateway) public onlyOwner { + require(bytes(_foreignGateways[caip2]).length == 0); + _foreignGateways[caip2] = foreignGateway; + // TODO emit event + } + + function getForeignGateway(string memory caip2) public view returns (string memory foreignGateway) { + return _foreignGateways[caip2]; } } diff --git a/contracts/crosschain/axelar/AxelarGatewayDestination.sol b/contracts/crosschain/axelar/AxelarGatewayDestination.sol new file mode 100644 index 00000000..5f8ade67 --- /dev/null +++ b/contracts/crosschain/axelar/AxelarGatewayDestination.sol @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {AxelarExecutable} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {AxelarGatewayBase} from "./AxelarGatewayBase.sol"; +import {IGatewayDestination} from "../IGatewayDestination.sol"; +import {IGatewayDestinationPassive} from "../IGatewayDestinationPassive.sol"; +import {IGatewayReceiver} from "../IGatewayReceiver.sol"; +import {CAIP2} from "../../utils/CAIP-2.sol"; +import {CAIP10} from "../../utils/CAIP-10.sol"; + +abstract contract AxelarGatewayDestination is + IGatewayDestination, + // IGatewayDestinationPassive, // TODO + AxelarGatewayBase, + AxelarExecutable +{ + // function validateReceivedMessage( + // bytes32 messageDestinationId, + // string calldata srcChain, // CAIP-2 chain ID + // string calldata srcAccount, // i.e. address + // bytes calldata payload, + // bytes calldata attributes + // ) public virtual { + // address dstAccount = CAIP10.toString(msg.sender); + // if (!_isValidReceivedMessage(messageDestinationId, srcChain, srcAccount, dstAccount, msg.sender, paylod, attributes)) { + // revert GatewayDestinationPassiveInvalidMessage(messageDestinationId); + // } + // _execute(string(fromCAIP2(destChain)), srcAccount, wrappedPayload); + // } + + // function _isValidReceivedMessage( + // bytes32 messageDestinationId, + // string calldata srcChain, // CAIP-2 chain ID + // string calldata srcAccount, // i.e. address + // string calldata dstAccount, + // bytes calldata paylod, + // bytes calldata attributes + // ) internal returns (bool) { + // bytes wrappedPayload = abi.encode(messageDestinationId, dstAccount, payload, attributes); + // return gateway.validateContractCall(messageDestinationId, srcChain, srcAccount, keccak256(wrappedPayload)); + // } + + // In this function: + // - `srcChain` is in the Axelar format. It should not be expected to be a proper CAIP-2 format + // - `srcAccount` is the sender of the crosschain message. That should be the foreign gateway on the chain which + // the message originates from. It is NOT the sender of the crosschain message + // + // Proper CAIP-10 encoding of the message sender (including the CAIP-2 name of the origin chain can be found in + // the mssage) + function _execute( + string calldata srcChain, + string calldata srcAccount, + bytes calldata wrappedPayload + ) internal virtual override { + // Parse the message package + // - message identifier (from the source, not unique ?) + // - source account (caller of this gateway) + // - destination account + // - payload + // - attributes + ( + bytes32 messageId, + string memory caip10Src, + string memory caip10Dst, + bytes memory payload, + bytes memory attributes + ) = abi.decode(wrappedPayload, (bytes32, string, string, bytes, bytes)); + + (string memory originChain, string memory originAccount) = CAIP10.parse(caip10Src); + (string memory targetChain, string memory targetAccount) = CAIP10.parse(caip10Dst); + + // check message validity + // - `srcChain` matches origin chain in the message (in caip2) + // - `srcAccount` is the foreign gateway on the origin chain. + require(Strings.equal(srcChain, fromCAIP2(originChain)), "Invalid origin chain"); + require(Strings.equal(srcAccount, getForeignGateway(originChain)), "Invalid origin gateway"); + require(CAIP2.isCurrentId(targetChain), "Invalid tardet chain"); + + // TODO: not available yet + // address destination = address(uint160(Strings.toUint(targetAccount))); + targetAccount; + address destination = address(0); + + IGatewayReceiver(destination).receiveMessage(messageId, originChain, originAccount, payload, attributes); + emit MessageExecuted(messageId); + } +} diff --git a/contracts/crosschain/axelar/AxelarGatewayIncoming.sol b/contracts/crosschain/axelar/AxelarGatewayIncoming.sol deleted file mode 100644 index d0c8ec74..00000000 --- a/contracts/crosschain/axelar/AxelarGatewayIncoming.sol +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {IAxelarGateway} from "@axelar-network/axelar-cgp-solidity/interfaces/IAxelarGateway.sol"; -import {IGatewayIncomingPassive} from "../IGatewayIncomingPassive.sol"; -import {IGatewayIncoming} from "../IGatewayIncoming.sol"; -import {IGatewayReceiver} from "../IGatewayReceiver.sol"; -import {AxelarCAIP2Equivalence} from "./AxelarCAIP2Equivalence.sol"; -import {AxelarExecutable} from "@axelar-network/axelar-gmp-sdk-solidity/executable/AxelarExecutable.sol"; -import {CAIP10} from "../../utils/CAIP-10.sol"; - -abstract contract AxelarGatewayIncoming is - AxelarExecutable, - AxelarCAIP2Equivalence, - IGatewayIncoming, - IGatewayIncomingPassive -{ - function validateReceivedMessage( - bytes32 messageId, - string calldata srcChain, // CAIP-2 chain ID - string calldata srcAccount, // i.e. address - bytes calldata payload, - bytes calldata attributes - ) public virtual { - address dstAccount = CAIP10.toString(msg.sender); - if (!_isValidReceivedMessage(messageId, srcChain, srcAccount, dstAccount, msg.sender, paylod, attributes)) { - revert GatewayIncomingPassiveInvalidMessage(messageId); - } - _execute(string(fromCAIP2(destChain)), srcAccount, wrappedPayload); - } - - function _isValidReceivedMessage( - bytes32 messageId, - string calldata srcChain, // CAIP-2 chain ID - string calldata srcAccount, // i.e. address - string calldata dstAccount, - bytes calldata paylod, - bytes calldata attributes - ) internal returns (bool) { - bytes wrappedPayload = abi.encode(messageId, dstAccount, payload, attributes); - return gateway.validateContractCall(messageId, srcChain, srcAccount, keccak256(wrappedPayload)); - } - - function _execute(string calldata srcChain, string calldata srcAccount, bytes calldata wrappedPayload) internal virtual { - (bytes32 messageId, string destAccount, bytes payload, bytes attributes) = abi.decode(wrappedPayload, (bytes32, string, bytes, bytes)); - IGatewayReceiver(destination).receiveMessage(messageId, srcChain, srcAccount, payload, attributes); - emit MessageExecuted(messageId); - } -} diff --git a/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol b/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol deleted file mode 100644 index 02531683..00000000 --- a/contracts/crosschain/axelar/AxelarGatewayOutgoing.sol +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; -import {IAxelarGateway} from "@axelar-network/axelar-cgp-solidity/interfaces/IAxelarGateway.sol"; -import {IAxelarGasService} from "@axelar-network/axelar-cgp-solidity/interfaces/IAxelarGasService.sol"; -import {IGatewayOutgoing} from "../IGatewayOutgoing.sol"; -import {AxelarCAIP2Equivalence} from "./AxelarCAIP2Equivalence.sol"; -import {CAIP2} from "../../utils/CAIP-2.sol"; -import {CAIP10} from "../../utils/CAIP-10.sol"; - -abstract contract AxelarGatewayOutgoing is IGatewayOutgoing, AxelarCAIP2Equivalence { - IAxelarGateway public immutable gateway; - - function sendMessage( - string calldata destChain, // CAIP-2 chain ID - string calldata destAccount, // i.e. address - bytes calldata payload, - bytes calldata attributes - ) external payable override returns (bytes32 messageId) { - // TODO: Handle ether (payable) - // TODO: Validate attributes - - // Validate there's an equivalent chain identifier supported by the gateway - if (!supported(destChain)) revert UnsupportedChain(destChain); - - // Create a message - Message memory message = Message( - CAIP10.currentId(Strings.toHexString(msg.sender)), - CAIP10.toString(destChain, destAccount), - payload, - attributes - ); - messageId = keccak256(abi.encode(message)); - emit MessageCreated(id, message); - - // Wrap the message - bytes wrappedPayload = abi.encode(messageId, destAccount, payload, attributes); - - // Send the message - address destGateway = address(0); // TODO - gateway.callContract(string(fromCAIP2(destChain)), destGateway, wrappedPayload); - emit MessageSent(messageId); - } -} diff --git a/contracts/crosschain/axelar/AxelarGatewaySource.sol b/contracts/crosschain/axelar/AxelarGatewaySource.sol new file mode 100644 index 00000000..0be02ae8 --- /dev/null +++ b/contracts/crosschain/axelar/AxelarGatewaySource.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.27; + +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {AxelarGatewayBase} from "./AxelarGatewayBase.sol"; +import {IGatewaySource} from "../IGatewaySource.sol"; +import {CAIP10} from "../../utils/CAIP-10.sol"; + +abstract contract AxelarGatewaySource is IGatewaySource, AxelarGatewayBase { + function sendMessage( + string calldata dstChain, // CAIP-2 chain ID + string calldata dstAccount, // i.e. address + bytes calldata payload, + bytes calldata attributes + ) external payable override returns (bytes32) { + // TODO: Handle ether (payable) + // TODO: Validate attributes + + // Validate there's an equivalent chain identifier supported by the gateway + require(supported(dstChain), UnsupportedChain(dstChain)); + string memory caip10Src = CAIP10.currentId(Strings.toHexString(msg.sender)); + string memory caip10Dst = CAIP10.toString(dstChain, dstAccount); + string memory axelarDst = fromCAIP2(dstChain); + string memory foreignGateway = getForeignGateway(dstChain); + + // Create a message package + // - message identifier (from the source, not unique ?) + // - source account (caller of this gateway) + // - destination account + // - payload + // - attributes + bytes32 messageId = bytes32(0); // TODO: counter ? + bytes memory package = abi.encode(messageId, caip10Src, caip10Dst, payload, attributes); + + // emit event + emit MessageCreated(messageId, Message(caip10Src, caip10Dst, payload, attributes)); + + // Send the message + localGateway.callContract(axelarDst, foreignGateway, package); + + return messageId; + } +} diff --git a/hardhat.config.js b/hardhat.config.js index 803dbaf3..1d8afdea 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -3,7 +3,7 @@ const { argv } = require('yargs/yargs')() .options({ compiler: { type: 'string', - default: '0.8.26', + default: '0.8.27', }, hardfork: { type: 'string', @@ -33,4 +33,7 @@ module.exports = { hardfork: argv.hardfork, }, }, + exposed: { + exclude: ['@axelar-network/**/*'], + }, }; diff --git a/lib/@openzeppelin-contracts b/lib/@openzeppelin-contracts index 52c36d41..cb7faaf4 160000 --- a/lib/@openzeppelin-contracts +++ b/lib/@openzeppelin-contracts @@ -1 +1 @@ -Subproject commit 52c36d412e8681053975396223d0ea39687fe33b +Subproject commit cb7faaf4db9d1ea443b507311487625220e5e215 diff --git a/remappings.txt b/remappings.txt index 138ddb98..1f3c23e4 100644 --- a/remappings.txt +++ b/remappings.txt @@ -6,5 +6,5 @@ @openzeppelin/community-contracts/=contracts/ -@axelar-network/axelar-cgp-solidity/=node_modules/@axelar-network/axelar-cgp-solidity/contracts/ -@axelar-network/axelar-gmp-sdk-solidity/=node_modules/@axelar-network/axelar-gmp-sdk-solidity/contracts/ +@axelar-network/axelar-cgp-solidity/=node_modules/@axelar-network/axelar-cgp-solidity/ +@axelar-network/axelar-gmp-sdk-solidity/=node_modules/@axelar-network/axelar-gmp-sdk-solidity/ From 01d98fde04358c09881e4b98c70748f41adb9410 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 5 Sep 2024 16:42:59 +0200 Subject: [PATCH 23/41] fix compilation --- contracts/crosschain/axelar/AxelarGatewayBase.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/crosschain/axelar/AxelarGatewayBase.sol b/contracts/crosschain/axelar/AxelarGatewayBase.sol index 83fc6fdb..41b2cb7e 100644 --- a/contracts/crosschain/axelar/AxelarGatewayBase.sol +++ b/contracts/crosschain/axelar/AxelarGatewayBase.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -import {IAxelarGateway} from "@axelar-network/axelar-cgp-solidity/contracts/interfaces/IAxelarGateway.sol"; +import {IAxelarGateway} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol"; import {ICAIP2Equivalence} from "../ICAIP2Equivalence.sol"; abstract contract AxelarGatewayBase is ICAIP2Equivalence, Ownable { From 85ee12a89f230285c440b5f0a68e9dbe801f004f Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 5 Sep 2024 17:25:02 +0200 Subject: [PATCH 24/41] minor update --- .../crosschain/axelar/AxelarGatewayDestination.sol | 10 ++++++---- contracts/crosschain/axelar/AxelarGatewaySource.sol | 3 +++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/contracts/crosschain/axelar/AxelarGatewayDestination.sol b/contracts/crosschain/axelar/AxelarGatewayDestination.sol index 5f8ade67..98c59242 100644 --- a/contracts/crosschain/axelar/AxelarGatewayDestination.sol +++ b/contracts/crosschain/axelar/AxelarGatewayDestination.sol @@ -39,8 +39,8 @@ abstract contract AxelarGatewayDestination is // bytes calldata paylod, // bytes calldata attributes // ) internal returns (bool) { - // bytes wrappedPayload = abi.encode(messageDestinationId, dstAccount, payload, attributes); - // return gateway.validateContractCall(messageDestinationId, srcChain, srcAccount, keccak256(wrappedPayload)); + // bytes package = abi.encode(messageDestinationId, dstAccount, payload, attributes); + // return gateway.validateContractCall(messageDestinationId, srcChain, srcAccount, keccak256(package)); // } // In this function: @@ -53,7 +53,7 @@ abstract contract AxelarGatewayDestination is function _execute( string calldata srcChain, string calldata srcAccount, - bytes calldata wrappedPayload + bytes calldata package ) internal virtual override { // Parse the message package // - message identifier (from the source, not unique ?) @@ -67,7 +67,7 @@ abstract contract AxelarGatewayDestination is string memory caip10Dst, bytes memory payload, bytes memory attributes - ) = abi.decode(wrappedPayload, (bytes32, string, string, bytes, bytes)); + ) = abi.decode(package, (bytes32, string, string, bytes, bytes)); (string memory originChain, string memory originAccount) = CAIP10.parse(caip10Src); (string memory targetChain, string memory targetAccount) = CAIP10.parse(caip10Dst); @@ -77,6 +77,7 @@ abstract contract AxelarGatewayDestination is // - `srcAccount` is the foreign gateway on the origin chain. require(Strings.equal(srcChain, fromCAIP2(originChain)), "Invalid origin chain"); require(Strings.equal(srcAccount, getForeignGateway(originChain)), "Invalid origin gateway"); + // This check is not required for security. That is enforced by axelar (+ source gateway) require(CAIP2.isCurrentId(targetChain), "Invalid tardet chain"); // TODO: not available yet @@ -85,6 +86,7 @@ abstract contract AxelarGatewayDestination is address destination = address(0); IGatewayReceiver(destination).receiveMessage(messageId, originChain, originAccount, payload, attributes); + emit MessageExecuted(messageId); } } diff --git a/contracts/crosschain/axelar/AxelarGatewaySource.sol b/contracts/crosschain/axelar/AxelarGatewaySource.sol index 0be02ae8..2f31efcd 100644 --- a/contracts/crosschain/axelar/AxelarGatewaySource.sol +++ b/contracts/crosschain/axelar/AxelarGatewaySource.sol @@ -39,6 +39,9 @@ abstract contract AxelarGatewaySource is IGatewaySource, AxelarGatewayBase { // Send the message localGateway.callContract(axelarDst, foreignGateway, package); + // TODO + // emit MessageSent(bytes32(0)); + return messageId; } } From cdc3c94172f54cf2259dbef594c593b6b93f13c0 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Thu, 5 Sep 2024 17:39:46 +0200 Subject: [PATCH 25/41] make attributes a bytes[] --- contracts/crosschain/IGatewayDestinationPassive.sol | 2 +- contracts/crosschain/IGatewayReceiver.sol | 2 +- contracts/crosschain/IGatewaySource.sol | 4 ++-- contracts/crosschain/axelar/AxelarGatewayDestination.sol | 4 ++-- contracts/crosschain/axelar/AxelarGatewaySource.sol | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/contracts/crosschain/IGatewayDestinationPassive.sol b/contracts/crosschain/IGatewayDestinationPassive.sol index 7732da17..206ae91d 100644 --- a/contracts/crosschain/IGatewayDestinationPassive.sol +++ b/contracts/crosschain/IGatewayDestinationPassive.sol @@ -10,6 +10,6 @@ interface IGatewayDestinationPassive { string calldata srcChain, string calldata srcAccount, bytes calldata payload, - bytes calldata attributes + bytes[] calldata attributes ) external; } diff --git a/contracts/crosschain/IGatewayReceiver.sol b/contracts/crosschain/IGatewayReceiver.sol index 6035ebfe..b4f603b9 100644 --- a/contracts/crosschain/IGatewayReceiver.sol +++ b/contracts/crosschain/IGatewayReceiver.sol @@ -8,6 +8,6 @@ interface IGatewayReceiver { string calldata srcChain, string calldata srcAccount, bytes calldata payload, - bytes calldata attributes + bytes[] calldata attributes ) external payable; } diff --git a/contracts/crosschain/IGatewaySource.sol b/contracts/crosschain/IGatewaySource.sol index 0c2f022a..06b374dd 100644 --- a/contracts/crosschain/IGatewaySource.sol +++ b/contracts/crosschain/IGatewaySource.sol @@ -7,7 +7,7 @@ interface IGatewaySource { string source; // CAIP-10 account ID string destination; // CAIP-10 account ID bytes payload; - bytes attributes; + bytes[] attributes; } event MessageCreated(bytes32 indexed messageId, Message message); @@ -17,6 +17,6 @@ interface IGatewaySource { string calldata destChain, // CAIP-2 chain ID string calldata destAccount, // i.e. address bytes calldata payload, - bytes calldata attributes + bytes[] calldata attributes ) external payable returns (bytes32 messageId); } diff --git a/contracts/crosschain/axelar/AxelarGatewayDestination.sol b/contracts/crosschain/axelar/AxelarGatewayDestination.sol index 98c59242..2f1223bf 100644 --- a/contracts/crosschain/axelar/AxelarGatewayDestination.sol +++ b/contracts/crosschain/axelar/AxelarGatewayDestination.sol @@ -66,8 +66,8 @@ abstract contract AxelarGatewayDestination is string memory caip10Src, string memory caip10Dst, bytes memory payload, - bytes memory attributes - ) = abi.decode(package, (bytes32, string, string, bytes, bytes)); + bytes[] memory attributes + ) = abi.decode(package, (bytes32, string, string, bytes, bytes[])); (string memory originChain, string memory originAccount) = CAIP10.parse(caip10Src); (string memory targetChain, string memory targetAccount) = CAIP10.parse(caip10Dst); diff --git a/contracts/crosschain/axelar/AxelarGatewaySource.sol b/contracts/crosschain/axelar/AxelarGatewaySource.sol index 2f31efcd..b9c6af41 100644 --- a/contracts/crosschain/axelar/AxelarGatewaySource.sol +++ b/contracts/crosschain/axelar/AxelarGatewaySource.sol @@ -12,7 +12,7 @@ abstract contract AxelarGatewaySource is IGatewaySource, AxelarGatewayBase { string calldata dstChain, // CAIP-2 chain ID string calldata dstAccount, // i.e. address bytes calldata payload, - bytes calldata attributes + bytes[] calldata attributes ) external payable override returns (bytes32) { // TODO: Handle ether (payable) // TODO: Validate attributes From d7ce22993d91e44a57147f03f1582b3d61925233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ernesto=20Garc=C3=ADa?= Date: Tue, 10 Sep 2024 01:07:43 -0600 Subject: [PATCH 26/41] Address comments and add some tests --- contracts/crosschain/ICAIP2Equivalence.sol | 3 - .../crosschain/axelar/AxelarGatewayBase.sol | 27 +++++--- .../axelar/AxelarGatewayDestination.sol | 34 ++-------- .../crosschain/axelar/AxelarGatewaySource.sol | 8 +-- contracts/mocks/AxelarGatewayMock.sol | 15 +++++ lib/@openzeppelin-contracts | 2 +- package-lock.json | 20 ------ package.json | 1 - remappings.txt | 1 - .../axelar/AxelarGatewayBase.test.js | 67 +++++++++++++++++++ .../axelar/AxelarGatewayDestination.test.js | 18 +++++ .../axelar/AxelarGatewaySource.test.js | 18 +++++ 12 files changed, 143 insertions(+), 71 deletions(-) create mode 100644 contracts/mocks/AxelarGatewayMock.sol create mode 100644 test/crosschain/axelar/AxelarGatewayBase.test.js create mode 100644 test/crosschain/axelar/AxelarGatewayDestination.test.js create mode 100644 test/crosschain/axelar/AxelarGatewaySource.test.js diff --git a/contracts/crosschain/ICAIP2Equivalence.sol b/contracts/crosschain/ICAIP2Equivalence.sol index 45b47bb6..02f49562 100644 --- a/contracts/crosschain/ICAIP2Equivalence.sol +++ b/contracts/crosschain/ICAIP2Equivalence.sol @@ -8,9 +8,6 @@ pragma solidity ^0.8.0; interface ICAIP2Equivalence { error UnsupportedChain(string caip2); - /// @dev Checks if a CAIP-2 chain identifier is registered as equivalent to a protocol-specific chain identifier. - function supported(string memory caip2) external view returns (bool); - /// @dev Retrieves the protocol-specific chain identifier equivalent to a CAIP-2 chain identifier. function fromCAIP2(string memory caip2) external view returns (string memory); } diff --git a/contracts/crosschain/axelar/AxelarGatewayBase.sol b/contracts/crosschain/axelar/AxelarGatewayBase.sol index 41b2cb7e..703ca4f4 100644 --- a/contracts/crosschain/axelar/AxelarGatewayBase.sol +++ b/contracts/crosschain/axelar/AxelarGatewayBase.sol @@ -7,31 +7,36 @@ import {IAxelarGateway} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/ import {ICAIP2Equivalence} from "../ICAIP2Equivalence.sol"; abstract contract AxelarGatewayBase is ICAIP2Equivalence, Ownable { + event RegisteredRemoteGateway(string caip2, string gatewayAddress); + event RegisteredCAIP2Equivalence(string caip2, string destinationChain); + IAxelarGateway public immutable localGateway; - mapping(string caip2 => string foreignGateway) private _foreignGateways; + mapping(string caip2 => string remoteGateway) private _remoteGateways; mapping(string caip2 => string destinationChain) private _equivalence; constructor(IAxelarGateway _gateway) { localGateway = _gateway; } - function supported(string memory caip2) public view returns (bool) { - return bytes(_equivalence[caip2]).length != 0; - } - function fromCAIP2(string memory caip2) public view returns (string memory) { return _equivalence[caip2]; } - function registerForeignGateway(string calldata caip2, string calldata foreignGateway) public onlyOwner { - require(bytes(_foreignGateways[caip2]).length == 0); - _foreignGateways[caip2] = foreignGateway; - // TODO emit event + function getRemoteGateway(string memory caip2) public view returns (string memory remoteGateway) { + return _remoteGateways[caip2]; + } + + function registerCAIP2Equivalence(string calldata caip2, string calldata axelarSupported) public onlyOwner { + require(bytes(_equivalence[caip2]).length == 0); + _equivalence[caip2] = axelarSupported; + emit RegisteredCAIP2Equivalence(caip2, axelarSupported); } - function getForeignGateway(string memory caip2) public view returns (string memory foreignGateway) { - return _foreignGateways[caip2]; + function registerRemoteGateway(string calldata caip2, string calldata remoteGateway) public onlyOwner { + require(bytes(_remoteGateways[caip2]).length == 0); + _remoteGateways[caip2] = remoteGateway; + emit RegisteredRemoteGateway(caip2, remoteGateway); } } diff --git a/contracts/crosschain/axelar/AxelarGatewayDestination.sol b/contracts/crosschain/axelar/AxelarGatewayDestination.sol index 2f1223bf..0a48f6d8 100644 --- a/contracts/crosschain/axelar/AxelarGatewayDestination.sol +++ b/contracts/crosschain/axelar/AxelarGatewayDestination.sol @@ -17,39 +17,13 @@ abstract contract AxelarGatewayDestination is AxelarGatewayBase, AxelarExecutable { - // function validateReceivedMessage( - // bytes32 messageDestinationId, - // string calldata srcChain, // CAIP-2 chain ID - // string calldata srcAccount, // i.e. address - // bytes calldata payload, - // bytes calldata attributes - // ) public virtual { - // address dstAccount = CAIP10.toString(msg.sender); - // if (!_isValidReceivedMessage(messageDestinationId, srcChain, srcAccount, dstAccount, msg.sender, paylod, attributes)) { - // revert GatewayDestinationPassiveInvalidMessage(messageDestinationId); - // } - // _execute(string(fromCAIP2(destChain)), srcAccount, wrappedPayload); - // } - - // function _isValidReceivedMessage( - // bytes32 messageDestinationId, - // string calldata srcChain, // CAIP-2 chain ID - // string calldata srcAccount, // i.e. address - // string calldata dstAccount, - // bytes calldata paylod, - // bytes calldata attributes - // ) internal returns (bool) { - // bytes package = abi.encode(messageDestinationId, dstAccount, payload, attributes); - // return gateway.validateContractCall(messageDestinationId, srcChain, srcAccount, keccak256(package)); - // } - // In this function: // - `srcChain` is in the Axelar format. It should not be expected to be a proper CAIP-2 format - // - `srcAccount` is the sender of the crosschain message. That should be the foreign gateway on the chain which + // - `srcAccount` is the sender of the crosschain message. That should be the remote gateway on the chain which // the message originates from. It is NOT the sender of the crosschain message // // Proper CAIP-10 encoding of the message sender (including the CAIP-2 name of the origin chain can be found in - // the mssage) + // the message) function _execute( string calldata srcChain, string calldata srcAccount, @@ -74,9 +48,9 @@ abstract contract AxelarGatewayDestination is // check message validity // - `srcChain` matches origin chain in the message (in caip2) - // - `srcAccount` is the foreign gateway on the origin chain. + // - `srcAccount` is the remote gateway on the origin chain. require(Strings.equal(srcChain, fromCAIP2(originChain)), "Invalid origin chain"); - require(Strings.equal(srcAccount, getForeignGateway(originChain)), "Invalid origin gateway"); + require(Strings.equal(srcAccount, getRemoteGateway(originChain)), "Invalid origin gateway"); // This check is not required for security. That is enforced by axelar (+ source gateway) require(CAIP2.isCurrentId(targetChain), "Invalid tardet chain"); diff --git a/contracts/crosschain/axelar/AxelarGatewaySource.sol b/contracts/crosschain/axelar/AxelarGatewaySource.sol index b9c6af41..f2fead43 100644 --- a/contracts/crosschain/axelar/AxelarGatewaySource.sol +++ b/contracts/crosschain/axelar/AxelarGatewaySource.sol @@ -18,11 +18,11 @@ abstract contract AxelarGatewaySource is IGatewaySource, AxelarGatewayBase { // TODO: Validate attributes // Validate there's an equivalent chain identifier supported by the gateway - require(supported(dstChain), UnsupportedChain(dstChain)); + string memory axelarDstChainId = fromCAIP2(dstChain); + require(bytes(axelarDstChainId).length > 0, UnsupportedChain(dstChain)); string memory caip10Src = CAIP10.currentId(Strings.toHexString(msg.sender)); string memory caip10Dst = CAIP10.toString(dstChain, dstAccount); - string memory axelarDst = fromCAIP2(dstChain); - string memory foreignGateway = getForeignGateway(dstChain); + string memory remoteGateway = getRemoteGateway(dstChain); // Create a message package // - message identifier (from the source, not unique ?) @@ -37,7 +37,7 @@ abstract contract AxelarGatewaySource is IGatewaySource, AxelarGatewayBase { emit MessageCreated(messageId, Message(caip10Src, caip10Dst, payload, attributes)); // Send the message - localGateway.callContract(axelarDst, foreignGateway, package); + localGateway.callContract(axelarDstChainId, remoteGateway, package); // TODO // emit MessageSent(bytes32(0)); diff --git a/contracts/mocks/AxelarGatewayMock.sol b/contracts/mocks/AxelarGatewayMock.sol new file mode 100644 index 00000000..27d737b3 --- /dev/null +++ b/contracts/mocks/AxelarGatewayMock.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.27; + +contract AxelarGatewayMock { + event CallContract(string destinationChain, string contractAddress, bytes payload); + + function callContract( + string calldata destinationChain, + string calldata contractAddress, + bytes calldata payload + ) external { + emit CallContract(destinationChain, contractAddress, payload); + } +} diff --git a/lib/@openzeppelin-contracts b/lib/@openzeppelin-contracts index cb7faaf4..52c36d41 160000 --- a/lib/@openzeppelin-contracts +++ b/lib/@openzeppelin-contracts @@ -1 +1 @@ -Subproject commit cb7faaf4db9d1ea443b507311487625220e5e215 +Subproject commit 52c36d412e8681053975396223d0ea39687fe33b diff --git a/package-lock.json b/package-lock.json index 0e196841..64b808f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,6 @@ "version": "0.0.1", "license": "MIT", "dependencies": { - "@axelar-network/axelar-cgp-solidity": "^6.3.1", "@axelar-network/axelar-gmp-sdk-solidity": "^5.10.0", "@openzeppelin/contracts": "^5.0.2", "@openzeppelin/contracts-upgradeable": "^5.0.2" @@ -33,25 +32,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@axelar-network/axelar-cgp-solidity": { - "version": "6.3.1", - "license": "MIT", - "dependencies": { - "@axelar-network/axelar-gmp-sdk-solidity": "5.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@axelar-network/axelar-cgp-solidity/node_modules/@axelar-network/axelar-gmp-sdk-solidity": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@axelar-network/axelar-gmp-sdk-solidity/-/axelar-gmp-sdk-solidity-5.8.0.tgz", - "integrity": "sha512-ThiCWK7lhwmsipgjKkw8c0z0ubB9toRMV9X0tRVOXHHSknKp5DCFfatbCwjpSC5GZRa+61ciTSqJNtCc7j9YoQ==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/@axelar-network/axelar-gmp-sdk-solidity": { "version": "5.10.0", "resolved": "https://registry.npmjs.org/@axelar-network/axelar-gmp-sdk-solidity/-/axelar-gmp-sdk-solidity-5.10.0.tgz", diff --git a/package.json b/package.json index 02b8f1aa..61fb6055 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,6 @@ "zeppelin" ], "dependencies": { - "@axelar-network/axelar-cgp-solidity": "^6.3.1", "@axelar-network/axelar-gmp-sdk-solidity": "^5.10.0", "@openzeppelin/contracts": "^5.0.2", "@openzeppelin/contracts-upgradeable": "^5.0.2" diff --git a/remappings.txt b/remappings.txt index 1f3c23e4..090202de 100644 --- a/remappings.txt +++ b/remappings.txt @@ -6,5 +6,4 @@ @openzeppelin/community-contracts/=contracts/ -@axelar-network/axelar-cgp-solidity/=node_modules/@axelar-network/axelar-cgp-solidity/ @axelar-network/axelar-gmp-sdk-solidity/=node_modules/@axelar-network/axelar-gmp-sdk-solidity/ diff --git a/test/crosschain/axelar/AxelarGatewayBase.test.js b/test/crosschain/axelar/AxelarGatewayBase.test.js new file mode 100644 index 00000000..bb3af846 --- /dev/null +++ b/test/crosschain/axelar/AxelarGatewayBase.test.js @@ -0,0 +1,67 @@ +const { ethers } = require('hardhat'); +const { expect } = require('chai'); +const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); +const { toBeHex, hexlify } = require('ethers'); + +async function fixture() { + const [owner, other] = await ethers.getSigners(); + + const localGateway = await ethers.deployContract('AxelarGatewayMock'); + const mock = await ethers.deployContract('$AxelarGatewayBase', [owner, localGateway]); + return { owner, other, mock }; +} + +describe('AxelarGatewayBase', function () { + beforeEach(async function () { + Object.assign(this, await loadFixture(fixture)); + }); + + describe('fromCAIP2', function () { + it('returns empty string if the chain is not supported', async function () { + expect(await this.mock['fromCAIP2(string)']('eip155:11155111')).to.equal(''); + }); + + it('returns the chain name if the chain is supported', async function () { + await this.mock.connect(this.owner).registerCAIP2Equivalence('eip155:1', 'Ethereum'); + expect(await this.mock['fromCAIP2(string)']('eip155:1')).to.equal('Ethereum'); + }); + }); + + describe('getRemoteGateway', function () { + it('returns empty string if there is no remote gateway', async function () { + expect(await this.mock.getRemoteGateway('unknown:unknown')).to.equal(''); + }); + + it('returns the remote gateway if it exists', async function () { + await this.mock.connect(this.owner).registerRemoteGateway('eip155:1', this.other.address); + expect(await this.mock.getRemoteGateway('eip155:1')).to.equal(this.other.address); + }); + }); + + describe('registerCAIP2Equivalence', function () { + it('emits an event', async function () { + await expect(this.mock.connect(this.owner).registerCAIP2Equivalence('eip155:1', 'Ethereum')) + .to.emit(this.mock, 'RegisteredCAIP2Equivalence') + .withArgs('eip155:1', 'Ethereum'); + }); + + it('reverts if the chain is already registered', async function () { + await this.mock.connect(this.owner).registerCAIP2Equivalence('eip155:1', 'Ethereum'); + await expect(this.mock.connect(this.owner).registerCAIP2Equivalence('eip155:1', 'Ethereum')).to.be.reverted; + }); + }); + + describe('registerRemoteGateway', function () { + it('emits an event', async function () { + await expect(this.mock.connect(this.owner).registerRemoteGateway('eip155:1', this.other.address)) + .to.emit(this.mock, 'RegisteredRemoteGateway') + .withArgs('eip155:1', this.other.address); + }); + + it('reverts if the chain is already registered', async function () { + await this.mock.connect(this.owner).registerRemoteGateway('eip155:1', this.other.address); // register once + await expect(this.mock.connect(this.owner).registerRemoteGateway('eip155:1', this.other.address)).to.be + .reverted; + }); + }); +}); diff --git a/test/crosschain/axelar/AxelarGatewayDestination.test.js b/test/crosschain/axelar/AxelarGatewayDestination.test.js new file mode 100644 index 00000000..fbaa7d9d --- /dev/null +++ b/test/crosschain/axelar/AxelarGatewayDestination.test.js @@ -0,0 +1,18 @@ +const { ethers } = require('hardhat'); +const { expect } = require('chai'); +const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); + +async function fixture() { + const mock = await ethers.deployContract('$AxelarGatewayDestination'); + return { mock }; +} + +describe('AxelarGatewayDestination', function () { + before(async function () { + Object.assign(this, await loadFixture(fixture)); + }); + + describe('_execute', function () { + // TODO: Add tests + }); +}); diff --git a/test/crosschain/axelar/AxelarGatewaySource.test.js b/test/crosschain/axelar/AxelarGatewaySource.test.js new file mode 100644 index 00000000..016fe60a --- /dev/null +++ b/test/crosschain/axelar/AxelarGatewaySource.test.js @@ -0,0 +1,18 @@ +const { ethers } = require('hardhat'); +const { expect } = require('chai'); +const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); + +async function fixture() { + const mock = await ethers.deployContract('$AxelarGatewaySource'); + return { mock }; +} + +describe('AxelarGatewaySource', function () { + before(async function () { + Object.assign(this, await loadFixture(fixture)); + }); + + describe('_execute', function () { + // TODO: Add tests + }); +}); From 6dc1c445f20aa09bde81e80aafd492f06484b584 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Tue, 10 Sep 2024 16:57:14 +0200 Subject: [PATCH 27/41] refactor and test caip utils --- .../axelar/AxelarGatewayDestination.sol | 8 ++- .../crosschain/axelar/AxelarGatewaySource.sol | 4 +- contracts/utils/Bytes.sol | 57 +++++++++++++-- contracts/utils/CAIP-10.sol | 47 +++--------- contracts/utils/CAIP-2.sol | 71 +++---------------- test/crosschain/utils/CAIP.test.js | 67 +++++++++++++++++ 6 files changed, 146 insertions(+), 108 deletions(-) create mode 100644 test/crosschain/utils/CAIP.test.js diff --git a/contracts/crosschain/axelar/AxelarGatewayDestination.sol b/contracts/crosschain/axelar/AxelarGatewayDestination.sol index 0a48f6d8..672a9061 100644 --- a/contracts/crosschain/axelar/AxelarGatewayDestination.sol +++ b/contracts/crosschain/axelar/AxelarGatewayDestination.sol @@ -17,6 +17,8 @@ abstract contract AxelarGatewayDestination is AxelarGatewayBase, AxelarExecutable { + using Strings for string; + // In this function: // - `srcChain` is in the Axelar format. It should not be expected to be a proper CAIP-2 format // - `srcAccount` is the sender of the crosschain message. That should be the remote gateway on the chain which @@ -49,10 +51,10 @@ abstract contract AxelarGatewayDestination is // check message validity // - `srcChain` matches origin chain in the message (in caip2) // - `srcAccount` is the remote gateway on the origin chain. - require(Strings.equal(srcChain, fromCAIP2(originChain)), "Invalid origin chain"); - require(Strings.equal(srcAccount, getRemoteGateway(originChain)), "Invalid origin gateway"); + require(fromCAIP2(originChain).equal(srcChain), "Invalid origin chain"); + require(getRemoteGateway(originChain).equal(srcAccount), "Invalid origin gateway"); // This check is not required for security. That is enforced by axelar (+ source gateway) - require(CAIP2.isCurrentId(targetChain), "Invalid tardet chain"); + require(CAIP2.format().equal(targetChain), "Invalid tardet chain"); // TODO: not available yet // address destination = address(uint160(Strings.toUint(targetAccount))); diff --git a/contracts/crosschain/axelar/AxelarGatewaySource.sol b/contracts/crosschain/axelar/AxelarGatewaySource.sol index f2fead43..f6d4ae18 100644 --- a/contracts/crosschain/axelar/AxelarGatewaySource.sol +++ b/contracts/crosschain/axelar/AxelarGatewaySource.sol @@ -20,8 +20,8 @@ abstract contract AxelarGatewaySource is IGatewaySource, AxelarGatewayBase { // Validate there's an equivalent chain identifier supported by the gateway string memory axelarDstChainId = fromCAIP2(dstChain); require(bytes(axelarDstChainId).length > 0, UnsupportedChain(dstChain)); - string memory caip10Src = CAIP10.currentId(Strings.toHexString(msg.sender)); - string memory caip10Dst = CAIP10.toString(dstChain, dstAccount); + string memory caip10Src = CAIP10.format(msg.sender); + string memory caip10Dst = CAIP10.format(dstChain, dstAccount); string memory remoteGateway = getRemoteGateway(dstChain); // Create a message package diff --git a/contracts/utils/Bytes.sol b/contracts/utils/Bytes.sol index ecd97f34..3032b3dc 100644 --- a/contracts/utils/Bytes.sol +++ b/contracts/utils/Bytes.sol @@ -2,13 +2,58 @@ pragma solidity ^0.8.0; library Bytes { - function find(bytes memory input, bytes1 chr, uint256 cursor) internal pure returns (uint256) { - uint256 length = input.length; - for (uint256 i = cursor; i < length; ++i) { - if (input[i] == chr) { - return i; + /// @dev Forward search for `s` in `buffer` + /// * If `s` is present in the buffer, returns the index of the first instance + /// * If `s` is not present in the buffer, returns the length of the buffer + function find(bytes memory buffer, bytes1 s) internal pure returns (uint256) { + return find(buffer, s, 0); + } + + /// @dev Forward search for `s` in `buffer` starting at position `pos` + /// * If `s` is present in the buffer (at or after `pos`), returns the index of the next instance + /// * If `s` is not present in the buffer (at or after `pos`), returns the length of the buffer + function find(bytes memory buffer, bytes1 s, uint256 pos) internal pure returns (uint256) { + unchecked { + uint256 length = buffer.length; + for (uint256 i = pos; i < length; ++i) { + if (buffer[i] == s) { + return i; + } } + return length; + } + } + + /// @dev Backward search for `s` in `buffer` + /// * If `s` is present in the buffer, returns the index of the last instance + /// * If `s` is not present in the buffer, returns the length of the buffer + function findLastOf(bytes memory buffer, bytes1 s) internal pure returns (uint256) { + return findLastOf(buffer, s, buffer.length); + } + + /// @dev Backward search for `s` in `buffer` starting at position `pos` + /// * If `s` is present in the buffer (before `pos`), returns the index of the previous instance + /// * If `s` is not present in the buffer (before `pos`), returns the length of the buffer + function findLastOf(bytes memory buffer, bytes1 s, uint256 pos) internal pure returns (uint256) { + unchecked { + for (uint256 i = pos; i > 0; --i) { + if (buffer[i - 1] == s) { + return i - 1; + } + } + return buffer.length; + } + } + + function slice(bytes memory buffer, uint256 start) internal pure returns (bytes memory) { + return slice(buffer, start, buffer.length); + } + + function slice(bytes memory buffer, uint256 start, uint256 end) internal pure returns (bytes memory) { + bytes memory result = new bytes(end - start); + for (uint256 i = start; i < end; ++i) { + result[i - start] = buffer[i]; } - return length; + return result; } } diff --git a/contracts/utils/CAIP-10.sol b/contracts/utils/CAIP-10.sol index 763195e3..1df51fa0 100644 --- a/contracts/utils/CAIP-10.sol +++ b/contracts/utils/CAIP-10.sol @@ -3,55 +3,30 @@ pragma solidity ^0.8.0; import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; -import {CAIP2} from "./CAIP-2.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; import {Bytes} from "./Bytes.sol"; +import {CAIP2} from "./CAIP-2.sol"; // account_id: chain_id + ":" + account_address // chain_id: [-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32} (See [CAIP-2][]) // account_address: [-.%a-zA-Z0-9]{1,128} library CAIP10 { using SafeCast for uint256; + using Strings for address; using Bytes for bytes; - bytes1 private constant COLON = ":"; - - function toString(string memory caip2, string memory accountId) internal pure returns (string memory) { - return string(abi.encodePacked(caip2, COLON, accountId)); + function format(address account) internal view returns (string memory) { + return format(CAIP2.format(), account.toHexString()); } - function parse(string memory caip10) internal pure returns (string memory caip2, string memory accountId) { - bytes memory accountBuffer = bytes(caip10); - uint8 firstSeparatorIndex = accountBuffer.find(COLON, 0).toUint8(); - uint256 lastSeparatorIndex = accountBuffer.find(COLON, firstSeparatorIndex).toUint8(); - return (_extractCAIP2(accountBuffer, lastSeparatorIndex), _extractAccountId(accountBuffer, lastSeparatorIndex)); + function format(string memory caip2, string memory account) internal pure returns (string memory) { + return string.concat(caip2, ":", account); } - function currentId(string memory accountId) internal view returns (string memory) { - (bytes8 namespace, bytes32 ref) = CAIP2.currentId(); - return toString(CAIP2.toString(namespace, ref), accountId); - } - - function _extractCAIP2( - bytes memory accountBuffer, - uint256 lastSeparatorIndex - ) private pure returns (string memory chainId) { - bytes memory _chainId = new bytes(lastSeparatorIndex); - for (uint256 i = 0; i < lastSeparatorIndex; i++) { - _chainId[i] = accountBuffer[i]; - } - return string(_chainId); - } + function parse(string memory caip10) internal pure returns (string memory caip2, string memory account) { + bytes memory buffer = bytes(caip10); - function _extractAccountId( - bytes memory accountBuffer, - uint256 lastSeparatorIndex - ) private pure returns (string memory) { - uint256 length = accountBuffer.length; - uint256 offset = lastSeparatorIndex - 1; - bytes memory _accountId = new bytes(length - offset); // Will overflow if no separator is found - for (uint256 i = lastSeparatorIndex + 1; i < length; i++) { - _accountId[i - offset] = accountBuffer[i]; - } - return string(_accountId); + uint256 pos = buffer.findLastOf(":"); + return (string(buffer.slice(0, pos)), string(buffer.slice(pos + 1))); } } diff --git a/contracts/utils/CAIP-2.sol b/contracts/utils/CAIP-2.sol index 558c6473..4490e701 100644 --- a/contracts/utils/CAIP-2.sol +++ b/contracts/utils/CAIP-2.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.0; import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; import {Bytes} from "./Bytes.sol"; // chain_id: namespace + ":" + reference @@ -10,73 +11,21 @@ import {Bytes} from "./Bytes.sol"; // reference: [-_a-zA-Z0-9]{1,32} library CAIP2 { using SafeCast for uint256; + using Strings for uint256; using Bytes for bytes; - bytes16 private constant HEX_DIGITS = "0123456789abcdef"; - bytes1 private constant SEMICOLON = ":"; - bytes32 private constant EVM_REFERENCE = bytes32("eip155"); // EIP-155 for EVM chains - - /// @dev Converts a namespace and reference to a CAIP2 chain identifier. - function toString(bytes8 namespace, bytes32 ref) internal pure returns (string memory) { - return string(abi.encodePacked(namespace, SEMICOLON, ref)); - } - - /// @dev Parses a CAIP2 identifier from a string by splitting it at the first semicolon. - /// The function parses both sides as `bytes8` and `bytes32` respectively without any validation. - function parse(string memory caip2) internal pure returns (bytes8 namespace, bytes32 ref) { - bytes memory chainBuffer = bytes(caip2); - uint8 semicolonIndex = chainBuffer.find(SEMICOLON, 0).toUint8(); - return (_extractNamespace(chainBuffer, semicolonIndex), _unsafeExtractReference(chainBuffer, semicolonIndex)); - } - - /// @dev Checks if the given CAIP2 identifier is the current chain. - function isCurrentId(string memory caip2) internal view returns (bool) { - (bytes8 namespace, bytes32 ref) = parse(caip2); - (bytes8 _namespace, bytes32 _ref) = currentId(); - return namespace == _namespace && ref == _ref; + function format() internal view returns (string memory) { + return format("eip155", block.chainid.toString()); } - /// @dev Returns the CAIP2 identifier of the current chain. - function currentId() internal view returns (bytes8 namespace, bytes32 ref) { - return (currentNamespace(), currentReference()); + function format(string memory namespace, string memory ref) internal pure returns (string memory) { + return string.concat(namespace, ":", ref); } - /// @dev Returns the CAIP2 identifier of the current chain. - /// Assumes block.chainId < type(uint64).max - function currentNamespace() internal view returns (bytes8 _chainId) { - unchecked { - uint256 id = block.chainid; - while (true) { - _chainId = bytes8(uint64(_chainId) - 1); - assembly ("memory-safe") { - mstore8(_chainId, byte(mod(id, 10), HEX_DIGITS)) - } - id /= 10; - if (id == 0) break; - } - } - } - - /// @dev Returns the reference of the current chain. - function currentReference() internal pure returns (bytes32) { - return EVM_REFERENCE; - } - - /// @dev Extracts the first `semicolonIndex` bytes from the chain buffer as a bytes8 namespace. - function _extractNamespace(bytes memory chainBuffer, uint8 semicolonIndex) private pure returns (bytes8 namespace) { - assembly ("memory-safe") { - let shift := sub(256, mul(semicolonIndex, 8)) - namespace := shl(shift, shr(shift, mload(add(chainBuffer, 0x20)))) - } - } + function parse(string memory caip2) internal pure returns (string memory namespace, string memory ref) { + bytes memory buffer = bytes(caip2); - /// @dev Extracts the reference from the chain buffer after the semicolon located at `offset`. - /// - /// IMPORTANT: The caller must make sure that the semicolon index is within the chain buffer length - /// and that there are 32 bytes available after the semicolon. Otherwise dirty memory could be read. - function _unsafeExtractReference(bytes memory chainBuffer, uint8 offset) private pure returns (bytes32 ref) { - assembly ("memory-safe") { - ref := mload(add(chainBuffer, add(0x20, offset))) - } + uint256 pos = buffer.find(":"); + return (string(buffer.slice(0, pos)), string(buffer.slice(pos + 1))); } } diff --git a/test/crosschain/utils/CAIP.test.js b/test/crosschain/utils/CAIP.test.js new file mode 100644 index 00000000..6dddc655 --- /dev/null +++ b/test/crosschain/utils/CAIP.test.js @@ -0,0 +1,67 @@ +const { ethers } = require('hardhat'); +const { expect } = require('chai'); +const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); + +const format = (...args) => args.join(':'); + +const SAMPLES = [].concat( + ['1', '56', '137', '43114', '250', '1284', '1313161554', '42161', '10', '8453', '5000', '42220', '2222', '314', '59144', '2031', '534352', '13371', '252', '81457'].map(reference => ({ + namespace: 'eip155', + reference, + account: ethers.Wallet.createRandom().address, // random address + })), + [ 'axelar-dojo-1', 'osmosis-1', 'cosmoshub-4', 'juno-1', 'emoney-3', 'injective-1', 'crescent-1', 'kaiyo-1', 'secret-4', 'secret-4', 'pacific-1', 'stargaze-1', 'mantle-1', 'fetchhub-4', 'kichain-2', 'evmos_9001-2', 'xstaxy-1', 'comdex-1', 'core-1', 'regen-1', 'umee-1', 'agoric-3', 'dimension_37-1', 'acre_9052-1', 'stride-1', 'carbon-1', 'sommelier-3', 'neutron-1', 'reb_1111-1', 'archway-1', 'pio-mainnet-1', 'ixo-5', 'migaloo-1', 'teritori-1', 'haqq_11235-1', 'celestia', 'agamotto', 'chihuahua-1', 'ssc-1', 'dymension_1100-1', 'fxcore', 'perun-1', 'bitsong-2b', 'pirin-1', 'lava-mainnet-1', 'phoenix-1', 'columbus-5' ].map(reference => ({ + namespace: 'cosmos', + reference, + account: ethers.encodeBase58(ethers.randomBytes(32)), // random base58 string + })), +).map(entry => Object.assign(entry, { caip2: format(entry.namespace, entry.reference), caip10: format(entry.namespace, entry.reference, entry.account) })); + +async function fixture() { + const caip2 = await ethers.deployContract('$CAIP2'); + const caip10 = await ethers.deployContract('$CAIP10'); + const { chainId } = await ethers.provider.getNetwork(); + return { caip2, caip10, chainId }; +} + +describe('CAIP utilities', function () { + beforeEach(async function () { + Object.assign(this, await loadFixture(fixture)); + }); + + describe('CAIP-2', function () { + it('format()', async function () { + expect(await this.caip2.$format()).to.equal(format('eip155', this.chainId)); + }); + + for (const { namespace, reference, caip2 } of SAMPLES) + it (`format(${namespace}, ${reference})`, async function () { + expect(await this.caip2.$format(namespace, reference)).to.equal(caip2); + }); + + for (const { namespace, reference, caip2 } of SAMPLES) + it(`parse(${caip2})`, async function () { + expect(await this.caip2.$parse(caip2)).to.deep.equal([ namespace, reference ]); + }); + }); + + describe('CAIP-10', function () { + const { address: account } = ethers.Wallet.createRandom(); + + it(`format(${account})`, async function () { + // lowercase encoding for now + expect(await this.caip10.$format(ethers.Typed.address(account))).to.equal(format('eip155', this.chainId, account.toLowerCase())); + }); + + for (const { account, caip2, caip10 } of SAMPLES) + it (`format(${caip2}, ${account})`, async function () { + expect(await this.caip10.$format(ethers.Typed.string(caip2), ethers.Typed.string(account))).to.equal(caip10); + }); + + for (const { account, caip2, caip10 } of SAMPLES) + it(`parse(${caip10})`, async function () { + expect(await this.caip10.$parse(caip10)).to.deep.equal([ caip2, account ]); + }); + }); + +}); From 4cdf242fbfa710316f3ebdb328625b70b6957677 Mon Sep 17 00:00:00 2001 From: Hadrien Croubois Date: Wed, 11 Sep 2024 11:25:18 +0200 Subject: [PATCH 28/41] up --- test/crosschain/chains.js | 93 ++++++++++++++++++++++++++++++ test/crosschain/utils/CAIP.test.js | 23 ++------ 2 files changed, 98 insertions(+), 18 deletions(-) create mode 100644 test/crosschain/chains.js diff --git a/test/crosschain/chains.js b/test/crosschain/chains.js new file mode 100644 index 00000000..be15cb92 --- /dev/null +++ b/test/crosschain/chains.js @@ -0,0 +1,93 @@ +const { ethers } = require('hardhat'); + +const mapValues = (obj, fn) => Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, fn(v)])); +const format = (...args) => args.join(':'); + +// EVM (https://axelarscan.io/resources/chains?type=evm) +const ethereum = { + 'Ethereum': '1', + 'optimism': '10', + 'binance': '56', + 'Polygon': '137', + 'Fantom': '250', + 'fraxtal': '252', + 'filecoin': '314', + 'Moonbeam': '1284', + 'centrifuge': '2031', + 'kava': '2222', + 'mantle': '5000', + 'base': '8453', + 'immutable': '13371', + 'arbitrum': '42161', + 'celo': '42220', + 'Avalanche': '43114', + 'linea': '59144', + 'blast': '81457', + 'scroll': '534352', + 'aurora': '1313161554', +}; + +// Cosmos (https://axelarscan.io/resources/chains?type=cosmos) +const cosmos = { + 'Axelarnet': 'axelar-dojo-1', + 'osmosis': 'osmosis-1', + 'cosmoshub': 'cosmoshub-4', + 'juno': 'juno-1', + 'e-money': 'emoney-3', + 'injective': 'injective-1', + 'crescent': 'crescent-1', + 'kujira': 'kaiyo-1', + 'secret-snip': 'secret-4', + 'secret': 'secret-4', + 'sei': 'pacific-1', + 'stargaze': 'stargaze-1', + 'assetmantle': 'mantle-1', + 'fetch': 'fetchhub-4', + 'ki': 'kichain-2', + 'evmos': 'evmos_9001-2', + 'aura': 'xstaxy-1', + 'comdex': 'comdex-1', + 'persistence': 'core-1', + 'regen': 'regen-1', + 'umee': 'umee-1', + 'agoric': 'agoric-3', + 'xpla': 'dimension_37-1', + 'acre': 'acre_9052-1', + 'stride': 'stride-1', + 'carbon': 'carbon-1', + 'sommelier': 'sommelier-3', + 'neutron': 'neutron-1', + 'rebus': 'reb_1111-1', + 'archway': 'archway-1', + 'provenance': 'pio-mainnet-1', + 'ixo': 'ixo-5', + 'migaloo': 'migaloo-1', + 'teritori': 'teritori-1', + 'haqq': 'haqq_11235-1', + 'celestia': 'celestia', + 'ojo': 'agamotto', + 'chihuahua': 'chihuahua-1', + 'saga': 'ssc-1', + 'dymension': 'dymension_1100-1', + 'fxcore': 'fxcore', + 'c4e': 'perun-1', + 'bitsong': 'bitsong-2b', + 'nolus': 'pirin-1', + 'lava': 'lava-mainnet-1', + 'terra-2': 'phoenix-1', + 'terra': 'columbus-5', +}; + +module.exports = { + CHAINS: mapValues( + Object.assign( + mapValues(ethereum, reference => ({ namespace: 'eip155', reference, account: ethers.Wallet.createRandom().address })), + mapValues(cosmos, reference => ({ namespace: 'cosmos', reference, account: ethers.encodeBase58(ethers.randomBytes(32)) })), + ), + entry => Object.assign(entry, { + caip2: format(entry.namespace, entry.reference), + caip10: format(entry.namespace, entry.reference, entry.account), + }), + ), + format, +}; diff --git a/test/crosschain/utils/CAIP.test.js b/test/crosschain/utils/CAIP.test.js index 6dddc655..58d0c49a 100644 --- a/test/crosschain/utils/CAIP.test.js +++ b/test/crosschain/utils/CAIP.test.js @@ -2,20 +2,7 @@ const { ethers } = require('hardhat'); const { expect } = require('chai'); const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers'); -const format = (...args) => args.join(':'); - -const SAMPLES = [].concat( - ['1', '56', '137', '43114', '250', '1284', '1313161554', '42161', '10', '8453', '5000', '42220', '2222', '314', '59144', '2031', '534352', '13371', '252', '81457'].map(reference => ({ - namespace: 'eip155', - reference, - account: ethers.Wallet.createRandom().address, // random address - })), - [ 'axelar-dojo-1', 'osmosis-1', 'cosmoshub-4', 'juno-1', 'emoney-3', 'injective-1', 'crescent-1', 'kaiyo-1', 'secret-4', 'secret-4', 'pacific-1', 'stargaze-1', 'mantle-1', 'fetchhub-4', 'kichain-2', 'evmos_9001-2', 'xstaxy-1', 'comdex-1', 'core-1', 'regen-1', 'umee-1', 'agoric-3', 'dimension_37-1', 'acre_9052-1', 'stride-1', 'carbon-1', 'sommelier-3', 'neutron-1', 'reb_1111-1', 'archway-1', 'pio-mainnet-1', 'ixo-5', 'migaloo-1', 'teritori-1', 'haqq_11235-1', 'celestia', 'agamotto', 'chihuahua-1', 'ssc-1', 'dymension_1100-1', 'fxcore', 'perun-1', 'bitsong-2b', 'pirin-1', 'lava-mainnet-1', 'phoenix-1', 'columbus-5' ].map(reference => ({ - namespace: 'cosmos', - reference, - account: ethers.encodeBase58(ethers.randomBytes(32)), // random base58 string - })), -).map(entry => Object.assign(entry, { caip2: format(entry.namespace, entry.reference), caip10: format(entry.namespace, entry.reference, entry.account) })); +const { CHAINS, format } = require('../chains'); async function fixture() { const caip2 = await ethers.deployContract('$CAIP2'); @@ -34,12 +21,12 @@ describe('CAIP utilities', function () { expect(await this.caip2.$format()).to.equal(format('eip155', this.chainId)); }); - for (const { namespace, reference, caip2 } of SAMPLES) + for (const { namespace, reference, caip2 } of Object.values(CHAINS)) it (`format(${namespace}, ${reference})`, async function () { expect(await this.caip2.$format(namespace, reference)).to.equal(caip2); }); - for (const { namespace, reference, caip2 } of SAMPLES) + for (const { namespace, reference, caip2 } of Object.values(CHAINS)) it(`parse(${caip2})`, async function () { expect(await this.caip2.$parse(caip2)).to.deep.equal([ namespace, reference ]); }); @@ -53,12 +40,12 @@ describe('CAIP utilities', function () { expect(await this.caip10.$format(ethers.Typed.address(account))).to.equal(format('eip155', this.chainId, account.toLowerCase())); }); - for (const { account, caip2, caip10 } of SAMPLES) + for (const { account, caip2, caip10 } of Object.values(CHAINS)) it (`format(${caip2}, ${account})`, async function () { expect(await this.caip10.$format(ethers.Typed.string(caip2), ethers.Typed.string(account))).to.equal(caip10); }); - for (const { account, caip2, caip10 } of SAMPLES) + for (const { account, caip2, caip10 } of Object.values(CHAINS)) it(`parse(${caip10})`, async function () { expect(await this.caip10.$parse(caip10)).to.deep.equal([ caip2, account ]); }); From a6bfb2d9a80d74ec6f2498284889af72cfca4b0a Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Wed, 11 Sep 2024 18:00:14 -0300 Subject: [PATCH 29/41] forge install: wormhole-solidity-sdk v0.1.0 --- .gitmodules | 3 +++ lib/wormhole-solidity-sdk | 1 + remappings.txt | 1 + 3 files changed, 5 insertions(+) create mode 160000 lib/wormhole-solidity-sdk diff --git a/.gitmodules b/.gitmodules index bd38a5e3..721872f0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std.git +[submodule "lib/wormhole-solidity-sdk"] + path = lib/wormhole-solidity-sdk + url = https://github.com/wormhole-foundation/wormhole-solidity-sdk diff --git a/lib/wormhole-solidity-sdk b/lib/wormhole-solidity-sdk new file mode 160000 index 00000000..b9e129e6 --- /dev/null +++ b/lib/wormhole-solidity-sdk @@ -0,0 +1 @@ +Subproject commit b9e129e65d34827d92fceeed8c87d3ecdfc801d0 diff --git a/remappings.txt b/remappings.txt index 090202de..3115f4c3 100644 --- a/remappings.txt +++ b/remappings.txt @@ -7,3 +7,4 @@ @openzeppelin/community-contracts/=contracts/ @axelar-network/axelar-gmp-sdk-solidity/=node_modules/@axelar-network/axelar-gmp-sdk-solidity/ +wormhole-solidity-sdk/=lib/wormhole-solidity-sdk/src/ From c07508ffe7ca67ae20273aacff72e8fc0e608f17 Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Wed, 11 Sep 2024 18:41:47 -0300 Subject: [PATCH 30/41] add wormhole adapter --- .../wormhole/WormholeGatewaySource.sol | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 contracts/crosschain/wormhole/WormholeGatewaySource.sol diff --git a/contracts/crosschain/wormhole/WormholeGatewaySource.sol b/contracts/crosschain/wormhole/WormholeGatewaySource.sol new file mode 100644 index 00000000..a9d4e5d2 --- /dev/null +++ b/contracts/crosschain/wormhole/WormholeGatewaySource.sol @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.27; + +import {IGatewaySource} from "../IGatewaySource.sol"; +import {CAIP10} from "../../utils/CAIP-10.sol"; +import {IWormholeRelayer, VaaKey} from "wormhole-solidity-sdk/interfaces/IWormholeRelayer.sol"; +import {toWormholeFormat} from "wormhole-solidity-sdk/Utils.sol"; + +function addressFromHexString(string memory hexString) pure returns (address) { + return address(0); // TODO +} + +contract WormholeGatewayBase { + function currentChain() public view returns (uint16) {} + + function fromCAIP2(string memory caip2) public view returns (uint16) { + return 0; // TODO + } + + function getRemoteGateway(string memory caip2) public view returns (string memory remoteGateway) { + return ""; // TODO + } +} + +// TODO: allow non-evm destination chains via non-evm-specific finalize/retry variants +contract WormholeGatewaySource is IGatewaySource, WormholeGatewayBase { + IWormholeRelayer public immutable wormholeRelayer; + + struct PendingMessage { + address sender; + uint16 dstChain; + string dstAccount; + bytes payload; + bytes[] attributes; + } + + uint256 nextOutboxId; + mapping(bytes32 => PendingMessage) private pending; + mapping(bytes32 => uint64) private sequences; + + constructor(IWormholeRelayer _wormholeRelayer) { + wormholeRelayer = _wormholeRelayer; + } + + function supportsAttribute(string calldata) public view returns (bool) { + return false; + } + + function sendMessage( + string calldata dstChain, + string calldata dstAccount, + bytes calldata payload, + bytes[] calldata attributes + ) external payable override returns (bytes32 outboxId) { + require(msg.value == 0); + require(attributes.length == 0); // no attributes currently supported + + outboxId = bytes32(nextOutboxId++); + + uint16 dstChainWormhole = fromCAIP2(dstChain); + pending[outboxId] = PendingMessage(msg.sender, dstChainWormhole, dstAccount, payload, attributes); + + string memory caip10Src = CAIP10.format(msg.sender); + string memory caip10Dst = CAIP10.format(dstChain, dstAccount); + emit MessageCreated(outboxId, Message(caip10Src, caip10Dst, payload, attributes)); + } + + function finalizeEvmMessage(bytes32 outboxId, uint256 gasLimit) external payable { + PendingMessage memory pmsg = pending[outboxId]; + require(pmsg.sender != address(0)); + address dstAddress = addressFromHexString(pmsg.dstAccount); + uint64 seq = wormholeRelayer.sendPayloadToEvm{value: msg.value}( + pmsg.dstChain, + dstAddress, + pmsg.payload, + 0, + gasLimit + ); + sequences[outboxId] = seq; + delete pending[outboxId].dstAccount; + delete pending[outboxId].payload; + delete pending[outboxId].attributes; + } + + function retryEvmMessage(bytes32 outboxId, uint256 gasLimit, address newDeliveryProvider) external { + uint64 seq = sequences[outboxId]; + require(seq != 0); + address sender = pending[outboxId].sender; + uint16 dstChain = pending[outboxId].dstChain; + // TODO: check if new sequence number needs to be stored for future retries + wormholeRelayer.resendToEvm( + VaaKey(currentChain(), toWormholeFormat(sender), seq), + dstChain, + 0, + gasLimit, + newDeliveryProvider + ); + } +} From a6af4a132fba70d2101e7f1c7e7150ae71fe85c7 Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Wed, 11 Sep 2024 18:44:20 -0300 Subject: [PATCH 31/41] rename WormholeGateway.sol --- .../wormhole/{WormholeGatewaySource.sol => WormholeGateway.sol} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename contracts/crosschain/wormhole/{WormholeGatewaySource.sol => WormholeGateway.sol} (100%) diff --git a/contracts/crosschain/wormhole/WormholeGatewaySource.sol b/contracts/crosschain/wormhole/WormholeGateway.sol similarity index 100% rename from contracts/crosschain/wormhole/WormholeGatewaySource.sol rename to contracts/crosschain/wormhole/WormholeGateway.sol From ed62ee24795acd1756282e31715942d0507fd4f0 Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Wed, 11 Sep 2024 18:58:04 -0300 Subject: [PATCH 32/41] add todos and beginning of wormhole dest gateway --- .../crosschain/wormhole/WormholeGateway.sol | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/contracts/crosschain/wormhole/WormholeGateway.sol b/contracts/crosschain/wormhole/WormholeGateway.sol index a9d4e5d2..d3d84b99 100644 --- a/contracts/crosschain/wormhole/WormholeGateway.sol +++ b/contracts/crosschain/wormhole/WormholeGateway.sol @@ -3,8 +3,11 @@ pragma solidity ^0.8.27; import {IGatewaySource} from "../IGatewaySource.sol"; +import {IGatewayDestination} from "../IGatewayDestination.sol"; +import {IGatewayReceiver} from "../IGatewayReceiver.sol"; import {CAIP10} from "../../utils/CAIP-10.sol"; import {IWormholeRelayer, VaaKey} from "wormhole-solidity-sdk/interfaces/IWormholeRelayer.sol"; +import {IWormholeReceiver} from "wormhole-solidity-sdk/interfaces/IWormholeReceiver.sol"; import {toWormholeFormat} from "wormhole-solidity-sdk/Utils.sol"; function addressFromHexString(string memory hexString) pure returns (address) { @@ -70,6 +73,7 @@ contract WormholeGatewaySource is IGatewaySource, WormholeGatewayBase { PendingMessage memory pmsg = pending[outboxId]; require(pmsg.sender != address(0)); address dstAddress = addressFromHexString(pmsg.dstAccount); + // TODO: fix this, payload needs to be wrapped and sent to adapter gateway uint64 seq = wormholeRelayer.sendPayloadToEvm{value: msg.value}( pmsg.dstChain, dstAddress, @@ -98,3 +102,16 @@ contract WormholeGatewaySource is IGatewaySource, WormholeGatewayBase { ); } } + +contract WormholeGatewayDestination is WormholeGatewayBase, IGatewayDestination, IWormholeReceiver { + function receiveWormholeMessages( + bytes memory payload, + bytes[] memory additionalMessages, + bytes32 sourceAddress, + uint16 sourceChain, + bytes32 deliveryHash + ) external payable { + require(additionalMessages.length == 0); // unsupported + // TODO + } +} From e1f803e94fa377c7a8ec4bb4816f31e16e9eee5b Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Thu, 12 Sep 2024 16:10:38 -0300 Subject: [PATCH 33/41] refactor adapter base --- contracts/crosschain/GatewayAdapterBase.sol | 21 +++++++++++++++++ .../crosschain/axelar/AxelarGatewayBase.sol | 14 ++--------- .../crosschain/wormhole/WormholeGateway.sol | 23 ++++++++----------- 3 files changed, 33 insertions(+), 25 deletions(-) create mode 100644 contracts/crosschain/GatewayAdapterBase.sol diff --git a/contracts/crosschain/GatewayAdapterBase.sol b/contracts/crosschain/GatewayAdapterBase.sol new file mode 100644 index 00000000..cf01c2c5 --- /dev/null +++ b/contracts/crosschain/GatewayAdapterBase.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; + +abstract contract GatewayAdapterBase is Ownable { + event RegisteredRemoteGateway(string caip2, string gatewayAddress); + + mapping(string caip2 => string remoteGateway) private _remoteGateways; + + function getRemoteGateway(string memory caip2) public view returns (string memory remoteGateway) { + return _remoteGateways[caip2]; + } + + function registerRemoteGateway(string calldata caip2, string calldata remoteGateway) public onlyOwner { + require(bytes(_remoteGateways[caip2]).length == 0); + _remoteGateways[caip2] = remoteGateway; + emit RegisteredRemoteGateway(caip2, remoteGateway); + } +} diff --git a/contracts/crosschain/axelar/AxelarGatewayBase.sol b/contracts/crosschain/axelar/AxelarGatewayBase.sol index 703ca4f4..2ee5bf4d 100644 --- a/contracts/crosschain/axelar/AxelarGatewayBase.sol +++ b/contracts/crosschain/axelar/AxelarGatewayBase.sol @@ -5,9 +5,9 @@ pragma solidity ^0.8.0; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {IAxelarGateway} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol"; import {ICAIP2Equivalence} from "../ICAIP2Equivalence.sol"; +import {GatewayAdapterBase} from "../GatewayAdapterBase.sol"; -abstract contract AxelarGatewayBase is ICAIP2Equivalence, Ownable { - event RegisteredRemoteGateway(string caip2, string gatewayAddress); +abstract contract AxelarGatewayBase is ICAIP2Equivalence, GatewayAdapterBase { event RegisteredCAIP2Equivalence(string caip2, string destinationChain); IAxelarGateway public immutable localGateway; @@ -23,21 +23,11 @@ abstract contract AxelarGatewayBase is ICAIP2Equivalence, Ownable { return _equivalence[caip2]; } - function getRemoteGateway(string memory caip2) public view returns (string memory remoteGateway) { - return _remoteGateways[caip2]; - } - function registerCAIP2Equivalence(string calldata caip2, string calldata axelarSupported) public onlyOwner { require(bytes(_equivalence[caip2]).length == 0); _equivalence[caip2] = axelarSupported; emit RegisteredCAIP2Equivalence(caip2, axelarSupported); } - - function registerRemoteGateway(string calldata caip2, string calldata remoteGateway) public onlyOwner { - require(bytes(_remoteGateways[caip2]).length == 0); - _remoteGateways[caip2] = remoteGateway; - emit RegisteredRemoteGateway(caip2, remoteGateway); - } } // EVM (https://axelarscan.io/resources/chains?type=evm) diff --git a/contracts/crosschain/wormhole/WormholeGateway.sol b/contracts/crosschain/wormhole/WormholeGateway.sol index d3d84b99..47bf56b8 100644 --- a/contracts/crosschain/wormhole/WormholeGateway.sol +++ b/contracts/crosschain/wormhole/WormholeGateway.sol @@ -5,6 +5,7 @@ pragma solidity ^0.8.27; import {IGatewaySource} from "../IGatewaySource.sol"; import {IGatewayDestination} from "../IGatewayDestination.sol"; import {IGatewayReceiver} from "../IGatewayReceiver.sol"; +import {GatewayAdapterBase} from "../GatewayAdapterBase.sol"; import {CAIP10} from "../../utils/CAIP-10.sol"; import {IWormholeRelayer, VaaKey} from "wormhole-solidity-sdk/interfaces/IWormholeRelayer.sol"; import {IWormholeReceiver} from "wormhole-solidity-sdk/interfaces/IWormholeReceiver.sol"; @@ -14,22 +15,22 @@ function addressFromHexString(string memory hexString) pure returns (address) { return address(0); // TODO } -contract WormholeGatewayBase { +abstract contract WormholeGatewayBase is GatewayAdapterBase { + IWormholeRelayer public immutable wormholeRelayer; + + constructor(IWormholeRelayer _wormholeRelayer) { + wormholeRelayer = _wormholeRelayer; + } + function currentChain() public view returns (uint16) {} function fromCAIP2(string memory caip2) public view returns (uint16) { return 0; // TODO } - - function getRemoteGateway(string memory caip2) public view returns (string memory remoteGateway) { - return ""; // TODO - } } // TODO: allow non-evm destination chains via non-evm-specific finalize/retry variants -contract WormholeGatewaySource is IGatewaySource, WormholeGatewayBase { - IWormholeRelayer public immutable wormholeRelayer; - +abstract contract WormholeGatewaySource is IGatewaySource, WormholeGatewayBase { struct PendingMessage { address sender; uint16 dstChain; @@ -42,10 +43,6 @@ contract WormholeGatewaySource is IGatewaySource, WormholeGatewayBase { mapping(bytes32 => PendingMessage) private pending; mapping(bytes32 => uint64) private sequences; - constructor(IWormholeRelayer _wormholeRelayer) { - wormholeRelayer = _wormholeRelayer; - } - function supportsAttribute(string calldata) public view returns (bool) { return false; } @@ -103,7 +100,7 @@ contract WormholeGatewaySource is IGatewaySource, WormholeGatewayBase { } } -contract WormholeGatewayDestination is WormholeGatewayBase, IGatewayDestination, IWormholeReceiver { +abstract contract WormholeGatewayDestination is WormholeGatewayBase, IGatewayDestination, IWormholeReceiver { function receiveWormholeMessages( bytes memory payload, bytes[] memory additionalMessages, From 1c2a04328e77182bb880a7391d7da0abd7917e48 Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Thu, 12 Sep 2024 16:13:27 -0300 Subject: [PATCH 34/41] clean up code --- .../crosschain/wormhole/WormholeGateway.sol | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/contracts/crosschain/wormhole/WormholeGateway.sol b/contracts/crosschain/wormhole/WormholeGateway.sol index 47bf56b8..b6416a66 100644 --- a/contracts/crosschain/wormhole/WormholeGateway.sol +++ b/contracts/crosschain/wormhole/WormholeGateway.sol @@ -67,32 +67,35 @@ abstract contract WormholeGatewaySource is IGatewaySource, WormholeGatewayBase { } function finalizeEvmMessage(bytes32 outboxId, uint256 gasLimit) external payable { - PendingMessage memory pmsg = pending[outboxId]; + PendingMessage storage pmsg = pending[outboxId]; + require(pmsg.sender != address(0)); - address dstAddress = addressFromHexString(pmsg.dstAccount); + // TODO: fix this, payload needs to be wrapped and sent to adapter gateway - uint64 seq = wormholeRelayer.sendPayloadToEvm{value: msg.value}( + sequences[outboxId] = wormholeRelayer.sendPayloadToEvm{value: msg.value}( pmsg.dstChain, - dstAddress, + addressFromHexString(pmsg.dstAccount), pmsg.payload, 0, gasLimit ); - sequences[outboxId] = seq; - delete pending[outboxId].dstAccount; - delete pending[outboxId].payload; - delete pending[outboxId].attributes; + + delete pmsg.dstAccount; + delete pmsg.payload; + delete pmsg.attributes; } function retryEvmMessage(bytes32 outboxId, uint256 gasLimit, address newDeliveryProvider) external { uint64 seq = sequences[outboxId]; + require(seq != 0); - address sender = pending[outboxId].sender; - uint16 dstChain = pending[outboxId].dstChain; + + PendingMessage storage pmsg = pending[outboxId]; + // TODO: check if new sequence number needs to be stored for future retries wormholeRelayer.resendToEvm( - VaaKey(currentChain(), toWormholeFormat(sender), seq), - dstChain, + VaaKey(currentChain(), toWormholeFormat(pmsg.sender), seq), + pmsg.dstChain, 0, gasLimit, newDeliveryProvider From d0986b95c4c02a46bcff4becf57d016778b0bc21 Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Thu, 12 Sep 2024 16:54:51 -0300 Subject: [PATCH 35/41] inline --- contracts/crosschain/wormhole/WormholeGateway.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contracts/crosschain/wormhole/WormholeGateway.sol b/contracts/crosschain/wormhole/WormholeGateway.sol index b6416a66..6386c12f 100644 --- a/contracts/crosschain/wormhole/WormholeGateway.sol +++ b/contracts/crosschain/wormhole/WormholeGateway.sol @@ -58,8 +58,7 @@ abstract contract WormholeGatewaySource is IGatewaySource, WormholeGatewayBase { outboxId = bytes32(nextOutboxId++); - uint16 dstChainWormhole = fromCAIP2(dstChain); - pending[outboxId] = PendingMessage(msg.sender, dstChainWormhole, dstAccount, payload, attributes); + pending[outboxId] = PendingMessage(msg.sender, fromCAIP2(dstChain), dstAccount, payload, attributes); string memory caip10Src = CAIP10.format(msg.sender); string memory caip10Dst = CAIP10.format(dstChain, dstAccount); From 0c65ff9f0f7d33c385452e8715d953e4a5fa160c Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Thu, 12 Sep 2024 17:00:45 -0300 Subject: [PATCH 36/41] Revert "refactor adapter base" This reverts commit e1f803e94fa377c7a8ec4bb4816f31e16e9eee5b. --- contracts/crosschain/GatewayAdapterBase.sol | 21 ----------------- .../crosschain/axelar/AxelarGatewayBase.sol | 14 +++++++++-- .../crosschain/wormhole/WormholeGateway.sol | 23 +++++++++++-------- 3 files changed, 25 insertions(+), 33 deletions(-) delete mode 100644 contracts/crosschain/GatewayAdapterBase.sol diff --git a/contracts/crosschain/GatewayAdapterBase.sol b/contracts/crosschain/GatewayAdapterBase.sol deleted file mode 100644 index cf01c2c5..00000000 --- a/contracts/crosschain/GatewayAdapterBase.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; - -abstract contract GatewayAdapterBase is Ownable { - event RegisteredRemoteGateway(string caip2, string gatewayAddress); - - mapping(string caip2 => string remoteGateway) private _remoteGateways; - - function getRemoteGateway(string memory caip2) public view returns (string memory remoteGateway) { - return _remoteGateways[caip2]; - } - - function registerRemoteGateway(string calldata caip2, string calldata remoteGateway) public onlyOwner { - require(bytes(_remoteGateways[caip2]).length == 0); - _remoteGateways[caip2] = remoteGateway; - emit RegisteredRemoteGateway(caip2, remoteGateway); - } -} diff --git a/contracts/crosschain/axelar/AxelarGatewayBase.sol b/contracts/crosschain/axelar/AxelarGatewayBase.sol index 2ee5bf4d..703ca4f4 100644 --- a/contracts/crosschain/axelar/AxelarGatewayBase.sol +++ b/contracts/crosschain/axelar/AxelarGatewayBase.sol @@ -5,9 +5,9 @@ pragma solidity ^0.8.0; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {IAxelarGateway} from "@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol"; import {ICAIP2Equivalence} from "../ICAIP2Equivalence.sol"; -import {GatewayAdapterBase} from "../GatewayAdapterBase.sol"; -abstract contract AxelarGatewayBase is ICAIP2Equivalence, GatewayAdapterBase { +abstract contract AxelarGatewayBase is ICAIP2Equivalence, Ownable { + event RegisteredRemoteGateway(string caip2, string gatewayAddress); event RegisteredCAIP2Equivalence(string caip2, string destinationChain); IAxelarGateway public immutable localGateway; @@ -23,11 +23,21 @@ abstract contract AxelarGatewayBase is ICAIP2Equivalence, GatewayAdapterBase { return _equivalence[caip2]; } + function getRemoteGateway(string memory caip2) public view returns (string memory remoteGateway) { + return _remoteGateways[caip2]; + } + function registerCAIP2Equivalence(string calldata caip2, string calldata axelarSupported) public onlyOwner { require(bytes(_equivalence[caip2]).length == 0); _equivalence[caip2] = axelarSupported; emit RegisteredCAIP2Equivalence(caip2, axelarSupported); } + + function registerRemoteGateway(string calldata caip2, string calldata remoteGateway) public onlyOwner { + require(bytes(_remoteGateways[caip2]).length == 0); + _remoteGateways[caip2] = remoteGateway; + emit RegisteredRemoteGateway(caip2, remoteGateway); + } } // EVM (https://axelarscan.io/resources/chains?type=evm) diff --git a/contracts/crosschain/wormhole/WormholeGateway.sol b/contracts/crosschain/wormhole/WormholeGateway.sol index 6386c12f..a5423cc3 100644 --- a/contracts/crosschain/wormhole/WormholeGateway.sol +++ b/contracts/crosschain/wormhole/WormholeGateway.sol @@ -5,7 +5,6 @@ pragma solidity ^0.8.27; import {IGatewaySource} from "../IGatewaySource.sol"; import {IGatewayDestination} from "../IGatewayDestination.sol"; import {IGatewayReceiver} from "../IGatewayReceiver.sol"; -import {GatewayAdapterBase} from "../GatewayAdapterBase.sol"; import {CAIP10} from "../../utils/CAIP-10.sol"; import {IWormholeRelayer, VaaKey} from "wormhole-solidity-sdk/interfaces/IWormholeRelayer.sol"; import {IWormholeReceiver} from "wormhole-solidity-sdk/interfaces/IWormholeReceiver.sol"; @@ -15,22 +14,22 @@ function addressFromHexString(string memory hexString) pure returns (address) { return address(0); // TODO } -abstract contract WormholeGatewayBase is GatewayAdapterBase { - IWormholeRelayer public immutable wormholeRelayer; - - constructor(IWormholeRelayer _wormholeRelayer) { - wormholeRelayer = _wormholeRelayer; - } - +contract WormholeGatewayBase { function currentChain() public view returns (uint16) {} function fromCAIP2(string memory caip2) public view returns (uint16) { return 0; // TODO } + + function getRemoteGateway(string memory caip2) public view returns (string memory remoteGateway) { + return ""; // TODO + } } // TODO: allow non-evm destination chains via non-evm-specific finalize/retry variants -abstract contract WormholeGatewaySource is IGatewaySource, WormholeGatewayBase { +contract WormholeGatewaySource is IGatewaySource, WormholeGatewayBase { + IWormholeRelayer public immutable wormholeRelayer; + struct PendingMessage { address sender; uint16 dstChain; @@ -43,6 +42,10 @@ abstract contract WormholeGatewaySource is IGatewaySource, WormholeGatewayBase { mapping(bytes32 => PendingMessage) private pending; mapping(bytes32 => uint64) private sequences; + constructor(IWormholeRelayer _wormholeRelayer) { + wormholeRelayer = _wormholeRelayer; + } + function supportsAttribute(string calldata) public view returns (bool) { return false; } @@ -102,7 +105,7 @@ abstract contract WormholeGatewaySource is IGatewaySource, WormholeGatewayBase { } } -abstract contract WormholeGatewayDestination is WormholeGatewayBase, IGatewayDestination, IWormholeReceiver { +contract WormholeGatewayDestination is WormholeGatewayBase, IGatewayDestination, IWormholeReceiver { function receiveWormholeMessages( bytes memory payload, bytes[] memory additionalMessages, From 8cb52eea7ab37614b0977c76398f6fe9b46c9cc9 Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Thu, 12 Sep 2024 17:18:16 -0300 Subject: [PATCH 37/41] implement wormhole destination gateway --- .../crosschain/wormhole/WormholeGateway.sol | 87 ++++++++++++++----- 1 file changed, 63 insertions(+), 24 deletions(-) diff --git a/contracts/crosschain/wormhole/WormholeGateway.sol b/contracts/crosschain/wormhole/WormholeGateway.sol index a5423cc3..ca020a77 100644 --- a/contracts/crosschain/wormhole/WormholeGateway.sol +++ b/contracts/crosschain/wormhole/WormholeGateway.sol @@ -2,37 +2,71 @@ pragma solidity ^0.8.27; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; import {IGatewaySource} from "../IGatewaySource.sol"; import {IGatewayDestination} from "../IGatewayDestination.sol"; import {IGatewayReceiver} from "../IGatewayReceiver.sol"; import {CAIP10} from "../../utils/CAIP-10.sol"; import {IWormholeRelayer, VaaKey} from "wormhole-solidity-sdk/interfaces/IWormholeRelayer.sol"; import {IWormholeReceiver} from "wormhole-solidity-sdk/interfaces/IWormholeReceiver.sol"; -import {toWormholeFormat} from "wormhole-solidity-sdk/Utils.sol"; +import {toWormholeFormat, fromWormholeFormat} from "wormhole-solidity-sdk/Utils.sol"; function addressFromHexString(string memory hexString) pure returns (address) { return address(0); // TODO } -contract WormholeGatewayBase { - function currentChain() public view returns (uint16) {} +abstract contract WormholeGatewayBase is Ownable { + event RegisteredRemoteGateway(string caip2, bytes32 remoteGateway); + event RegisteredCAIP2Equivalence(string caip2, uint16 wormholeId); + + IWormholeRelayer public immutable wormholeRelayer; + uint16 public immutable currentChain; + + mapping(string caip2 => bytes32 remoteGateway) private _remoteGateways; + mapping(string caip2 => uint32 wormholeId) private _equivalence; + mapping(uint16 wormholeId => string caip2) private _equivalence2; + + constructor(IWormholeRelayer _wormholeRelayer, uint16 _currentChain) { + wormholeRelayer = _wormholeRelayer; + currentChain = _currentChain; + } + + function supportedChain(string memory caip2) public view returns (bool) { + return (_equivalence[caip2] & (1 << 16)) != 0; + } function fromCAIP2(string memory caip2) public view returns (uint16) { - return 0; // TODO + return uint16(_equivalence[caip2]); + } + + function toCAIP2(uint16 wormholeId) public view returns (string memory) { + return _equivalence2[wormholeId]; + } + + function getRemoteGateway(string memory caip2) public view returns (bytes32) { + return _remoteGateways[caip2]; } - function getRemoteGateway(string memory caip2) public view returns (string memory remoteGateway) { - return ""; // TODO + function registerCAIP2Equivalence(string calldata caip2, uint16 wormholeId) public onlyOwner { + require(_equivalence[caip2] == 0); + _equivalence[caip2] = wormholeId | (1 << 16); + _equivalence2[wormholeId] = caip2; + emit RegisteredCAIP2Equivalence(caip2, wormholeId); + } + + function registerRemoteGateway(string calldata caip2, bytes32 remoteGateway) public onlyOwner { + require(_remoteGateways[caip2] == 0); + _remoteGateways[caip2] = remoteGateway; + emit RegisteredRemoteGateway(caip2, remoteGateway); } } // TODO: allow non-evm destination chains via non-evm-specific finalize/retry variants -contract WormholeGatewaySource is IGatewaySource, WormholeGatewayBase { - IWormholeRelayer public immutable wormholeRelayer; - +abstract contract WormholeGatewaySource is IGatewaySource, WormholeGatewayBase { struct PendingMessage { address sender; - uint16 dstChain; + string dstChain; string dstAccount; bytes payload; bytes[] attributes; @@ -42,10 +76,6 @@ contract WormholeGatewaySource is IGatewaySource, WormholeGatewayBase { mapping(bytes32 => PendingMessage) private pending; mapping(bytes32 => uint64) private sequences; - constructor(IWormholeRelayer _wormholeRelayer) { - wormholeRelayer = _wormholeRelayer; - } - function supportsAttribute(string calldata) public view returns (bool) { return false; } @@ -61,7 +91,8 @@ contract WormholeGatewaySource is IGatewaySource, WormholeGatewayBase { outboxId = bytes32(nextOutboxId++); - pending[outboxId] = PendingMessage(msg.sender, fromCAIP2(dstChain), dstAccount, payload, attributes); + require(supportedChain(dstChain)); + pending[outboxId] = PendingMessage(msg.sender, dstChain, dstAccount, payload, attributes); string memory caip10Src = CAIP10.format(msg.sender); string memory caip10Dst = CAIP10.format(dstChain, dstAccount); @@ -73,11 +104,12 @@ contract WormholeGatewaySource is IGatewaySource, WormholeGatewayBase { require(pmsg.sender != address(0)); - // TODO: fix this, payload needs to be wrapped and sent to adapter gateway + bytes memory adapterPayload = abi.encode(pmsg.sender, addressFromHexString(pmsg.dstAccount), pmsg.payload); + sequences[outboxId] = wormholeRelayer.sendPayloadToEvm{value: msg.value}( - pmsg.dstChain, - addressFromHexString(pmsg.dstAccount), - pmsg.payload, + fromCAIP2(pmsg.dstChain), + fromWormholeFormat(getRemoteGateway(pmsg.dstChain)), + adapterPayload, 0, gasLimit ); @@ -96,8 +128,8 @@ contract WormholeGatewaySource is IGatewaySource, WormholeGatewayBase { // TODO: check if new sequence number needs to be stored for future retries wormholeRelayer.resendToEvm( - VaaKey(currentChain(), toWormholeFormat(pmsg.sender), seq), - pmsg.dstChain, + VaaKey(currentChain, toWormholeFormat(pmsg.sender), seq), + fromCAIP2(pmsg.dstChain), 0, gasLimit, newDeliveryProvider @@ -105,15 +137,22 @@ contract WormholeGatewaySource is IGatewaySource, WormholeGatewayBase { } } -contract WormholeGatewayDestination is WormholeGatewayBase, IGatewayDestination, IWormholeReceiver { +abstract contract WormholeGatewayDestination is WormholeGatewayBase, IGatewayDestination, IWormholeReceiver { + using Strings for address; + function receiveWormholeMessages( - bytes memory payload, + bytes memory adapterPayload, bytes[] memory additionalMessages, bytes32 sourceAddress, uint16 sourceChain, bytes32 deliveryHash ) external payable { + string memory sourceChainCAIP2 = toCAIP2(sourceChain); + + require(sourceAddress == getRemoteGateway(sourceChainCAIP2)); require(additionalMessages.length == 0); // unsupported - // TODO + + (address sender, IGatewayReceiver destination, bytes memory payload) = abi.decode(adapterPayload, (address, IGatewayReceiver, bytes)); + destination.receiveMessage(deliveryHash, sourceChainCAIP2, sender.toHexString(), payload, new bytes[](0)); } } From f9da9d3fd1b35c3bc95d210006835b84f4c924e5 Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Thu, 12 Sep 2024 17:18:33 -0300 Subject: [PATCH 38/41] lint --- contracts/crosschain/wormhole/WormholeGateway.sol | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/contracts/crosschain/wormhole/WormholeGateway.sol b/contracts/crosschain/wormhole/WormholeGateway.sol index ca020a77..1cbe901f 100644 --- a/contracts/crosschain/wormhole/WormholeGateway.sol +++ b/contracts/crosschain/wormhole/WormholeGateway.sol @@ -152,7 +152,10 @@ abstract contract WormholeGatewayDestination is WormholeGatewayBase, IGatewayDes require(sourceAddress == getRemoteGateway(sourceChainCAIP2)); require(additionalMessages.length == 0); // unsupported - (address sender, IGatewayReceiver destination, bytes memory payload) = abi.decode(adapterPayload, (address, IGatewayReceiver, bytes)); + (address sender, IGatewayReceiver destination, bytes memory payload) = abi.decode( + adapterPayload, + (address, IGatewayReceiver, bytes) + ); destination.receiveMessage(deliveryHash, sourceChainCAIP2, sender.toHexString(), payload, new bytes[](0)); } } From 00aae9e830c3ea83e4b71bf7b7d007861dde407d Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Thu, 12 Sep 2024 18:23:42 -0300 Subject: [PATCH 39/41] add arbitrum gateway --- .../crosschain/arbitrum/ArbitrumGateway.sol | 102 + package-lock.json | 1980 ++++++++++++----- package.json | 1 + 3 files changed, 1579 insertions(+), 504 deletions(-) create mode 100644 contracts/crosschain/arbitrum/ArbitrumGateway.sol diff --git a/contracts/crosschain/arbitrum/ArbitrumGateway.sol b/contracts/crosschain/arbitrum/ArbitrumGateway.sol new file mode 100644 index 00000000..8fe34e49 --- /dev/null +++ b/contracts/crosschain/arbitrum/ArbitrumGateway.sol @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.27; + +import {IInbox} from "@arbitrum/nitro-contracts/src/bridge/IInbox.sol"; +import {ArbSys} from "@arbitrum/nitro-contracts/src/precompiles/ArbSys.sol"; +import {IGatewaySource} from "../IGatewaySource.sol"; +import {IGatewayDestination} from "../IGatewayDestination.sol"; +import {IGatewayReceiver} from "../IGatewayReceiver.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; + +function addressFromHexString(string memory hexString) pure returns (address) { + return address(0); // TODO +} + +interface IArbitrumGatewayL2Destination { + function deliverMessage(address sender, address receiver, bytes calldata payload) external; +} + +abstract contract ArbitrumGatewayL1Source is IGatewaySource { + IInbox private _inbox; // TODO + address private _remoteGateway; + + struct PendingMessage { + address sender; + address receiver; + uint256 value; + bytes payload; + } + + uint256 private _nextOutboxId; + mapping (bytes32 outboxId => PendingMessage) private _pending; + + function sendMessage( + string calldata destChain, + string calldata destAccount, + bytes calldata payload, + bytes[] calldata attributes + ) external payable override returns (bytes32 outboxId) { + require(Strings.equal(destChain, "eip155:42161")); + require(attributes.length == 0); + + address receiver = addressFromHexString(destAccount); + require(receiver != address(0)); + + outboxId = bytes32(_nextOutboxId++); + _pending[outboxId] = PendingMessage(msg.sender, receiver, msg.value, payload); + } + + function finalizeMessage( + bytes32 outboxId, + uint256 maxSubmissionCost, + address excessFeeRefundAddress, + address callValueRefundAddress, + uint256 gasLimit, + uint256 maxFeePerGas + ) external payable { + PendingMessage storage pmsg = _pending[outboxId]; + + require(pmsg.receiver != address(0)); + + bytes memory adapterPayload = abi.encodeCall( + IArbitrumGatewayL2Destination.deliverMessage, + (pmsg.sender, pmsg.receiver, pmsg.payload) + ); + + _inbox.createRetryableTicket{value: msg.value + pmsg.value}( + _remoteGateway, + pmsg.value, + maxSubmissionCost, + excessFeeRefundAddress, + callValueRefundAddress, + gasLimit, + maxFeePerGas, + adapterPayload + ); + + delete pmsg.receiver; + delete pmsg.value; + delete pmsg.payload; + } +} + +abstract contract ArbitrumGatewayL1Destination is IGatewayDestination, IArbitrumGatewayL2Destination { + using Strings for address; + + ArbSys private _arbSys; // TODO + address private _remoteGateway; + + function deliverMessage(address sender, address receiver, bytes calldata payload) external { + require(_arbSys.wasMyCallersAddressAliased()); + require(_arbSys.myCallersAddressWithoutAliasing() == _remoteGateway); + + IGatewayReceiver(receiver).receiveMessage( + 0, + "eip155:1", + sender.toHexString(), + payload, + new bytes[](0) + ); + } +} diff --git a/package-lock.json b/package-lock.json index 64b808f4..536a8a5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "license": "MIT", "dependencies": { + "@arbitrum/nitro-contracts": "^2.1.0", "@axelar-network/axelar-gmp-sdk-solidity": "^5.10.0", "@openzeppelin/contracts": "^5.0.2", "@openzeppelin/contracts-upgradeable": "^5.0.2" @@ -29,9 +30,37 @@ }, "node_modules/@adraffy/ens-normalize": { "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", "dev": true, "license": "MIT" }, + "node_modules/@arbitrum/nitro-contracts": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@arbitrum/nitro-contracts/-/nitro-contracts-2.1.0.tgz", + "integrity": "sha512-Bh89H5/ihWVcAI2eNBc1McybZAmpTHJ5eFLauLz8bjicZTOD545vSYAreJN41Uo+AlHKVAMtODUQdoP0NVtg3Q==", + "hasInstallScript": true, + "license": "BUSL-1.1", + "dependencies": { + "@offchainlabs/upgrade-executor": "1.1.0-beta.0", + "@openzeppelin/contracts": "4.5.0", + "@openzeppelin/contracts-upgradeable": "4.5.2", + "patch-package": "^6.4.7", + "solady": "0.0.182" + } + }, + "node_modules/@arbitrum/nitro-contracts/node_modules/@openzeppelin/contracts": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.5.0.tgz", + "integrity": "sha512-fdkzKPYMjrRiPK6K4y64e6GzULR7R7RwxSigHS8DDp7aWDeoReqsQI+cxHV1UuhAqX69L1lAaWDxenfP+xiqzA==", + "license": "MIT" + }, + "node_modules/@arbitrum/nitro-contracts/node_modules/@openzeppelin/contracts-upgradeable": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.5.2.tgz", + "integrity": "sha512-xgWZYaPlrEOQo3cBj97Ufiuv79SPd8Brh4GcFYhPgb6WvAq4ppz8dWKL6h+jLAK01rUqMRp/TS9AdXgAeNvCLA==", + "license": "MIT" + }, "node_modules/@axelar-network/axelar-gmp-sdk-solidity": { "version": "5.10.0", "resolved": "https://registry.npmjs.org/@axelar-network/axelar-gmp-sdk-solidity/-/axelar-gmp-sdk-solidity-5.10.0.tgz", @@ -95,35 +124,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@ethereumjs/util/node_modules/@scure/bip32": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", - "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/curves": "~1.4.0", - "@noble/hashes": "~1.4.0", - "@scure/base": "~1.1.6" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@ethereumjs/util/node_modules/@scure/bip39": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", - "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "~1.4.0", - "@scure/base": "~1.1.6" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/@ethereumjs/util/node_modules/ethereum-cryptography": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", @@ -139,6 +139,8 @@ }, "node_modules/@ethersproject/abi": { "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", + "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", "dev": true, "funding": [ { @@ -165,6 +167,8 @@ }, "node_modules/@ethersproject/abstract-provider": { "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", + "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", "dev": true, "funding": [ { @@ -189,6 +193,8 @@ }, "node_modules/@ethersproject/abstract-signer": { "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", + "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", "dev": true, "funding": [ { @@ -211,6 +217,8 @@ }, "node_modules/@ethersproject/address": { "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", + "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", "dev": true, "funding": [ { @@ -233,6 +241,8 @@ }, "node_modules/@ethersproject/base64": { "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", + "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", "dev": true, "funding": [ { @@ -251,6 +261,8 @@ }, "node_modules/@ethersproject/bignumber": { "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", + "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", "dev": true, "funding": [ { @@ -271,6 +283,8 @@ }, "node_modules/@ethersproject/bytes": { "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", + "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", "dev": true, "funding": [ { @@ -289,6 +303,8 @@ }, "node_modules/@ethersproject/constants": { "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", + "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", "dev": true, "funding": [ { @@ -307,6 +323,8 @@ }, "node_modules/@ethersproject/hash": { "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", + "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", "dev": true, "funding": [ { @@ -333,6 +351,8 @@ }, "node_modules/@ethersproject/keccak256": { "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", + "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", "dev": true, "funding": [ { @@ -352,6 +372,8 @@ }, "node_modules/@ethersproject/logger": { "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", + "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", "dev": true, "funding": [ { @@ -367,6 +389,8 @@ }, "node_modules/@ethersproject/networks": { "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", + "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", "dev": true, "funding": [ { @@ -385,6 +409,8 @@ }, "node_modules/@ethersproject/properties": { "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", + "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", "dev": true, "funding": [ { @@ -403,6 +429,8 @@ }, "node_modules/@ethersproject/rlp": { "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", + "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", "dev": true, "funding": [ { @@ -422,6 +450,8 @@ }, "node_modules/@ethersproject/signing-key": { "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", + "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", "dev": true, "funding": [ { @@ -445,6 +475,8 @@ }, "node_modules/@ethersproject/strings": { "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", + "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", "dev": true, "funding": [ { @@ -465,6 +497,8 @@ }, "node_modules/@ethersproject/transactions": { "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", + "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", "dev": true, "funding": [ { @@ -491,6 +525,8 @@ }, "node_modules/@ethersproject/web": { "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", + "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", "dev": true, "funding": [ { @@ -513,6 +549,8 @@ }, "node_modules/@fastify/busboy": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", "dev": true, "license": "MIT", "engines": { @@ -521,6 +559,8 @@ }, "node_modules/@metamask/eth-sig-util": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz", + "integrity": "sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==", "dev": true, "license": "ISC", "dependencies": { @@ -534,8 +574,43 @@ "node": ">=12.0.0" } }, + "node_modules/@metamask/eth-sig-util/node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@metamask/eth-sig-util/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@metamask/eth-sig-util/node_modules/ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + }, "node_modules/@noble/curves": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", "dev": true, "license": "MIT", "dependencies": { @@ -547,6 +622,8 @@ }, "node_modules/@noble/hashes": { "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", "dev": true, "license": "MIT", "engines": { @@ -558,6 +635,8 @@ }, "node_modules/@noble/secp256k1": { "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", + "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==", "dev": true, "funding": [ { @@ -606,39 +685,98 @@ } }, "node_modules/@nomicfoundation/edr": { - "version": "0.3.7", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.5.2.tgz", + "integrity": "sha512-hW/iLvUQZNTVjFyX/I40rtKvvDOqUEyIi96T28YaLfmPL+3LW2lxmYLUXEJ6MI14HzqxDqrLyhf6IbjAa2r3Dw==", "dev": true, "license": "MIT", + "dependencies": { + "@nomicfoundation/edr-darwin-arm64": "0.5.2", + "@nomicfoundation/edr-darwin-x64": "0.5.2", + "@nomicfoundation/edr-linux-arm64-gnu": "0.5.2", + "@nomicfoundation/edr-linux-arm64-musl": "0.5.2", + "@nomicfoundation/edr-linux-x64-gnu": "0.5.2", + "@nomicfoundation/edr-linux-x64-musl": "0.5.2", + "@nomicfoundation/edr-win32-x64-msvc": "0.5.2" + }, "engines": { "node": ">= 18" - }, - "optionalDependencies": { - "@nomicfoundation/edr-darwin-arm64": "0.3.7", - "@nomicfoundation/edr-darwin-x64": "0.3.7", - "@nomicfoundation/edr-linux-arm64-gnu": "0.3.7", - "@nomicfoundation/edr-linux-arm64-musl": "0.3.7", - "@nomicfoundation/edr-linux-x64-gnu": "0.3.7", - "@nomicfoundation/edr-linux-x64-musl": "0.3.7", - "@nomicfoundation/edr-win32-x64-msvc": "0.3.7" } }, "node_modules/@nomicfoundation/edr-darwin-arm64": { - "version": "0.3.7", - "cpu": [ - "arm64" - ], + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.5.2.tgz", + "integrity": "sha512-Gm4wOPKhbDjGTIRyFA2QUAPfCXA1AHxYOKt3yLSGJkQkdy9a5WW+qtqKeEKHc/+4wpJSLtsGQfpzyIzggFfo/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-darwin-x64": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.5.2.tgz", + "integrity": "sha512-ClyABq2dFCsrYEED3/UIO0c7p4H1/4vvlswFlqUyBpOkJccr75qIYvahOSJRM62WgUFRhbSS0OJXFRwc/PwmVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.5.2.tgz", + "integrity": "sha512-HWMTVk1iOabfvU2RvrKLDgtFjJZTC42CpHiw2h6rfpsgRqMahvIlx2jdjWYzFNy1jZKPTN1AStQ/91MRrg5KnA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-arm64-musl": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.5.2.tgz", + "integrity": "sha512-CwsQ10xFx/QAD5y3/g5alm9+jFVuhc7uYMhrZAu9UVF+KtVjeCvafj0PaVsZ8qyijjqVuVsJ8hD1x5ob7SMcGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-x64-gnu": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.5.2.tgz", + "integrity": "sha512-CWVCEdhWJ3fmUpzWHCRnC0/VLBDbqtqTGTR6yyY1Ep3S3BOrHEAvt7h5gx85r2vLcztisu2vlDq51auie4IU1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-linux-x64-musl": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.5.2.tgz", + "integrity": "sha512-+aJDfwhkddy2pP5u1ISg3IZVAm0dO836tRlDTFWtvvSMQ5hRGqPcWwlsbobhDQsIxhPJyT7phL0orCg5W3WMeA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@nomicfoundation/edr-win32-x64-msvc": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.5.2.tgz", + "integrity": "sha512-CcvvuA3sAv7liFNPsIR/68YlH6rrybKzYttLlMr80d4GKJjwJ5OKb3YgE6FdZZnOfP19HEHhsLcE0DPLtY3r0w==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { "node": ">= 18" } }, "node_modules/@nomicfoundation/ethereumjs-common": { "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.4.tgz", + "integrity": "sha512-9Rgb658lcWsjiicr5GzNCjI1llow/7r0k50dLL95OJ+6iZJcVbi15r3Y0xh2cIO+zgX0WIHcbzIu6FeQf9KPrg==", "dev": true, "license": "MIT", "dependencies": { @@ -647,6 +785,8 @@ }, "node_modules/@nomicfoundation/ethereumjs-rlp": { "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.4.tgz", + "integrity": "sha512-8H1S3s8F6QueOc/X92SdrA4RDenpiAEqMg5vJH99kcQaCy/a3Q6fgseo75mgWlbanGJXSlAPtnCeG9jvfTYXlw==", "dev": true, "license": "MPL-2.0", "bin": { @@ -658,6 +798,8 @@ }, "node_modules/@nomicfoundation/ethereumjs-tx": { "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.4.tgz", + "integrity": "sha512-Xjv8wAKJGMrP1f0n2PeyfFCCojHd7iS3s/Ab7qzF1S64kxZ8Z22LCMynArYsVqiFx6rzYy548HNVEyI+AYN/kw==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -678,30 +820,10 @@ } } }, - "node_modules/@nomicfoundation/ethereumjs-tx/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - }, "node_modules/@nomicfoundation/ethereumjs-util": { "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.4.tgz", + "integrity": "sha512-sLOzjnSrlx9Bb9EFNtHzK/FJFsfg2re6bsGqinFinH1gCqVfz9YYlXiMWwDM4C/L4ywuHFCYwfKTVr/QHQcU0Q==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -720,30 +842,10 @@ } } }, - "node_modules/@nomicfoundation/ethereumjs-util/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - }, "node_modules/@nomicfoundation/hardhat-chai-matchers": { - "version": "2.0.6", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-chai-matchers/-/hardhat-chai-matchers-2.0.7.tgz", + "integrity": "sha512-RQfsiTwdf0SP+DtuNYvm4921X6VirCQq0Xyh+mnuGlTwEFSPZ/o27oQC+l+3Y/l48DDU7+ZcYBR+Fp+Rp94LfQ==", "dev": true, "license": "MIT", "dependencies": { @@ -760,7 +862,9 @@ } }, "node_modules/@nomicfoundation/hardhat-ethers": { - "version": "3.0.5", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-3.0.8.tgz", + "integrity": "sha512-zhOZ4hdRORls31DTOqg+GmEZM0ujly8GGIuRY7t7szEk2zW/arY1qDug/py8AEktT00v5K+b6RvbVog+va51IA==", "dev": true, "license": "MIT", "dependencies": { @@ -785,95 +889,143 @@ "hardhat": "^2.9.5" } }, - "node_modules/@nomicfoundation/hardhat-network-helpers/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "node_modules/@nomicfoundation/solidity-analyzer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.2.tgz", + "integrity": "sha512-q4n32/FNKIhQ3zQGGw5CvPF6GTvDCpYwIf7bEY/dZTZbgfDsHyjJwURxUJf3VQuuJj+fDIFl4+KkBVbw4Ef6jA==", "dev": true, "license": "MIT", - "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" + "engines": { + "node": ">= 12" + }, + "optionalDependencies": { + "@nomicfoundation/solidity-analyzer-darwin-arm64": "0.1.2", + "@nomicfoundation/solidity-analyzer-darwin-x64": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-arm64-musl": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-x64-gnu": "0.1.2", + "@nomicfoundation/solidity-analyzer-linux-x64-musl": "0.1.2", + "@nomicfoundation/solidity-analyzer-win32-x64-msvc": "0.1.2" } }, - "node_modules/@nomicfoundation/hardhat-network-helpers/node_modules/ethereumjs-util": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", - "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", + "node_modules/@nomicfoundation/solidity-analyzer-darwin-arm64": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.2.tgz", + "integrity": "sha512-JaqcWPDZENCvm++lFFGjrDd8mxtf+CtLd2MiXvMNTBD33dContTZ9TWETwNFwg7JTJT5Q9HEecH7FA+HTSsIUw==", "dev": true, - "license": "MPL-2.0", - "dependencies": { - "@types/bn.js": "^5.1.0", - "bn.js": "^5.1.2", - "create-hash": "^1.1.2", - "ethereum-cryptography": "^0.1.3", - "rlp": "^2.2.4" - }, + "license": "MIT", + "optional": true, "engines": { - "node": ">=10.0.0" + "node": ">= 12" } }, - "node_modules/@nomicfoundation/solidity-analyzer": { - "version": "0.1.1", + "node_modules/@nomicfoundation/solidity-analyzer-darwin-x64": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.2.tgz", + "integrity": "sha512-fZNmVztrSXC03e9RONBT+CiksSeYcxI1wlzqyr0L7hsQlK1fzV+f04g2JtQ1c/Fe74ZwdV6aQBdd6Uwl1052sw==", "dev": true, "license": "MIT", + "optional": true, "engines": { "node": ">= 12" - }, - "optionalDependencies": { - "@nomicfoundation/solidity-analyzer-darwin-arm64": "0.1.1", - "@nomicfoundation/solidity-analyzer-darwin-x64": "0.1.1", - "@nomicfoundation/solidity-analyzer-freebsd-x64": "0.1.1", - "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": "0.1.1", - "@nomicfoundation/solidity-analyzer-linux-arm64-musl": "0.1.1", - "@nomicfoundation/solidity-analyzer-linux-x64-gnu": "0.1.1", - "@nomicfoundation/solidity-analyzer-linux-x64-musl": "0.1.1", - "@nomicfoundation/solidity-analyzer-win32-arm64-msvc": "0.1.1", - "@nomicfoundation/solidity-analyzer-win32-ia32-msvc": "0.1.1", - "@nomicfoundation/solidity-analyzer-win32-x64-msvc": "0.1.1" } }, - "node_modules/@nomicfoundation/solidity-analyzer-darwin-arm64": { - "version": "0.1.1", - "cpu": [ - "arm64" - ], + "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-gnu": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.2.tgz", + "integrity": "sha512-3d54oc+9ZVBuB6nbp8wHylk4xh0N0Gc+bk+/uJae+rUgbOBwQSfuGIbAZt1wBXs5REkSmynEGcqx6DutoK0tPA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-musl": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.2.tgz", + "integrity": "sha512-iDJfR2qf55vgsg7BtJa7iPiFAsYf2d0Tv/0B+vhtnI16+wfQeTbP7teookbGvAo0eJo7aLLm0xfS/GTkvHIucA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-gnu": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.2.tgz", + "integrity": "sha512-9dlHMAt5/2cpWyuJ9fQNOUXFB/vgSFORg1jpjX1Mh9hJ/MfZXlDdHQ+DpFCs32Zk5pxRBb07yGvSHk9/fezL+g==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-musl": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.2.tgz", + "integrity": "sha512-GzzVeeJob3lfrSlDKQw2bRJ8rBf6mEYaWY+gW0JnTDHINA0s2gPR4km5RLIj1xeZZOYz4zRw+AEeYgLRqB2NXg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-win32-x64-msvc": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.2.tgz", + "integrity": "sha512-Fdjli4DCcFHb4Zgsz0uEJXZ2K7VEO+w5KVv7HmT7WO10iODdU9csC2az4jrhEsRtiR9Gfd74FlG0NYlw1BMdyA==", "dev": true, "license": "MIT", "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": ">= 10" + "node": ">= 12" + } + }, + "node_modules/@offchainlabs/upgrade-executor": { + "version": "1.1.0-beta.0", + "resolved": "https://registry.npmjs.org/@offchainlabs/upgrade-executor/-/upgrade-executor-1.1.0-beta.0.tgz", + "integrity": "sha512-mpn6PHjH/KDDjNX0pXHEKdyv8m6DVGQiI2nGzQn0JbM1nOSHJpWx6fvfjtH7YxHJ6zBZTcsKkqGkFKDtCfoSLw==", + "license": "Apache 2.0", + "dependencies": { + "@openzeppelin/contracts": "4.7.3", + "@openzeppelin/contracts-upgradeable": "4.7.3" } }, + "node_modules/@offchainlabs/upgrade-executor/node_modules/@openzeppelin/contracts": { + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.7.3.tgz", + "integrity": "sha512-dGRS0agJzu8ybo44pCIf3xBaPQN/65AIXNgK8+4gzKd5kbvlqyxryUYVLJv7fK98Seyd2hDZzVEHSWAh0Bt1Yw==", + "license": "MIT" + }, + "node_modules/@offchainlabs/upgrade-executor/node_modules/@openzeppelin/contracts-upgradeable": { + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.7.3.tgz", + "integrity": "sha512-+wuegAMaLcZnLCJIvrVUDzA9z/Wp93f0Dla/4jJvIhijRrPabjQbZe6fWiECLaJyfn5ci9fqf9vTw3xpQOad2A==", + "license": "MIT" + }, "node_modules/@openzeppelin/contracts": { "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.0.2.tgz", + "integrity": "sha512-ytPc6eLGcHHnapAZ9S+5qsdomhjo6QBHTDRRBFfTxXIpsicMhVPouPgmUPebZZZGX7vt9USA+Z+0M0dSVtSUEA==", "license": "MIT" }, "node_modules/@openzeppelin/contracts-upgradeable": { "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-5.0.2.tgz", + "integrity": "sha512-0MmkHSHiW2NRFiT9/r5Lu4eJq5UJ4/tzlOgYXNAIj/ONkQTVnz22pLxDvp4C4uZ9he7ZFvGn3Driptn1/iU7tQ==", "license": "MIT", "peerDependencies": { "@openzeppelin/contracts": "5.0.2" } }, "node_modules/@scure/base": { - "version": "1.1.6", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.8.tgz", + "integrity": "sha512-6CyAclxj3Nb0XT7GHK6K4zK6k2xJm6E4Ft0Ohjt4WgegiFUHEtFb2CGzmPmGBwoIhrLsqNLYfLr04Y1GePrzZg==", "dev": true, "license": "MIT", "funding": { @@ -881,60 +1033,77 @@ } }, "node_modules/@scure/bip32": { - "version": "1.1.5", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", + "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], "license": "MIT", "dependencies": { - "@noble/hashes": "~1.2.0", - "@noble/secp256k1": "~1.7.0", - "@scure/base": "~1.1.0" + "@noble/curves": "~1.4.0", + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, "node_modules/@scure/bip32/node_modules/@noble/hashes": { - "version": "1.2.0", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } }, "node_modules/@scure/bip39": { - "version": "1.1.1", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], "license": "MIT", "dependencies": { - "@noble/hashes": "~1.2.0", - "@scure/base": "~1.1.0" + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, "node_modules/@scure/bip39/node_modules/@noble/hashes": { - "version": "1.2.0", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } }, "node_modules/@sentry/core": { "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz", + "integrity": "sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -950,11 +1119,15 @@ }, "node_modules/@sentry/core/node_modules/tslib": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true, "license": "0BSD" }, "node_modules/@sentry/hub": { "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz", + "integrity": "sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -968,11 +1141,15 @@ }, "node_modules/@sentry/hub/node_modules/tslib": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true, "license": "0BSD" }, "node_modules/@sentry/minimal": { "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz", + "integrity": "sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -986,11 +1163,15 @@ }, "node_modules/@sentry/minimal/node_modules/tslib": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true, "license": "0BSD" }, "node_modules/@sentry/node": { "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.30.0.tgz", + "integrity": "sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -1010,11 +1191,15 @@ }, "node_modules/@sentry/node/node_modules/tslib": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true, "license": "0BSD" }, "node_modules/@sentry/tracing": { "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz", + "integrity": "sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==", "dev": true, "license": "MIT", "dependencies": { @@ -1030,11 +1215,15 @@ }, "node_modules/@sentry/tracing/node_modules/tslib": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true, "license": "0BSD" }, "node_modules/@sentry/types": { "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz", + "integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -1043,6 +1232,8 @@ }, "node_modules/@sentry/utils": { "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz", + "integrity": "sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -1055,16 +1246,22 @@ }, "node_modules/@sentry/utils/node_modules/tslib": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true, "license": "0BSD" }, "node_modules/@solidity-parser/parser": { - "version": "0.17.0", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.18.0.tgz", + "integrity": "sha512-yfORGUIPgLck41qyN7nbwJRAx17/jAIXCTanHOJZhB6PJ1iAk/84b/xlsVKFSyNyLXIj0dhppoE0+CRws7wlzA==", "dev": true, "license": "MIT" }, "node_modules/@types/bn.js": { "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", + "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", "dev": true, "license": "MIT", "dependencies": { @@ -1072,12 +1269,16 @@ } }, "node_modules/@types/chai": { - "version": "4.3.16", + "version": "4.3.19", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.19.tgz", + "integrity": "sha512-2hHHvQBVE2FiSK4eN0Br6snX9MtolHaTo/batnLjlGRhoQzlCL61iVpxoqO7SfFyOw+P/pwv+0zNHzKoGWz9Cw==", "dev": true, "license": "MIT" }, "node_modules/@types/chai-as-promised": { "version": "7.1.8", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.8.tgz", + "integrity": "sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw==", "dev": true, "license": "MIT", "dependencies": { @@ -1097,6 +1298,8 @@ }, "node_modules/@types/lru-cache": { "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==", "dev": true, "license": "MIT" }, @@ -1108,12 +1311,19 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "18.15.13", + "version": "22.5.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", + "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } }, "node_modules/@types/pbkdf2": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==", "dev": true, "license": "MIT", "dependencies": { @@ -1122,12 +1332,20 @@ }, "node_modules/@types/secp256k1": { "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.6.tgz", + "integrity": "sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" } }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "license": "BSD-2-Clause" + }, "node_modules/abbrev": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", @@ -1137,6 +1355,8 @@ }, "node_modules/adm-zip": { "version": "0.4.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", + "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", "dev": true, "license": "MIT", "engines": { @@ -1145,11 +1365,15 @@ }, "node_modules/aes-js": { "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", "dev": true, "license": "MIT" }, "node_modules/agent-base": { "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1161,6 +1385,8 @@ }, "node_modules/aggregate-error": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "license": "MIT", "dependencies": { @@ -1184,6 +1410,8 @@ }, "node_modules/ansi-align": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", "dev": true, "license": "ISC", "dependencies": { @@ -1192,6 +1420,8 @@ }, "node_modules/ansi-colors": { "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, "license": "MIT", "engines": { @@ -1200,6 +1430,8 @@ }, "node_modules/ansi-escapes": { "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1214,6 +1446,8 @@ }, "node_modules/ansi-regex": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, "license": "MIT", "engines": { @@ -1221,21 +1455,22 @@ } }, "node_modules/ansi-styles": { - "version": "4.3.0", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "color-convert": "^1.9.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=4" } }, "node_modules/anymatch": { "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "license": "ISC", "dependencies": { @@ -1248,6 +1483,8 @@ }, "node_modules/argparse": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, "license": "Python-2.0" }, @@ -1263,6 +1500,8 @@ }, "node_modules/assertion-error": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true, "license": "MIT", "engines": { @@ -1276,13 +1515,25 @@ "dev": true, "license": "MIT" }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", - "dev": true, + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, "node_modules/base-x": { - "version": "3.0.9", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.10.tgz", + "integrity": "sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1291,6 +1542,8 @@ }, "node_modules/binary-extensions": { "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "license": "MIT", "engines": { @@ -1302,16 +1555,22 @@ }, "node_modules/blakejs": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", + "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==", "dev": true, "license": "MIT" }, "node_modules/bn.js": { "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", "dev": true, "license": "MIT" }, "node_modules/boxen": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", + "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1331,8 +1590,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/boxen/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/boxen/node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { @@ -1346,8 +1623,30 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/boxen/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/boxen/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, "node_modules/boxen/node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", "engines": { @@ -1356,6 +1655,8 @@ }, "node_modules/boxen/node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { @@ -1367,6 +1668,8 @@ }, "node_modules/boxen/node_modules/type-fest": { "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -1378,7 +1681,8 @@ }, "node_modules/brace-expansion": { "version": "1.1.11", - "dev": true, + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -1389,7 +1693,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -1400,16 +1703,22 @@ }, "node_modules/brorand": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", "dev": true, "license": "MIT" }, "node_modules/browser-stdout": { "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true, "license": "ISC" }, "node_modules/browserify-aes": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "license": "MIT", "dependencies": { @@ -1423,6 +1732,8 @@ }, "node_modules/bs58": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", "dev": true, "license": "MIT", "dependencies": { @@ -1431,6 +1742,8 @@ }, "node_modules/bs58check": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", "dev": true, "license": "MIT", "dependencies": { @@ -1441,16 +1754,22 @@ }, "node_modules/buffer-from": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true, "license": "MIT" }, "node_modules/buffer-xor": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", "dev": true, "license": "MIT" }, "node_modules/bytes": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, "license": "MIT", "engines": { @@ -1459,6 +1778,8 @@ }, "node_modules/camelcase": { "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "license": "MIT", "engines": { @@ -1469,7 +1790,9 @@ } }, "node_modules/chai": { - "version": "4.4.1", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", "dev": true, "license": "MIT", "dependencies": { @@ -1479,25 +1802,29 @@ "get-func-name": "^2.0.2", "loupe": "^2.3.6", "pathval": "^1.1.1", - "type-detect": "^4.0.8" + "type-detect": "^4.1.0" }, "engines": { "node": ">=4" } }, "node_modules/chai-as-promised": { - "version": "7.1.1", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.2.tgz", + "integrity": "sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==", "dev": true, "license": "WTFPL", "dependencies": { "check-error": "^1.0.2" }, "peerDependencies": { - "chai": ">= 2.1.2 < 5" + "chai": ">= 2.1.2 < 6" } }, "node_modules/chalk": { "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1509,32 +1836,10 @@ "node": ">=4" } }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "3.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/color-convert": { - "version": "1.9.3", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/chalk/node_modules/color-name": { - "version": "1.1.3", - "dev": true, - "license": "MIT" - }, "node_modules/check-error": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", "dev": true, "license": "MIT", "dependencies": { @@ -1546,6 +1851,8 @@ }, "node_modules/chokidar": { "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "license": "MIT", "dependencies": { @@ -1569,11 +1876,14 @@ }, "node_modules/ci-info": { "version": "2.0.0", - "dev": true, + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", "license": "MIT" }, "node_modules/cipher-base": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1583,6 +1893,8 @@ }, "node_modules/clean-stack": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true, "license": "MIT", "engines": { @@ -1591,6 +1903,8 @@ }, "node_modules/cli-boxes": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", "dev": true, "license": "MIT", "engines": { @@ -1602,6 +1916,8 @@ }, "node_modules/cliui": { "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "license": "ISC", "dependencies": { @@ -1614,38 +1930,49 @@ } }, "node_modules/color-convert": { - "version": "2.0.1", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "license": "MIT", "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "color-name": "1.1.3" } }, "node_modules/color-name": { - "version": "1.1.4", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true, "license": "MIT" }, "node_modules/command-exists": { "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==", "dev": true, "license": "MIT" }, "node_modules/commander": { - "version": "3.0.2", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 12" + } }, "node_modules/concat-map": { "version": "0.0.1", - "dev": true, + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "license": "MIT" }, "node_modules/cookie": { "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", "dev": true, "license": "MIT", "engines": { @@ -1654,6 +1981,8 @@ }, "node_modules/create-hash": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "license": "MIT", "dependencies": { @@ -1666,6 +1995,8 @@ }, "node_modules/create-hmac": { "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "license": "MIT", "dependencies": { @@ -1677,6 +2008,31 @@ "sha.js": "^2.4.8" } }, + "node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "license": "MIT", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/cross-spawn/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/death": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/death/-/death-1.1.0.tgz", @@ -1684,11 +2040,13 @@ "dev": true }, "node_modules/debug": { - "version": "4.3.4", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1701,6 +2059,8 @@ }, "node_modules/decamelize": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true, "license": "MIT", "engines": { @@ -1711,7 +2071,9 @@ } }, "node_modules/deep-eql": { - "version": "4.1.3", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", "dev": true, "license": "MIT", "dependencies": { @@ -1730,6 +2092,8 @@ }, "node_modules/depd": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true, "license": "MIT", "engines": { @@ -1737,7 +2101,9 @@ } }, "node_modules/diff": { - "version": "5.0.0", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -1771,6 +2137,8 @@ }, "node_modules/elliptic": { "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1785,16 +2153,22 @@ }, "node_modules/elliptic/node_modules/bn.js": { "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true, "license": "MIT" }, "node_modules/emoji-regex": { "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, "license": "MIT" }, "node_modules/enquirer": { "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1807,6 +2181,8 @@ }, "node_modules/env-paths": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true, "license": "MIT", "engines": { @@ -1814,7 +2190,9 @@ } }, "node_modules/escalade": { - "version": "3.1.2", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "license": "MIT", "engines": { @@ -1823,6 +2201,8 @@ }, "node_modules/escape-string-regexp": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "license": "MIT", "engines": { @@ -1852,19 +2232,6 @@ "source-map": "~0.2.0" } }, - "node_modules/escodegen/node_modules/source-map": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", - "integrity": "sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA==", - "dev": true, - "optional": true, - "dependencies": { - "amdefine": ">=0.0.4" - }, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/esprima": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", @@ -1922,29 +2289,33 @@ } }, "node_modules/ethereum-cryptography": { - "version": "1.2.0", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", "dev": true, "license": "MIT", "dependencies": { - "@noble/hashes": "1.2.0", - "@noble/secp256k1": "1.7.1", - "@scure/bip32": "1.1.5", - "@scure/bip39": "1.1.1" + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" } }, - "node_modules/ethereum-cryptography/node_modules/@noble/hashes": { - "version": "1.2.0", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT" - }, "node_modules/ethereumjs-abi": { "version": "0.6.8", + "resolved": "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz", + "integrity": "sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==", "dev": true, "license": "MIT", "dependencies": { @@ -1952,13 +2323,27 @@ "ethereumjs-util": "^6.0.0" } }, + "node_modules/ethereumjs-abi/node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/ethereumjs-abi/node_modules/bn.js": { "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true, "license": "MIT" }, - "node_modules/ethereumjs-util": { + "node_modules/ethereumjs-abi/node_modules/ethereumjs-util": { "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -1971,43 +2356,27 @@ "rlp": "^2.2.3" } }, - "node_modules/ethereumjs-util/node_modules/@types/bn.js": { - "version": "4.11.6", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/ethereumjs-util/node_modules/bn.js": { - "version": "4.12.0", - "dev": true, - "license": "MIT" - }, - "node_modules/ethereumjs-util/node_modules/ethereum-cryptography": { - "version": "0.1.3", + "node_modules/ethereumjs-util": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", + "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", "dev": true, - "license": "MIT", + "license": "MPL-2.0", "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" + "@types/bn.js": "^5.1.0", + "bn.js": "^5.1.2", + "create-hash": "^1.1.2", + "ethereum-cryptography": "^0.1.3", + "rlp": "^2.2.4" + }, + "engines": { + "node": ">=10.0.0" } }, "node_modules/ethers": { - "version": "6.12.1", + "version": "6.13.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.13.2.tgz", + "integrity": "sha512-9VkriTTed+/27BGuY1s0hf441kqwHJ1wtN2edksEtiRvXx+soxRX3iSXTfFqq2+YwrOqbDoTHjIhQnjJRlzKmg==", "dev": true, "funding": [ { @@ -2027,12 +2396,19 @@ "@types/node": "18.15.13", "aes-js": "4.0.0-beta.5", "tslib": "2.4.0", - "ws": "8.5.0" + "ws": "8.17.1" }, "engines": { "node": ">=14.0.0" } }, + "node_modules/ethers/node_modules/@types/node": { + "version": "18.15.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz", + "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==", + "dev": true, + "license": "MIT" + }, "node_modules/ethjs-unit": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", @@ -2057,6 +2433,8 @@ }, "node_modules/ethjs-util": { "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", "dev": true, "license": "MIT", "dependencies": { @@ -2070,6 +2448,8 @@ }, "node_modules/evp_bytestokey": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", "dev": true, "license": "MIT", "dependencies": { @@ -2115,7 +2495,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -2126,6 +2505,8 @@ }, "node_modules/find-up": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2135,8 +2516,19 @@ "node": ">=4" } }, + "node_modules/find-yarn-workspace-root": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", + "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", + "license": "Apache-2.0", + "dependencies": { + "micromatch": "^4.0.2" + } + }, "node_modules/flat": { "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, "license": "BSD-3-Clause", "bin": { @@ -2144,7 +2536,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.6", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "dev": true, "funding": [ { @@ -2164,11 +2558,15 @@ }, "node_modules/fp-ts": { "version": "1.19.3", + "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-1.19.3.tgz", + "integrity": "sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==", "dev": true, "license": "MIT" }, "node_modules/fs-extra": { "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dev": true, "license": "MIT", "dependencies": { @@ -2182,12 +2580,16 @@ }, "node_modules/fs.realpath": { "version": "1.0.0", - "dev": true, + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "license": "ISC" }, "node_modules/fsevents": { "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, + "hasInstallScript": true, "license": "MIT", "optional": true, "os": [ @@ -2199,6 +2601,8 @@ }, "node_modules/get-caller-file": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, "license": "ISC", "engines": { @@ -2207,6 +2611,8 @@ }, "node_modules/get-func-name": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true, "license": "MIT", "engines": { @@ -2229,7 +2635,9 @@ }, "node_modules/glob": { "version": "7.2.0", - "dev": true, + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -2248,6 +2656,8 @@ }, "node_modules/glob-parent": { "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "license": "ISC", "dependencies": { @@ -2307,7 +2717,8 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", - "dev": true, + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, "node_modules/handlebars": { @@ -2332,14 +2743,26 @@ "uglify-js": "^3.1.4" } }, + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/hardhat": { - "version": "2.22.3", + "version": "2.22.10", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.22.10.tgz", + "integrity": "sha512-JRUDdiystjniAvBGFmJRsiIZSOP2/6s++8xRDe3TzLeQXlWWHsXBrd9wd3JWFyKXvgMqMeLL5Sz/oNxXKYw9vg==", "dev": true, "license": "MIT", "dependencies": { "@ethersproject/abi": "^5.1.2", "@metamask/eth-sig-util": "^4.0.0", - "@nomicfoundation/edr": "^0.3.5", + "@nomicfoundation/edr": "^0.5.2", "@nomicfoundation/ethereumjs-common": "4.0.4", "@nomicfoundation/ethereumjs-tx": "5.0.4", "@nomicfoundation/ethereumjs-util": "9.0.4", @@ -2373,7 +2796,7 @@ "raw-body": "^2.4.1", "resolve": "1.17.0", "semver": "^6.3.0", - "solc": "0.7.3", + "solc": "0.8.26", "source-map-support": "^0.5.13", "stacktrace-parser": "^0.1.10", "tsort": "0.0.1", @@ -2411,8 +2834,71 @@ "hardhat": "^2.3.0" } }, + "node_modules/hardhat/node_modules/@noble/hashes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", + "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/hardhat/node_modules/@scure/bip32": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz", + "integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.2.0", + "@noble/secp256k1": "~1.7.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/hardhat/node_modules/@scure/bip39": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz", + "integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.2.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/hardhat/node_modules/ethereum-cryptography": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz", + "integrity": "sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.2.0", + "@noble/secp256k1": "1.7.1", + "@scure/bip32": "1.1.5", + "@scure/bip39": "1.1.1" + } + }, "node_modules/hardhat/node_modules/ws": { - "version": "7.5.9", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "license": "MIT", "engines": { @@ -2433,6 +2919,8 @@ }, "node_modules/has-flag": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "license": "MIT", "engines": { @@ -2441,6 +2929,8 @@ }, "node_modules/hash-base": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", "dev": true, "license": "MIT", "dependencies": { @@ -2454,6 +2944,8 @@ }, "node_modules/hash.js": { "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", "dev": true, "license": "MIT", "dependencies": { @@ -2463,6 +2955,8 @@ }, "node_modules/he": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, "license": "MIT", "bin": { @@ -2478,6 +2972,8 @@ }, "node_modules/hmac-drbg": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", "dev": true, "license": "MIT", "dependencies": { @@ -2488,6 +2984,8 @@ }, "node_modules/http-errors": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2503,6 +3001,8 @@ }, "node_modules/https-proxy-agent": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, "license": "MIT", "dependencies": { @@ -2515,6 +3015,8 @@ }, "node_modules/iconv-lite": { "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "license": "MIT", "dependencies": { @@ -2535,12 +3037,16 @@ } }, "node_modules/immutable": { - "version": "4.3.5", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", "dev": true, "license": "MIT" }, "node_modules/indent-string": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, "license": "MIT", "engines": { @@ -2549,7 +3055,9 @@ }, "node_modules/inflight": { "version": "1.0.6", - "dev": true, + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "license": "ISC", "dependencies": { "once": "^1.3.0", @@ -2558,7 +3066,8 @@ }, "node_modules/inherits": { "version": "2.0.4", - "dev": true, + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, "node_modules/ini": { @@ -2580,6 +3089,8 @@ }, "node_modules/io-ts": { "version": "1.10.4", + "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-1.10.4.tgz", + "integrity": "sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==", "dev": true, "license": "MIT", "dependencies": { @@ -2588,6 +3099,8 @@ }, "node_modules/is-binary-path": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "license": "MIT", "dependencies": { @@ -2597,8 +3110,37 @@ "node": ">=8" } }, + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "license": "MIT", + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "license": "MIT", "engines": { @@ -2607,6 +3149,8 @@ }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "license": "MIT", "engines": { @@ -2615,6 +3159,8 @@ }, "node_modules/is-glob": { "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "license": "MIT", "dependencies": { @@ -2626,6 +3172,8 @@ }, "node_modules/is-hex-prefixed": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==", "dev": true, "license": "MIT", "engines": { @@ -2637,7 +3185,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -2645,6 +3192,8 @@ }, "node_modules/is-plain-obj": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true, "license": "MIT", "engines": { @@ -2653,6 +3202,8 @@ }, "node_modules/is-unicode-supported": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, "license": "MIT", "engines": { @@ -2662,20 +3213,35 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/js-sha3": { "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", "dev": true, "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "license": "MIT", "dependencies": { @@ -2687,6 +3253,8 @@ }, "node_modules/jsonfile": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, "license": "MIT", "optionalDependencies": { @@ -2705,6 +3273,8 @@ }, "node_modules/keccak": { "version": "3.0.4", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz", + "integrity": "sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -2727,12 +3297,13 @@ "node": ">=0.10.0" } }, - "node_modules/klaw": { - "version": "1.3.1", - "dev": true, + "node_modules/klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", "license": "MIT", - "optionalDependencies": { - "graceful-fs": "^4.1.9" + "dependencies": { + "graceful-fs": "^4.1.11" } }, "node_modules/levn": { @@ -2751,6 +3322,8 @@ }, "node_modules/locate-path": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", "dev": true, "license": "MIT", "dependencies": { @@ -2763,16 +3336,22 @@ }, "node_modules/lodash": { "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true, "license": "MIT" }, "node_modules/lodash.isequal": { "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", "dev": true, "license": "MIT" }, "node_modules/log-symbols": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "license": "MIT", "dependencies": { @@ -2786,8 +3365,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/log-symbols/node_modules/chalk": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { @@ -2801,8 +3398,30 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, "node_modules/log-symbols/node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", "engines": { @@ -2811,6 +3430,8 @@ }, "node_modules/log-symbols/node_modules/supports-color": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { @@ -2822,6 +3443,8 @@ }, "node_modules/loupe": { "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", "dev": true, "license": "MIT", "dependencies": { @@ -2830,11 +3453,15 @@ }, "node_modules/lru_map": { "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==", "dev": true, "license": "MIT" }, "node_modules/md5.js": { "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "dev": true, "license": "MIT", "dependencies": { @@ -2845,6 +3472,8 @@ }, "node_modules/memorystream": { "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", "dev": true, "engines": { "node": ">= 0.10.0" @@ -2871,7 +3500,6 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -2883,17 +3511,22 @@ }, "node_modules/minimalistic-assert": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", "dev": true, "license": "ISC" }, "node_modules/minimalistic-crypto-utils": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", "dev": true, "license": "MIT" }, "node_modules/minimatch": { "version": "3.1.2", - "dev": true, + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -2906,7 +3539,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2927,6 +3559,8 @@ }, "node_modules/mnemonist": { "version": "0.38.5", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz", + "integrity": "sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==", "dev": true, "license": "MIT", "dependencies": { @@ -2934,30 +3568,32 @@ } }, "node_modules/mocha": { - "version": "10.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "8.1.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", + "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" }, "bin": { "_mocha": "bin/_mocha", @@ -2967,50 +3603,20 @@ "node": ">= 14.0.0" } }, - "node_modules/mocha/node_modules/ansi-colors": { - "version": "4.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/mocha/node_modules/brace-expansion": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, - "node_modules/mocha/node_modules/chokidar": { - "version": "3.5.3", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, "node_modules/mocha/node_modules/cliui": { "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "license": "ISC", "dependencies": { @@ -3021,6 +3627,8 @@ }, "node_modules/mocha/node_modules/escape-string-regexp": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", "engines": { @@ -3032,6 +3640,8 @@ }, "node_modules/mocha/node_modules/find-up": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", "dependencies": { @@ -3047,6 +3657,9 @@ }, "node_modules/mocha/node_modules/glob": { "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", "dependencies": { @@ -3065,6 +3678,8 @@ }, "node_modules/mocha/node_modules/has-flag": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", "engines": { @@ -3073,6 +3688,8 @@ }, "node_modules/mocha/node_modules/locate-path": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", "dependencies": { @@ -3086,7 +3703,9 @@ } }, "node_modules/mocha/node_modules/minimatch": { - "version": "5.0.1", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "license": "ISC", "dependencies": { @@ -3096,13 +3715,10 @@ "node": ">=10" } }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "dev": true, - "license": "MIT" - }, "node_modules/mocha/node_modules/p-limit": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3117,6 +3733,8 @@ }, "node_modules/mocha/node_modules/p-locate": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", "dependencies": { @@ -3131,6 +3749,8 @@ }, "node_modules/mocha/node_modules/path-exists": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", "engines": { @@ -3139,6 +3759,8 @@ }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -3153,6 +3775,8 @@ }, "node_modules/mocha/node_modules/yargs": { "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "license": "MIT", "dependencies": { @@ -3168,16 +3792,10 @@ "node": ">=10" } }, - "node_modules/mocha/node_modules/yargs-parser": { - "version": "20.2.4", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, "node_modules/ms": { - "version": "2.1.2", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "license": "MIT" }, @@ -3188,8 +3806,16 @@ "dev": true, "license": "MIT" }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "license": "MIT" + }, "node_modules/node-addon-api": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==", "dev": true, "license": "MIT" }, @@ -3204,7 +3830,9 @@ } }, "node_modules/node-gyp-build": { - "version": "4.8.1", + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz", + "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==", "dev": true, "license": "MIT", "bin": { @@ -3228,6 +3856,8 @@ }, "node_modules/normalize-path": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, "license": "MIT", "engines": { @@ -3258,17 +3888,36 @@ }, "node_modules/obliterator": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", + "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==", "dev": true, "license": "MIT" }, "node_modules/once": { "version": "1.4.0", - "dev": true, + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "license": "ISC", "dependencies": { "wrappy": "1" } }, + "node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -3289,12 +3938,15 @@ }, "node_modules/ordinal": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ordinal/-/ordinal-1.0.3.tgz", + "integrity": "sha512-cMddMgb2QElm8G7vdaa02jhUNbTSrhsgAGUz1OokD83uJTwSUn+nKoNoKVVaRa08yF6sgfO7Maou1+bgLd9rdQ==", "dev": true, "license": "MIT" }, "node_modules/os-tmpdir": { "version": "1.0.2", - "dev": true, + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -3302,6 +3954,8 @@ }, "node_modules/p-limit": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", "dev": true, "license": "MIT", "dependencies": { @@ -3313,6 +3967,8 @@ }, "node_modules/p-locate": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", "dev": true, "license": "MIT", "dependencies": { @@ -3324,6 +3980,8 @@ }, "node_modules/p-map": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3338,14 +3996,171 @@ }, "node_modules/p-try": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", "dev": true, "license": "MIT", "engines": { "node": ">=4" } }, + "node_modules/patch-package": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-6.5.1.tgz", + "integrity": "sha512-I/4Zsalfhc6bphmJTlrLoOcAF87jcxko4q0qsv4bGcurbr8IskEOtdnt9iCmsQVGL1B+iUhSQqweyTLJfCF9rA==", + "license": "MIT", + "dependencies": { + "@yarnpkg/lockfile": "^1.1.0", + "chalk": "^4.1.2", + "cross-spawn": "^6.0.5", + "find-yarn-workspace-root": "^2.0.0", + "fs-extra": "^9.0.0", + "is-ci": "^2.0.0", + "klaw-sync": "^6.0.0", + "minimist": "^1.2.6", + "open": "^7.4.2", + "rimraf": "^2.6.3", + "semver": "^5.6.0", + "slash": "^2.0.0", + "tmp": "^0.0.33", + "yaml": "^1.10.2" + }, + "bin": { + "patch-package": "index.js" + }, + "engines": { + "node": ">=10", + "npm": ">5" + } + }, + "node_modules/patch-package/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/patch-package/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/patch-package/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/patch-package/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/patch-package/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/patch-package/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/patch-package/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/patch-package/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/patch-package/node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/patch-package/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/patch-package/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/path-exists": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", "dev": true, "license": "MIT", "engines": { @@ -3354,14 +4169,26 @@ }, "node_modules/path-is-absolute": { "version": "1.0.1", - "dev": true, + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/path-parse": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true, "license": "MIT" }, @@ -3377,6 +4204,8 @@ }, "node_modules/pathval": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true, "license": "MIT", "engines": { @@ -3385,6 +4214,8 @@ }, "node_modules/pbkdf2": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", "dev": true, "license": "MIT", "dependencies": { @@ -3400,7 +4231,8 @@ }, "node_modules/picomatch": { "version": "2.3.1", - "dev": true, + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "license": "MIT", "engines": { "node": ">=8.6" @@ -3429,7 +4261,9 @@ } }, "node_modules/prettier": { - "version": "3.2.5", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, "license": "MIT", "bin": { @@ -3443,13 +4277,14 @@ } }, "node_modules/prettier-plugin-solidity": { - "version": "1.3.1", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.4.1.tgz", + "integrity": "sha512-Mq8EtfacVZ/0+uDKTtHZGW3Aa7vEbX/BNx63hmVg6YTiTXSiuKP0amj0G6pGwjmLaOfymWh3QgXEZkjQbU8QRg==", "dev": true, "license": "MIT", "dependencies": { - "@solidity-parser/parser": "^0.17.0", - "semver": "^7.5.4", - "solidity-comments-extractor": "^0.0.8" + "@solidity-parser/parser": "^0.18.0", + "semver": "^7.5.4" }, "engines": { "node": ">=16" @@ -3459,7 +4294,9 @@ } }, "node_modules/prettier-plugin-solidity/node_modules/semver": { - "version": "7.6.2", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, "license": "ISC", "bin": { @@ -3492,6 +4329,8 @@ }, "node_modules/randombytes": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3500,6 +4339,8 @@ }, "node_modules/raw-body": { "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dev": true, "license": "MIT", "dependencies": { @@ -3514,6 +4355,8 @@ }, "node_modules/readable-stream": { "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "license": "MIT", "dependencies": { @@ -3527,6 +4370,8 @@ }, "node_modules/readdirp": { "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "license": "MIT", "dependencies": { @@ -3563,14 +4408,8 @@ }, "node_modules/require-directory": { "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "license": "MIT", "engines": { @@ -3579,6 +4418,8 @@ }, "node_modules/resolve": { "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", "dev": true, "license": "MIT", "dependencies": { @@ -3601,7 +4442,9 @@ }, "node_modules/rimraf": { "version": "2.7.1", - "dev": true, + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "license": "ISC", "dependencies": { "glob": "^7.1.3" @@ -3612,6 +4455,8 @@ }, "node_modules/ripemd160": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", "dev": true, "license": "MIT", "dependencies": { @@ -3621,6 +4466,8 @@ }, "node_modules/rlp": { "version": "2.2.7", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", + "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -3656,6 +4503,8 @@ }, "node_modules/safe-buffer": { "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, "funding": [ { @@ -3675,6 +4524,8 @@ }, "node_modules/safer-buffer": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "license": "MIT" }, @@ -3792,11 +4643,15 @@ }, "node_modules/scrypt-js": { "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", "dev": true, "license": "MIT" }, "node_modules/secp256k1": { "version": "4.0.3", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -3811,6 +4666,8 @@ }, "node_modules/semver": { "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { @@ -3818,7 +4675,9 @@ } }, "node_modules/serialize-javascript": { - "version": "6.0.0", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -3827,16 +4686,22 @@ }, "node_modules/setimmediate": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", "dev": true, "license": "MIT" }, "node_modules/setprototypeof": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "dev": true, "license": "ISC" }, "node_modules/sha.js": { "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "license": "(MIT AND BSD-3-Clause)", "dependencies": { @@ -3847,6 +4712,27 @@ "sha.js": "bin.js" } }, + "node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/shelljs": { "version": "0.8.5", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", @@ -3875,50 +4761,38 @@ "node": ">=8" } }, + "node_modules/solady": { + "version": "0.0.182", + "resolved": "https://registry.npmjs.org/solady/-/solady-0.0.182.tgz", + "integrity": "sha512-FW6xo1akJoYpkXMzu58/56FcNU3HYYNamEbnFO3iSibXk0nSHo0DV2Gu/zI3FPg3So5CCX6IYli1TT1IWATnvg==", + "license": "MIT" + }, "node_modules/solc": { - "version": "0.7.3", + "version": "0.8.26", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.26.tgz", + "integrity": "sha512-yiPQNVf5rBFHwN6SIf3TUUvVAFKcQqmSUFeq+fb6pNRCo0ZCgpYOZDi3BVoezCPIAcKrVYd/qXlBLUP9wVrZ9g==", "dev": true, "license": "MIT", "dependencies": { "command-exists": "^1.2.8", - "commander": "3.0.2", + "commander": "^8.1.0", "follow-redirects": "^1.12.1", - "fs-extra": "^0.30.0", "js-sha3": "0.8.0", "memorystream": "^0.3.1", - "require-from-string": "^2.0.0", "semver": "^5.5.0", "tmp": "0.0.33" }, "bin": { - "solcjs": "solcjs" + "solcjs": "solc.js" }, "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/solc/node_modules/fs-extra": { - "version": "0.30.0", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^2.1.0", - "klaw": "^1.0.0", - "path-is-absolute": "^1.0.0", - "rimraf": "^2.2.8" - } - }, - "node_modules/solc/node_modules/jsonfile": { - "version": "2.4.0", - "dev": true, - "license": "MIT", - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "node": ">=10.0.0" } }, "node_modules/solc/node_modules/semver": { "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "license": "ISC", "bin": { @@ -3926,14 +4800,9 @@ } }, "node_modules/solidity-ast": { - "version": "0.4.58", - "resolved": "https://registry.npmjs.org/solidity-ast/-/solidity-ast-0.4.58.tgz", - "integrity": "sha512-fiAEDlMEc+xziMn0IpZf2vUbqxyXYZK4BqBiTaz2ZUqOP0p1fdJzUc9xpv74Jdxb5BLAiCUFv5UenkXIpHn3cA==", - "dev": true, - "license": "MIT" - }, - "node_modules/solidity-comments-extractor": { - "version": "0.0.8", + "version": "0.4.59", + "resolved": "https://registry.npmjs.org/solidity-ast/-/solidity-ast-0.4.59.tgz", + "integrity": "sha512-I+CX0wrYUN9jDfYtcgWSe+OAowaXy8/1YQy7NS4ni5IBDmIYBq7ZzaP/7QqouLjzZapmQtvGLqCaYgoUWqBo5g==", "dev": true, "license": "MIT" }, @@ -3971,13 +4840,6 @@ "hardhat": "^2.11.0" } }, - "node_modules/solidity-coverage/node_modules/@solidity-parser/parser": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.18.0.tgz", - "integrity": "sha512-yfORGUIPgLck41qyN7nbwJRAx17/jAIXCTanHOJZhB6PJ1iAk/84b/xlsVKFSyNyLXIj0dhppoE0+CRws7wlzA==", - "dev": true, - "license": "MIT" - }, "node_modules/solidity-coverage/node_modules/fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -4007,15 +4869,22 @@ } }, "node_modules/source-map": { - "version": "0.6.1", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA==", "dev": true, - "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "amdefine": ">=0.0.4" + }, "engines": { - "node": ">=0.10.0" + "node": ">=0.8.0" } }, "node_modules/source-map-support": { "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "license": "MIT", "dependencies": { @@ -4023,6 +4892,16 @@ "source-map": "^0.6.0" } }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -4032,6 +4911,8 @@ }, "node_modules/stacktrace-parser": { "version": "0.1.10", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", + "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", "dev": true, "license": "MIT", "dependencies": { @@ -4043,6 +4924,8 @@ }, "node_modules/stacktrace-parser/node_modules/type-fest": { "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -4051,6 +4934,8 @@ }, "node_modules/statuses": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true, "license": "MIT", "engines": { @@ -4059,6 +4944,8 @@ }, "node_modules/string_decoder": { "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==", "dev": true, "license": "MIT", "dependencies": { @@ -4067,6 +4954,8 @@ }, "node_modules/string-width": { "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", "dependencies": { @@ -4080,6 +4969,8 @@ }, "node_modules/strip-ansi": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", "dependencies": { @@ -4091,6 +4982,8 @@ }, "node_modules/strip-hex-prefix": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", "dev": true, "license": "MIT", "dependencies": { @@ -4103,6 +4996,8 @@ }, "node_modules/strip-json-comments": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", "engines": { @@ -4114,6 +5009,8 @@ }, "node_modules/supports-color": { "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "license": "MIT", "dependencies": { @@ -4125,7 +5022,8 @@ }, "node_modules/tmp": { "version": "0.0.33", - "dev": true, + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "license": "MIT", "dependencies": { "os-tmpdir": "~1.0.2" @@ -4138,7 +5036,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -4149,6 +5046,8 @@ }, "node_modules/toidentifier": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true, "license": "MIT", "engines": { @@ -4157,21 +5056,29 @@ }, "node_modules/tslib": { "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", "dev": true, "license": "0BSD" }, "node_modules/tsort": { "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tsort/-/tsort-0.0.1.tgz", + "integrity": "sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==", "dev": true, "license": "MIT" }, "node_modules/tweetnacl": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", "dev": true, "license": "Unlicense" }, "node_modules/tweetnacl-util": { "version": "0.15.1", + "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", + "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==", "dev": true, "license": "Unlicense" }, @@ -4189,7 +5096,9 @@ } }, "node_modules/type-detect": { - "version": "4.0.8", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", "dev": true, "license": "MIT", "engines": { @@ -4198,6 +5107,8 @@ }, "node_modules/type-fest": { "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -4223,6 +5134,8 @@ }, "node_modules/undici": { "version": "5.28.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", + "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", "dev": true, "license": "MIT", "dependencies": { @@ -4232,8 +5145,17 @@ "node": ">=14.0" } }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + }, "node_modules/universalify": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, "license": "MIT", "engines": { @@ -4242,6 +5164,8 @@ }, "node_modules/unpipe": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true, "license": "MIT", "engines": { @@ -4257,11 +5181,15 @@ }, "node_modules/util-deprecate": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true, "license": "MIT" }, "node_modules/uuid": { "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, "license": "MIT", "bin": { @@ -4314,35 +5242,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/web3-utils/node_modules/@scure/bip32": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", - "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/curves": "~1.4.0", - "@noble/hashes": "~1.4.0", - "@scure/base": "~1.1.6" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/web3-utils/node_modules/@scure/bip39": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", - "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "~1.4.0", - "@scure/base": "~1.1.6" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/web3-utils/node_modules/ethereum-cryptography": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", @@ -4360,7 +5259,6 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -4371,6 +5269,8 @@ }, "node_modules/widest-line": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", "dev": true, "license": "MIT", "dependencies": { @@ -4398,12 +5298,16 @@ "license": "MIT" }, "node_modules/workerpool": { - "version": "6.2.1", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", "dev": true, "license": "Apache-2.0" }, "node_modules/wrap-ansi": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "license": "MIT", "dependencies": { @@ -4418,13 +5322,52 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, "node_modules/wrappy": { "version": "1.0.2", - "dev": true, + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, "node_modules/ws": { - "version": "8.5.0", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "dev": true, "license": "MIT", "engines": { @@ -4432,7 +5375,7 @@ }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -4445,14 +5388,27 @@ }, "node_modules/y18n": { "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, "license": "ISC", "engines": { "node": ">=10" } }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/yargs": { "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "license": "MIT", "dependencies": { @@ -4469,15 +5425,19 @@ } }, "node_modules/yargs-parser": { - "version": "21.1.1", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, "license": "ISC", "engines": { - "node": ">=12" + "node": ">=10" } }, "node_modules/yargs-unparser": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, "license": "MIT", "dependencies": { @@ -4490,8 +5450,20 @@ "node": ">=10" } }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index 61fb6055..e65aad66 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "zeppelin" ], "dependencies": { + "@arbitrum/nitro-contracts": "^2.1.0", "@axelar-network/axelar-gmp-sdk-solidity": "^5.10.0", "@openzeppelin/contracts": "^5.0.2", "@openzeppelin/contracts-upgradeable": "^5.0.2" From 3e6ea7e0a89b2c23b67e3ea062a33a04cf3a4897 Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Fri, 13 Sep 2024 13:52:22 -0300 Subject: [PATCH 40/41] add arbitrum l2 source --- .../crosschain/arbitrum/ArbitrumGateway.sol | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/contracts/crosschain/arbitrum/ArbitrumGateway.sol b/contracts/crosschain/arbitrum/ArbitrumGateway.sol index 8fe34e49..4079cd60 100644 --- a/contracts/crosschain/arbitrum/ArbitrumGateway.sol +++ b/contracts/crosschain/arbitrum/ArbitrumGateway.sol @@ -9,6 +9,9 @@ import {IGatewayDestination} from "../IGatewayDestination.sol"; import {IGatewayReceiver} from "../IGatewayReceiver.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +string constant CHAINID_ETH = "eip155:1"; +string constant CHAINID_ARB = "eip155:42161"; + function addressFromHexString(string memory hexString) pure returns (address) { return address(0); // TODO } @@ -17,7 +20,7 @@ interface IArbitrumGatewayL2Destination { function deliverMessage(address sender, address receiver, bytes calldata payload) external; } -abstract contract ArbitrumGatewayL1Source is IGatewaySource { +contract ArbitrumGatewayL1Source is IGatewaySource { IInbox private _inbox; // TODO address private _remoteGateway; @@ -37,7 +40,7 @@ abstract contract ArbitrumGatewayL1Source is IGatewaySource { bytes calldata payload, bytes[] calldata attributes ) external payable override returns (bytes32 outboxId) { - require(Strings.equal(destChain, "eip155:42161")); + require(Strings.equal(destChain, CHAINID_ARB)); require(attributes.length == 0); address receiver = addressFromHexString(destAccount); @@ -81,7 +84,7 @@ abstract contract ArbitrumGatewayL1Source is IGatewaySource { } } -abstract contract ArbitrumGatewayL1Destination is IGatewayDestination, IArbitrumGatewayL2Destination { +contract ArbitrumGatewayL1Destination is IGatewayDestination, IArbitrumGatewayL2Destination { using Strings for address; ArbSys private _arbSys; // TODO @@ -93,10 +96,41 @@ abstract contract ArbitrumGatewayL1Destination is IGatewayDestination, IArbitrum IGatewayReceiver(receiver).receiveMessage( 0, - "eip155:1", + CHAINID_ETH, sender.toHexString(), payload, new bytes[](0) ); } } + +contract ArbitrumGatewayL2Source is IGatewaySource { + using Strings for address; + + ArbSys private _arbSys; // TODO + address private _remoteGateway; + + function sendMessage( + string calldata destChain, + string calldata destAccount, + bytes calldata payload, + bytes[] calldata attributes + ) external payable override returns (bytes32) { + require(Strings.equal(destChain, CHAINID_ETH)); + require(attributes.length == 0); + + address receiver = addressFromHexString(destAccount); + + bytes memory receiveMessage = abi.encodeCall(IGatewayReceiver.receiveMessage, ( + 0, + CHAINID_ARB, + msg.sender.toHexString(), + payload, + new bytes[](0) + )); + + _arbSys.sendTxToL1(receiver, receiveMessage); + + return 0; + } +} From 191a2301dc048048792756f6fe2637e57fe07a0f Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Fri, 13 Sep 2024 13:53:23 -0300 Subject: [PATCH 41/41] lint --- .../crosschain/arbitrum/ArbitrumGateway.sol | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/contracts/crosschain/arbitrum/ArbitrumGateway.sol b/contracts/crosschain/arbitrum/ArbitrumGateway.sol index 4079cd60..a5c5e560 100644 --- a/contracts/crosschain/arbitrum/ArbitrumGateway.sol +++ b/contracts/crosschain/arbitrum/ArbitrumGateway.sol @@ -32,7 +32,7 @@ contract ArbitrumGatewayL1Source is IGatewaySource { } uint256 private _nextOutboxId; - mapping (bytes32 outboxId => PendingMessage) private _pending; + mapping(bytes32 outboxId => PendingMessage) private _pending; function sendMessage( string calldata destChain, @@ -94,13 +94,7 @@ contract ArbitrumGatewayL1Destination is IGatewayDestination, IArbitrumGatewayL2 require(_arbSys.wasMyCallersAddressAliased()); require(_arbSys.myCallersAddressWithoutAliasing() == _remoteGateway); - IGatewayReceiver(receiver).receiveMessage( - 0, - CHAINID_ETH, - sender.toHexString(), - payload, - new bytes[](0) - ); + IGatewayReceiver(receiver).receiveMessage(0, CHAINID_ETH, sender.toHexString(), payload, new bytes[](0)); } } @@ -121,13 +115,10 @@ contract ArbitrumGatewayL2Source is IGatewaySource { address receiver = addressFromHexString(destAccount); - bytes memory receiveMessage = abi.encodeCall(IGatewayReceiver.receiveMessage, ( - 0, - CHAINID_ARB, - msg.sender.toHexString(), - payload, - new bytes[](0) - )); + bytes memory receiveMessage = abi.encodeCall( + IGatewayReceiver.receiveMessage, + (0, CHAINID_ARB, msg.sender.toHexString(), payload, new bytes[](0)) + ); _arbSys.sendTxToL1(receiver, receiveMessage);