Putting it together
Run the full flow in one binary: faucet -> deposit -> wait -> spend.
Example: faucet, deposit, finalize, spend
Section titled “Example: faucet, deposit, finalize, spend”use alloy_primitives::U256;use alloy_provider::ProviderBuilder;use alloy_provider::network::{EthereumWallet, TxSigner};use alloy_signer_local::LocalSigner;use std::str::FromStr;use std::time::{Duration, UNIX_EPOCH};use tides_core_types::TidesChainId;use trident_sdk::{EvmToEvmSpendRequest, TridentClient, envs};
#[tokio::main]async fn main() { // ---- START EXAMPLE CONFIG ---- // `chain_name` is used to find correct deployment, can use `TidesChainId` otherwise // You can choose any of supported networks, see `TridentClient::get_deployments` for available deployments. // `TEST_TOKEN` is an example token deployment on each chain for testing purposes. // let src_chain_name = "base-sepolia"; // let src_rpc = "https://base-sepolia-public.nodies.app"; // let src_token = envs::testnet::sepolia_base::TEST_TOKEN; let src_chain_name = "arc-testnet"; let src_rpc = "https://rpc.testnet.arc.network"; let src_token = envs::testnet::arc_testnet::TEST_TOKEN;
// let dst_chain_name = "base-sepolia"; // let dst_rpc = "https://base-sepolia-public.nodies.app"; let dst_chain_name = "ethereum-sepolia"; let dst_rpc = "https://ethereum-sepolia-rpc.publicnode.com";
// Hex encoded private key to sign transaction both on source and destination chains let private_key = "0xYOUR_TEST_PRIVATE_KEY";
// Choose the amount. let amount = U256::from(10u64).pow(U256::from(18u64)); // ---- END EXAMPLE CONFIG ----
// Build a signer from your private key. let signer = LocalSigner::from_str(private_key).expect("valid private key");
// Connect to Trident testnet attester & resolve deployments. let mut client = TridentClient::connect_testnet() .await .expect("attester connect");
let src_dep = client .get_deployment_by_name(src_chain_name) .unwrap_or_else(|| panic!("deployment '{}' not found", src_chain_name)) .clone(); let dst_dep = client .get_deployment_by_name(dst_chain_name) .unwrap_or_else(|| panic!("deployment '{}' not found", dst_chain_name)) .clone();
let source_chain_id = src_dep.chain_id as TidesChainId; let destination_chain_id = dst_dep.chain_id as TidesChainId;
// Providers bound to the signer (for tx submission on each chain). let src_provider = ProviderBuilder::new() .wallet(EthereumWallet::from(signer.clone())) .connect_http(src_rpc.parse().expect("src rpc")); let dst_provider = ProviderBuilder::new() .wallet(EthereumWallet::from(signer.clone())) .connect_http(dst_rpc.parse().expect("dst rpc"));
println!("1) Requesting some money from faucet...");
let recipient = TxSigner::address(&signer); let _faucet_receipt = client .evm_faucet(&src_provider, src_token, recipient, amount) .await .expect("faucet mint on source");
let source_balance_before = client .evm_balance(source_chain_id, recipient, src_token) .await .expect("balances");
println!("2) Depositing token amount to Trident vault...");
// Use ERC-20 approve let deposit_result = client .evm_deposit_with_approval(&src_provider, source_chain_id, src_token, amount) .await .expect("deposit with approval");
println!( "Deposited balance on {} at block {:?}", src_dep.chain_name, deposit_result.deposit_tx_receipt.block_number ); // Or use ERC-7597 permit // let _deposit_result = client // .evm_sign_and_deposit_with_permit(&src_provider, &signer, source_chain_id, src_token, amount, U256::MAX) // .await // .expect("evm_sign_and_deposit_with_permit");
// 3) Wait for finalization on source chain by checking available balance via the attester // Block finalization time is different on every chain so delay may vary. loop { let source_balance_after = client .evm_balance(source_chain_id, recipient, src_token) .await .expect("balances");
println!( "[{}] Available amount on source chain: {}", std::time::SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_millis(), source_balance_after, );
if source_balance_after > source_balance_before { println!("3) Deposit has been finalized"); break; }
println!("Deposit hasn't arrived yet, wait 10 seconds"); tokio::time::sleep(Duration::from_secs(10)).await; }
// 4) Craft and spend (Base Sepolia -> Ethereum Sepolia) let req = EvmToEvmSpendRequest::new( &signer, source_chain_id, src_token, amount, // spend 1 token cross-chain &dst_provider, destination_chain_id, recipient, );
println!("4) Submitting EvmToEvmSpendRequest..."); let _spend_resp = client.evm_to_evm_spend(req).await.expect("spend src->dst");
println!( "Submitted spend from {} to {} for 1 token", src_chain_name, dst_chain_name );}How it works
Section titled “How it works”Configure the flow
Section titled “Configure the flow”let src_chain_name = "arc-testnet";let src_rpc = "https://rpc.testnet.arc.network";let src_token = envs::testnet::arc_testnet::TEST_TOKEN;let dst_chain_name = "ethereum-sepolia";let dst_rpc = "https://ethereum-sepolia-rpc.publicnode.com";let private_key = "0xYOUR_TEST_PRIVATE_KEY";let amount = U256::from(10u64).pow(U256::from(18u64));Pick source/destination chains, RPCs, token, private key, and the amount to move end-to-end (defaults use Arc -> Ethereum Sepolia).
Connect to Trident and resolve deployments
Section titled “Connect to Trident and resolve deployments”let signer = LocalSigner::from_str(private_key).expect("valid private key");let mut client = TridentClient::connect_testnet() .await .expect("attester connect");let src_dep = client .get_deployment_by_name(src_chain_name) .unwrap_or_else(|| panic!("deployment '{}' not found", src_chain_name)) .clone();let dst_dep = client .get_deployment_by_name(dst_chain_name) .unwrap_or_else(|| panic!("deployment '{}' not found", dst_chain_name)) .clone();let source_chain_id = src_dep.chain_id as TidesChainId;let destination_chain_id = dst_dep.chain_id as TidesChainId;Create the signer, connect to the attester, and fetch deployment metadata (including chain_ids) for both chains.
Bind providers
Section titled “Bind providers”let src_provider = ProviderBuilder::new() .wallet(EthereumWallet::from(signer.clone())) .connect_http(src_rpc.parse().expect("src rpc"));let dst_provider = ProviderBuilder::new() .wallet(EthereumWallet::from(signer.clone())) .connect_http(dst_rpc.parse().expect("dst rpc"));Attach the signer to RPC providers on both chains so faucet, deposit, and spend transactions use the same key.
Request faucet funds
Section titled “Request faucet funds”let recipient = TxSigner::address(&signer);let _faucet_receipt = client .evm_faucet(&src_provider, src_token, recipient, amount) .await .expect("faucet mint on source");let source_balance_before = client .evm_balance(source_chain_id, recipient, src_token) .await .expect("balances");Mint test tokens on the source chain and record the attester-reported balance before depositing.
Deposit with approval
Section titled “Deposit with approval”let deposit_result = client .evm_deposit_with_approval(&src_provider, source_chain_id, src_token, amount) .await .expect("deposit with approval");Approve and deposit in one call; the receipt includes the block where the deposit landed.
Wait for attester finalization
Section titled “Wait for attester finalization”loop { let source_balance_after = client .evm_balance(source_chain_id, recipient, src_token) .await .expect("balances"); if source_balance_after > source_balance_before { println!("3) Deposit has been finalized"); break; } tokio::time::sleep(Duration::from_secs(10)).await;}Poll evm_balance until it increases, signaling the attester has finalized the deposit.
Spend cross-chain
Section titled “Spend cross-chain”let req = EvmToEvmSpendRequest::new( &signer, source_chain_id, src_token, amount, &dst_provider, destination_chain_id, recipient,);let _spend_resp = client.evm_to_evm_spend(req).await.expect("spend src->dst");Build the spend request and submit it to deliver the funds from the source chain to the destination chain.