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}