stellar_axelar_example/
contract.rs

1use stellar_axelar_gas_service::AxelarGasServiceClient;
2use stellar_axelar_gateway::executable::{AxelarExecutableInterface, CustomAxelarExecutable};
3use stellar_axelar_gateway::AxelarGatewayMessagingClient;
4use stellar_axelar_std::events::Event;
5use stellar_axelar_std::types::Token;
6use stellar_axelar_std::{
7    contract, contracterror, contractimpl, ensure, soroban_sdk, token, Address, AxelarExecutable,
8    Bytes, BytesN, Env, InterchainTokenExecutable, String,
9};
10use stellar_interchain_token_service::executable::CustomInterchainTokenExecutable;
11use stellar_interchain_token_service::InterchainTokenServiceClient;
12
13use crate::event::{ExecutedEvent, TokenReceivedEvent, TokenSentEvent};
14use crate::interface::AxelarExampleInterface;
15use crate::storage;
16
17#[contract]
18#[derive(InterchainTokenExecutable, AxelarExecutable)]
19pub struct AxelarExample;
20
21#[contracterror]
22#[derive(Copy, Clone, Debug, Eq, PartialEq)]
23#[repr(u32)]
24pub enum AxelarExampleError {
25    NotApproved = 1,
26    InvalidItsAddress = 2,
27    InvalidAmount = 3,
28}
29
30impl CustomAxelarExecutable for AxelarExample {
31    type Error = AxelarExampleError;
32
33    fn __gateway(env: &Env) -> Address {
34        storage::gateway(env)
35    }
36
37    fn __execute(
38        env: &Env,
39        source_chain: String,
40        message_id: String,
41        source_address: String,
42        payload: Bytes,
43    ) -> Result<(), Self::Error> {
44        ExecutedEvent {
45            source_chain,
46            message_id,
47            source_address,
48            payload,
49        }
50        .emit(env);
51
52        Ok(())
53    }
54}
55
56impl CustomInterchainTokenExecutable for AxelarExample {
57    type Error = AxelarExampleError;
58
59    fn __interchain_token_service(env: &Env) -> Address {
60        storage::interchain_token_service(env)
61    }
62
63    fn __authorized_execute_with_token(
64        env: &Env,
65        source_chain: String,
66        message_id: String,
67        source_address: Bytes,
68        payload: Bytes,
69        token_id: BytesN<32>,
70        token_address: Address,
71        amount: i128,
72    ) -> Result<(), Self::Error> {
73        ensure!(amount >= 0, AxelarExampleError::InvalidAmount);
74
75        let destination_address = Address::from_string_bytes(&payload);
76
77        let token = token::TokenClient::new(env, &token_address);
78        token.transfer(
79            &env.current_contract_address(),
80            &destination_address,
81            &amount,
82        );
83
84        TokenReceivedEvent {
85            source_chain,
86            message_id,
87            source_address,
88            token_id,
89            token_address,
90            amount,
91            payload,
92        }
93        .emit(env);
94
95        Ok(())
96    }
97}
98
99#[contractimpl]
100impl AxelarExample {
101    pub fn __constructor(
102        env: &Env,
103        gateway: Address,
104        gas_service: Address,
105        interchain_token_service: Address,
106    ) {
107        storage::set_gateway(env, &gateway);
108        storage::set_gas_service(env, &gas_service);
109        storage::set_interchain_token_service(env, &interchain_token_service);
110    }
111}
112
113#[contractimpl]
114impl AxelarExampleInterface for AxelarExample {
115    fn gas_service(env: &Env) -> Address {
116        storage::gas_service(env)
117    }
118
119    fn send(
120        env: &Env,
121        caller: Address,
122        destination_chain: String,
123        destination_address: String,
124        message: Bytes,
125        gas_token: Option<Token>,
126    ) {
127        let gateway = AxelarGatewayMessagingClient::new(env, &Self::gateway(env));
128        let gas_service = AxelarGasServiceClient::new(env, &Self::gas_service(env));
129
130        caller.require_auth();
131
132        if let Some(gas_token) = gas_token {
133            gas_service.pay_gas(
134                &env.current_contract_address(),
135                &destination_chain,
136                &destination_address,
137                &message,
138                &caller,
139                &gas_token,
140                &Bytes::new(env),
141            );
142        }
143
144        gateway.call_contract(
145            &env.current_contract_address(),
146            &destination_chain,
147            &destination_address,
148            &message,
149        );
150    }
151
152    fn send_token(
153        env: &Env,
154        caller: Address,
155        token_id: BytesN<32>,
156        destination_chain: String,
157        destination_app_contract: Bytes,
158        amount: i128,
159        recipient: Option<Bytes>,
160        gas_token: Option<Token>,
161    ) -> Result<(), AxelarExampleError> {
162        caller.require_auth();
163
164        let client = InterchainTokenServiceClient::new(env, &Self::interchain_token_service(env));
165
166        client.interchain_transfer(
167            &caller,
168            &token_id,
169            &destination_chain,
170            &destination_app_contract,
171            &amount,
172            &recipient,
173            &gas_token,
174        );
175
176        TokenSentEvent {
177            sender: caller,
178            token_id,
179            destination_chain,
180            destination_app_contract,
181            amount,
182            recipient,
183        }
184        .emit(env);
185
186        Ok(())
187    }
188}