Skip to main content

stellar_interchain_token_service/
contract.rs

1use soroban_token_sdk::metadata::TokenMetadata;
2use stellar_axelar_gas_service::AxelarGasServiceClient;
3use stellar_axelar_gateway::executable::{AxelarExecutableInterface, CustomAxelarExecutable};
4use stellar_axelar_gateway::AxelarGatewayMessagingClient;
5use stellar_axelar_std::address::AddressExt;
6use stellar_axelar_std::events::Event;
7use stellar_axelar_std::token::StellarAssetClient;
8use stellar_axelar_std::types::Token;
9use stellar_axelar_std::xdr::ToXdr;
10use stellar_axelar_std::{
11    contract, contractimpl, ensure, interfaces, only_operator, only_owner, soroban_sdk, vec,
12    when_not_paused, Address, AxelarExecutable, Bytes, BytesN, Env, IntoVal, Operatable, Ownable,
13    Pausable, String, Symbol, Upgradable, Val,
14};
15use stellar_token_manager::TokenManagerClient;
16use token_id::UnregisteredTokenId;
17
18use crate::error::ContractError;
19use crate::event::{
20    InterchainTokenDeploymentStartedEvent, InterchainTransferReceivedEvent,
21    InterchainTransferSentEvent, LinkTokenReceivedEvent, LinkTokenStartedEvent,
22    TokenMetadataRegisteredEvent, TrustedChainRemovedEvent, TrustedChainSetEvent,
23};
24use crate::flow_limit::FlowDirection;
25use crate::interface::InterchainTokenServiceInterface;
26use crate::storage::{self, TokenIdConfigValue};
27use crate::token_manager::TokenManagerClientExt;
28use crate::token_metadata::TokenMetadataExt;
29use crate::types::{
30    DeployInterchainToken, HubMessage, InterchainTransfer, LinkToken, Message,
31    RegisterTokenMetadata, TokenManagerType,
32};
33use crate::{deployer, flow_limit, token_handler, token_id, token_metadata};
34
35const ITS_HUB_CHAIN_NAME: &str = "axelar";
36const EXECUTE_WITH_INTERCHAIN_TOKEN: &str = "execute_with_interchain_token";
37
38#[contract]
39#[derive(Operatable, Ownable, Pausable, Upgradable, AxelarExecutable)]
40pub struct InterchainTokenService;
41
42#[contractimpl]
43impl InterchainTokenService {
44    pub fn __constructor(
45        env: Env,
46        owner: Address,
47        operator: Address,
48        gateway: Address,
49        gas_service: Address,
50        its_hub_address: String,
51        chain_name: String,
52        native_token_address: Address,
53        interchain_token_wasm_hash: BytesN<32>,
54        token_manager_wasm_hash: BytesN<32>,
55    ) {
56        interfaces::set_owner(&env, &owner);
57        interfaces::set_operator(&env, &operator);
58        storage::set_gateway(&env, &gateway);
59        storage::set_gas_service(&env, &gas_service);
60        storage::set_its_hub_address(&env, &its_hub_address);
61        storage::set_chain_name(&env, &chain_name);
62        storage::set_native_token_address(&env, &native_token_address);
63        storage::set_interchain_token_wasm_hash(&env, &interchain_token_wasm_hash);
64        storage::set_token_manager_wasm_hash(&env, &token_manager_wasm_hash);
65    }
66}
67
68#[contractimpl]
69impl InterchainTokenServiceInterface for InterchainTokenService {
70    fn gas_service(env: &Env) -> Address {
71        storage::gas_service(env)
72    }
73
74    fn chain_name(env: &Env) -> String {
75        storage::chain_name(env)
76    }
77
78    fn its_hub_chain_name(env: &Env) -> String {
79        String::from_str(env, ITS_HUB_CHAIN_NAME)
80    }
81
82    fn its_hub_address(env: &Env) -> String {
83        storage::its_hub_address(env)
84    }
85
86    fn native_token_address(env: &Env) -> Address {
87        storage::native_token_address(env)
88    }
89
90    fn interchain_token_wasm_hash(env: &Env) -> BytesN<32> {
91        storage::interchain_token_wasm_hash(env)
92    }
93
94    fn token_manager_wasm_hash(env: &Env) -> BytesN<32> {
95        storage::token_manager_wasm_hash(env)
96    }
97
98    fn is_trusted_chain(env: &Env, chain: String) -> bool {
99        storage::is_trusted_chain(env, chain)
100    }
101
102    #[only_operator]
103    fn set_trusted_chain(env: &Env, chain: String) -> Result<(), ContractError> {
104        ensure!(
105            !storage::is_trusted_chain(env, chain.clone()),
106            ContractError::TrustedChainAlreadySet
107        );
108
109        storage::set_trusted_chain_status(env, chain.clone());
110
111        TrustedChainSetEvent { chain }.emit(env);
112
113        Ok(())
114    }
115
116    #[only_operator]
117    fn remove_trusted_chain(env: &Env, chain: String) -> Result<(), ContractError> {
118        ensure!(
119            storage::is_trusted_chain(env, chain.clone()),
120            ContractError::TrustedChainNotSet
121        );
122
123        storage::remove_trusted_chain_status(env, chain.clone());
124
125        TrustedChainRemovedEvent { chain }.emit(env);
126
127        Ok(())
128    }
129
130    fn interchain_token_id(env: &Env, deployer: Address, salt: BytesN<32>) -> BytesN<32> {
131        token_id::interchain_token_id(env, Self::chain_name_hash(env), deployer, salt)
132    }
133
134    fn canonical_interchain_token_id(env: &Env, token_address: Address) -> BytesN<32> {
135        token_id::canonical_interchain_token_id(env, Self::chain_name_hash(env), token_address)
136    }
137
138    fn linked_token_id(env: &Env, deployer: Address, salt: BytesN<32>) -> BytesN<32> {
139        token_id::linked_token_id(env, Self::chain_name_hash(env), deployer, salt)
140    }
141
142    fn interchain_token_address(env: &Env, token_id: BytesN<32>) -> Address {
143        deployer::interchain_token_address(env, token_id)
144    }
145
146    fn token_manager_address(env: &Env, token_id: BytesN<32>) -> Address {
147        deployer::token_manager_address(env, token_id)
148    }
149
150    fn registered_token_address(env: &Env, token_id: BytesN<32>) -> Address {
151        storage::token_id_config(env, token_id).token_address
152    }
153
154    fn deployed_token_manager(env: &Env, token_id: BytesN<32>) -> Address {
155        storage::token_id_config(env, token_id).token_manager
156    }
157
158    fn token_manager_type(env: &Env, token_id: BytesN<32>) -> TokenManagerType {
159        storage::token_id_config(env, token_id).token_manager_type
160    }
161
162    fn flow_limit(env: &Env, token_id: BytesN<32>) -> Option<i128> {
163        flow_limit::flow_limit(env, token_id)
164    }
165
166    fn flow_out_amount(env: &Env, token_id: BytesN<32>) -> i128 {
167        flow_limit::flow_out_amount(env, token_id)
168    }
169
170    fn flow_in_amount(env: &Env, token_id: BytesN<32>) -> i128 {
171        flow_limit::flow_in_amount(env, token_id)
172    }
173
174    #[only_operator]
175    fn set_flow_limit(
176        env: &Env,
177        token_id: BytesN<32>,
178        flow_limit: Option<i128>,
179    ) -> Result<(), ContractError> {
180        flow_limit::set_flow_limit(env, token_id, flow_limit)
181    }
182
183    #[when_not_paused]
184    fn deploy_interchain_token(
185        env: &Env,
186        caller: Address,
187        salt: BytesN<32>,
188        token_metadata: TokenMetadata,
189        initial_supply: i128,
190        minter: Option<Address>,
191    ) -> Result<BytesN<32>, ContractError> {
192        caller.require_auth();
193
194        ensure!(initial_supply >= 0, ContractError::InvalidInitialSupply);
195        ensure!(
196            initial_supply > 0 || minter.is_some(),
197            ContractError::InvalidTokenConfig
198        );
199
200        let token_id = Self::interchain_token_id(env, caller.clone(), salt);
201
202        token_metadata.validate()?;
203
204        let unregistered_token_id = token_id::ensure_token_not_registered(env, token_id.clone())?;
205
206        let token_address = Self::deploy_token(env, unregistered_token_id, token_metadata, minter)?;
207
208        if initial_supply > 0 {
209            StellarAssetClient::new(env, &token_address).mint(&caller, &initial_supply);
210        }
211
212        Ok(token_id)
213    }
214
215    #[when_not_paused]
216    fn deploy_remote_interchain_token(
217        env: &Env,
218        caller: Address,
219        salt: BytesN<32>,
220        destination_chain: String,
221        gas_token: Option<Token>,
222    ) -> Result<BytesN<32>, ContractError> {
223        caller.require_auth();
224
225        let token_id = Self::interchain_token_id(env, caller.clone(), salt);
226
227        Self::deploy_remote_token(env, caller, token_id.clone(), destination_chain, gas_token)?;
228
229        Ok(token_id)
230    }
231
232    #[when_not_paused]
233    fn register_canonical_token(
234        env: &Env,
235        token_address: Address,
236    ) -> Result<BytesN<32>, ContractError> {
237        // Validates the token address and its associated token metadata
238        let _ =
239            token_metadata::token_metadata(env, &token_address, &Self::native_token_address(env))?;
240
241        let token_id = Self::canonical_interchain_token_id(env, token_address.clone());
242
243        let unregistered_token_id = token_id::ensure_token_not_registered(env, token_id.clone())?;
244
245        let _: Address = Self::deploy_token_manager(
246            env,
247            unregistered_token_id,
248            token_address,
249            TokenManagerType::LockUnlock,
250        );
251
252        Ok(token_id)
253    }
254
255    #[when_not_paused]
256    fn deploy_remote_canonical_token(
257        env: &Env,
258        token_address: Address,
259        destination_chain: String,
260        spender: Address,
261        gas_token: Option<Token>,
262    ) -> Result<BytesN<32>, ContractError> {
263        spender.require_auth();
264
265        let token_id = Self::canonical_interchain_token_id(env, token_address);
266
267        Self::deploy_remote_token(env, spender, token_id.clone(), destination_chain, gas_token)?;
268
269        Ok(token_id)
270    }
271
272    #[when_not_paused]
273    fn register_token_metadata(
274        env: &Env,
275        token_address: Address,
276        spender: Address,
277        gas_token: Option<Token>,
278    ) -> Result<(), ContractError> {
279        spender.require_auth();
280
281        let token_metadata =
282            token_metadata::token_metadata(env, &token_address, &Self::native_token_address(env))?;
283
284        // RegisterTokenMetadata expects u8 decimals, but TokenMetadata uses u32.
285        // The unwrap() is safe because token_metadata validation ensures decimals <= 255.
286        let hub_message = HubMessage::RegisterTokenMetadata(RegisterTokenMetadata {
287            decimals: u8::try_from(token_metadata.decimal).unwrap(),
288            token_address: token_address.to_string_bytes(),
289        });
290
291        Self::send_to_hub(env, spender, hub_message, gas_token)?;
292
293        TokenMetadataRegisteredEvent {
294            token_address,
295            decimals: token_metadata.decimal,
296        }
297        .emit(env);
298
299        Ok(())
300    }
301
302    #[when_not_paused]
303    fn register_custom_token(
304        env: &Env,
305        deployer: Address,
306        salt: BytesN<32>,
307        token_address: Address,
308        token_manager_type: TokenManagerType,
309    ) -> Result<BytesN<32>, ContractError> {
310        deployer.require_auth();
311
312        // Custom token managers can't be deployed with native interchain token type, which is reserved for interchain tokens
313        ensure!(
314            token_manager_type != TokenManagerType::NativeInterchainToken,
315            ContractError::InvalidTokenManagerType
316        );
317
318        // Validates the token address and its associated token metadata
319        let _ =
320            token_metadata::token_metadata(env, &token_address, &Self::native_token_address(env))?;
321
322        let token_id = Self::linked_token_id(env, deployer, salt);
323
324        let unregistered_token_id = token_id::ensure_token_not_registered(env, token_id.clone())?;
325
326        let _: Address = Self::deploy_token_manager(
327            env,
328            unregistered_token_id,
329            token_address,
330            token_manager_type,
331        );
332
333        Ok(token_id)
334    }
335
336    #[when_not_paused]
337    fn link_token(
338        env: &Env,
339        deployer: Address,
340        salt: BytesN<32>,
341        destination_chain: String,
342        destination_token_address: Bytes,
343        token_manager_type: TokenManagerType,
344        link_params: Option<Bytes>,
345        gas_token: Option<Token>,
346    ) -> Result<BytesN<32>, ContractError> {
347        deployer.require_auth();
348
349        ensure!(
350            !destination_token_address.is_empty(),
351            ContractError::InvalidDestinationTokenAddress
352        );
353
354        // Custom token managers can't be deployed with native interchain token type, which is reserved for interchain tokens
355        ensure!(
356            token_manager_type != TokenManagerType::NativeInterchainToken,
357            ContractError::InvalidTokenManagerType
358        );
359
360        let token_id = Self::linked_token_id(env, deployer.clone(), salt);
361        let token_address = Self::token_id_config(env, token_id.clone())?.token_address;
362
363        let message = Message::LinkToken(LinkToken {
364            token_id: token_id.clone(),
365            token_manager_type,
366            source_token_address: token_address.to_string_bytes(),
367            destination_token_address: destination_token_address.clone(),
368            params: link_params.clone(),
369        });
370
371        LinkTokenStartedEvent {
372            token_id: token_id.clone(),
373            destination_chain: destination_chain.clone(),
374            source_token_address: token_address.to_string_bytes(),
375            destination_token_address,
376            token_manager_type,
377            params: link_params,
378        }
379        .emit(env);
380
381        Self::pay_gas_and_call_contract(env, deployer, destination_chain, message, gas_token)?;
382
383        Ok(token_id)
384    }
385
386    #[when_not_paused]
387    fn interchain_transfer(
388        env: &Env,
389        caller: Address,
390        token_id: BytesN<32>,
391        destination_chain: String,
392        destination_address: Bytes,
393        amount: i128,
394        data: Option<Bytes>,
395        gas_token: Option<Token>,
396    ) -> Result<(), ContractError> {
397        ensure!(amount > 0, ContractError::InvalidAmount);
398
399        ensure!(
400            !destination_address.is_empty(),
401            ContractError::InvalidDestinationAddress
402        );
403
404        if let Some(ref data) = data {
405            ensure!(!data.is_empty(), ContractError::InvalidData);
406        }
407
408        caller.require_auth();
409
410        token_handler::take_token(
411            env,
412            &caller,
413            Self::token_id_config(env, token_id.clone())?,
414            amount,
415        )?;
416
417        FlowDirection::Out.add_flow(env, token_id.clone(), amount)?;
418
419        InterchainTransferSentEvent {
420            token_id: token_id.clone(),
421            source_address: caller.clone(),
422            destination_chain: destination_chain.clone(),
423            destination_address: destination_address.clone(),
424            amount,
425            data_hash: data
426                .as_ref()
427                .map(|data| env.crypto().keccak256(data).into()),
428        }
429        .emit(env);
430
431        let message = Message::InterchainTransfer(InterchainTransfer {
432            token_id,
433            source_address: caller.to_string_bytes(),
434            destination_address,
435            amount,
436            data,
437        });
438
439        Self::pay_gas_and_call_contract(env, caller, destination_chain, message, gas_token)?;
440
441        Ok(())
442    }
443
444    #[only_owner]
445    fn transfer_token_admin(
446        env: &Env,
447        token_id: BytesN<32>,
448        new_admin: Address,
449    ) -> Result<(), ContractError> {
450        let TokenIdConfigValue {
451            token_address,
452            token_manager_type,
453            token_manager,
454        } = Self::token_id_config(env, token_id)?;
455
456        ensure!(
457            token_manager_type == TokenManagerType::MintBurn,
458            ContractError::InvalidTokenManagerType
459        );
460
461        TokenManagerClient::new(env, &token_manager).set_admin(env, &token_address, &new_admin);
462
463        Ok(())
464    }
465}
466
467impl InterchainTokenService {
468    fn send_to_hub(
469        env: &Env,
470        spender: Address,
471        hub_message: HubMessage,
472        gas_token: Option<Token>,
473    ) -> Result<(), ContractError> {
474        let gateway = AxelarGatewayMessagingClient::new(env, &Self::gateway(env));
475        let gas_service = AxelarGasServiceClient::new(env, &Self::gas_service(env));
476
477        let hub_chain = Self::its_hub_chain_name(env);
478        let hub_address = Self::its_hub_address(env);
479
480        let payload = hub_message.abi_encode(env)?;
481
482        if let Some(gas_token) = gas_token {
483            gas_service.pay_gas(
484                &env.current_contract_address(),
485                &hub_chain,
486                &hub_address,
487                &payload,
488                &spender,
489                &gas_token,
490                &Bytes::new(env),
491            );
492        }
493
494        gateway.call_contract(
495            &env.current_contract_address(),
496            &hub_chain,
497            &hub_address,
498            &payload,
499        );
500
501        Ok(())
502    }
503
504    fn pay_gas_and_call_contract(
505        env: &Env,
506        caller: Address,
507        destination_chain: String,
508        message: Message,
509        gas_token: Option<Token>,
510    ) -> Result<(), ContractError> {
511        // Validate the destination chain only for non-interchain transfer messages.
512        // Self-transfers are allowed only in interchain transfers, as they may be useful
513        // when broadcasting to a batch that includes the current chain.
514        if !matches!(message, Message::InterchainTransfer(_)) {
515            ensure!(
516                destination_chain != Self::chain_name(env),
517                ContractError::InvalidDestinationChain
518            );
519        }
520
521        ensure!(
522            Self::is_trusted_chain(env, destination_chain.clone()),
523            ContractError::UntrustedChain
524        );
525
526        let hub_message = HubMessage::SendToHub {
527            destination_chain,
528            message,
529        };
530
531        Self::send_to_hub(env, caller, hub_message, gas_token)?;
532
533        Ok(())
534    }
535
536    /// Validate that the message is coming from the ITS Hub and decode the message
537    fn get_execute_params(
538        env: &Env,
539        source_chain: String,
540        source_address: String,
541        payload: Bytes,
542    ) -> Result<(String, Message), ContractError> {
543        ensure!(
544            source_chain == Self::its_hub_chain_name(env),
545            ContractError::NotHubChain
546        );
547        ensure!(
548            source_address == Self::its_hub_address(env),
549            ContractError::NotHubAddress
550        );
551
552        let HubMessage::ReceiveFromHub {
553            source_chain: original_source_chain,
554            message,
555        } = HubMessage::abi_decode(env, &payload)?
556        else {
557            return Err(ContractError::InvalidMessageType);
558        };
559        ensure!(
560            storage::is_trusted_chain(env, original_source_chain.clone()),
561            ContractError::UntrustedChain
562        );
563
564        Ok((original_source_chain, message))
565    }
566
567    fn set_token_id_config(env: &Env, token_id: BytesN<32>, token_data: TokenIdConfigValue) {
568        storage::set_token_id_config(env, token_id, &token_data);
569    }
570
571    /// Retrieves the configuration value for the specified token ID.
572    ///
573    /// # Arguments
574    /// - `token_id`: A 32-byte unique identifier for the token.
575    ///
576    /// # Returns
577    /// - `Ok(TokenIdConfigValue)`: The configuration value if it exists.
578    ///
579    /// # Errors
580    /// - `ContractError::InvalidTokenId`: If the token ID does not exist in storage.
581    fn token_id_config(
582        env: &Env,
583        token_id: BytesN<32>,
584    ) -> Result<TokenIdConfigValue, ContractError> {
585        storage::try_token_id_config(env, token_id).ok_or(ContractError::InvalidTokenId)
586    }
587
588    fn chain_name_hash(env: &Env) -> BytesN<32> {
589        let chain_name = Self::chain_name(env);
590        env.crypto().keccak256(&chain_name.to_xdr(env)).into()
591    }
592
593    /// Deploys a remote token on a specified destination chain.
594    ///
595    /// This function retrieves and validates the token's metadata
596    /// and emits an event indicating the start of the token deployment process.
597    /// It also constructs and sends the deployment message to the remote chain.
598    ///
599    /// # Arguments
600    /// * `caller` - Address of the caller initiating the deployment.
601    /// * `token_id` - The token ID for the remote token being deployed.
602    /// * `destination_chain` - The name of the destination chain where the token will be deployed.
603    /// * `gas_token` - An optional gas token used to pay for gas during the deployment.
604    ///
605    /// # Errors
606    /// - `ContractError::InvalidDestinationChain`: If the `destination_chain` is the current chain.
607    /// - `ContractError::InvalidTokenId`: If the token ID is invalid.
608    /// - Errors propagated from `token_metadata`.
609    /// - Any error propagated from `pay_gas_and_call_contract`.
610    ///
611    /// # Authorization
612    /// - The `caller` must authenticate.
613    fn deploy_remote_token(
614        env: &Env,
615        caller: Address,
616        token_id: BytesN<32>,
617        destination_chain: String,
618        gas_token: Option<Token>,
619    ) -> Result<(), ContractError> {
620        let token_address = Self::token_id_config(env, token_id.clone())?.token_address;
621        let TokenMetadata {
622            name,
623            symbol,
624            decimal,
625        } = token_metadata::token_metadata(env, &token_address, &Self::native_token_address(env))?;
626
627        let message = Message::DeployInterchainToken(DeployInterchainToken {
628            token_id: token_id.clone(),
629            name: name.clone(),
630            symbol: symbol.clone(),
631            decimals: decimal as u8,
632            minter: None,
633        });
634
635        InterchainTokenDeploymentStartedEvent {
636            token_id,
637            token_address,
638            destination_chain: destination_chain.clone(),
639            name,
640            symbol,
641            decimals: decimal,
642            minter: None,
643        }
644        .emit(env);
645
646        Self::pay_gas_and_call_contract(env, caller, destination_chain, message, gas_token)?;
647
648        Ok(())
649    }
650
651    fn execute_transfer_message(
652        env: &Env,
653        source_chain: &String,
654        message_id: String,
655        InterchainTransfer {
656            token_id,
657            source_address,
658            destination_address,
659            amount,
660            data,
661        }: InterchainTransfer,
662    ) -> Result<(), ContractError> {
663        ensure!(amount > 0, ContractError::InvalidAmount);
664
665        let destination_address = Address::from_string_bytes(&destination_address);
666
667        let token_config_value = Self::token_id_config(env, token_id.clone())?;
668        let token_address = token_config_value.token_address.clone();
669
670        FlowDirection::In.add_flow(env, token_id.clone(), amount)?;
671
672        token_handler::give_token(env, &destination_address, token_config_value, amount)?;
673
674        InterchainTransferReceivedEvent {
675            source_chain: source_chain.clone(),
676            token_id: token_id.clone(),
677            source_address: source_address.clone(),
678            destination_address: destination_address.clone(),
679            amount,
680            data_hash: data
681                .as_ref()
682                .map(|data| env.crypto().keccak256(data).into()),
683        }
684        .emit(env);
685
686        if let Some(payload) = data {
687            Self::execute_contract_with_token(
688                env,
689                destination_address,
690                source_chain,
691                message_id,
692                source_address,
693                payload,
694                token_id,
695                token_address,
696                amount,
697            );
698        }
699
700        Ok(())
701    }
702
703    fn execute_contract_with_token(
704        env: &Env,
705        destination_address: Address,
706        source_chain: &String,
707        message_id: String,
708        source_address: Bytes,
709        payload: Bytes,
710        token_id: BytesN<32>,
711        token_address: Address,
712        amount: i128,
713    ) {
714        // Due to limitations of the soroban-sdk, there is no type-safe client for contract execution.
715        // The invocation might return a value, so we use Val as the return type to avoid panics
716        env.invoke_contract::<Val>(
717            &destination_address,
718            &Symbol::new(env, EXECUTE_WITH_INTERCHAIN_TOKEN),
719            vec![
720                env,
721                source_chain.to_val(),
722                message_id.to_val(),
723                source_address.to_val(),
724                payload.to_val(),
725                token_id.to_val(),
726                token_address.to_val(),
727                amount.into_val(env),
728            ],
729        );
730    }
731
732    fn execute_deploy_message(
733        env: &Env,
734        DeployInterchainToken {
735            token_id,
736            name,
737            symbol,
738            decimals,
739            minter,
740        }: DeployInterchainToken,
741    ) -> Result<(), ContractError> {
742        let token_metadata = TokenMetadata::new(name, symbol, decimals as u32)?;
743
744        // Note: attempt to convert a byte string which doesn't represent a valid Soroban address fails at the Host level
745        let minter = minter.map(|m| Address::from_string_bytes(&m));
746
747        let unregistered_token_id = token_id::ensure_token_not_registered(env, token_id)?;
748        let _: Address = Self::deploy_token(env, unregistered_token_id, token_metadata, minter)?;
749
750        Ok(())
751    }
752
753    fn execute_link_token_message(
754        env: &Env,
755        source_chain: String,
756        LinkToken {
757            token_id,
758            token_manager_type,
759            source_token_address,
760            destination_token_address,
761            params,
762        }: LinkToken,
763    ) -> Result<(), ContractError> {
764        let token_address = Address::from_string_bytes(&destination_token_address);
765
766        // Validates the token address and its associated token metadata
767        let _ =
768            token_metadata::token_metadata(env, &token_address, &Self::native_token_address(env))?;
769
770        let unregistered_token_id = token_id::ensure_token_not_registered(env, token_id)?;
771
772        let _: Address = Self::deploy_token_manager(
773            env,
774            unregistered_token_id.clone(),
775            token_address,
776            token_manager_type,
777        );
778
779        LinkTokenReceivedEvent {
780            source_chain,
781            token_id: unregistered_token_id.into(),
782            source_token_address,
783            destination_token_address,
784            token_manager_type,
785            params,
786        }
787        .emit(env);
788
789        Ok(())
790    }
791
792    fn deploy_token_manager(
793        env: &Env,
794        unregistered_token_id: UnregisteredTokenId,
795        token_address: Address,
796        token_manager_type: TokenManagerType,
797    ) -> Address {
798        let token_id: BytesN<32> = unregistered_token_id.into();
799        let token_manager = deployer::deploy_token_manager(
800            env,
801            Self::token_manager_wasm_hash(env),
802            token_id.clone(),
803            token_address.clone(),
804            token_manager_type,
805        );
806
807        Self::set_token_id_config(
808            env,
809            token_id,
810            TokenIdConfigValue {
811                token_address: token_address.clone(),
812                token_manager: token_manager.clone(),
813                token_manager_type,
814            },
815        );
816
817        token_handler::post_token_manager_deploy(
818            env,
819            token_manager_type,
820            token_manager.clone(),
821            token_address,
822        );
823
824        token_manager
825    }
826
827    /// Deploy an interchain token on the current chain and its corresponding token manager.
828    ///
829    /// # Arguments
830    /// * `unregistered_token_id` - The unregistered token ID for the interchain token being deployed.
831    /// * `token_metadata` - The metadata for the interchain token being deployed.
832    /// * `minter` - An optional address of an additional minter for the interchain token being deployed.
833    fn deploy_token(
834        env: &Env,
835        unregistered_token_id: UnregisteredTokenId,
836        token_metadata: TokenMetadata,
837        minter: Option<Address>,
838    ) -> Result<Address, ContractError> {
839        let token_address = deployer::deploy_interchain_token(
840            env,
841            Self::interchain_token_wasm_hash(env),
842            minter,
843            unregistered_token_id.clone(),
844            token_metadata,
845        );
846
847        Self::deploy_token_manager(
848            env,
849            unregistered_token_id,
850            token_address.clone(),
851            TokenManagerType::NativeInterchainToken,
852        );
853
854        Ok(token_address)
855    }
856}
857
858impl CustomAxelarExecutable for InterchainTokenService {
859    type Error = ContractError;
860
861    fn __gateway(env: &Env) -> Address {
862        storage::gateway(env)
863    }
864
865    #[when_not_paused]
866    fn __execute(
867        env: &Env,
868        source_chain: String,
869        message_id: String,
870        source_address: String,
871        payload: Bytes,
872    ) -> Result<(), Self::Error> {
873        let (source_chain, message) =
874            Self::get_execute_params(env, source_chain, source_address, payload)?;
875
876        match message {
877            Message::InterchainTransfer(message) => {
878                Self::execute_transfer_message(env, &source_chain, message_id, message)
879            }
880            Message::DeployInterchainToken(message) => Self::execute_deploy_message(env, message),
881            Message::LinkToken(message) => {
882                Self::execute_link_token_message(env, source_chain, message)
883            }
884        }?;
885
886        Ok(())
887    }
888}