Axelar Executable

The Axelar Executable is a component of the Axelar General Message Passing (GMP) flow, allowing the execution of custom logic in response to messages from different blockchains. By simply inheriting from the Axelar Executable your contract can process and respond to incoming cross-chain GMP data.

For a plain GMP executable message you can inherit from the Axelar Executable contract.

  1. Import AxelarExecutable from the Axelar GMP SDK to enable cross-chain capabilities.

    import "@axelar-network/axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutable.sol";
  2. Inherit Axelar Executable Functions:

    contract MyContract is AxelarExecutable {}
  3. Implement the virtual functions defined in AxelarExecutable on your own contracts. The functions you implement on your own contract will be automatically triggered by an Axelar relayer on the destination chain once the multichain transaction arrives on the destination chain

If you are sending a GMP message with a token and need to handle the executable for a token + gmp msg then you will need to inherit from the Axelar Executable With Token contract.

  1. Import AxelarExecutableWithToken from the Axelar GMP SDK to enable cross-chain capabilities.

    import "@axelar-network/axelar-gmp-sdk-solidity/contracts/executable/AxelarExecutableWithToken.sol";
  2. Inherit Axelar AxelarExecutableWithToken Functions:

    contract MyContract is AxelarExecutableWithToken {}
  3. Implement the virtual functions defined in AxelarExecutableWithToken on your own contracts. The functions you implement on your own contract will be automatically triggered by an Axelar relayer on the destination chain once the multichain transaction arrives on the destination chain

Once you have inherited from the respective Executable contract you can now defined the execution function. This varies slightly depending on whether you are using an execution for a plain GMP message or a GMP message with a token.

/*** For GMP Message ***/
/**
@param commandId identifier tx that is guaranteed to be unique from the Axelar network
@param sourceChain The chain where the GMP message is sent from
@param sourceAddress The address on where the GMP msg is sent from
@param payload The GMP message being sent
**/
function _execute(bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes calldata payload) internal override {
// Implement your logic on the destination chain
string memory myGmpMessage = abi.decode(payload, (string));
}
/*** For GMP Message + Token ***/
/**
@param commandId identifier tx that is guaranteed to be unique from the Axelar network
@param sourceChain The chain where the GMP message is sent from
@param sourceAddress The address on where the GMP msg is sent from
@param payload The GMP message being sent
@param tokenSymbol The token being sent
@param amount The amount of the token being sent
**/
function _executeWithToken(bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes calldata payload, string calldata tokenSymbol, uint256 amount) internal override {
address memory receiver = abi.decode(payload, (address));
address tokenAddress = gateway.tokenAddresses(tokenSymbol);
IERC20(tokenAddress).transfer(receiver, amount);
}

The AxelarExecutable contract allows developers to override the _execute and the _executeWithToken() function on your own contract. However, the function that is actually triggered by the Axelar relayers are external execute() and executeWithToken() functions. Since these contracts are external with no relevant modifiers they can in theory be triggered by anyone. This can be a security vulnerability as your own contract’s implementation is dependent on the data being passed into the execute() function (which triggers in the internal _execute() function you’re overriding).

To solve this problem, AxelarExecutable triggers the Axelar Gateway’s validateContractCall() function. This function will validate that the incoming message was approved by the Axelar validators and return true if it was. It will then mark the message as a valid to ensure that it is not called more than once. Only once the message is marked as valid will your _execute() function be triggered.

By simply inheriting AxelarExecutable and overriding the _execute() function defined their you can be confident that the message you’re receiving is an authentic message from the Axelar network.

If your contract is upgradeable then you may be unable to inherit from AxelarExecutable as it implements a constructor . To get around this issue you can simply call the external execute() function (or executeWithToken()). By simply making the execute() function available the relayer will still trigger this function on your contract and it will run the same way the internal _execute() function would if you were overriding from AxelarExecutable. The catch is that the validateContractCall() function will not be automatically implemented the way it would be if you were inheriting from AxelarExecutable. To get around this problem simply make sure you implement the validateContractCall() functionality yourself in your execute() function to ensure the authenticity of the incoming message. Once implemented your execute() function should look like this

function execute(
bytes32 commandId,
string calldata sourceChain,
string calldata sourceAddress,
bytes calldata payload
) external {
bytes32 payloadHash = keccak256(payload);
if (
!gateway.validateContractCall(
commandId,
sourceChain,
sourceAddress,
payloadHash
)
) revert NotApprovedByGateway();
//The rest of your implementation
}

Edit on GitHub