use stellar_axelar_gas_service::testutils::{setup_gas_service, setup_gas_token};
use stellar_axelar_gas_service::AxelarGasServiceClient;
use stellar_axelar_gateway::event::{ContractCalledEvent, MessageApprovedEvent};
use stellar_axelar_gateway::testutils::{
generate_proof, get_approve_hash, setup_gateway, TestSignerSet,
};
use stellar_axelar_gateway::types::Message;
use stellar_axelar_gateway::AxelarGatewayClient;
use stellar_axelar_std::address::AddressExt;
use stellar_axelar_std::testutils::{
Address as _, AuthorizedFunction, AuthorizedInvocation, StellarAssetContract,
};
use stellar_axelar_std::token::{self, StellarAssetClient};
use stellar_axelar_std::traits::BytesExt;
use stellar_axelar_std::types::Token;
use stellar_axelar_std::{
assert_contract_err, assert_ok, auth_invocation, events, vec, Address, Bytes, BytesN, Env,
IntoVal, String, Symbol,
};
use stellar_interchain_token_service::testutils::setup_its;
use stellar_interchain_token_service::InterchainTokenServiceClient;
use crate::contract::AxelarExampleError;
use crate::event::{ExecutedEvent, TokenReceivedEvent, TokenSentEvent};
use crate::{AxelarExample, AxelarExampleClient};
const SOURCE_CHAIN_NAME: &str = "source";
const DESTINATION_CHAIN_NAME: &str = "destination";
struct TestConfig<'a> {
signers: TestSignerSet,
gateway: AxelarGatewayClient<'a>,
gas_service: AxelarGasServiceClient<'a>,
its: InterchainTokenServiceClient<'a>,
app: AxelarExampleClient<'a>,
}
fn setup_app<'a>(env: &Env, chain_name: String) -> TestConfig<'a> {
let (signers, gateway) = setup_gateway(env, 0, 5);
let gas_service = setup_gas_service(env);
let its = setup_its(env, &gateway, &gas_service, Some(chain_name));
let app = env.register(
AxelarExample,
(&gateway.address, &gas_service.address, &its.address),
);
let app = AxelarExampleClient::new(env, &app);
TestConfig {
signers,
gateway,
gas_service,
its,
app,
}
}
fn setup_tokens(env: &Env, user: &Address, amount: i128) -> (StellarAssetContract, Token) {
let token = env.register_stellar_asset_contract_v2(user.clone());
StellarAssetClient::new(env, &token.address())
.mock_all_auths()
.mint(user, &amount);
let gas_token = setup_gas_token(env, user);
StellarAssetClient::new(env, &gas_token.address)
.mock_all_auths()
.mint(user, &1);
(token, gas_token)
}
#[test]
fn gmp_example() {
let env = Env::default();
let user: Address = Address::generate(&env);
let source_chain = String::from_str(&env, SOURCE_CHAIN_NAME);
let TestConfig {
gas_service: source_gas_service,
app: source_app,
..
} = setup_app(&env, source_chain.clone());
let destination_chain = String::from_str(&env, DESTINATION_CHAIN_NAME);
let TestConfig {
signers: destination_signers,
gateway: destination_gateway_client,
app: destination_app,
..
} = setup_app(&env, destination_chain.clone());
let source_address = source_app.address.to_string();
let destination_address = destination_app.address.to_string();
let payload = Bytes::from_hex(&env, "dead");
let payload_hash: BytesN<32> = env.crypto().keccak256(&payload).into();
let asset = &env.register_stellar_asset_contract_v2(user.clone());
let asset_client = StellarAssetClient::new(&env, &asset.address());
let gas_amount: i128 = 100;
let gas_token = Token {
address: asset.address(),
amount: gas_amount,
};
asset_client.mock_all_auths().mint(&user, &gas_amount);
source_app.mock_all_auths().send(
&user,
&destination_chain,
&destination_address,
&payload,
&Some(gas_token.clone()),
);
let transfer_auth = auth_invocation!(
user,
asset_client.transfer(&user, &source_gas_service.address, gas_token.amount)
);
let source_gas_service_client = source_gas_service;
let pay_gas_auth = auth_invocation!(
user,
source_gas_service_client.pay_gas(
source_app.address.clone(),
destination_chain.clone(),
destination_address.clone(),
payload.clone(),
&user,
gas_token.clone(),
&Bytes::new(&env)
),
transfer_auth
);
let source_app = source_app;
let send_auth = auth_invocation!(
user,
source_app.send(
&user,
destination_chain,
destination_address,
payload.clone(),
gas_token
),
pay_gas_auth
);
assert_eq!(env.auths(), send_auth);
let message_id = String::from_str(&env, "test");
let contract_call_event = events::fmt_last_emitted_event::<ContractCalledEvent>(&env);
let messages = vec![
&env,
Message {
source_chain: source_chain.clone(),
message_id: message_id.clone(),
source_address: source_address.clone(),
contract_address: destination_app.address.clone(),
payload_hash,
},
];
let data_hash = get_approve_hash(&env, messages.clone());
let proof = generate_proof(&env, data_hash, destination_signers);
destination_gateway_client.approve_messages(&messages, &proof);
let message_approved_event = events::fmt_last_emitted_event::<MessageApprovedEvent>(&env);
destination_app.execute(&source_chain, &message_id, &source_address, &payload);
let executed_event = events::fmt_last_emitted_event::<ExecutedEvent>(&env);
goldie::assert!([contract_call_event, message_approved_event, executed_event].join("\n\n"));
}
#[test]
fn its_example() {
let env = Env::default();
let user = Address::generate(&env);
let source_chain = String::from_str(&env, SOURCE_CHAIN_NAME);
let TestConfig {
its: source_its,
app: source_app,
..
} = setup_app(&env, source_chain.clone());
let hub_chain = source_its.its_hub_chain_name();
let hub_address = source_its.its_hub_address();
let destination_chain = String::from_str(&env, DESTINATION_CHAIN_NAME);
let TestConfig {
signers: destination_signers,
gateway: destination_gateway,
its: destination_its,
app: destination_app,
..
} = setup_app(&env, destination_chain.clone());
let transfer_amount = 1000;
let (token, gas_token) = setup_tokens(&env, &user, transfer_amount);
source_its
.mock_all_auths()
.set_trusted_chain(&destination_chain);
destination_its
.mock_all_auths()
.set_trusted_chain(&source_chain);
let recipient = Address::generate(&env);
source_its
.mock_all_auths()
.register_canonical_token(&token.address());
let token_id = source_its.mock_all_auths().deploy_remote_canonical_token(
&token.address(),
&destination_chain,
&user,
&Some(gas_token.clone()),
);
let deploy_msg_payload = assert_ok!(
stellar_interchain_token_service::types::HubMessage::ReceiveFromHub {
source_chain: source_chain.clone(),
message: stellar_interchain_token_service::types::Message::DeployInterchainToken(
stellar_interchain_token_service::types::DeployInterchainToken {
token_id: token_id.clone(),
name: String::from_str(&env, "Test"),
symbol: String::from_str(&env, "TEST"),
decimals: 18,
minter: None,
},
),
}
.abi_encode(&env)
);
let message_id = String::from_str(&env, "deploy-message-id");
let deploy_messages = vec![
&env,
Message {
source_chain: hub_chain.clone(),
message_id: message_id.clone(),
source_address: hub_address.clone(),
contract_address: destination_its.address.clone(),
payload_hash: env.crypto().keccak256(&deploy_msg_payload).into(),
},
];
let proof = generate_proof(
&env,
get_approve_hash(&env, deploy_messages.clone()),
destination_signers.clone(),
);
destination_gateway.approve_messages(&deploy_messages, &proof);
destination_its.execute(&hub_chain, &message_id, &hub_address, &deploy_msg_payload);
source_app.mock_all_auths().send_token(
&user,
&token_id,
&destination_chain,
&destination_app.address.to_string_bytes(),
&transfer_amount,
&Some(recipient.to_string_bytes()),
&Some(gas_token),
);
let token_sent_event = events::fmt_last_emitted_event::<TokenSentEvent>(&env);
let transfer_msg_payload = assert_ok!(
stellar_interchain_token_service::types::HubMessage::ReceiveFromHub {
source_chain,
message: stellar_interchain_token_service::types::Message::InterchainTransfer(
stellar_interchain_token_service::types::InterchainTransfer {
token_id: token_id.clone(),
source_address: user.to_string_bytes(),
destination_address: destination_app.address.to_string_bytes(),
amount: transfer_amount,
data: Some(recipient.to_string_bytes()),
},
),
}
.abi_encode(&env)
);
let message_id = String::from_str(&env, "transfer-message-id");
let transfer_messages = vec![
&env,
Message {
source_chain: hub_chain.clone(),
message_id: message_id.clone(),
source_address: hub_address.clone(),
contract_address: destination_its.address.clone(),
payload_hash: env.crypto().keccak256(&transfer_msg_payload).into(),
},
];
let proof = generate_proof(
&env,
get_approve_hash(&env, transfer_messages.clone()),
destination_signers,
);
destination_gateway.approve_messages(&transfer_messages, &proof);
let message_approved_event = events::fmt_last_emitted_event::<MessageApprovedEvent>(&env);
destination_its.execute(&hub_chain, &message_id, &hub_address, &transfer_msg_payload);
let token_received_event = events::fmt_last_emitted_event::<TokenReceivedEvent>(&env);
goldie::assert!([
token_sent_event,
message_approved_event,
token_received_event
]
.join("\n\n"));
let destination_token =
token::TokenClient::new(&env, &destination_its.registered_token_address(&token_id));
assert_eq!(destination_token.balance(&destination_app.address), 0);
let recipient = Address::from_string_bytes(&recipient.to_string_bytes());
assert_eq!(destination_token.balance(&destination_app.address), 0);
assert_eq!(destination_token.balance(&recipient), transfer_amount);
}
#[test]
fn constructor_succeeds() {
let env = Env::default();
let source_chain = String::from_str(&env, SOURCE_CHAIN_NAME);
let TestConfig {
gateway,
gas_service,
its,
app,
..
} = setup_app(&env, source_chain);
assert_eq!(app.gateway(), gateway.address);
assert_eq!(app.gas_service(), gas_service.address);
assert_eq!(app.interchain_token_service(), its.address);
}
#[test]
fn execute_fails_with_not_approved() {
let env = Env::default();
let source_chain = String::from_str(&env, SOURCE_CHAIN_NAME);
let TestConfig { app, .. } = setup_app(&env, source_chain);
let source_chain = String::from_str(&env, "ethereum");
let message_id = String::from_str(&env, "test");
let source_address = String::from_str(&env, "0x123");
let payload = Bytes::from_array(&env, &[1u8; 32]);
assert_contract_err!(
app.try_execute(&source_chain, &message_id, &source_address, &payload),
AxelarExampleError::NotApproved
);
}