From ef7044bac3833c32497786201627946a5ab7bd90 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Tue, 29 Oct 2024 03:23:52 -0400 Subject: [PATCH 1/3] refactor(express)!: split express executable with token functionality --- ...l => AxelarExpressExecutableWithToken.sol} | 52 ++++- .../express/AxelarValuedExpressExecutable.sol | 137 ++--------- ...AxelarValuedExpressExecutableWithToken.sol | 213 ++++++++++++++++++ contracts/express/ExpressExecutorTracker.sol | 12 +- .../interfaces/IAxelarExpressExecutable.sol | 82 +------ .../IAxelarExpressExecutableWithToken.sol | 89 ++++++++ .../IAxelarValuedExpressExecutable.sol | 18 -- ...AxelarValuedExpressExecutableWithToken.sol | 30 +++ .../express/TestAxelarExpressExecutable.sol | 6 +- .../TestAxelarValuedExpressExecutable.sol | 6 +- .../test/gmp/DestinationChainSwapExpress.sol | 6 +- contracts/test/gmp/ExecutableSample.sol | 6 +- contracts/test/gmp/ExpressExecutableTest.sol | 6 +- .../test/gmp/ValuedExpressExecutableTest.sol | 6 +- 14 files changed, 423 insertions(+), 246 deletions(-) rename contracts/express/{AxelarExpressExecutable.sol => AxelarExpressExecutableWithToken.sol} (72%) create mode 100644 contracts/express/AxelarValuedExpressExecutableWithToken.sol create mode 100644 contracts/interfaces/IAxelarExpressExecutableWithToken.sol create mode 100644 contracts/interfaces/IAxelarValuedExpressExecutableWithToken.sol diff --git a/contracts/express/AxelarExpressExecutable.sol b/contracts/express/AxelarExpressExecutableWithToken.sol similarity index 72% rename from contracts/express/AxelarExpressExecutable.sol rename to contracts/express/AxelarExpressExecutableWithToken.sol index d4b82f10..bbe0f044 100644 --- a/contracts/express/AxelarExpressExecutable.sol +++ b/contracts/express/AxelarExpressExecutableWithToken.sol @@ -6,11 +6,16 @@ import { AxelarExecutable } from '../executable/AxelarExecutable.sol'; import { AxelarExecutableWithToken } from '../executable/AxelarExecutableWithToken.sol'; import { IAxelarExecutable } from '../interfaces/IAxelarExecutable.sol'; import { IAxelarExecutableWithToken } from '../interfaces/IAxelarExecutableWithToken.sol'; +import { IAxelarExpressExecutableWithToken } from '../interfaces/IAxelarExpressExecutableWithToken.sol'; import { ExpressExecutorTracker } from './ExpressExecutorTracker.sol'; import { SafeTokenTransferFrom, SafeTokenTransfer } from '../libs/SafeTransfer.sol'; import { IERC20 } from '../interfaces/IERC20.sol'; -abstract contract AxelarExpressExecutable is ExpressExecutorTracker, AxelarExecutableWithToken { +abstract contract AxelarExpressExecutableWithToken is + IAxelarExpressExecutableWithToken, + ExpressExecutorTracker, + AxelarExecutableWithToken +{ using SafeTokenTransfer for IERC20; using SafeTokenTransferFrom for IERC20; @@ -141,4 +146,49 @@ abstract contract AxelarExpressExecutable is ExpressExecutorTracker, AxelarExecu _executeWithToken(commandId, sourceChain, sourceAddress, payload, symbol, amount); } + + /** + * @notice Returns the express executor for a given command. + * @param commandId The commandId for the contractCall. + * @param sourceChain The source chain. + * @param sourceAddress The source address. + * @param payloadHash The hash of the payload. + * @return expressExecutor The address of the express executor. + */ + function getExpressExecutor( + bytes32 commandId, + string calldata sourceChain, + string calldata sourceAddress, + bytes32 payloadHash + ) external view returns (address expressExecutor) { + expressExecutor = _getExpressExecutor(commandId, sourceChain, sourceAddress, payloadHash); + } + + /** + * @notice Returns the express executor with token for a given command. + * @param commandId The commandId for the contractCallWithToken. + * @param sourceChain The source chain. + * @param sourceAddress The source address. + * @param payloadHash The hash of the payload. + * @param symbol The token symbol. + * @param amount The amount of tokens. + * @return expressExecutor The address of the express executor. + */ + function getExpressExecutorWithToken( + bytes32 commandId, + string calldata sourceChain, + string calldata sourceAddress, + bytes32 payloadHash, + string calldata symbol, + uint256 amount + ) external view returns (address expressExecutor) { + expressExecutor = _getExpressExecutorWithToken( + commandId, + sourceChain, + sourceAddress, + payloadHash, + symbol, + amount + ); + } } diff --git a/contracts/express/AxelarValuedExpressExecutable.sol b/contracts/express/AxelarValuedExpressExecutable.sol index 7642a5f6..babe0cfa 100644 --- a/contracts/express/AxelarValuedExpressExecutable.sol +++ b/contracts/express/AxelarValuedExpressExecutable.sol @@ -3,9 +3,7 @@ pragma solidity ^0.8.0; import { AxelarExecutable } from '../executable/AxelarExecutable.sol'; -import { AxelarExecutableWithToken } from '../executable/AxelarExecutableWithToken.sol'; import { IAxelarExecutable } from '../interfaces/IAxelarExecutable.sol'; -import { IAxelarExecutableWithToken } from '../interfaces/IAxelarExecutableWithToken.sol'; import { IERC20 } from '../interfaces/IERC20.sol'; import { IAxelarValuedExpressExecutable } from '../interfaces/IAxelarValuedExpressExecutable.sol'; import { SafeTokenTransferFrom, SafeTokenTransfer } from '../libs/SafeTransfer.sol'; @@ -14,14 +12,14 @@ import { ExpressExecutorTracker } from './ExpressExecutorTracker.sol'; abstract contract AxelarValuedExpressExecutable is ExpressExecutorTracker, - AxelarExecutableWithToken, + AxelarExecutable, IAxelarValuedExpressExecutable { using SafeTokenTransfer for IERC20; using SafeTokenTransferFrom for IERC20; using SafeNativeTransfer for address payable; - constructor(address gateway_) AxelarExecutableWithToken(gateway_) {} + constructor(address gateway_) AxelarExecutable(gateway_) {} // Returns the amount of token that this call is worth. If `tokenAddress` is `0`, then value is in terms of the native token, otherwise it's in terms of the token address. function contractCallValue( @@ -30,16 +28,6 @@ abstract contract AxelarValuedExpressExecutable is bytes calldata payload ) public view virtual returns (address tokenAddress, uint256 value); - // Returns the amount of token that this call is worth. If `tokenAddress` is `0`, then value is in terms of the native token, otherwise it's in terms of the token address. - // The returned call value is in addition to the `amount` of token `symbol` being transferred with the call. - function contractCallWithTokenValue( - string calldata sourceChain, - string calldata sourceAddress, - bytes calldata payload, - string calldata symbol, - uint256 amount - ) public view virtual returns (address tokenAddress, uint256 value); - function execute( bytes32 commandId, string calldata sourceChain, @@ -67,68 +55,6 @@ abstract contract AxelarValuedExpressExecutable is } } - function executeWithToken( - bytes32 commandId, - string calldata sourceChain, - string calldata sourceAddress, - bytes calldata payload, - string calldata tokenSymbol, - uint256 amount - ) external override(AxelarExecutableWithToken, IAxelarExecutableWithToken) { - bytes32 payloadHash = keccak256(payload); - if ( - !gatewayWithToken().validateContractCallAndMint( - commandId, - sourceChain, - sourceAddress, - payloadHash, - tokenSymbol, - amount - ) - ) revert NotApprovedByGateway(); - - address expressExecutor = _popExpressExecutorWithToken( - commandId, - sourceChain, - sourceAddress, - payloadHash, - tokenSymbol, - amount - ); - - if (expressExecutor == address(0)) { - _executeWithToken(commandId, sourceChain, sourceAddress, payload, tokenSymbol, amount); - return; - } - - // slither-disable-next-line reentrancy-events - emit ExpressExecutionWithTokenFulfilled( - commandId, - sourceChain, - sourceAddress, - payloadHash, - tokenSymbol, - amount, - expressExecutor - ); - - { - (address tokenAddress, uint256 value) = contractCallWithTokenValue( - sourceChain, - sourceAddress, - payload, - tokenSymbol, - amount - ); - _transferToExecutor(expressExecutor, tokenAddress, value); - } - - { - address gatewayToken = gatewayWithToken().tokenAddresses(tokenSymbol); - IERC20(gatewayToken).safeTransfer(expressExecutor, amount); - } - } - function expressExecute( bytes32 commandId, string calldata sourceChain, @@ -152,56 +78,21 @@ abstract contract AxelarValuedExpressExecutable is _execute(commandId, sourceChain, sourceAddress, payload); } - function expressExecuteWithToken( + /** + * @notice Returns the express executor for a given command. + * @param commandId The commandId for the contractCall. + * @param sourceChain The source chain. + * @param sourceAddress The source address. + * @param payloadHash The hash of the payload. + * @return expressExecutor The address of the express executor. + */ + function getExpressExecutor( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, - bytes calldata payload, - string calldata symbol, - uint256 amount - ) external payable virtual { - if (gatewayWithToken().isCommandExecuted(commandId)) revert AlreadyExecuted(); - - address expressExecutor = msg.sender; - bytes32 payloadHash = keccak256(payload); - - emit ExpressExecutedWithToken( - commandId, - sourceChain, - sourceAddress, - payloadHash, - symbol, - amount, - expressExecutor - ); - - _setExpressExecutorWithToken( - commandId, - sourceChain, - sourceAddress, - payloadHash, - symbol, - amount, - expressExecutor - ); - - { - (address tokenAddress, uint256 value) = contractCallWithTokenValue( - sourceChain, - sourceAddress, - payload, - symbol, - amount - ); - _transferFromExecutor(expressExecutor, tokenAddress, value); - } - - { - address gatewayToken = gatewayWithToken().tokenAddresses(symbol); - IERC20(gatewayToken).safeTransferFrom(expressExecutor, address(this), amount); - } - - _executeWithToken(commandId, sourceChain, sourceAddress, payload, symbol, amount); + bytes32 payloadHash + ) external view returns (address expressExecutor) { + expressExecutor = _getExpressExecutor(commandId, sourceChain, sourceAddress, payloadHash); } function _transferToExecutor( diff --git a/contracts/express/AxelarValuedExpressExecutableWithToken.sol b/contracts/express/AxelarValuedExpressExecutableWithToken.sol new file mode 100644 index 00000000..47cb4f33 --- /dev/null +++ b/contracts/express/AxelarValuedExpressExecutableWithToken.sol @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import { AxelarValuedExpressExecutable } from './AxelarValuedExpressExecutable.sol'; +import { IAxelarGatewayWithToken } from '../interfaces/IAxelarGatewayWithToken.sol'; +import { IERC20 } from '../interfaces/IERC20.sol'; +import { IAxelarValuedExpressExecutable } from '../interfaces/IAxelarValuedExpressExecutable.sol'; +import { IAxelarValuedExpressExecutableWithToken } from '../interfaces/IAxelarValuedExpressExecutableWithToken.sol'; +import { SafeTokenTransferFrom, SafeTokenTransfer } from '../libs/SafeTransfer.sol'; +import { SafeNativeTransfer } from '../libs/SafeNativeTransfer.sol'; + +abstract contract AxelarValuedExpressExecutableWithToken is + AxelarValuedExpressExecutable, + IAxelarValuedExpressExecutableWithToken +{ + using SafeTokenTransfer for IERC20; + using SafeTokenTransferFrom for IERC20; + using SafeNativeTransfer for address payable; + + constructor(address gateway_) AxelarValuedExpressExecutable(gateway_) {} + + // Returns the amount of token that this call is worth. If `tokenAddress` is `0`, then value is in terms of the native token, otherwise it's in terms of the token address. + function contractCallValue( + string calldata sourceChain, + string calldata sourceAddress, + bytes calldata payload + ) + public + view + virtual + override(AxelarValuedExpressExecutable, IAxelarValuedExpressExecutable) + returns (address tokenAddress, uint256 value); + + // Returns the amount of token that this call is worth. If `tokenAddress` is `0`, then value is in terms of the native token, otherwise it's in terms of the token address. + // The returned call value is in addition to the `amount` of token `symbol` being transferred with the call. + function contractCallWithTokenValue( + string calldata sourceChain, + string calldata sourceAddress, + bytes calldata payload, + string calldata symbol, + uint256 amount + ) public view virtual returns (address tokenAddress, uint256 value); + + function executeWithToken( + bytes32 commandId, + string calldata sourceChain, + string calldata sourceAddress, + bytes calldata payload, + string calldata tokenSymbol, + uint256 amount + ) external override { + bytes32 payloadHash = keccak256(payload); + if ( + !gatewayWithToken().validateContractCallAndMint( + commandId, + sourceChain, + sourceAddress, + payloadHash, + tokenSymbol, + amount + ) + ) revert NotApprovedByGateway(); + + address expressExecutor = _popExpressExecutorWithToken( + commandId, + sourceChain, + sourceAddress, + payloadHash, + tokenSymbol, + amount + ); + + if (expressExecutor == address(0)) { + _executeWithToken(commandId, sourceChain, sourceAddress, payload, tokenSymbol, amount); + return; + } + + // slither-disable-next-line reentrancy-events + emit ExpressExecutionWithTokenFulfilled( + commandId, + sourceChain, + sourceAddress, + payloadHash, + tokenSymbol, + amount, + expressExecutor + ); + + { + (address tokenAddress, uint256 value) = contractCallWithTokenValue( + sourceChain, + sourceAddress, + payload, + tokenSymbol, + amount + ); + _transferToExecutor(expressExecutor, tokenAddress, value); + } + + { + address gatewayToken = gatewayWithToken().tokenAddresses(tokenSymbol); + IERC20(gatewayToken).safeTransfer(expressExecutor, amount); + } + } + + function expressExecuteWithToken( + bytes32 commandId, + string calldata sourceChain, + string calldata sourceAddress, + bytes calldata payload, + string calldata symbol, + uint256 amount + ) external payable virtual { + if (gatewayWithToken().isCommandExecuted(commandId)) revert AlreadyExecuted(); + + address expressExecutor = msg.sender; + bytes32 payloadHash = keccak256(payload); + + emit ExpressExecutedWithToken( + commandId, + sourceChain, + sourceAddress, + payloadHash, + symbol, + amount, + expressExecutor + ); + + _setExpressExecutorWithToken( + commandId, + sourceChain, + sourceAddress, + payloadHash, + symbol, + amount, + expressExecutor + ); + + { + (address tokenAddress, uint256 value) = contractCallWithTokenValue( + sourceChain, + sourceAddress, + payload, + symbol, + amount + ); + _transferFromExecutor(expressExecutor, tokenAddress, value); + } + + { + address gatewayToken = gatewayWithToken().tokenAddresses(symbol); + IERC20(gatewayToken).safeTransferFrom(expressExecutor, address(this), amount); + } + + _executeWithToken(commandId, sourceChain, sourceAddress, payload, symbol, amount); + } + + /** + * @notice Returns the express executor with token for a given command. + * @param commandId The commandId for the contractCallWithToken. + * @param sourceChain The source chain. + * @param sourceAddress The source address. + * @param payloadHash The hash of the payload. + * @param symbol The token symbol. + * @param amount The amount of tokens. + * @return expressExecutor The address of the express executor. + */ + function getExpressExecutorWithToken( + bytes32 commandId, + string calldata sourceChain, + string calldata sourceAddress, + bytes32 payloadHash, + string calldata symbol, + uint256 amount + ) external view returns (address expressExecutor) { + expressExecutor = _getExpressExecutorWithToken( + commandId, + sourceChain, + sourceAddress, + payloadHash, + symbol, + amount + ); + } + + /** + * @dev Internal virtual function to be overridden by child contracts to execute the command with token transfer. + * It allows child contracts to define their custom command execution logic involving tokens. + * @param commandId The unique identifier of the cross-chain message being executed. + * @param sourceChain The name of the source chain from which the message originated. + * @param sourceAddress The address on the source chain that sent the message. + * @param payload The payload of the message payload. + * @param tokenSymbol The symbol of the token to be transferred. + * @param amount The amount of tokens to be transferred. + */ + function _executeWithToken( + bytes32 commandId, + string calldata sourceChain, + string calldata sourceAddress, + bytes calldata payload, + string calldata tokenSymbol, + uint256 amount + ) internal virtual; + + /** + * @notice Returns the address of the IAxelarGatewayWithToken contract. + * @return The Axelar Gateway with Token instance. + */ + function gatewayWithToken() internal view returns (IAxelarGatewayWithToken) { + return IAxelarGatewayWithToken(gatewayAddress); + } +} diff --git a/contracts/express/ExpressExecutorTracker.sol b/contracts/express/ExpressExecutorTracker.sol index 91c296c0..2eabea62 100644 --- a/contracts/express/ExpressExecutorTracker.sol +++ b/contracts/express/ExpressExecutorTracker.sol @@ -2,9 +2,9 @@ pragma solidity ^0.8.0; -import { IAxelarExpressExecutable } from '../interfaces/IAxelarExpressExecutable.sol'; +abstract contract ExpressExecutorTracker { + error ExpressExecutorAlreadySet(); -abstract contract ExpressExecutorTracker is IAxelarExpressExecutable { bytes32 internal constant PREFIX_EXPRESS_EXECUTE = keccak256('express-execute'); bytes32 internal constant PREFIX_EXPRESS_EXECUTE_WITH_TOKEN = keccak256('express-execute-with-token'); @@ -38,12 +38,12 @@ abstract contract ExpressExecutorTracker is IAxelarExpressExecutable { ); } - function getExpressExecutor( + function _getExpressExecutor( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes32 payloadHash - ) external view returns (address expressExecutor) { + ) internal view returns (address expressExecutor) { bytes32 slot = _expressExecuteSlot(commandId, sourceChain, sourceAddress, payloadHash); assembly { @@ -51,14 +51,14 @@ abstract contract ExpressExecutorTracker is IAxelarExpressExecutable { } } - function getExpressExecutorWithToken( + function _getExpressExecutorWithToken( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes32 payloadHash, string calldata symbol, uint256 amount - ) external view returns (address expressExecutor) { + ) internal view returns (address expressExecutor) { bytes32 slot = _expressExecuteWithTokenSlot(commandId, sourceChain, sourceAddress, payloadHash, symbol, amount); assembly { diff --git a/contracts/interfaces/IAxelarExpressExecutable.sol b/contracts/interfaces/IAxelarExpressExecutable.sol index f81ecbb9..9020512c 100644 --- a/contracts/interfaces/IAxelarExpressExecutable.sol +++ b/contracts/interfaces/IAxelarExpressExecutable.sol @@ -2,17 +2,16 @@ pragma solidity ^0.8.0; -import { IAxelarExecutableWithToken } from './IAxelarExecutableWithToken.sol'; +import { IAxelarExecutable } from './IAxelarExecutable.sol'; /** * @title IAxelarExpressExecutable * @notice Interface for the Axelar Express Executable contract. */ -interface IAxelarExpressExecutable is IAxelarExecutableWithToken { +interface IAxelarExpressExecutable is IAxelarExecutable { // Custom errors error AlreadyExecuted(); error InsufficientValue(); - error ExpressExecutorAlreadySet(); /** * @notice Emitted when an express execution is successfully performed. @@ -30,26 +29,6 @@ interface IAxelarExpressExecutable is IAxelarExecutableWithToken { address indexed expressExecutor ); - /** - * @notice Emitted when an express execution with a token is successfully performed. - * @param commandId The unique identifier for the command. - * @param sourceChain The source chain. - * @param sourceAddress The source address. - * @param payloadHash The hash of the payload. - * @param symbol The token symbol. - * @param amount The amount of tokens. - * @param expressExecutor The address of the express executor. - */ - event ExpressExecutedWithToken( - bytes32 indexed commandId, - string sourceChain, - string sourceAddress, - bytes32 payloadHash, - string symbol, - uint256 indexed amount, - address indexed expressExecutor - ); - /** * @notice Emitted when an express execution is fulfilled. * @param commandId The commandId for the contractCall. @@ -66,26 +45,6 @@ interface IAxelarExpressExecutable is IAxelarExecutableWithToken { address indexed expressExecutor ); - /** - * @notice Emitted when an express execution with a token is fulfilled. - * @param commandId The commandId for the contractCallWithToken. - * @param sourceChain The source chain. - * @param sourceAddress The source address. - * @param payloadHash The hash of the payload. - * @param symbol The token symbol. - * @param amount The amount of tokens. - * @param expressExecutor The address of the express executor. - */ - event ExpressExecutionWithTokenFulfilled( - bytes32 indexed commandId, - string sourceChain, - string sourceAddress, - bytes32 payloadHash, - string symbol, - uint256 indexed amount, - address indexed expressExecutor - ); - /** * @notice Returns the express executor for a given command. * @param commandId The commandId for the contractCall. @@ -101,25 +60,6 @@ interface IAxelarExpressExecutable is IAxelarExecutableWithToken { bytes32 payloadHash ) external view returns (address expressExecutor); - /** - * @notice Returns the express executor with token for a given command. - * @param commandId The commandId for the contractCallWithToken. - * @param sourceChain The source chain. - * @param sourceAddress The source address. - * @param payloadHash The hash of the payload. - * @param symbol The token symbol. - * @param amount The amount of tokens. - * @return expressExecutor The address of the express executor. - */ - function getExpressExecutorWithToken( - bytes32 commandId, - string calldata sourceChain, - string calldata sourceAddress, - bytes32 payloadHash, - string calldata symbol, - uint256 amount - ) external view returns (address expressExecutor); - /** * @notice Express executes a contract call. * @param commandId The commandId for the contractCall. @@ -133,22 +73,4 @@ interface IAxelarExpressExecutable is IAxelarExecutableWithToken { string calldata sourceAddress, bytes calldata payload ) external payable; - - /** - * @notice Express executes a contract call with token. - * @param commandId The commandId for the contractCallWithToken. - * @param sourceChain The source chain. - * @param sourceAddress The source address. - * @param payload The payload data. - * @param symbol The token symbol. - * @param amount The amount of token. - */ - function expressExecuteWithToken( - bytes32 commandId, - string calldata sourceChain, - string calldata sourceAddress, - bytes calldata payload, - string calldata symbol, - uint256 amount - ) external payable; } diff --git a/contracts/interfaces/IAxelarExpressExecutableWithToken.sol b/contracts/interfaces/IAxelarExpressExecutableWithToken.sol new file mode 100644 index 00000000..b3e43b94 --- /dev/null +++ b/contracts/interfaces/IAxelarExpressExecutableWithToken.sol @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import { IAxelarExpressExecutable } from './IAxelarExpressExecutable.sol'; +import { IAxelarExecutableWithToken } from './IAxelarExecutableWithToken.sol'; + +/** + * @title IAxelarExpressExecutableWithToken + * @notice Interface for the Axelar Express Executable contract with token. + */ +interface IAxelarExpressExecutableWithToken is IAxelarExpressExecutable, IAxelarExecutableWithToken { + /** + * @notice Emitted when an express execution with a token is successfully performed. + * @param commandId The unique identifier for the command. + * @param sourceChain The source chain. + * @param sourceAddress The source address. + * @param payloadHash The hash of the payload. + * @param symbol The token symbol. + * @param amount The amount of tokens. + * @param expressExecutor The address of the express executor. + */ + event ExpressExecutedWithToken( + bytes32 indexed commandId, + string sourceChain, + string sourceAddress, + bytes32 payloadHash, + string symbol, + uint256 indexed amount, + address indexed expressExecutor + ); + + /** + * @notice Emitted when an express execution with a token is fulfilled. + * @param commandId The commandId for the contractCallWithToken. + * @param sourceChain The source chain. + * @param sourceAddress The source address. + * @param payloadHash The hash of the payload. + * @param symbol The token symbol. + * @param amount The amount of tokens. + * @param expressExecutor The address of the express executor. + */ + event ExpressExecutionWithTokenFulfilled( + bytes32 indexed commandId, + string sourceChain, + string sourceAddress, + bytes32 payloadHash, + string symbol, + uint256 indexed amount, + address indexed expressExecutor + ); + + /** + * @notice Returns the express executor with token for a given command. + * @param commandId The commandId for the contractCallWithToken. + * @param sourceChain The source chain. + * @param sourceAddress The source address. + * @param payloadHash The hash of the payload. + * @param symbol The token symbol. + * @param amount The amount of tokens. + * @return expressExecutor The address of the express executor. + */ + function getExpressExecutorWithToken( + bytes32 commandId, + string calldata sourceChain, + string calldata sourceAddress, + bytes32 payloadHash, + string calldata symbol, + uint256 amount + ) external view returns (address expressExecutor); + + /** + * @notice Express executes a contract call with token. + * @param commandId The commandId for the contractCallWithToken. + * @param sourceChain The source chain. + * @param sourceAddress The source address. + * @param payload The payload data. + * @param symbol The token symbol. + * @param amount The amount of token. + */ + function expressExecuteWithToken( + bytes32 commandId, + string calldata sourceChain, + string calldata sourceAddress, + bytes calldata payload, + string calldata symbol, + uint256 amount + ) external payable; +} diff --git a/contracts/interfaces/IAxelarValuedExpressExecutable.sol b/contracts/interfaces/IAxelarValuedExpressExecutable.sol index 89e5b535..c093b8e0 100644 --- a/contracts/interfaces/IAxelarValuedExpressExecutable.sol +++ b/contracts/interfaces/IAxelarValuedExpressExecutable.sol @@ -22,22 +22,4 @@ interface IAxelarValuedExpressExecutable is IAxelarExpressExecutable { string calldata sourceAddress, bytes calldata payload ) external view returns (address tokenAddress, uint256 value); - - /** - * @dev Returns the value (token address and amount) associated with a contract call with token. - * @param sourceChain The source blockchain. - * @param sourceAddress The source address. - * @param payload The payload data. - * @param symbol The token symbol. - * @param amount The amount of tokens. - * @return tokenAddress The address of the token used. - * @return value The value associated with the contract call. - */ - function contractCallWithTokenValue( - string calldata sourceChain, - string calldata sourceAddress, - bytes calldata payload, - string calldata symbol, - uint256 amount - ) external view returns (address tokenAddress, uint256 value); } diff --git a/contracts/interfaces/IAxelarValuedExpressExecutableWithToken.sol b/contracts/interfaces/IAxelarValuedExpressExecutableWithToken.sol new file mode 100644 index 00000000..d6eede1e --- /dev/null +++ b/contracts/interfaces/IAxelarValuedExpressExecutableWithToken.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import { IAxelarExpressExecutableWithToken } from './IAxelarExpressExecutableWithToken.sol'; +import { IAxelarValuedExpressExecutable } from './IAxelarValuedExpressExecutable.sol'; + +/** + * @title IAxelarValuedExpressExecutableWithToken + * @dev Interface for the Axelar Valued Express Executable With Token contract. + */ +interface IAxelarValuedExpressExecutableWithToken is IAxelarExpressExecutableWithToken, IAxelarValuedExpressExecutable { + /** + * @dev Returns the value (token address and amount) associated with a contract call with token. + * @param sourceChain The source blockchain. + * @param sourceAddress The source address. + * @param payload The payload data. + * @param symbol The token symbol. + * @param amount The amount of tokens. + * @return tokenAddress The address of the token used. + * @return value The value associated with the contract call. + */ + function contractCallWithTokenValue( + string calldata sourceChain, + string calldata sourceAddress, + bytes calldata payload, + string calldata symbol, + uint256 amount + ) external view returns (address tokenAddress, uint256 value); +} diff --git a/contracts/test/express/TestAxelarExpressExecutable.sol b/contracts/test/express/TestAxelarExpressExecutable.sol index 90e29576..071c90c9 100644 --- a/contracts/test/express/TestAxelarExpressExecutable.sol +++ b/contracts/test/express/TestAxelarExpressExecutable.sol @@ -2,10 +2,10 @@ pragma solidity ^0.8.0; -import { AxelarExpressExecutable } from '../../express/AxelarExpressExecutable.sol'; +import { AxelarExpressExecutableWithToken } from '../../express/AxelarExpressExecutableWithToken.sol'; -contract TestAxelarExpressExecutable is AxelarExpressExecutable { - constructor(address gateway_) AxelarExpressExecutable(gateway_) { +contract TestAxelarExpressExecutable is AxelarExpressExecutableWithToken { + constructor(address gateway_) AxelarExpressExecutableWithToken(gateway_) { if ( PREFIX_EXPRESS_EXECUTE != keccak256('express-execute') || PREFIX_EXPRESS_EXECUTE_WITH_TOKEN != keccak256('express-execute-with-token') diff --git a/contracts/test/express/TestAxelarValuedExpressExecutable.sol b/contracts/test/express/TestAxelarValuedExpressExecutable.sol index dbb69272..4809b842 100644 --- a/contracts/test/express/TestAxelarValuedExpressExecutable.sol +++ b/contracts/test/express/TestAxelarValuedExpressExecutable.sol @@ -2,10 +2,10 @@ pragma solidity ^0.8.0; -import { AxelarValuedExpressExecutable } from '../../express/AxelarValuedExpressExecutable.sol'; +import { AxelarValuedExpressExecutableWithToken } from '../../express/AxelarValuedExpressExecutableWithToken.sol'; -contract TestAxelarValuedExpressExecutable is AxelarValuedExpressExecutable { - constructor(address gateway_) AxelarValuedExpressExecutable(gateway_) {} +contract TestAxelarValuedExpressExecutable is AxelarValuedExpressExecutableWithToken { + constructor(address gateway_) AxelarValuedExpressExecutableWithToken(gateway_) {} function contractCallValue( string calldata, /* sourceChain */ diff --git a/contracts/test/gmp/DestinationChainSwapExpress.sol b/contracts/test/gmp/DestinationChainSwapExpress.sol index bf2c5870..f99228ba 100644 --- a/contracts/test/gmp/DestinationChainSwapExpress.sol +++ b/contracts/test/gmp/DestinationChainSwapExpress.sol @@ -2,11 +2,11 @@ pragma solidity ^0.8.0; -import { AxelarExpressExecutable } from '../../express/AxelarExpressExecutable.sol'; +import { AxelarExpressExecutableWithToken } from '../../express/AxelarExpressExecutableWithToken.sol'; import { IERC20 } from '../../interfaces/IERC20.sol'; import { DestinationChainTokenSwapper } from './DestinationChainTokenSwapper.sol'; -contract DestinationChainSwapExpress is AxelarExpressExecutable { +contract DestinationChainSwapExpress is AxelarExpressExecutableWithToken { DestinationChainTokenSwapper public immutable swapper; event Executed(bytes32 commandId, string sourceChain, string sourceAddress, bytes payload); @@ -19,7 +19,7 @@ contract DestinationChainSwapExpress is AxelarExpressExecutable { uint256 amount ); - constructor(address gatewayAddress, address swapperAddress) AxelarExpressExecutable(gatewayAddress) { + constructor(address gatewayAddress, address swapperAddress) AxelarExpressExecutableWithToken(gatewayAddress) { swapper = DestinationChainTokenSwapper(swapperAddress); } diff --git a/contracts/test/gmp/ExecutableSample.sol b/contracts/test/gmp/ExecutableSample.sol index 3f510864..a3b6968d 100644 --- a/contracts/test/gmp/ExecutableSample.sol +++ b/contracts/test/gmp/ExecutableSample.sol @@ -2,9 +2,9 @@ pragma solidity ^0.8.0; -import { AxelarExpressExecutable } from '../../express/AxelarExpressExecutable.sol'; +import { AxelarExpressExecutableWithToken } from '../../express/AxelarExpressExecutableWithToken.sol'; -contract ExecutableSample is AxelarExpressExecutable { +contract ExecutableSample is AxelarExpressExecutableWithToken { string public value; string public sourceChain; string public sourceAddress; @@ -19,7 +19,7 @@ contract ExecutableSample is AxelarExpressExecutable { uint256 amount ); - constructor(address gateway_) AxelarExpressExecutable(gateway_) {} + constructor(address gateway_) AxelarExpressExecutableWithToken(gateway_) {} // Call this function to update the value of this contract along with all its siblings'. function setRemoteValue( diff --git a/contracts/test/gmp/ExpressExecutableTest.sol b/contracts/test/gmp/ExpressExecutableTest.sol index 603eb9c2..6aca2ce8 100644 --- a/contracts/test/gmp/ExpressExecutableTest.sol +++ b/contracts/test/gmp/ExpressExecutableTest.sol @@ -2,9 +2,9 @@ pragma solidity ^0.8.0; -import { AxelarExpressExecutable } from '../../express/AxelarExpressExecutable.sol'; +import { AxelarExpressExecutableWithToken } from '../../express/AxelarExpressExecutableWithToken.sol'; -contract AxelarExpressExecutableTest is AxelarExpressExecutable { +contract AxelarExpressExecutableTest is AxelarExpressExecutableWithToken { event Executed(bytes32 commandId, string sourceChain, string sourceAddress, bytes payload); event ExecutedWithToken( bytes32 commandId, @@ -15,7 +15,7 @@ contract AxelarExpressExecutableTest is AxelarExpressExecutable { uint256 amount ); - constructor(address gateway_) AxelarExpressExecutable(gateway_) {} + constructor(address gateway_) AxelarExpressExecutableWithToken(gateway_) {} function _execute( bytes32 commandId, diff --git a/contracts/test/gmp/ValuedExpressExecutableTest.sol b/contracts/test/gmp/ValuedExpressExecutableTest.sol index a10e7000..8774b0cf 100644 --- a/contracts/test/gmp/ValuedExpressExecutableTest.sol +++ b/contracts/test/gmp/ValuedExpressExecutableTest.sol @@ -2,9 +2,9 @@ pragma solidity ^0.8.0; -import { AxelarValuedExpressExecutable } from '../../express/AxelarValuedExpressExecutable.sol'; +import { AxelarValuedExpressExecutableWithToken } from '../../express/AxelarValuedExpressExecutableWithToken.sol'; -contract AxelarValuedExpressExecutableTest is AxelarValuedExpressExecutable { +contract AxelarValuedExpressExecutableTest is AxelarValuedExpressExecutableWithToken { event Executed(bytes32 commandId, string sourceChain, string sourceAddress, bytes payload); event ExecutedWithToken( bytes32 commandId, @@ -19,7 +19,7 @@ contract AxelarValuedExpressExecutableTest is AxelarValuedExpressExecutable { uint256 public callWithTokenValue; address public expressToken; - constructor(address gateway_) AxelarValuedExpressExecutable(gateway_) {} + constructor(address gateway_) AxelarValuedExpressExecutableWithToken(gateway_) {} function setExpressToken(address expressToken_) external { expressToken = expressToken_; From 6e33132d03362e0a4a7988499d2ee7cb17eb6909 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Tue, 29 Oct 2024 03:49:37 -0400 Subject: [PATCH 2/3] changeset --- .changeset/empty-toes-draw.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/empty-toes-draw.md diff --git a/.changeset/empty-toes-draw.md b/.changeset/empty-toes-draw.md new file mode 100644 index 00000000..804ac7fd --- /dev/null +++ b/.changeset/empty-toes-draw.md @@ -0,0 +1,5 @@ +--- +'@axelar-network/axelar-gmp-sdk-solidity': patch +--- + +split express executable gateway token contracts From 9d02b3380ef2eb3a4384ebdf779b0bc0befd46a1 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Tue, 29 Oct 2024 17:51:36 -0400 Subject: [PATCH 3/3] address comments --- contracts/interfaces/IAxelarValuedExpressExecutable.sol | 2 +- .../interfaces/IAxelarValuedExpressExecutableWithToken.sol | 2 +- contracts/test/gmp/ValuedExpressExecutableTest.sol | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/interfaces/IAxelarValuedExpressExecutable.sol b/contracts/interfaces/IAxelarValuedExpressExecutable.sol index c093b8e0..3aee044e 100644 --- a/contracts/interfaces/IAxelarValuedExpressExecutable.sol +++ b/contracts/interfaces/IAxelarValuedExpressExecutable.sol @@ -11,7 +11,7 @@ import { IAxelarExpressExecutable } from './IAxelarExpressExecutable.sol'; interface IAxelarValuedExpressExecutable is IAxelarExpressExecutable { /** * @dev Returns the value (token address and amount) associated with a contract call - * @param sourceChain The source blockchain. + * @param sourceChain The source chain. * @param sourceAddress The source address. * @param payload The payload data. * @return tokenAddress The address of the token used. diff --git a/contracts/interfaces/IAxelarValuedExpressExecutableWithToken.sol b/contracts/interfaces/IAxelarValuedExpressExecutableWithToken.sol index d6eede1e..796204aa 100644 --- a/contracts/interfaces/IAxelarValuedExpressExecutableWithToken.sol +++ b/contracts/interfaces/IAxelarValuedExpressExecutableWithToken.sol @@ -12,7 +12,7 @@ import { IAxelarValuedExpressExecutable } from './IAxelarValuedExpressExecutable interface IAxelarValuedExpressExecutableWithToken is IAxelarExpressExecutableWithToken, IAxelarValuedExpressExecutable { /** * @dev Returns the value (token address and amount) associated with a contract call with token. - * @param sourceChain The source blockchain. + * @param sourceChain The source chain. * @param sourceAddress The source address. * @param payload The payload data. * @param symbol The token symbol. diff --git a/contracts/test/gmp/ValuedExpressExecutableTest.sol b/contracts/test/gmp/ValuedExpressExecutableTest.sol index 8774b0cf..4d19630b 100644 --- a/contracts/test/gmp/ValuedExpressExecutableTest.sol +++ b/contracts/test/gmp/ValuedExpressExecutableTest.sol @@ -29,7 +29,7 @@ contract AxelarValuedExpressExecutableTest is AxelarValuedExpressExecutableWithT callValue = callValue_; } - // Returns the amount of native token that that this call is worth. + // Returns the amount of token (corresponding to `tokenAddress`) that this call is worth. If `tokenAddress` is address(0), then amount is in terms of the native token. function contractCallValue( string calldata, /*sourceChain*/ string calldata, /*sourceAddress*/ @@ -39,7 +39,7 @@ contract AxelarValuedExpressExecutableTest is AxelarValuedExpressExecutableWithT tokenAddress = expressToken; } - // Returns the amount of token that that this call is worth. If `native` is true then native token is used, otherwise the token specified by `symbol` is used. + // Returns the amount of token (corresponding to `tokenAddress`) that this call is worth. If `tokenAddress` is address(0), then amount is in terms of the native token. function contractCallWithTokenValue( string calldata, /*sourceChain*/ string calldata, /*sourceAddress*/