Skip to content
Closed
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
9 changes: 8 additions & 1 deletion tests/e2e/cases/multichain_usdt_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,12 +223,19 @@ impl MultichainTransferSetup {
// Account upgrade deployed onchain.
upgrade_account_eagerly(&env, &[key.to_authorized()], &key, AuthKind::Auth).await?;

let destination_decimals =
IERC20::new(env.erc20, env.provider_for(2)).decimals().call().await?;

// Get initial balances on all chains
let mut balances = Vec::with_capacity(num_chains);
for i in 0..num_chains {
let balance =
IERC20::new(env.erc20, env.provider_for(i)).balanceOf(wallet).call().await?;
balances.push(balance);
let decimals = IERC20::new(env.erc20, env.provider_for(i)).decimals().call().await?;
balances.push(
balance * U256::from(10u128.pow(destination_decimals as u32))
/ U256::from(10u128.pow(decimals as u32)),
);
}

// Calculate the total balance
Expand Down
88 changes: 53 additions & 35 deletions tests/e2e/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ use relay::{
signers::DynSigner,
spawn::{RelayHandle, try_spawn},
types::{
AssetDescriptor, AssetUid, Assets, IFunder,
AssetDescriptor, AssetUid, Assets,
IERC20::{self, IERC20Instance},
IFunder,
rpc::{AuthorizeKeyResponse, GetKeysParameters},
},
};
Expand Down Expand Up @@ -557,36 +559,46 @@ impl Environment {
let chain_ids =
try_join_all(providers.iter().map(|provider| provider.get_chain_id())).await?;

// Build assets for chains. Every chain has the same assets, so we just build this once and
// clone.
//
// Each ERC20 has the UID derived from the order it was deployed. The native token is given
// the UID ETH. Everything is assumed to have 18 decimals.
//
// Only ETH and the first ERC20 is relayable across chains.
let erc20s = contracts.erc20s.iter().enumerate().map(|(idx, contract)| {
(
AssetUid::new(idx.to_string()),
AssetDescriptor {
address: *contract,
decimals: 18,
fee_token: true,
interop: idx == 0,
},
)
});
let assets = Assets::new(HashMap::from_iter(
std::iter::once((
AssetUid::new("eth".to_string()),
AssetDescriptor {
address: Address::ZERO,
decimals: 18,
fee_token: true,
interop: false,
},
))
.chain(erc20s),
));
let assets = try_join_all(providers.iter().map(async |provider| {
// Build assets for chains. Every chain has the same assets, but can have different
// decimals, so we need to query ERC20s for each chain.
//
// Each ERC20 has the UID derived from the order it was deployed. The native token
// is given the UID ETH with 18 decimals.
//
// Only ETH and the first ERC20 is relayable across chains.
alloy::contract::Result::<_>::Ok(Assets::new(HashMap::from_iter(
std::iter::once((
AssetUid::new("eth".to_string()),
AssetDescriptor {
address: Address::ZERO,
decimals: 18,
fee_token: true,
interop: false,
},
))
.chain(
try_join_all(contracts.erc20s.iter().enumerate().map(
|(idx, contract)| async move {
alloy::contract::Result::<_>::Ok((
AssetUid::new(idx.to_string()),
AssetDescriptor {
address: *contract,
decimals: IERC20Instance::new(*contract, provider)
.decimals()
.call()
.await?,
fee_token: true,
interop: idx == 0,
},
))
},
))
.await?,
),
)))
}))
.await?;

let database_url = if let Ok(db_url) = std::env::var("DATABASE_URL") {
let opts = PgConnectOptions::from_str(&db_url)?;
Expand Down Expand Up @@ -639,8 +651,8 @@ impl Environment {
.with_port(0)
.with_metrics_port(0)
.with_chains(HashMap::from_iter(
chain_ids.iter().enumerate().zip(endpoints.into_iter()).map(
|((idx, chain_id), endpoint)| {
itertools::izip!(chain_ids.iter().enumerate(), endpoints, assets).map(
|((idx, chain_id), endpoint, assets)| {
(
Chain::from_id(*chain_id),
ChainConfig {
Expand Down Expand Up @@ -963,8 +975,9 @@ pub async fn mint_erc20s<P: Provider>(
for erc20 in erc20s {
// Mint tokens for both signers.
for addr in addresses {
let decimals = IERC20::new(*erc20, &provider).decimals().call().await?;
MockErc20::new(*erc20, &provider)
.mint(*addr, U256::from(100e18))
.mint(*addr, U256::from(100 * 10u128.pow(decimals as u32)))
.send()
.await
.wrap_err("Minting failed")?
Expand Down Expand Up @@ -1176,14 +1189,19 @@ async fn deploy_simulator<P: Provider>(

/// Deploy a single ERC20 token
async fn deploy_erc20<P: Provider>(provider: &P, contracts_path: &Path) -> eyre::Result<Address> {
// Either 6 or 18 decimals to test interop between same asset UIDs with different decimals.
//
// TODO: is there a nicer way to do this deterministically?
let decimals_ = if provider.get_chain_id().await? % 2 == 0 { 6 } else { 18 };
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather not tie this to chain ID, and instead maybe pass rng all the way to this function, so that we can set the seed if we want. Making it a derivative of chain ID means that all ERC-20 on that chain will have the same number of decimals, right?

Copy link
Member

@klkvr klkvr Sep 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah agree that this feels a bit hacky, though I wouldn't want it to be random either because with 4 retries any failing test will likely just be retried and succeed

another option would be to run all kinds of configurations like we do with payment configs

relay/tests/e2e/mod.rs

Lines 45 to 51 in f8d6585

/// Runs all configurations (in both ERC20 and native payment methods).
pub async fn run_e2e<'a, F>(build_txs: F) -> Result<()>
where
F: Fn(&Environment) -> Vec<TxContext<'a>> + Send + Sync + Copy,
{
run_configs(build_txs, PaymentConfig::iter()).await
}

deploy_contract(
provider,
&contracts_path.join("MockERC20.sol/MockERC20.json"),
Some(
MockErc20::constructorCall {
name_: "mockName".into(),
symbol_: "mockSymbol".into(),
decimals_: 18,

decimals_,
}
.abi_encode()
.into(),
Expand Down
Loading