Soroban Contracts

The core contracts for Axelar’s integration with Stellar Soroban can be found at the axelar-cgp-soroban repository.

The Rust based GMP contracts can be thought of similarly to their EVM counterparts.

At the core of there are two main contracts involved in sending a GMP message, these are the Gateway Contract and the Gas Service.

The Gateway facilitates sending and receiving of cross-chain messages to other chains via the Axelar Network.

For sending a GMP message, the callContract() function needs to be triggered.

The callContract function triggers your cross-chain message from Stellar to the Axelar Network. When sending a cross-chain message you must specify the destination of your cross-chain message and a given payload.

💡

Note: In Soroban, you don’t need to pass the env explicitly when writing contract functions; it is automatically provided to the contract entry points

The callContract function takes five parameters.

  1. env: Standard soroban env that provides access to the environment the contract is executing within.
  2. caller: The sender of the contract call.
  3. destination_chain: Name of the chain the message is being sent to.
  4. destination_address: Address on the destination chain the message is being sent to.
  5. payload: A bytes representation of the cross-chain message being sent.
pub fn call_contract(
env: Env,
caller: Address,
destination_chain: String,
destination_address: String,
payload: Bytes,
){}

The Gas Service handles cross-chain gas payment when making a GMP request.

When sending a GMP message before triggering the call_contract() function on the Gateway, the pay_gas() must be triggered first to pay for the cross-chain transaction.

The pay_gas() allows users to pay for the entirety of the cross-chain transaction in a given token.

The pay_gas() takes seven parameters.

  1. env: Standard soroban env that provides access to the environment the contract is executing within.
  2. sender: The sender of the cross-chain call to the Gateway.
  3. destination_chain: Name of the chain the message is being sent to.
  4. destination_address: Address on the destination chain the message is being sent to.
  5. payload: A bytes representation of the cross-chain message being sent.
  6. spender: The address spending the funds to cover the transaction.
  7. token: The token being used for payment

Note: The expected token is a struct type that requires an address and amount.

fn pay_gas(
env: Env,
sender: Address,
destination_chain: String,
destination_address: String,
payload: Bytes,
spender: Address,
token: Token,
metadata: Bytes,
) -> Result<(), ContractError>;

The Executable is a Rust Trait (not an individual contract) that will be used to execute a cross-chain call on a Soroban dapp when Stellar is the receiving chain of a cross-chain message.

The execute() function will be triggered by an Axelar relayer when the cross-chain message arrives. Your contract should implement the execute() function to handle the incoming cross-chain GMP data.

The execute() function takes five parameters.

  1. env: Standard soroban env that provides access to the environment the contract is executing within.
  2. source_chain: The source chain where the gmp message is coming from.
  3. message_id: Identifier for incoming GMP message.
  4. source_address: The address on the source chain where the gmp message is coming from/
  5. payload: A bytes representation of the cross-chain message being sent.
fn execute(
env: Env,
source_chain: String,
message_id: String,
source_address: String,
payload: Bytes,
);

The validate() function on the Executable will trigger the validate_message() that is defined on the Gateway.

The validate() function takes five parameters.

  1. env: Standard soroban env that provides access to the environment the contract is executing within.
  2. source_chain: The source chain where the gmp message is coming from.
  3. message_id: Identifier for incoming GMP message.
  4. source_address: The address on the source chain where the gmp message is coming from/
  5. payload: A bytes representation of the cross-chain message being sent.
fn validate(
env: Env,
source_chain: String,
message_id: String,
source_address: String,
payload: Bytes,
) {}

The validate() function will trigger the validate_message(), this will confirm that the incoming GMP message is an authenticated message that has been verified by the Amplifier Verifiers. Without ensuring that the message is validated malicious actors could in theory pass invalid data to be executed by this function, which is why it is critical to ensure that the data being passed in has been marked as approved on the Gateway

pub fn validate_message(
env: Env,
caller: Address,
source_chain: String,
message_id: String,
source_address: String,
payload_hash: BytesN<32>,
) -> bool {}
  1. env: Standard soroban env that provides access to the environment the contract is executing within.
  2. caller: The intended destination_address of the contract call.
  3. source_chain: The source chain where the gmp message is coming from.
  4. message_id: Identifier for incoming GMP message.
  5. source_address: The address on the source chain where the gmp message is coming from/
  6. payload: A bytes representation of the cross-chain message being sent.

Returns a bool indicating whether the incoming GMP message is valid or not.

Edit on GitHub