From a0ebc99fa1b3f3438c6c0429142adc34d965dcd4 Mon Sep 17 00:00:00 2001 From: David Meister Date: Mon, 29 Jun 2026 17:41:53 +0000 Subject: [PATCH 1/4] perf: eliminate redundant read_dispair calls in try_into_call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit try_into_call previously called read_dispair 3 times (once directly and once inside each try_parse_rainlang call), totalling 12 sequential RPC round-trips just to read 4 immutable deployer addresses. Add parse_bytecode_with_dispair, which takes a pre-fetched DISPaiR and skips the read_dispair step. try_parse_rainlang becomes a wrapper around read_dispair + parse_bytecode_with_dispair. try_into_call now calls read_dispair once and passes the result to both parse calls via clone/move. RPC calls for a single deposit+addOrder submission: 14 → 6 (saves 8 sequential round-trips, ~2.4s at 300ms RTT). Adds assertions to test_into_add_order_call verifying both config.evaluable and tasks[0].evaluable carry the correct interpreter and store addresses from the single shared dispair read. Closes https://github.com/rainlanguage/raindex/issues/2776 Co-Authored-By: Claude --- crates/common/src/add_order.rs | 63 +++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/crates/common/src/add_order.rs b/crates/common/src/add_order.rs index e73069fb79..7f0320bf8a 100644 --- a/crates/common/src/add_order.rs +++ b/crates/common/src/add_order.rs @@ -177,14 +177,14 @@ impl AddOrderArgs { Ok(DISPaiR::new(deployer, interpreter, store, parser)) } - /// Call parser to parse rainlang into bytecode and constants. - async fn try_parse_rainlang( + /// Parse rainlang into bytecode using a pre-fetched DISPaiR. + /// Called by try_into_call to avoid redundant read_dispair round-trips. + async fn parse_bytecode_with_dispair( &self, - rpcs: Vec, - rainlang: String, + rpcs: &[String], + rainlang: &str, + dispair: DISPaiR, ) -> Result, AddOrderArgsError> { - let dispair = self.read_dispair(&rpcs).await?; - let urls = rpcs .iter() .map(|rpc| rpc.parse::()) @@ -193,13 +193,23 @@ impl AddOrderArgs { let provider = mk_read_provider(&urls)?; let parser: ParserV2 = dispair.into(); let rainlang_parsed: parse2Return = parser - .parse_text(rainlang.as_str(), &provider) + .parse_text(rainlang, &provider) .await .map_err(AddOrderArgsError::ParserError)?; - Ok(rainlang_parsed.bytecode.into()) } + /// Call parser to parse rainlang into bytecode and constants. + async fn try_parse_rainlang( + &self, + rpcs: Vec, + rainlang: String, + ) -> Result, AddOrderArgsError> { + let dispair = self.read_dispair(&rpcs).await?; + self.parse_bytecode_with_dispair(&rpcs, &rainlang, dispair) + .await + } + /// Generate RainlangSource meta fn try_generate_meta(&self, rainlang: String) -> Result, AddOrderArgsError> { let mut meta_docs = Vec::new(); @@ -252,24 +262,26 @@ impl AddOrderArgs { &self, rpcs: Vec, ) -> Result { + let dispair = self.read_dispair(&rpcs).await?; + let interpreter = dispair.interpreter; + let store = dispair.store; + let rainlang = self.compose_to_rainlang()?; let bytecode = self - .try_parse_rainlang(rpcs.clone(), rainlang.clone()) + .parse_bytecode_with_dispair(&rpcs, &rainlang, dispair.clone()) .await?; let meta = self.try_generate_meta(rainlang)?; - let dispair = self.read_dispair(&rpcs).await?; - // get the evaluable for the post action let post_rainlang = self.compose_addorder_post_task()?; let post_bytecode = self - .try_parse_rainlang(rpcs.clone(), post_rainlang.clone()) + .parse_bytecode_with_dispair(&rpcs, &post_rainlang, dispair) .await?; let post_evaluable = EvaluableV4 { - interpreter: dispair.interpreter, - store: dispair.store, + interpreter, + store, bytecode: post_bytecode.into(), }; @@ -283,8 +295,8 @@ impl AddOrderArgs { validInputs: self.inputs.clone(), validOutputs: self.outputs.clone(), evaluable: EvaluableV4 { - interpreter: dispair.interpreter, - store: dispair.store, + interpreter, + store, bytecode: bytecode.into(), }, meta: meta.into(), @@ -921,6 +933,25 @@ _ _: 0 0; assert_eq!(add_order_call.tasks[0].evaluable.bytecode.len(), 111); assert_eq!(add_order_call.tasks[0].signedContext.len(), 0); + + // Both config.evaluable and tasks[0].evaluable must use the same + // interpreter/store from one shared read_dispair call. + assert_eq!( + add_order_call.config.evaluable.interpreter, + *local_evm.interpreter.address() + ); + assert_eq!( + add_order_call.config.evaluable.store, + *local_evm.store.address() + ); + assert_eq!( + add_order_call.config.evaluable.interpreter, + add_order_call.tasks[0].evaluable.interpreter + ); + assert_eq!( + add_order_call.config.evaluable.store, + add_order_call.tasks[0].evaluable.store + ); } #[tokio::test] From 85559f506f7f8e83155743c9bf11491fccdbad07 Mon Sep 17 00:00:00 2001 From: David Meister Date: Mon, 29 Jun 2026 17:49:46 +0000 Subject: [PATCH 2/4] fix(ci): rs-static dead_code try_parse_rainlang [3b-attempt] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mark try_parse_rainlang as #[cfg(test)] — after the try_into_call refactor it is only called from tests, so the production build flags it as dead code under -D warnings. Co-Authored-By: Claude --- crates/common/src/add_order.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/common/src/add_order.rs b/crates/common/src/add_order.rs index 7f0320bf8a..4a18ffa39b 100644 --- a/crates/common/src/add_order.rs +++ b/crates/common/src/add_order.rs @@ -199,7 +199,7 @@ impl AddOrderArgs { Ok(rainlang_parsed.bytecode.into()) } - /// Call parser to parse rainlang into bytecode and constants. + #[cfg(test)] async fn try_parse_rainlang( &self, rpcs: Vec, From 752260dafb8571fb1ae3984646ee9ce565ec4d77 Mon Sep 17 00:00:00 2001 From: David Meister Date: Fri, 10 Jul 2026 05:29:25 +0000 Subject: [PATCH 3/4] fix(rework): dedup set_select_token token_info and approval-calldata decimals fetches set_select_token reuses a complete token record already in the yaml for the same network+address (recorded by an earlier selection) instead of refetching token_info, so selecting one token for both the input and output slot costs a single multicall (2 -> 1). Incomplete records and records on other networks never shortcut the fetch, and re-selecting a key with its current address also skips it. generate_approval_calldatas reads decimals from the deposits map (which now carries them from get_token_info) instead of issuing a decimals() RPC per token (1 -> 0); the only remaining round-trip per token is the allowance read. This also aligns the approval amount's decimals source with the deposit calldata path (yaml-first with on-chain fallback). Both behaviors are pinned by mock hit-count tests, mutation-validated against reintroduced duplicate fetches, dropped network/address/ completeness guards, lookup-after-removal ordering, and corrupted map decimals. Co-Authored-By: Claude --- .../raindex_order_builder/order_operations.rs | 93 +++++- .../raindex_order_builder/select_tokens.rs | 270 +++++++++++++++++- 2 files changed, 340 insertions(+), 23 deletions(-) diff --git a/crates/common/src/raindex_order_builder/order_operations.rs b/crates/common/src/raindex_order_builder/order_operations.rs index 6d8573dc15..1e36d1ebfd 100644 --- a/crates/common/src/raindex_order_builder/order_operations.rs +++ b/crates/common/src/raindex_order_builder/order_operations.rs @@ -127,14 +127,17 @@ impl RaindexOrderBuilder { }) } + /// Maps each deposit's token address to its amount and decimals. Decimals + /// travel with the map so downstream consumers reuse the token info this + /// resolution already produced instead of issuing their own decimals RPC. async fn get_deposits_as_map( &self, - ) -> Result, RaindexOrderBuilderError> { - let mut map: HashMap = HashMap::new(); + ) -> Result, RaindexOrderBuilderError> { + let mut map: HashMap = HashMap::new(); for d in self.get_deposits()? { let token_info = self.get_token_info(d.token.clone()).await?; let amount = Float::parse(d.amount)?; - map.insert(token_info.address, amount); + map.insert(token_info.address, (amount, token_info.decimals)); } Ok(map) } @@ -153,7 +156,7 @@ impl RaindexOrderBuilder { .enumerate() .filter_map(|(index, output)| { output.token.as_ref().and_then(|token| { - deposits_map.get(&token.address).map(|amount| { + deposits_map.get(&token.address).map(|(amount, _)| { Ok(VaultAndDeposit { order_io: output.clone(), deposit_amount: *amount, @@ -238,16 +241,10 @@ impl RaindexOrderBuilder { let mut calldatas = Vec::new(); - for (token_address, deposit_amount) in &deposits_map { + // Decimals arrive with the deposits map (resolved by get_token_info), + // so the only round-trip per token here is the allowance read. + for (token_address, (deposit_amount, decimals)) in &deposits_map { let tx_args = self.get_transaction_args()?; - let rpcs = tx_args - .rpcs - .iter() - .map(|rpc| Url::parse(rpc)) - .collect::, _>>()?; - - let erc20 = ERC20::new(rpcs, *token_address); - let decimals = erc20.decimals().await?; // An allowance read needs only the token, owner and raindex spender - // no deposit context. @@ -258,12 +255,12 @@ impl RaindexOrderBuilder { tx_args.raindex_address, ) .await?; - let allowance_float = Float::from_fixed_decimal(allowance, decimals)?; + let allowance_float = Float::from_fixed_decimal(allowance, *decimals)?; if !allowance_float.eq(*deposit_amount)? { let calldata = approveCall { spender: tx_args.raindex_address, - amount: deposit_amount.to_fixed_decimal(decimals)?, + amount: deposit_amount.to_fixed_decimal(*decimals)?, } .abi_encode(); @@ -870,6 +867,72 @@ mod tests { .unwrap() } + // Approval calldata generation reuses the decimals that already travel + // with the deposits map (resolved once per token by get_token_info from + // the yaml record), so the only RPC per token is the allowance read. + // The decimals mock answers 18 - the wrong value vs the yaml's 6 - so + // any decimals() round-trip corrupts the amount assertions below on top + // of its hit count. + #[tokio::test] + async fn test_generate_approval_calldatas_reuses_deposit_token_decimals() { + let server = MockServer::start_async().await; + let yaml = get_yaml().replace("http://localhost:8085/rpc-url", &server.url("/rpc")); + + let decimals_mock = server.mock(|when, then| { + when.method(POST).path("/rpc").body_contains("313ce567"); + then.json_body(json!({ + "jsonrpc": "2.0", + "id": 1, + "result": "0x0000000000000000000000000000000000000000000000000000000000000012", + })); + }); + let allowance_mock = server.mock(|when, then| { + when.method(POST).path("/rpc").body_contains("dd62ed3e"); + then.json_body(json!({ + "jsonrpc": "2.0", + "id": 1, + "result": "0x0000000000000000000000000000000000000000000000000000000000000000", + })); + }); + + let mut builder = + RaindexOrderBuilder::new_with_deployment(yaml, None, "some-deployment".to_string()) + .await + .unwrap(); + builder + .set_deposit("token1".to_string(), "5".to_string()) + .await + .unwrap(); + + let result = builder + .generate_approval_calldatas( + "0x0000000000000000000000000000000000000005".to_string(), + ) + .await + .unwrap(); + + let calldatas = match result { + ApprovalCalldataResult::Calldatas(calldatas) => calldatas, + ApprovalCalldataResult::NoDeposits => panic!("expected calldatas"), + }; + assert_eq!(calldatas.len(), 1); + assert_eq!( + calldatas[0].token, + Address::from_str("0xc2132d05d31c914a87c6611c10748aeb04b58e8f").unwrap() + ); + + let decoded = approveCall::abi_decode(&calldatas[0].calldata).unwrap(); + // 5 tokens at token1's yaml-recorded 6 decimals. + assert_eq!(decoded.amount, U256::from(5_000_000u64)); + assert_eq!( + decoded.spender, + Address::from_str("0xc95A5f8eFe14d7a20BD2E5BAFEC4E71f8Ce0B9A6").unwrap() + ); + + assert_eq!(allowance_mock.hits(), 1); + assert_eq!(decimals_mock.hits(), 0); + } + #[tokio::test] async fn test_should_emit_meta_call_false_when_meta_exists() { let server = MockServer::start_async().await; diff --git a/crates/common/src/raindex_order_builder/select_tokens.rs b/crates/common/src/raindex_order_builder/select_tokens.rs index d0ad904673..2bab2d6a70 100644 --- a/crates/common/src/raindex_order_builder/select_tokens.rs +++ b/crates/common/src/raindex_order_builder/select_tokens.rs @@ -1,4 +1,5 @@ use super::*; +use crate::erc20::TokenInfo; use crate::raindex_client::vaults::AccountBalance; use futures::StreamExt; use rain_math_float::Float; @@ -63,12 +64,6 @@ impl RaindexOrderBuilder { return Err(RaindexOrderBuilderError::TokenNotFound(key.clone())); } - if TokenCfg::parse_from_yaml(self.dotrain_order.dotrain_yaml().documents, &key, None) - .is_ok() - { - TokenCfg::remove_record_from_yaml(self.dotrain_order.raindex_yaml().documents, &key)?; - } - let address = Address::from_str(&address)?; let order_key = DeploymentCfg::parse_order_key( @@ -80,8 +75,21 @@ impl RaindexOrderBuilder { let rpcs = NetworkCfg::parse_rpcs(self.dotrain_order.dotrain_yaml().documents, &network_key)?; - let erc20 = ERC20::new(rpcs, address); - let token_info = erc20.token_info(None).await?; + // Selecting the same token address for multiple slots (e.g. both the + // input and output side of an order) costs a single token_info fetch: + // later selections reuse the metadata already recorded in the yaml. + // The lookup runs before the key's own stale record is removed, so + // re-selecting a key with its current address also skips the fetch. + let token_info = match self.recorded_token_info(&network_key, address) { + Some(token_info) => token_info, + None => ERC20::new(rpcs, address).token_info(None).await?, + }; + + if TokenCfg::parse_from_yaml(self.dotrain_order.dotrain_yaml().documents, &key, None) + .is_ok() + { + TokenCfg::remove_record_from_yaml(self.dotrain_order.raindex_yaml().documents, &key)?; + } TokenCfg::add_record_to_yaml( self.dotrain_order.raindex_yaml().documents, @@ -96,6 +104,33 @@ impl RaindexOrderBuilder { Ok(()) } + /// Returns the metadata of a token already recorded in the yaml for + /// `address` on `network_key`, when such a record carries the full + /// decimals/name/symbol set. Incomplete records don't qualify: their + /// missing fields still have to come from an on-chain fetch. The lookup + /// is best-effort - an unparseable tokens section reads as "nothing + /// recorded" rather than an error, so it can only ever save a fetch. + fn recorded_token_info(&self, network_key: &str, address: Address) -> Option { + self.dotrain_order + .raindex_yaml() + .get_tokens() + .unwrap_or_default() + .into_values() + .find_map(|token| { + if token.network.key != network_key || token.address != address { + return None; + } + match (token.decimals, token.label, token.symbol) { + (Some(decimals), Some(label), Some(symbol)) => Some(TokenInfo { + decimals, + name: label, + symbol, + }), + _ => None, + } + }) + } + pub fn unset_select_token(&mut self, key: String) -> Result<(), RaindexOrderBuilderError> { let select_tokens = OrderBuilderCfg::parse_select_tokens( self.dotrain_order.dotrain_yaml().documents, @@ -792,6 +827,225 @@ networks: ); } + // A deployment whose order uses two distinct select-token slots (input + // token3, output token4) plus a spare slot (token5), and a tokens + // section holding an incomplete record (decimals only) for the address + // token5 gets selected to, plus a complete record for the shared + // address on a different network (which must never satisfy a selection + // on this order's network). + const SHARED_ADDRESS_YAML_TEMPLATE: &str = r#" +builder: + name: Fixed limit + description: Fixed limit order + deployments: + some-deployment: + name: Select token deployment + description: Select token deployment description + deposits: + - token: token3 + fields: + - binding: binding-1 + name: Field 1 name + description: Field 1 description + presets: + - name: Preset 1 + value: "0" + select-tokens: + - key: token3 + name: Token 3 + description: Token 3 description + - key: token4 + name: Token 4 + description: Token 4 description + - key: token5 + name: Token 5 + description: Token 5 description +subgraphs: + some-sg: https://www.some-sg.com +metaboards: + some-network: https://metaboard.com +rainlangs: + some-deployer: + network: some-network + address: 0xF14E09601A47552De6aBd3A0B165607FaFd2B5Ba +raindexes: + some-raindex: + address: 0xc95A5f8eFe14d7a20BD2E5BAFEC4E71f8Ce0B9A6 + network: some-network + subgraph: some-sg + deployment-block: 12345 +tokens: + partial-token: + network: some-network + address: 0x00000000000000000000000000000000000000bb + decimals: 6 + foreign-token: + network: other-network + address: 0x00000000000000000000000000000000000000aa + decimals: 18 + label: Foreign Token + symbol: FT +scenarios: + some-scenario: + rainlang: some-deployer + bindings: + test-binding: 5 +orders: + some-order: + rainlang: some-deployer + inputs: + - token: token3 + outputs: + - token: token4 +deployments: + some-deployment: + scenario: some-scenario + order: some-order +--- +#test-binding ! +#calculate-io +_ _: 0 0; +#handle-io +:; +#handle-add-order +:; +"#; + + const TOKEN_INFO_RESULT: &str = "0x000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000007546f6b656e2031000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000025431000000000000000000000000000000000000000000000000000000000000"; + + // Selecting the same token address for two slots costs a single + // token_info fetch: the second selection reuses the yaml record the + // first one wrote. An incomplete pre-existing record (decimals only) + // never shortcuts the fetch. + #[tokio::test] + async fn test_set_select_token_same_address_fetches_once() { + let server = MockServer::start_async().await; + let yaml = format!( + r#" +version: {spec_version} +networks: + some-network: + rpcs: + - {rpc_url} + chain-id: 123 + network-id: 123 + currency: ETH + other-network: + rpcs: + - {rpc_url} + chain-id: 456 + network-id: 456 + currency: ETH +{yaml} +"#, + spec_version = SpecVersion::current(), + yaml = SHARED_ADDRESS_YAML_TEMPLATE, + rpc_url = server.url("/rpc") + ); + + // Each mock is pinned to the token address inside the multicall + // aggregate calldata, so hit counts are per-address. + let shared_info_mock = server.mock(|when, then| { + when.method("POST") + .path("/rpc") + .body_contains("252dba42") + .body_contains("00000000000000000000000000000000000000aa"); + then.json_body(json!({ + "jsonrpc": "2.0", + "id": 1, + "result": TOKEN_INFO_RESULT, + })); + }); + let partial_info_mock = server.mock(|when, then| { + when.method("POST") + .path("/rpc") + .body_contains("252dba42") + .body_contains("00000000000000000000000000000000000000bb"); + then.json_body(json!({ + "jsonrpc": "2.0", + "id": 1, + "result": TOKEN_INFO_RESULT, + })); + }); + + let mut builder = RaindexOrderBuilder::new_with_deployment( + yaml.to_string(), + None, + "some-deployment".to_string(), + ) + .await + .unwrap(); + + // foreign-token records this address in full on other-network, but + // metadata only carries over within the order's network: the first + // selection still fetches, and gets some-network's values (6 + // decimals, "Token 1"), not other-network's (18, "Foreign Token"). + builder + .set_select_token( + "token3".to_string(), + "0x00000000000000000000000000000000000000aa".to_string(), + ) + .await + .unwrap(); + assert_eq!(shared_info_mock.hits(), 1); + + // Output slot selects the same address: no second fetch. + builder + .set_select_token( + "token4".to_string(), + "0x00000000000000000000000000000000000000aa".to_string(), + ) + .await + .unwrap(); + assert_eq!(shared_info_mock.hits(), 1); + + // The reused metadata lands on the output slot's record in full. + let deployment = builder.get_current_deployment().unwrap(); + let input_token = deployment.deployment.order.inputs[0] + .token + .as_ref() + .unwrap(); + let output_token = deployment.deployment.order.outputs[0] + .token + .as_ref() + .unwrap(); + assert_eq!( + output_token.address, + Address::from_str("0x00000000000000000000000000000000000000aa").unwrap() + ); + assert_eq!(output_token.decimals, Some(6)); + assert_eq!(output_token.label, Some("Token 1".to_string())); + assert_eq!(output_token.symbol, Some("T1".to_string())); + assert_eq!(input_token.decimals, output_token.decimals); + assert_eq!(input_token.label, output_token.label); + assert_eq!(input_token.symbol, output_token.symbol); + + // partial-token records this address with decimals only, which is + // not enough to satisfy a selection: the full info gets fetched. + builder + .set_select_token( + "token5".to_string(), + "0x00000000000000000000000000000000000000bb".to_string(), + ) + .await + .unwrap(); + assert_eq!(partial_info_mock.hits(), 1); + assert_eq!(shared_info_mock.hits(), 1); + + // Re-selecting a key with its current address reuses the record + // that same key wrote (token5 holds the only complete record for + // this address): no refetch. + builder + .set_select_token( + "token5".to_string(), + "0x00000000000000000000000000000000000000bb".to_string(), + ) + .await + .unwrap(); + assert_eq!(partial_info_mock.hits(), 1); + assert_eq!(shared_info_mock.hits(), 1); + } + #[tokio::test] async fn test_get_account_balance() { let server = MockServer::start_async().await; From 1bbbc20babaac83f536a84e5d4abc057c3591acc Mon Sep 17 00:00:00 2001 From: David Meister Date: Mon, 13 Jul 2026 05:20:14 +0000 Subject: [PATCH 4/4] fix(ci): test-js-bindings drop stale decimals mocks after approval-calldata dedup [3b-attempt] generate_approval_calldatas no longer issues a per-token decimals() RPC (decimals come from the deposits map), so the sequence-based mocked decimals responses in the approval tests were consumed by the allowance reads instead, turning a 1000e18 allowance into 18 wei and producing an extra approval calldata (AssertionError: 2 == 1 at builder.test.ts:1463). Remove the now-dead decimals mocks so each deposited token mocks exactly its allowance read. Co-Authored-By: Claude Fable 5 --- packages/raindex/test/js_api/builder.test.ts | 30 ++++---------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/packages/raindex/test/js_api/builder.test.ts b/packages/raindex/test/js_api/builder.test.ts index 40588ea1c5..d07059f49e 100644 --- a/packages/raindex/test/js_api/builder.test.ts +++ b/packages/raindex/test/js_api/builder.test.ts @@ -1421,28 +1421,16 @@ ${dotrain}`; }); it("generates approval calldatas", async () => { - // decimal call - await mockServer - .forPost("/rpc-url") - .once() - .thenSendJsonRpcResult( - "0x0000000000000000000000000000000000000000000000000000000000000012", - ); - // allowance - 1000 * 10^18 + // Decimals come from the deposits map, so the only RPC per token is the + // allowance read: one mocked response per deposited token. + // token1 allowance - 1000 * 10^18 await mockServer .forPost("/rpc-url") .once() .thenSendJsonRpcResult( "0x00000000000000000000000000000000000000000000003635C9ADC5DEA00000", ); - // decimal call - await mockServer - .forPost("/rpc-url") - .once() - .thenSendJsonRpcResult( - "0x0000000000000000000000000000000000000000000000000000000000000012", - ); - // allowance - 1000 * 10^18 + // token2 allowance - 1000 * 10^18 await mockServer .forPost("/rpc-url") .once() @@ -1484,14 +1472,8 @@ ${dotrain}`; }); it("overwrites approvals when allowance is higher than deposit", async () => { - // decimal call - await mockServer - .forPost("/rpc-url") - .once() - .thenSendJsonRpcResult( - "0x0000000000000000000000000000000000000000000000000000000000000012", - ); - // allowance - 5000 * 10^18 + // Decimals come from the deposits map, so the only RPC is the token2 + // allowance read - 5000 * 10^18 await mockServer .forPost("/rpc-url") .once()