From 6e00ba4d16463e5f7c74ca26798bacd5008c3848 Mon Sep 17 00:00:00 2001 From: "Max Kalashnikoff | maksy.eth" Date: Fri, 8 Nov 2024 12:00:52 +0100 Subject: [PATCH] feat(CA): adding metadata into the route endpoint response (#831) --- integration/chain_orchestrator.test.ts | 14 +++++++--- src/handlers/chain_agnostic/check.rs | 2 +- src/handlers/chain_agnostic/mod.rs | 14 +++++----- src/handlers/chain_agnostic/route.rs | 37 +++++++++++++++++++++----- 4 files changed, 51 insertions(+), 16 deletions(-) diff --git a/integration/chain_orchestrator.test.ts b/integration/chain_orchestrator.test.ts index 60e9a7e6e..d173669a5 100644 --- a/integration/chain_orchestrator.test.ts +++ b/integration/chain_orchestrator.test.ts @@ -13,19 +13,21 @@ describe('Chain abstraction orchestrator', () => { const from_address_with_funds = "0x2aae531a81461f029cd55cb46703211c9227ba05"; const usdc_funds_on_base = 3_000_000; const usdc_funds_on_optimism = 1_057_151; + const usdc_token_symbol = "USDC"; // Amount to send to Optimism const amount_to_send = 3_000_000 // Amount bridging multiplier const amount_multiplier = 5; // +5% topup // How much needs to be topped up - const amount_to_topup = (amount_to_send - usdc_funds_on_optimism) * (100 + amount_multiplier) / 100; + const amount_to_topup = Math.round((amount_to_send - usdc_funds_on_optimism) * (100 + amount_multiplier) / 100); // Default gas esimation is default with 6x increase const gas_estimate = "0xf9e82"; const receiver_address = "0x739ff389c8eBd9339E69611d46Eec6212179BB67"; const chain_id_optimism = "eip155:10"; - const chain_id_base = "eip155:8453"; const usdc_contract_optimism = "0x0b2c639c533813f4aa9d7837caf62653d097ff85"; + const chain_id_base = "eip155:8453"; + const usdc_contract_base = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"; let orchestration_id = ""; @@ -195,7 +197,7 @@ describe('Chain abstraction orchestrator', () => { expect(approvalTransaction.nonce).not.toBe("0x00") expect(approvalTransaction.gas).toBe(gas_estimate) const decodedData = erc20Interface.decodeFunctionData('approve', approvalTransaction.data); - expect(decodedData.amount.toString()).toBe(amount_to_topup.toString().split('.')[0]) + expect(decodedData.amount.toString()).toBe(amount_to_topup.toString()) // Second transaction expected to be the bridging to the Base const bridgingTransaction = data.transactions[1] @@ -207,6 +209,12 @@ describe('Chain abstraction orchestrator', () => { const initialTransaction = data.transactions[2] expect(initialTransaction.data).toBe(transactionObj.transaction.data) + // Check the metadata fundingFrom + const fundingFrom = data.metadata.fundingFrom[0] + expect(fundingFrom.chainId).toBe(chain_id_base) + expect(fundingFrom.symbol).toBe(usdc_token_symbol) + expect(fundingFrom.tokenContract).toBe(usdc_contract_base) + expect(fundingFrom.amount).toBe("0x" + amount_to_topup.toString(16)) // Set the Orchestration ID for the next test orchestration_id = data.orchestrationId; }) diff --git a/src/handlers/chain_agnostic/check.rs b/src/handlers/chain_agnostic/check.rs index d90a81742..fb9055575 100644 --- a/src/handlers/chain_agnostic/check.rs +++ b/src/handlers/chain_agnostic/check.rs @@ -145,7 +145,7 @@ async fn handler_internal( + (erc20_topup_value * U256::from(BRIDGING_AMOUNT_MULTIPLIER)) / U256::from(100); // Check for possible bridging by iterating over supported assets - if let Some((bridge_chain_id, bridge_contract)) = + if let Some((bridge_chain_id, _, bridge_contract)) = check_bridging_for_erc20_transfer(query_params.project_id, erc20_topup_value, from_address) .await? { diff --git a/src/handlers/chain_agnostic/mod.rs b/src/handlers/chain_agnostic/mod.rs index a7c7fbbfb..6821556c4 100644 --- a/src/handlers/chain_agnostic/mod.rs +++ b/src/handlers/chain_agnostic/mod.rs @@ -71,23 +71,25 @@ pub async fn check_erc20_balances( Ok(balances) } +/// Check available assets for bridging and return +/// the chain_id, token symbol and contract_address pub async fn check_bridging_for_erc20_transfer( rpc_project_id: String, value: U256, sender: Address, -) -> Result, RpcError> { +) -> Result, RpcError> { // Check ERC20 tokens balance for each of supported assets - let mut contracts_per_chain: HashMap> = HashMap::new(); - for (_, chain_map) in BRIDGING_AVAILABLE_ASSETS.entries() { + let mut contracts_per_chain: HashMap<(String, String), Vec> = HashMap::new(); + for (token_symbol, chain_map) in BRIDGING_AVAILABLE_ASSETS.entries() { for (chain_id, contract_address) in chain_map.entries() { contracts_per_chain - .entry((*chain_id).to_string()) + .entry((token_symbol.to_string(), (*chain_id).to_string())) .or_default() .push((*contract_address).to_string()); } } // Making the check for each chain_id - for (chain_id, contracts) in contracts_per_chain { + for ((token_symbol, chain_id), contracts) in contracts_per_chain { let erc20_balances = check_erc20_balances( rpc_project_id.clone(), sender, @@ -100,7 +102,7 @@ pub async fn check_bridging_for_erc20_transfer( .await?; for (contract, balance) in erc20_balances { if balance >= value { - return Ok(Some((chain_id, contract))); + return Ok(Some((chain_id, token_symbol, contract))); } } } diff --git a/src/handlers/chain_agnostic/route.rs b/src/handlers/chain_agnostic/route.rs index 1e4a1fca9..b79e2f642 100644 --- a/src/handlers/chain_agnostic/route.rs +++ b/src/handlers/chain_agnostic/route.rs @@ -67,6 +67,22 @@ pub struct RequiresMultiChainResponse { pub struct RouteResponse { orchestration_id: String, transactions: Vec, + metadata: Metadata, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Metadata { + funding_from: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct FundingMetadata { + chain_id: String, + token_contract: Address, + symbol: String, + amount: String, } pub async fn handler( @@ -147,12 +163,13 @@ async fn handler_internal( + (erc20_topup_value * U256::from(BRIDGING_AMOUNT_MULTIPLIER)) / U256::from(100); // Check for possible bridging by iterating over supported assets - if let Some((bridge_chain_id, bridge_contract)) = check_bridging_for_erc20_transfer( - query_params.project_id.clone(), - erc20_topup_value, - from_address, - ) - .await? + if let Some((bridge_chain_id, bridge_token_symbol, bridge_contract)) = + check_bridging_for_erc20_transfer( + query_params.project_id.clone(), + erc20_topup_value, + from_address, + ) + .await? { // Skip bridging if that's the same chainId and contract address if bridge_chain_id == request_payload.transaction.chain_id && bridge_contract == to_address @@ -300,6 +317,14 @@ async fn handler_internal( return Ok(Json(RouteResponse { orchestration_id, transactions: routes, + metadata: Metadata { + funding_from: vec![FundingMetadata { + chain_id: bridge_chain_id, + token_contract: bridge_contract, + symbol: bridge_token_symbol, + amount: format!("0x{:x}", erc20_topup_value), + }], + }, }) .into_response()); }