From 93291a198c25b50976a5c69e0f26cd8be4e148cc Mon Sep 17 00:00:00 2001 From: benjamin852 Date: Mon, 11 Nov 2024 19:04:04 +0700 Subject: [PATCH 1/8] initial soroban contracts doc --- .../soroban-gmp/gmp-message-example.mdx | 5 ++ .../soroban-gmp/its.mdx | 1 + .../soroban-gmp/soroban-contracts.mdx | 76 +++++++++++++++++++ src/layouts/navigation.ts | 19 ++++- 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 src/content/docs/dev/general-message-passing/soroban-gmp/gmp-message-example.mdx create mode 100644 src/content/docs/dev/general-message-passing/soroban-gmp/its.mdx create mode 100644 src/content/docs/dev/general-message-passing/soroban-gmp/soroban-contracts.mdx diff --git a/src/content/docs/dev/general-message-passing/soroban-gmp/gmp-message-example.mdx b/src/content/docs/dev/general-message-passing/soroban-gmp/gmp-message-example.mdx new file mode 100644 index 000000000..c5cc44b5b --- /dev/null +++ b/src/content/docs/dev/general-message-passing/soroban-gmp/gmp-message-example.mdx @@ -0,0 +1,5 @@ +# Soroban GMP Example + +## Soroban to EVM + +## EVM to Soroban \ No newline at end of file diff --git a/src/content/docs/dev/general-message-passing/soroban-gmp/its.mdx b/src/content/docs/dev/general-message-passing/soroban-gmp/its.mdx new file mode 100644 index 000000000..393f356cc --- /dev/null +++ b/src/content/docs/dev/general-message-passing/soroban-gmp/its.mdx @@ -0,0 +1 @@ +# Soroban ITS \ No newline at end of file diff --git a/src/content/docs/dev/general-message-passing/soroban-gmp/soroban-contracts.mdx b/src/content/docs/dev/general-message-passing/soroban-gmp/soroban-contracts.mdx new file mode 100644 index 000000000..26cdf121f --- /dev/null +++ b/src/content/docs/dev/general-message-passing/soroban-gmp/soroban-contracts.mdx @@ -0,0 +1,76 @@ +# Soroban Contracts + +The core contracts for Axelar's integration with Stellar Soroban can be found at the [axelar-cgp-soroban](https://github.com/axelarnetwork/axelar-cgp-soroban) repository. + +The Rust based GMP contracts can be thought of similarly to their [EVM](/dev/general-message-passing/gmp-messages/) counterparts. + +At the core of there are two main contracts involved in sending a GMP message, these are the [Gateway Contract](https://github.com/axelarnetwork/axelar-cgp-soroban/tree/main/contracts/axelar-gateway) and the [Gas Service](https://github.com/axelarnetwork/axelar-cgp-soroban/tree/main/contracts/axelar-gas-service). + +## Soroban Gateway +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. + +### CallContract +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. + +The `callContract` function takes five parameters. +1. `env`: Standard soroban [env](https://developers.stellar.org/docs/learn/encyclopedia/contract-development/environment-concepts) that provides access to the environment the contract is executing within. +1. `caller`: (optional) The sender of the contract call. Inferred as.... ******** TODO REVIEW************************* +1. `destination_chain`: Name of the chain the message is being sent to. +1. `destination_address`: Address on the destination chain the message is being sent to. +1. `payload`: A `bytes` representation of the cross-chain message being sent. + +```rust +pub fn call_contract( + env: Env, + caller: Address, + destination_chain: String, + destination_address: String, + payload: Bytes, +){} +``` + +## Soroban Gas Service +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_for_contract_call()` must be triggered first to pay for the cross-chain transaction. + +### PayGasForContractCall() + The `pay_gas_for_contract_call()` allows users to pay for the entirety of the cross-chain transaction in a given token. + + The `pay_gas_for_contract_call()` takes seven parameters. +1. `env`: Standard soroban [env](https://developers.stellar.org/docs/learn/encyclopedia/contract-development/environment-concepts) that provides access to the environment the contract is executing within. +1. `sender`: (optional) The sender of the gas payment call. Inferred as.... ****** TODO REVIEW ******** +1. `destination_chain`: Name of the chain the message is being sent to. +1. `destination_address`: Address on the destination chain the message is being sent to. +1. `payload`: A `bytes` representation of the cross-chain message being sent. +1. `refund_address`: An address to be refunded if gas amount was overpaid. +1. `token`: The token being used for payment + +Note: The expected [token](https://github.com/axelarnetwork/axelar-cgp-soroban/blob/main/packages/axelar-soroban-std/src/types.rs#L5) is a struct type that requires an `address` and `amount`. + +```rust + pub fn pay_gas_for_contract_call( + env: Env, + sender: Address, + destination_chain: String, + destination_address: String, + payload: Bytes, + refund_address: Address, + token: Token, + ) -> Result<(), ContractError> {} +``` + +## Soroban Executable +The [Executable](https://github.com/axelarnetwork/axelar-cgp-soroban/blob/main/contracts/axelar-gateway/src/executable.rs#L9) is a Rust Trait 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. + +### Execute +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`: +1. `source_chain` +1. `message_id` +1. `source_address`: +1. `payload`: \ No newline at end of file diff --git a/src/layouts/navigation.ts b/src/layouts/navigation.ts index 333e0b17d..a8e0672c3 100644 --- a/src/layouts/navigation.ts +++ b/src/layouts/navigation.ts @@ -116,7 +116,6 @@ export const getNavigation = (section) => { title: "Send Messages with Tokens", href: "/dev/general-message-passing/gmp-tokens-with-messages/", }, - { title: "Gas Service", children: [ @@ -232,6 +231,24 @@ export const getNavigation = (section) => { }, ], }, + { + title: "Stellar Soroban GMP", + children: [ + { + title: "Soroban Contracts", + href: "/dev/general-message-passing/soroban-gmp/soroban-contracts/" + }, + { + title: "GMP Example", + href: "/dev/general-message-passing/soroban-gmp/gmp-message-example/" + }, + { + title: "ITS", + href: "/dev/general-message-passing/soroban-gmp/its/" + } + ] + + }, { title: "Solidity Utilities", href: "/dev/solidity-utilities/", From 9557535f41c85a3db63f178d3da01de8eff83d73 Mon Sep 17 00:00:00 2001 From: benjamin852 Date: Thu, 14 Nov 2024 17:12:26 +0700 Subject: [PATCH 2/8] feat: executable soroban doc --- .../soroban-gmp/soroban-contracts.mdx | 84 +++++++++++++++---- 1 file changed, 68 insertions(+), 16 deletions(-) diff --git a/src/content/docs/dev/general-message-passing/soroban-gmp/soroban-contracts.mdx b/src/content/docs/dev/general-message-passing/soroban-gmp/soroban-contracts.mdx index 26cdf121f..76a1a46a9 100644 --- a/src/content/docs/dev/general-message-passing/soroban-gmp/soroban-contracts.mdx +++ b/src/content/docs/dev/general-message-passing/soroban-gmp/soroban-contracts.mdx @@ -41,7 +41,7 @@ When sending a GMP message before triggering the `call_contract()` function on t The `pay_gas_for_contract_call()` takes seven parameters. 1. `env`: Standard soroban [env](https://developers.stellar.org/docs/learn/encyclopedia/contract-development/environment-concepts) that provides access to the environment the contract is executing within. -1. `sender`: (optional) The sender of the gas payment call. Inferred as.... ****** TODO REVIEW ******** +1. `sender`: (optional) The sender of the gas payment call. 1. `destination_chain`: Name of the chain the message is being sent to. 1. `destination_address`: Address on the destination chain the message is being sent to. 1. `payload`: A `bytes` representation of the cross-chain message being sent. @@ -51,26 +51,78 @@ When sending a GMP message before triggering the `call_contract()` function on t Note: The expected [token](https://github.com/axelarnetwork/axelar-cgp-soroban/blob/main/packages/axelar-soroban-std/src/types.rs#L5) is a struct type that requires an `address` and `amount`. ```rust - pub fn pay_gas_for_contract_call( - env: Env, - sender: Address, - destination_chain: String, - destination_address: String, - payload: Bytes, - refund_address: Address, - token: Token, - ) -> Result<(), ContractError> {} +pub fn pay_gas_for_contract_call( + env: Env, + sender: Address, + destination_chain: String, + destination_address: String, + payload: Bytes, + refund_address: Address, + token: Token, +) -> Result<(), ContractError> {} ``` ## Soroban Executable -The [Executable](https://github.com/axelarnetwork/axelar-cgp-soroban/blob/main/contracts/axelar-gateway/src/executable.rs#L9) is a Rust Trait 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 [Executable](https://github.com/axelarnetwork/axelar-cgp-soroban/blob/main/contracts/axelar-gateway/src/executable.rs#L9) 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. ### Execute 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`: -1. `source_chain` -1. `message_id` -1. `source_address`: -1. `payload`: \ No newline at end of file +1. `env`: Standard soroban [env](https://developers.stellar.org/docs/learn/encyclopedia/contract-development/environment-concepts) that provides access to the environment the contract is executing within. +1. `source_chain`: The source chain where the gmp message is coming from. +1. `message_id`: Identifier for incoming GMP message. +1. `source_address`: The address on the source chain where the gmp message is coming from/ +1. `payload`: A `bytes` representation of the cross-chain message being sent. + +```rust +fn execute( + env: Env, + source_chain: String, + message_id: String, + source_address: String, + payload: Bytes, +); +``` + +### Validate & Validate Message +The `validate()` function on the `Executable` will trigger the `validate_message()` that is [defined](https://github.com/axelarnetwork/axelar-cgp-soroban/blob/main/contracts/axelar-gateway/src/contract.rs#L128) on the `Gateway`. + +The `validate()` function takes five parameters. +1. `env`: Standard soroban [env](https://developers.stellar.org/docs/learn/encyclopedia/contract-development/environment-concepts) that provides access to the environment the contract is executing within. +1. `source_chain`: The source chain where the gmp message is coming from. +1. `message_id`: Identifier for incoming GMP message. +1. `source_address`: The address on the source chain where the gmp message is coming from/ +1. `payload`: A `bytes` representation of the cross-chain message being sent. + +```rust + 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. + +```rust + 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](https://developers.stellar.org/docs/learn/encyclopedia/contract-development/environment-concepts) that provides access to the environment the contract is executing within. +1. `caller`: (optional) The sender of the contract call. +1. `source_chain`: The source chain where the gmp message is coming from. +1. `message_id`: Identifier for incoming GMP message. +1. `source_address`: The address on the source chain where the gmp message is coming from/ +1. `payload`: A `bytes` representation of the cross-chain message being sent. + +Returns a `bool` indicating whether the incoming GMP message is valid or not. \ No newline at end of file From b3e3cca5ade0a38410c09aa02bbd8e617d353c23 Mon Sep 17 00:00:00 2001 From: benjamin852 Date: Mon, 18 Nov 2024 10:54:38 +0200 Subject: [PATCH 3/8] chore: addressed team pr feedback --- .../soroban-gmp/soroban-contracts.mdx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/content/docs/dev/general-message-passing/soroban-gmp/soroban-contracts.mdx b/src/content/docs/dev/general-message-passing/soroban-gmp/soroban-contracts.mdx index 76a1a46a9..b238f25ba 100644 --- a/src/content/docs/dev/general-message-passing/soroban-gmp/soroban-contracts.mdx +++ b/src/content/docs/dev/general-message-passing/soroban-gmp/soroban-contracts.mdx @@ -1,3 +1,5 @@ +import { Callout } from "../../../../../components/callout"; + # Soroban Contracts The core contracts for Axelar's integration with Stellar Soroban can be found at the [axelar-cgp-soroban](https://github.com/axelarnetwork/axelar-cgp-soroban) repository. @@ -13,10 +15,14 @@ For sending a GMP message, the `callContract()` function needs to be triggered. ### CallContract 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](https://developers.stellar.org/docs/learn/encyclopedia/contract-development/environment-concepts) that provides access to the environment the contract is executing within. -1. `caller`: (optional) The sender of the contract call. Inferred as.... ******** TODO REVIEW************************* +1. `caller`: The sender of the contract call. 1. `destination_chain`: Name of the chain the message is being sent to. 1. `destination_address`: Address on the destination chain the message is being sent to. 1. `payload`: A `bytes` representation of the cross-chain message being sent. @@ -41,7 +47,7 @@ When sending a GMP message before triggering the `call_contract()` function on t The `pay_gas_for_contract_call()` takes seven parameters. 1. `env`: Standard soroban [env](https://developers.stellar.org/docs/learn/encyclopedia/contract-development/environment-concepts) that provides access to the environment the contract is executing within. -1. `sender`: (optional) The sender of the gas payment call. +1. `sender`: The sender of the gas payment call. 1. `destination_chain`: Name of the chain the message is being sent to. 1. `destination_address`: Address on the destination chain the message is being sent to. 1. `payload`: A `bytes` representation of the cross-chain message being sent. @@ -119,7 +125,7 @@ The `validate()` function will trigger the `validate_message()`, this will confi ``` 1. `env`: Standard soroban [env](https://developers.stellar.org/docs/learn/encyclopedia/contract-development/environment-concepts) that provides access to the environment the contract is executing within. -1. `caller`: (optional) The sender of the contract call. +1. `caller`: The intended `destination_address` of the contract call. 1. `source_chain`: The source chain where the gmp message is coming from. 1. `message_id`: Identifier for incoming GMP message. 1. `source_address`: The address on the source chain where the gmp message is coming from/ From b7028c4cb9fb468481e01063a6cb2732f95348b3 Mon Sep 17 00:00:00 2001 From: benjamin852 Date: Sun, 1 Dec 2024 20:12:47 -0500 Subject: [PATCH 4/8] chore: removed soroba its & ex --- .../soroban-gmp/gmp-message-example.mdx | 5 ----- .../docs/dev/general-message-passing/soroban-gmp/its.mdx | 1 - 2 files changed, 6 deletions(-) delete mode 100644 src/content/docs/dev/general-message-passing/soroban-gmp/gmp-message-example.mdx delete mode 100644 src/content/docs/dev/general-message-passing/soroban-gmp/its.mdx diff --git a/src/content/docs/dev/general-message-passing/soroban-gmp/gmp-message-example.mdx b/src/content/docs/dev/general-message-passing/soroban-gmp/gmp-message-example.mdx deleted file mode 100644 index c5cc44b5b..000000000 --- a/src/content/docs/dev/general-message-passing/soroban-gmp/gmp-message-example.mdx +++ /dev/null @@ -1,5 +0,0 @@ -# Soroban GMP Example - -## Soroban to EVM - -## EVM to Soroban \ No newline at end of file diff --git a/src/content/docs/dev/general-message-passing/soroban-gmp/its.mdx b/src/content/docs/dev/general-message-passing/soroban-gmp/its.mdx deleted file mode 100644 index 393f356cc..000000000 --- a/src/content/docs/dev/general-message-passing/soroban-gmp/its.mdx +++ /dev/null @@ -1 +0,0 @@ -# Soroban ITS \ No newline at end of file From 750eb04b83b773d2032af262742e11e78cfa7005 Mon Sep 17 00:00:00 2001 From: benjamin852 Date: Sun, 1 Dec 2024 22:04:08 -0500 Subject: [PATCH 5/8] feat: beginning example doc --- .../soroban-gmp/gmp-example.mdx | 64 +++++++++++++++++++ src/layouts/navigation.ts | 2 +- 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 src/content/docs/dev/general-message-passing/soroban-gmp/gmp-example.mdx diff --git a/src/content/docs/dev/general-message-passing/soroban-gmp/gmp-example.mdx b/src/content/docs/dev/general-message-passing/soroban-gmp/gmp-example.mdx new file mode 100644 index 000000000..2345d94df --- /dev/null +++ b/src/content/docs/dev/general-message-passing/soroban-gmp/gmp-example.mdx @@ -0,0 +1,64 @@ +# Stellar Soroban GMP Example + +Axelar [General Message Passing (GMP)](https://www.axelar.network/blog/general-message-passing-and-how-can-it-change-web3) allows for messages to be sent between two chains. Such messages can be used to power natively cross-chain decentralized applications. With the integration of Stellar to Axelar, Soroban based contracts can now send messages to other [blockchain ecosystems](https://docs.axelar.dev/dev/reference/mainnet-chain-names) connected to Axelar, including (but not limited to) EVM chains. + +## Setup + +To begin, make sure the [Stellar CLI](https://github.com/stellar/stellar-cli) is [setup](https://developers.stellar.org/docs/build/smart-contracts/getting-started/setup) on your local machine. + +Once the Stellar CLI is setup you can run `stellar contract init axelar-gmp`. This will create a new Stellar Soroban project. + +Now that you have a Stellar project you can run `cargo install axelar-cgp-soroban` to install the Axelar related functionality to be used in your contract. + +## Contract + +### Constructor + +In the [constructor](https://github.com/stellar/stellar-protocol/blob/5ef1b45cbab54f57c5ddfc98adb964f26b68bb3b/core/cap-0058.md) function you will need to pass in the Axelar [Gateway]("/dev/general-message-passing/soroban-gmp/soroban-contracts/#soroban-gateway") and [Gas Service]("/dev/general-message-passing/soroban-gmp/soroban-contracts/#soroban-gas-service") contracts. + +```rust +pub fn __constructor(env: Env, gateway: Address, gas_service: Address) {} +``` + +Note: In addition to the `gateway` and `gas service` you will also set an `env` param. This is an instance of the Env struct, which is a core part of the Soroban SDK. The Env struct provides access to various environment-specific functions and capabilities necessary for interacting with the contract's storage, managing resources, and executing contract logic within the Soroban runtime. + + + +### Storage + +The `gateway` and `gas service` contracts can be stored in the contract's [instance storage](https://developers.stellar.org/docs/learn/encyclopedia/storage/state-archival#instance). Before you can save the two parameters in storage, you must first define the storage variables. Create a new file called `storage_types.rs` + +This can be done as follows: + +```rust +use soroban_sdk::contracttype; + +#[contracttype] +#[derive(Clone, Debug)] +pub enum DataKey { + Gateway, + GasService, +} +``` + +The `storage_types.rs` file defines the DataKey enum, used as a typed storage key within the contract. + +`#[contracttype]`: Marks the enum for serialization and storage by the Soroban SDK. +Enum Variants: +Gateway: Represents the storage key for the gateway contract. +GasService: Represents the storage key for the gas service contract. + +With the storage now setup you can store your `gateway` and `gas serivce` contracts in storage from the `constructor`. The complete `constructor` should now look like this. + +```rust + pub fn __constructor(env: Env, gateway: Address, gas_service: Address) { + env.storage().instance().set(&DataKey::Gateway, &gateway); + env.storage() + .instance() + .set(&DataKey::GasService, &gas_service); + } +``` + + The constructor stores each param at a specific `key` in the `DataKey` `enum` you created before. + + \ No newline at end of file diff --git a/src/layouts/navigation.ts b/src/layouts/navigation.ts index a8e0672c3..c355a8030 100644 --- a/src/layouts/navigation.ts +++ b/src/layouts/navigation.ts @@ -240,7 +240,7 @@ export const getNavigation = (section) => { }, { title: "GMP Example", - href: "/dev/general-message-passing/soroban-gmp/gmp-message-example/" + href: "/dev/general-message-passing/soroban-gmp/gmp-example/" }, { title: "ITS", From 852640d7c065e662a0102646e2fe3b6cb975d568 Mon Sep 17 00:00:00 2001 From: benjamin852 Date: Wed, 4 Dec 2024 14:35:56 -0500 Subject: [PATCH 6/8] feat: wip sendMessage function --- .../soroban-gmp/gmp-example.mdx | 48 +++++++++++++++++-- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/src/content/docs/dev/general-message-passing/soroban-gmp/gmp-example.mdx b/src/content/docs/dev/general-message-passing/soroban-gmp/gmp-example.mdx index 2345d94df..db6c79fdf 100644 --- a/src/content/docs/dev/general-message-passing/soroban-gmp/gmp-example.mdx +++ b/src/content/docs/dev/general-message-passing/soroban-gmp/gmp-example.mdx @@ -22,8 +22,6 @@ pub fn __constructor(env: Env, gateway: Address, gas_service: Address) {} Note: In addition to the `gateway` and `gas service` you will also set an `env` param. This is an instance of the Env struct, which is a core part of the Soroban SDK. The Env struct provides access to various environment-specific functions and capabilities necessary for interacting with the contract's storage, managing resources, and executing contract logic within the Soroban runtime. - - ### Storage The `gateway` and `gas service` contracts can be stored in the contract's [instance storage](https://developers.stellar.org/docs/learn/encyclopedia/storage/state-archival#instance). Before you can save the two parameters in storage, you must first define the storage variables. Create a new file called `storage_types.rs` @@ -58,7 +56,47 @@ With the storage now setup you can store your `gateway` and `gas serivce` contra .set(&DataKey::GasService, &gas_service); } ``` - - The constructor stores each param at a specific `key` in the `DataKey` `enum` you created before. - \ No newline at end of file +The constructor stores each param at a specific `key` in the `DataKey` `enum` you created before. + +### Gas Service + +The `Gas Service` contract implements the [functionality](https://github.com/axelarnetwork/axelar-cgp-soroban/blob/main/contracts/axelar-gas-service/src/contract.rs) to pay for cross-chain transactions. + +```rust + pub fn gas_service(env: &Env) -> Address { + env.storage().instance().get(&DataKey::GasService).unwrap() + } +``` + +### Send Cross-Chain Message + +To send a cross-chain message you can define a new function called `send()`. It will take the following function signature + +```rust + pub fn send( + env: Env, + caller: Address, + destination_chain: String, + destination_address: String, + message: Bytes, + gas_token: Token, + ) {} +``` + +The function takes the following parameters + +1. `env`: The environment object, providing access to the blockchain runtime. +1. `caller`: The address of the account initiating the message send. +1. `destination_chain`: The name of the destination blockchain. +1. `destination_address`: The address on the destination blockchain the call is being sent to. +1. `message`: The message being sent to the destination chain. +1. `gas_token`: The token used to pay for gas fees. + + +Now you can access an instance for the `Gateway` and `GasService` contracts set in storage in the `constructor`. + +```rust + let gateway = AxelarGatewayMessagingClient::new(&env, &Self::gateway(&env)); + let gas_service = AxelarGasServiceClient::new(&env, &Self::gas_service(&env)); +``` \ No newline at end of file From 07cf21c19d13982346998d8653ee9beac57ccbec Mon Sep 17 00:00:00 2001 From: benjamin852 Date: Fri, 6 Dec 2024 17:36:20 -0500 Subject: [PATCH 7/8] chore: updated pay gas fn sig --- .../soroban-gmp/gmp-example.mdx | 112 +++++++++++++++++- .../soroban-gmp/soroban-contracts.mdx | 18 +-- 2 files changed, 119 insertions(+), 11 deletions(-) diff --git a/src/content/docs/dev/general-message-passing/soroban-gmp/gmp-example.mdx b/src/content/docs/dev/general-message-passing/soroban-gmp/gmp-example.mdx index db6c79fdf..0223bfe94 100644 --- a/src/content/docs/dev/general-message-passing/soroban-gmp/gmp-example.mdx +++ b/src/content/docs/dev/general-message-passing/soroban-gmp/gmp-example.mdx @@ -8,10 +8,56 @@ To begin, make sure the [Stellar CLI](https://github.com/stellar/stellar-cli) is Once the Stellar CLI is setup you can run `stellar contract init axelar-gmp`. This will create a new Stellar Soroban project. +### File Structure + Now that you have a Stellar project you can run `cargo install axelar-cgp-soroban` to install the Axelar related functionality to be used in your contract. +For simplicity, you can remove the root files and unnest the files in the `src` folder to be at the root of your project so that your file structures now look like this. + +```bash +Cargo.lock +Cargo.toml +Makefile +src +``` + +The files in the `src` folder can now be broken out into a `contract.rs` file, an `event.rs`, and `lib.rs`. + +### Integrate Axelar Soroban Dependency + +To integrate with the Axelar Network you will need to leverage the contracts in the [Axelar CGP Soroban](https://github.com/axelarnetwork/axelar-cgp-soroban) repository. Currently, this repo is not available on crates.io. To access it's functionality you can reference it as a [git dependency](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#specifying-dependencies-from-git-repositories). You can add a dependency for each subcrate in the CGP Soroban repo as follows: + +```toml +[dependencies] +axelar-gateway = { git = "https://github.com/axelarnetwork/axelar-cgp-soroban", subdir = "contracts/axelar-gateway", features = [ + 'library', +] } +axelar-gas-service = { git = "https://github.com/axelarnetwork/axelar-cgp-soroban", subdir = "contracts/axelar-gas-service", features = [ + 'library', +] } +axelar-soroban-std = { git = "https://github.com/axelarnetwork/axelar-cgp-soroban", subdir = "packages/axelar-soroban-std" } +``` + +## Lib + +The `lib.rs` file which contains the `hello_world` code can be moved to the `contract.rs` file. You can replace all that code with a module representation of the contract. Your `lib.rs` file should look like this: + +```rust +#![no_std] + +pub mod contract; +``` + ## Contract +You can rename your contract from the default name `Contract` to `AxelarGMP`. You can then begin writing out the `constructor`. Before writing functionality of the contract you can also make several key data types available from the `Soroban SDK` + +```rust +use soroban_sdk::{contract, contractimpl, Address, Env, String, Vec}; +``` + +These unique types will be used throughout the contract. + ### Constructor In the [constructor](https://github.com/stellar/stellar-protocol/blob/5ef1b45cbab54f57c5ddfc98adb964f26b68bb3b/core/cap-0058.md) function you will need to pass in the Axelar [Gateway]("/dev/general-message-passing/soroban-gmp/soroban-contracts/#soroban-gateway") and [Gas Service]("/dev/general-message-passing/soroban-gmp/soroban-contracts/#soroban-gas-service") contracts. @@ -46,6 +92,24 @@ Enum Variants: Gateway: Represents the storage key for the gateway contract. GasService: Represents the storage key for the gas service contract. +For this `enum` to be reachable you must: + +1. Define the `storage_types.rs` file in the `lib.rs` file, which at this point should look like this. + +```rust +#![no_std] + +pub mod contract; + +mod storage_types; +``` + +2. Make the file available to your `contracts.rs` repo + +```rust +use crate::storage_types::DataKey; +``` + With the storage now setup you can store your `gateway` and `gas serivce` contracts in storage from the `constructor`. The complete `constructor` should now look like this. ```rust @@ -69,6 +133,16 @@ The `Gas Service` contract implements the [functionality](https://github.com/axe } ``` +### Gateway + +The `Gateway` contract implements the [functionality](https://github.com/axelarnetwork/axelar-cgp-soroban/blob/main/contracts/axelar-gateway/src/contract.rs) to send cross-chain transactions. + +```rust + fn gateway(env: &Env) -> Address { + env.storage().instance().get(&DataKey::Gateway).unwrap() + } +``` + ### Send Cross-Chain Message To send a cross-chain message you can define a new function called `send()`. It will take the following function signature @@ -84,6 +158,24 @@ To send a cross-chain message you can define a new function called `send()`. It ) {} ``` +For the `token` type to be accepted you must make it available as follows: + +```rust +use axelar_soroban_std::types::Token; +``` + +You will also need the `GasService` and `Gateway` client types available for other functionality in this function. + +The entire list of imported data types should now be + +```rust +use axelar_gas_service::AxelarGasServiceClient; +use axelar_gateway::AxelarGatewayMessagingClient; +use crate::storage_types::DataKey; +use axelar_soroban_std::types::Token; +use soroban_sdk::{contract, contractimpl, Address, Bytes, Env, String}; // Import the gateway +``` + The function takes the following parameters 1. `env`: The environment object, providing access to the blockchain runtime. @@ -93,10 +185,24 @@ The function takes the following parameters 1. `message`: The message being sent to the destination chain. 1. `gas_token`: The token used to pay for gas fees. - -Now you can access an instance for the `Gateway` and `GasService` contracts set in storage in the `constructor`. +Now you can access an instance for the `Gateway` and `GasService` contracts set in storage in the `constructor`. ```rust let gateway = AxelarGatewayMessagingClient::new(&env, &Self::gateway(&env)); let gas_service = AxelarGasServiceClient::new(&env, &Self::gas_service(&env)); -``` \ No newline at end of file +``` + +Before, triggering the cross-chain transaction you must verify the authenticity of the caller's signature by calling [require_auth](https://developers.stellar.org/docs/build/smart-contracts/example-contracts/auth#require_auth) + +```rust +caller.require_auth(); +``` + +You can now move on to triggering the cross-chain message. This involves two steps. + +1. Pay the `GasService` +1. Trigger the cross-chain call on the `Gateway` + +The process of paying the `GasService` involves running the `pay_gas()` function, defined on the `GasService` contract. + +``` diff --git a/src/content/docs/dev/general-message-passing/soroban-gmp/soroban-contracts.mdx b/src/content/docs/dev/general-message-passing/soroban-gmp/soroban-contracts.mdx index b238f25ba..7c2447e10 100644 --- a/src/content/docs/dev/general-message-passing/soroban-gmp/soroban-contracts.mdx +++ b/src/content/docs/dev/general-message-passing/soroban-gmp/soroban-contracts.mdx @@ -40,32 +40,34 @@ pub fn call_contract( ## Soroban Gas Service 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_for_contract_call()` must be triggered first to pay for the cross-chain transaction. +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. ### PayGasForContractCall() - The `pay_gas_for_contract_call()` allows users to pay for the entirety of the cross-chain transaction in a given token. + The `pay_gas()` allows users to pay for the entirety of the cross-chain transaction in a given token. - The `pay_gas_for_contract_call()` takes seven parameters. + The `pay_gas()` takes seven parameters. 1. `env`: Standard soroban [env](https://developers.stellar.org/docs/learn/encyclopedia/contract-development/environment-concepts) that provides access to the environment the contract is executing within. -1. `sender`: The sender of the gas payment call. +1. `sender`: The sender of the cross-chain call to the Gateway. 1. `destination_chain`: Name of the chain the message is being sent to. 1. `destination_address`: Address on the destination chain the message is being sent to. 1. `payload`: A `bytes` representation of the cross-chain message being sent. -1. `refund_address`: An address to be refunded if gas amount was overpaid. +1. `spender`: The `address` spending the funds to cover the transaction. 1. `token`: The token being used for payment +1. `metadata`: Data sent along with the cross-chain tx Note: The expected [token](https://github.com/axelarnetwork/axelar-cgp-soroban/blob/main/packages/axelar-soroban-std/src/types.rs#L5) is a struct type that requires an `address` and `amount`. ```rust -pub fn pay_gas_for_contract_call( +fn pay_gas( env: Env, sender: Address, destination_chain: String, destination_address: String, payload: Bytes, - refund_address: Address, + spender: Address, token: Token, -) -> Result<(), ContractError> {} + metadata: Bytes, +) -> Result<(), ContractError>; ``` ## Soroban Executable From 533e147abdd1d60839f95d32d633111c9ac3a4a3 Mon Sep 17 00:00:00 2001 From: benjamin852 Date: Sun, 8 Dec 2024 22:11:28 -0500 Subject: [PATCH 8/8] feat: doc for codealong gmp example complete --- .../soroban-gmp/gmp-example.mdx | 215 +++++++++++++++++- .../soroban-gmp/soroban-contracts.mdx | 6 +- 2 files changed, 216 insertions(+), 5 deletions(-) diff --git a/src/content/docs/dev/general-message-passing/soroban-gmp/gmp-example.mdx b/src/content/docs/dev/general-message-passing/soroban-gmp/gmp-example.mdx index 0223bfe94..7c66439a8 100644 --- a/src/content/docs/dev/general-message-passing/soroban-gmp/gmp-example.mdx +++ b/src/content/docs/dev/general-message-passing/soroban-gmp/gmp-example.mdx @@ -53,7 +53,7 @@ pub mod contract; You can rename your contract from the default name `Contract` to `AxelarGMP`. You can then begin writing out the `constructor`. Before writing functionality of the contract you can also make several key data types available from the `Soroban SDK` ```rust -use soroban_sdk::{contract, contractimpl, Address, Env, String, Vec}; +use soroban_sdk::{contract, contractimpl, Address, Env, String, Vec}; ``` These unique types will be used throughout the contract. @@ -198,11 +198,222 @@ Before, triggering the cross-chain transaction you must verify the authenticity caller.require_auth(); ``` +#### Pay Gas + You can now move on to triggering the cross-chain message. This involves two steps. 1. Pay the `GasService` 1. Trigger the cross-chain call on the `Gateway` -The process of paying the `GasService` involves running the `pay_gas()` function, defined on the `GasService` contract. +The process of paying the `GasService` involves running the [pay_gas()](/dev/general-message-passing/soroban-gmp/soroban-contracts/#paygas) function. + +```rust + gas_service.pay_gas( + &env.current_contract_address(), + &destination_chain, + &destination_address, + &message, + &caller, + &gas_token, + &Bytes::new(&env), + ); +``` + +Once triggered, the gas that is paid to the `GasService` will be used to cover the cost of the relaying the transaction between the two chains, verifying the transaction on the Axelar Network, and gas costs of executing the transaction on the destination chain. + +The function takes seven arguments passed in as references: + +1. `&env.current_contract_address()`: The address of the sender +1. `&destination_chain`: The name of the destination chain +1. `&destination_address`: The receiving address of the cross-chain call on the destination chain +1. `&message`: The GMP message being sent to the destination chain +1. `&caller`: The address paying the gas cost +1. `&gas_token`: The token being used to cover the gas cost + {/* 1. `env`: The metadata <<<<< CONFIRM */} + +#### Call Contract + +With the transaction now paid for, the final step for sending a cross-chain message is to trigger the `callContract()` function on the `Gateway`. + +```rust + gateway.call_contract( + &env.current_contract_address(), + &destination_chain, + &destination_address, + &message, + ); +``` + +For this function you will pass in + +1. `&env.current_contract_address()`: The address of the +1. `&destination_chain`: The name of the destination chain +1. `&destination_address`: The receiving address of the cross-chain call on the destination chain +1. `&message`: The GMP message being sent to the destination chain + +Your completed `send()` function now should be as follows: + +```rust + pub fn send( + env: Env, + caller: Address, + destination_chain: String, + destination_address: String, + message: Bytes, + gas_token: Token, + ) { + let gateway = AxelarGatewayMessagingClient::new(&env, &Self::gateway(&env)); + let gas_service = AxelarGasServiceClient::new(&env, &Self::gas_service(&env)); + + caller.require_auth(); + + gas_service.pay_gas( + &env.current_contract_address(), + &destination_chain, + &destination_address, + &message, + &caller, + &gas_token, + &Bytes::new(&env), + ); + + gateway.call_contract( + &env.current_contract_address(), + &destination_chain, + &destination_address, + &message, + ); + } +``` + +### Receive Cross-Chain Message + +Now that you are able to _send_ a cross-chain call you still need to handle the logic to _receive_ a cross-chain call when it sent from another blockchain to your Soroban contract. + +When an inbound message is received, an Axelar Relayer will look to trigger the `execute()` function on your contract. To implement the `execute` functionality you can will implement the `AxelarExecutableInterface` [trait](https://doc.rust-lang.org/book/ch10-02-traits.html) to handle the `execute` functionality. + +Before defining your new trait you must first make the trait's dependency from the [Axelar-CGP-Soroban repo](https://github.com/axelarnetwork/axelar-cgp-soroban/blob/main/contracts/axelar-gateway/src/executable.rs#L16) available. + +```rust +use axelar_gateway::executable::AxelarExecutableInterface; +``` + +Now you can implement trait `AxelarExecutableInterface` trait. + +```rust +#[contractimpl] +impl AxelarExecutableInterface for AxelarGMP { + +} +``` + +At this point your editor will be complaining that your implementation is missing the trait's required functionality, `gateway()` and `execute()` functionality. + +To resolve the first issue you can move the `gateway()` getter that you defined in the previous section of the contract to your new implementation. + +To resolve the second issue you can paste in the signature for the `execute()` function as it's defined in the original trait in the dependency. + +```rust +fn execute( + env: Env, + source_chain: String, + message_id: String, + source_address: String, + payload: Bytes, +) {} +``` + +#### execute() + +With the `execute()` signature now defined you can begin to write out the logic that will be used to handle the message. + +The first thing you will want to do is to validate the incoming message. This can be done by triggering the third available function in the Axelar Trait, `validate_message()`. This function will confirm that the Gateway has received an approval from the Axelar verifier set that the message is in fact authentic. You pass it into `let _` to tell the compiler that you do not care about the result of this function, to avoid the unused result warning. + +```rust +let _ = Self::validate_message(&env, &source_chain, &message_id, &source_address, &payload); +``` + +#### event + +With your message now validated you can [emit an event](https://developers.stellar.org/docs/learn/encyclopedia/contract-development/events) to mark that your message was received at the destination chain. + +For maximum modularity you can define the event in a separate `event.rs` file. You can go ahead and make several Soroban SDK types available in the new file. + +```rust +use soroban_sdk::{Bytes, Env, String, Symbol}; +``` + +Next you can define a new function called `executed()` where you can implement the event emission. This function will accept the same five parameters as the `execute()` function in the `contract.rs` file. The first thing you will want to do in the new function is define the event [topics](https://developers.stellar.org/docs/build/smart-contracts/example-contracts/events#event-topics). Topics allow for filtering events, making it easier for off-chain applications to monitor specific changes within on-chain contracts. For your event's topics you can pass in the following: + +Note: The different names between the `executed()` function in the `event.rs` file with the `execute()` function in the `contract.rs` file. + +```rust + let topics = ( + Symbol::new(env, "executed"), + source_chain, + message_id, + source_address, + payload, + ); +``` + +These topics include +1. The name of the event, which is simply a [symbol type](https://developers.stellar.org/docs/learn/encyclopedia/contract-development/types/built-in-types#symbol-symbol) of `executed`. +1. The originating chain of the tx. +1. A unique message id for the cross-chain message. +1. The originating address of the cross-chain message. + +With the topics now set you can emit your event using the `publish` function. +```rust +env.events().publish(topics, (payload,)); +``` + +The `publish()` function requires the topics, which you previously defined as well as the `payload`, which contains the actual contents of the cross-chain message being emitted. + +Your completed `event.rs` file should be as follows + +```rust +use soroban_sdk::{Bytes, Env, String, Symbol}; + +pub fn executed( + env: &Env, + source_chain: String, + message_id: String, + source_address: String, + payload: Bytes, +) { + let topics = ( + Symbol::new(env, "executed"), + source_chain, + message_id, + source_address, + ); + + env.events().publish(topics, (payload,)); +} +``` + +The final thing you now must do is to make the code in the `event.rs` file as a `mod` in the `lib.rs` file. + +```rust +mod event; ``` + +With your event now defined you may return to your `contract.rs` file to trigger it in your `execute()` function. First you must make the code from `event.rs` available: + +```rust +use crate::event; +``` + +Now you can trigger the `executed()` function that will in turn trigger the `executed` event. + +```rust +event::executed(&env, source_chain, message_id, source_address, payload); +``` + +At this point once your message is received on the destination chain your `execute()` function will ensure that it has been marked as `approved` on the gateway, then it will emit the `executed()` event along with the unique parameters to your cross-chain transaction that was sent to this Soroban contract! + +## Testing + +Your `gmp-example` contract is now complete. If you run the command `stellar contract build`, your code should compile without any errors. \ No newline at end of file diff --git a/src/content/docs/dev/general-message-passing/soroban-gmp/soroban-contracts.mdx b/src/content/docs/dev/general-message-passing/soroban-gmp/soroban-contracts.mdx index 7c2447e10..ff875928f 100644 --- a/src/content/docs/dev/general-message-passing/soroban-gmp/soroban-contracts.mdx +++ b/src/content/docs/dev/general-message-passing/soroban-gmp/soroban-contracts.mdx @@ -42,7 +42,7 @@ 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. -### PayGasForContractCall() +### PayGas() 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. @@ -53,7 +53,7 @@ When sending a GMP message before triggering the `call_contract()` function on t 1. `payload`: A `bytes` representation of the cross-chain message being sent. 1. `spender`: The `address` spending the funds to cover the transaction. 1. `token`: The token being used for payment -1. `metadata`: Data sent along with the cross-chain tx +{/* 1. `metadata`: Data sent along with the cross-chain tx <<<<<< CONFIRM */} Note: The expected [token](https://github.com/axelarnetwork/axelar-cgp-soroban/blob/main/packages/axelar-soroban-std/src/types.rs#L5) is a struct type that requires an `address` and `amount`. @@ -113,7 +113,7 @@ The `validate()` function takes five parameters. ) {} ``` -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. +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 ```rust pub fn validate_message(