Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: the execute function of the ExecutableInterface trait returns a result #132

Merged
merged 5 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 20 additions & 11 deletions contracts/axelar-gateway/src/executable.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
use axelar_soroban_std::ensure;
use soroban_sdk::{contractclient, Address, Bytes, Env, String};
use soroban_sdk::{Address, Bytes, Env, String};

use crate::AxelarGatewayMessagingClient;
use soroban_sdk::contracterror;

#[contracterror]
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[repr(u32)]
pub enum ExecutableError {
NotApproved = 1,
pub trait NotApprovedError {
fn not_approved() -> Self;
}

/// Generates the implementation for the [`NotApprovedError`] trait for the given error type
#[macro_export]
macro_rules! impl_not_approved_error {
($error:ident) => {
impl NotApprovedError for $error {
fn not_approved() -> Self {
Self::NotApproved
}
}
};
}

/// Interface for an Axelar Executable app.
#[contractclient(name = "AxelarExecutableClient")]
pub trait AxelarExecutableInterface {
type Error: Into<soroban_sdk::Error> + NotApprovedError;

/// Return the trusted gateway contract id.
fn gateway(env: &Env) -> Address;

Expand All @@ -24,7 +33,7 @@ pub trait AxelarExecutableInterface {
message_id: String,
source_address: String,
payload: Bytes,
);
) -> Result<(), <Self as AxelarExecutableInterface>::Error>;

/// Validate if a gateway has approved a message.
/// This should be called from an implementation of `execute` before executing custom app logic.
Expand All @@ -35,7 +44,7 @@ pub trait AxelarExecutableInterface {
message_id: &String,
source_address: &String,
payload: &Bytes,
) -> Result<(), ExecutableError> {
) -> Result<(), <Self as AxelarExecutableInterface>::Error> {
let gateway = AxelarGatewayMessagingClient::new(env, &Self::gateway(env));

// Validate that the message was approved by the gateway
Expand All @@ -47,7 +56,7 @@ pub trait AxelarExecutableInterface {
source_address,
&env.crypto().keccak256(payload).into(),
),
ExecutableError::NotApproved
Self::Error::not_approved()
);

Ok(())
Expand Down
22 changes: 17 additions & 5 deletions contracts/example/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
use crate::event;
use axelar_gas_service::AxelarGasServiceClient;
use axelar_gateway::AxelarGatewayMessagingClient;
use axelar_gateway::{impl_not_approved_error, AxelarGatewayMessagingClient};
use axelar_soroban_std::types::Token;
use soroban_sdk::{contract, contractimpl, Address, Bytes, Env, String};
use soroban_sdk::{contract, contracterror, contractimpl, Address, Bytes, Env, String};

use crate::storage_types::DataKey;

use axelar_gateway::executable::AxelarExecutableInterface;
use axelar_gateway::executable::{AxelarExecutableInterface, NotApprovedError};

#[contract]
pub struct Example;

#[contracterror]
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[repr(u32)]
pub enum ExampleError {
NotApproved = 1,
}

impl_not_approved_error!(ExampleError);

#[contractimpl]
impl AxelarExecutableInterface for Example {
type Error = ExampleError;

fn gateway(env: &Env) -> Address {
env.storage().instance().get(&DataKey::Gateway).unwrap()
}
Expand All @@ -23,10 +34,11 @@ impl AxelarExecutableInterface for Example {
message_id: String,
source_address: String,
payload: Bytes,
) {
let _ = Self::validate_message(&env, &source_chain, &message_id, &source_address, &payload);
) -> Result<(), ExampleError> {
Self::validate_message(&env, &source_chain, &message_id, &source_address, &payload)?;

event::executed(&env, source_chain, message_id, source_address, payload);
Ok(())
}
}

Expand Down
60 changes: 26 additions & 34 deletions contracts/interchain-token-service/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,32 @@
use crate::abi::{get_message_type, MessageType as EncodedMessageType};
use crate::error::ContractError;
use crate::event::{
InterchainTokenDeployedEvent, InterchainTokenDeploymentStartedEvent,
InterchainTokenIdClaimedEvent, InterchainTransferReceivedEvent, InterchainTransferSentEvent,
TrustedChainRemovedEvent, TrustedChainSetEvent,
};
use crate::executable::InterchainTokenExecutableClient;
use crate::flow_limit::FlowDirection;
use crate::interface::InterchainTokenServiceInterface;
use crate::storage_types::{DataKey, TokenIdConfigValue};
use crate::types::{
DeployInterchainToken, HubMessage, InterchainTransfer, Message, TokenManagerType,
};
use crate::{flow_limit, token_handler};
use axelar_gas_service::AxelarGasServiceClient;
use axelar_gateway::{executable::AxelarExecutableInterface, AxelarGatewayMessagingClient};
use axelar_soroban_std::events::Event;
use axelar_soroban_std::token::validate_token_metadata;
use axelar_soroban_std::ttl::{extend_instance_ttl, extend_persistent_ttl};
use axelar_soroban_std::{
address::AddressExt,
ensure,
events::Event,
interfaces,
token::validate_token_metadata,
ttl::{extend_instance_ttl, extend_persistent_ttl},
types::Token,
Operatable, Ownable, Upgradable,
address::AddressExt, ensure, interfaces, types::Token, Operatable, Ownable, Upgradable,
};
use interchain_token::InterchainTokenClient;
use soroban_sdk::{
contract, contractimpl, panic_with_error,
token::{self, StellarAssetClient},
xdr::{FromXdr, ToXdr},
Address, Bytes, BytesN, Env, String,
};
use soroban_sdk::token::{self, StellarAssetClient};
use soroban_sdk::xdr::{FromXdr, ToXdr};
use soroban_sdk::{contract, contractimpl, Address, Bytes, BytesN, Env, String};
use soroban_token_sdk::metadata::TokenMetadata;

use crate::{
abi::{get_message_type, MessageType as EncodedMessageType},
error::ContractError,
event::{
InterchainTokenDeployedEvent, InterchainTokenDeploymentStartedEvent,
InterchainTokenIdClaimedEvent, InterchainTransferReceivedEvent,
InterchainTransferSentEvent, TrustedChainRemovedEvent, TrustedChainSetEvent,
},
executable::InterchainTokenExecutableClient,
flow_limit::{self, FlowDirection},
interface::InterchainTokenServiceInterface,
storage_types::{DataKey, TokenIdConfigValue},
token_handler,
types::{DeployInterchainToken, HubMessage, InterchainTransfer, Message, TokenManagerType},
};

const ITS_HUB_CHAIN_NAME: &str = "axelar";
const PREFIX_INTERCHAIN_TOKEN_ID: &str = "its-interchain-token-id";
const PREFIX_INTERCHAIN_TOKEN_SALT: &str = "interchain-token-salt";
Expand Down Expand Up @@ -441,6 +433,8 @@ impl InterchainTokenServiceInterface for InterchainTokenService {

#[contractimpl]
impl AxelarExecutableInterface for InterchainTokenService {
type Error = ContractError;

fn gateway(env: &Env) -> Address {
env.storage()
.instance()
Expand All @@ -454,12 +448,10 @@ impl AxelarExecutableInterface for InterchainTokenService {
message_id: String,
source_address: String,
payload: Bytes,
) {
Self::validate_message(&env, &source_chain, &message_id, &source_address, &payload)
.unwrap_or_else(|err| panic_with_error!(env, err));
) -> Result<(), ContractError> {
Self::validate_message(&env, &source_chain, &message_id, &source_address, &payload)?;

Self::execute_message(&env, source_chain, message_id, source_address, payload)
.unwrap_or_else(|err| panic_with_error!(env, err));
}
}

Expand Down
5 changes: 5 additions & 0 deletions contracts/interchain-token-service/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use axelar_gateway::executable::NotApprovedError;
use axelar_gateway::impl_not_approved_error;
use soroban_sdk::contracterror;

#[contracterror]
Expand Down Expand Up @@ -25,4 +27,7 @@ pub enum ContractError {
InvalidFlowLimit = 19,
FlowLimitExceeded = 20,
FlowAmountOverflow = 21,
NotApproved = 22,
}

impl_not_approved_error!(ContractError);
2 changes: 1 addition & 1 deletion contracts/interchain-token-service/tests/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use utils::{approve_gateway_messages, register_chains, setup_env, setup_its_toke
use interchain_token::InterchainTokenClient;

#[test]
#[should_panic(expected = "Error(Contract, #1)")] // ExecutableError::NotApproved
#[should_panic(expected = "Error(Contract, #22)")] // ContractError::NotApproved
fn execute_fails_without_gateway_approval() {
let (env, client, _, _, _) = setup_env();

Expand Down
Loading