diff --git a/crates/common/src/dotrain_order/calldata.rs b/crates/common/src/dotrain_order/calldata.rs deleted file mode 100644 index 4d14f50d7..000000000 --- a/crates/common/src/dotrain_order/calldata.rs +++ /dev/null @@ -1,183 +0,0 @@ -use super::*; -use crate::{ - add_order::{AddOrderArgs, AddOrderArgsError}, - deposit::{DepositArgs, DepositError}, - transaction::{TransactionArgs, WritableTransactionExecuteError}, -}; -use alloy::{ - hex::FromHexError, - primitives::{Bytes, U256}, -}; -use rain_orderbook_app_settings::{deployment::DeploymentCfg, orderbook::OrderbookCfg}; -use std::{collections::HashMap, str::FromStr, sync::Arc}; - -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] -#[cfg_attr(target_family = "wasm", derive(Tsify))] -pub struct ApprovalCalldata { - #[cfg_attr(target_family = "wasm", tsify(type = "string"))] - pub token: Address, - #[cfg_attr(target_family = "wasm", tsify(type = "string"))] - pub calldata: Bytes, -} -#[cfg(target_family = "wasm")] -impl_wasm_traits!(ApprovalCalldata); - -impl DotrainOrder { - fn get_deployment( - &self, - deployment_name: &str, - ) -> Result { - Ok(self.dotrain_yaml.get_deployment(deployment_name)?) - } - - fn get_orderbook( - &self, - deployment_name: &str, - ) -> Result, DotrainOrderCalldataError> { - self.get_deployment(deployment_name)? - .order - .orderbook - .clone() - .ok_or(DotrainOrderCalldataError::OrderbookNotFound) - } - - pub async fn generate_approval_calldatas( - &self, - deployment_name: &str, - owner: &str, - token_deposits: &HashMap, - ) -> Result, DotrainOrderCalldataError> { - let deployment = self.get_deployment(deployment_name)?; - let orderbook = self.get_orderbook(deployment_name)?; - - let mut calldatas = Vec::new(); - - for (i, output) in deployment.order.outputs.iter().enumerate() { - let output_token = output - .token - .as_ref() - .ok_or_else(|| DotrainOrderCalldataError::OutputTokenNotFound(i.to_string()))?; - - if let Some(deposit_amount) = token_deposits.get(&output_token.key) { - let deposit_amount = deposit_amount.to_owned(); - let deposit_args = DepositArgs { - token: output_token.address, - amount: deposit_amount, - vault_id: U256::default(), - }; - let transaction_args = TransactionArgs { - orderbook_address: orderbook.address, - rpc_url: orderbook.network.rpc.to_string(), - ..Default::default() - }; - - let allowance = deposit_args - .read_allowance(Address::from_str(owner)?, transaction_args.clone()) - .await?; - - if allowance < deposit_amount { - let approve_call = deposit_args.get_approve_calldata(transaction_args).await?; - calldatas.push(ApprovalCalldata { - token: output_token.address, - calldata: Bytes::copy_from_slice(&approve_call), - }); - } - } - } - - Ok(calldatas) - } - - pub async fn generate_deposit_calldatas( - &mut self, - deployment_name: &str, - token_deposits: &HashMap<(U256, Address), U256>, - ) -> Result, DotrainOrderCalldataError> { - let deployment = self.get_deployment(deployment_name)?; - let mut calldatas = Vec::new(); - - for (i, output) in deployment.order.outputs.iter().enumerate() { - let output_token = output - .token - .as_ref() - .ok_or_else(|| DotrainOrderCalldataError::OutputTokenNotFound(i.to_string()))?; - let vault_id = output - .vault_id - .ok_or(DotrainOrderCalldataError::VaultIdNotFound(i.to_string()))?; - - let token_deposit = token_deposits - .get(&(vault_id, output_token.address)) - .ok_or(DotrainOrderCalldataError::TokenNotFound( - output_token.address.to_string(), - ))?; - - if *token_deposit == U256::ZERO { - continue; - } - - let calldata = DepositArgs { - token: output_token.address, - amount: token_deposit.to_owned(), - vault_id, - } - .get_deposit_calldata() - .await?; - - calldatas.push(Bytes::copy_from_slice(&calldata)); - } - - Ok(calldatas) - } - - pub async fn generate_add_order_calldata( - &mut self, - deployment_name: &str, - ) -> Result { - let deployment = self.get_deployment(deployment_name)?; - let orderbook = self.get_orderbook(deployment_name)?; - - let calldata = AddOrderArgs::new_from_deployment(self.dotrain().to_string(), deployment) - .await? - .get_add_order_calldata(TransactionArgs { - orderbook_address: orderbook.address, - rpc_url: orderbook.network.rpc.to_string(), - ..Default::default() - }) - .await?; - - Ok(Bytes::copy_from_slice(&calldata)) - } -} - -#[derive(Debug, Error)] -pub enum DotrainOrderCalldataError { - #[error("Deployment not found {0}")] - DeploymentNotFound(String), - - #[error("Orderbook not found")] - OrderbookNotFound, - - #[error("Token not found for output index: {0}")] - OutputTokenNotFound(String), - - #[error("Vault id not found for output index: {0}")] - VaultIdNotFound(String), - - #[error("Token not found {0}")] - TokenNotFound(String), - - #[error(transparent)] - DepositError(#[from] DepositError), - - #[error(transparent)] - WritableTransactionExecuteError(#[from] WritableTransactionExecuteError), - - #[error(transparent)] - FromHexError(#[from] FromHexError), - - #[error(transparent)] - AddOrderArgsError(#[from] AddOrderArgsError), - - #[error(transparent)] - YamlError(#[from] YamlError), -} diff --git a/crates/common/src/dotrain_order/mod.rs b/crates/common/src/dotrain_order/mod.rs index c3c3be3a9..5dc86df76 100644 --- a/crates/common/src/dotrain_order/mod.rs +++ b/crates/common/src/dotrain_order/mod.rs @@ -15,9 +15,7 @@ use rain_orderbook_app_settings::ParseConfigSourceError; use serde::{Deserialize, Serialize}; use thiserror::Error; #[cfg(target_family = "wasm")] -use wasm_bindgen_utils::{impl_wasm_traits, prelude::*}; - -pub mod calldata; +use wasm_bindgen_utils::prelude::*; #[derive(Debug, Clone, Serialize, Deserialize, Default)] #[cfg_attr(target_family = "wasm", wasm_bindgen)] diff --git a/crates/js_api/src/gui/mod.rs b/crates/js_api/src/gui/mod.rs index dd95ba2e5..8dc8de73b 100644 --- a/crates/js_api/src/gui/mod.rs +++ b/crates/js_api/src/gui/mod.rs @@ -14,7 +14,7 @@ use rain_orderbook_app_settings::{ }; use rain_orderbook_common::{ dotrain::{types::patterns::FRONTMATTER_SEPARATOR, RainDocument}, - dotrain_order::{calldata::DotrainOrderCalldataError, DotrainOrder, DotrainOrderError}, + dotrain_order::{DotrainOrder, DotrainOrderError}, erc20::ERC20, }; use serde::{Deserialize, Serialize}; @@ -296,8 +296,6 @@ pub enum GuiError { #[error(transparent)] SerdeWasmBindgenError(#[from] serde_wasm_bindgen::Error), #[error(transparent)] - DotrainOrderCalldataError(#[from] DotrainOrderCalldataError), - #[error(transparent)] YamlError(#[from] YamlError), } impl From for JsValue { diff --git a/crates/js_api/src/gui/order_operations.rs b/crates/js_api/src/gui/order_operations.rs index 361c3a692..f25114d6d 100644 --- a/crates/js_api/src/gui/order_operations.rs +++ b/crates/js_api/src/gui/order_operations.rs @@ -6,12 +6,13 @@ use alloy::{ }; use rain_orderbook_app_settings::{order::OrderIOCfg, orderbook::OrderbookCfg}; use rain_orderbook_bindings::OrderBook::multicallCall; -use rain_orderbook_common::{deposit::DepositArgs, dotrain_order, transaction::TransactionArgs}; +use rain_orderbook_common::{ + add_order::AddOrderArgs, deposit::DepositArgs, transaction::TransactionArgs, +}; use std::{collections::HashMap, str::FromStr, sync::Arc}; pub enum CalldataFunction { Allowance, - Approval, Deposit, AddOrder, DepositAndAddOrder, @@ -37,7 +38,7 @@ impl_wasm_traits!(AllowancesResult); #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Tsify)] pub enum ApprovalCalldataResult { NoDeposits, - Calldatas(Vec), + Calldatas(Vec), } impl_wasm_traits!(ApprovalCalldataResult); @@ -88,6 +89,24 @@ pub struct DeploymentTransactionArgs { } impl_wasm_traits!(DeploymentTransactionArgs); +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +#[cfg_attr(target_family = "wasm", derive(Tsify))] +pub struct ApprovalCalldata { + #[cfg_attr(target_family = "wasm", tsify(type = "string"))] + pub token: Address, + #[cfg_attr(target_family = "wasm", tsify(type = "string"))] + pub calldata: Bytes, +} +#[cfg(target_family = "wasm")] +impl_wasm_traits!(ApprovalCalldata); + +#[derive(Debug)] +pub struct VaultAndDeposit { + pub order_io: OrderIOCfg, + pub deposit_amount: U256, + pub index: usize, +} + #[wasm_bindgen] impl DotrainOrderGui { fn get_orderbook(&self) -> Result, GuiError> { @@ -103,12 +122,21 @@ impl DotrainOrderGui { .cloned() } - async fn get_deposits_as_map(&self) -> Result, GuiError> { - let mut map: HashMap = HashMap::new(); + fn get_transaction_args(&self) -> Result { + let orderbook = self.get_orderbook()?; + Ok(TransactionArgs { + orderbook_address: orderbook.address, + rpc_url: orderbook.network.rpc.to_string(), + ..Default::default() + }) + } + + async fn get_deposits_as_map(&self) -> Result, GuiError> { + 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 = parse_units(&d.amount, token_info.decimals)?.into(); - map.insert(d.token, amount); + map.insert(token_info.address, amount); } Ok(map) } @@ -116,7 +144,7 @@ impl DotrainOrderGui { async fn get_vaults_and_deposits( &self, deployment: &GuiDeploymentCfg, - ) -> Result, GuiError> { + ) -> Result, GuiError> { let deposits_map = self.get_deposits_as_map().await?; let results = deployment .deployment @@ -124,19 +152,17 @@ impl DotrainOrderGui { .outputs .clone() .into_iter() - .filter(|output| { - output - .token - .as_ref() - .map_or(false, |token| deposits_map.contains_key(&token.key)) - }) - .map(|output| { - if output.token.is_none() { - return Err(GuiError::SelectTokensNotSet); - } - let token = output.token.as_ref().unwrap(); - - Ok((output.clone(), *deposits_map.get(&token.key).unwrap())) + .enumerate() + .filter_map(|(index, output)| { + output.token.as_ref().and_then(|token| { + deposits_map.get(&token.address).map(|amount| { + Ok(VaultAndDeposit { + order_io: output.clone(), + deposit_amount: *amount, + index, + }) + }) + }) }) .collect::, GuiError>>()?; Ok(results) @@ -144,19 +170,11 @@ impl DotrainOrderGui { async fn check_allowance( &self, - orderbook: &OrderbookCfg, deposit_args: &DepositArgs, owner: &str, ) -> Result { let allowance = deposit_args - .read_allowance( - Address::from_str(owner)?, - TransactionArgs { - orderbook_address: orderbook.address, - rpc_url: orderbook.network.rpc.to_string(), - ..Default::default() - }, - ) + .read_allowance(Address::from_str(owner)?, self.get_transaction_args()?) .await?; Ok(TokenAllowance { token: deposit_args.token, @@ -191,23 +209,28 @@ impl DotrainOrderGui { pub async fn check_allowances(&mut self, owner: String) -> Result { let deployment = self.prepare_calldata_generation(CalldataFunction::Allowance)?; - let orderbook = self.get_orderbook()?; let vaults_and_deposits = self.get_vaults_and_deposits(&deployment).await?; let mut results = Vec::new(); - for (order_io, amount) in vaults_and_deposits.iter() { - if order_io.token.is_none() { - return Err(GuiError::SelectTokensNotSet); - } - let token = order_io.token.as_ref().unwrap(); - - let deposit_args = DepositArgs { - token: token.address, - vault_id: rand::random(), - amount: *amount, - }; + for VaultAndDeposit { + order_io, + deposit_amount, + index: _, + } in vaults_and_deposits + { let allowance = self - .check_allowance(&orderbook, &deposit_args, &owner) + .check_allowance( + &DepositArgs { + token: order_io + .token + .as_ref() + .ok_or(GuiError::SelectTokensNotSet)? + .address, + vault_id: rand::random(), + amount: deposit_amount, + }, + &owner, + ) .await?; results.push(allowance); } @@ -223,17 +246,33 @@ impl DotrainOrderGui { &mut self, owner: String, ) -> Result { - let deployment = self.prepare_calldata_generation(CalldataFunction::Approval)?; - let deposits_map = self.get_deposits_as_map().await?; if deposits_map.is_empty() { return Ok(ApprovalCalldataResult::NoDeposits); } - let calldatas = self - .dotrain_order - .generate_approval_calldatas(&deployment.key, &owner, &deposits_map) - .await?; + let transaction_args = self.get_transaction_args()?; + + let mut calldatas = Vec::new(); + for (token_address, deposit_amount) in deposits_map { + let deposit_args = DepositArgs { + token: token_address.clone(), + amount: deposit_amount, + vault_id: U256::default(), + }; + + let token_allowance = self.check_allowance(&deposit_args, &owner).await?; + if token_allowance.allowance < deposit_amount { + let approve_call = deposit_args + .get_approve_calldata(transaction_args.clone()) + .await?; + calldatas.push(ApprovalCalldata { + token: token_address, + calldata: Bytes::copy_from_slice(&approve_call), + }); + } + } + Ok(ApprovalCalldataResult::Calldatas(calldatas)) } @@ -265,34 +304,39 @@ impl DotrainOrderGui { pub async fn generate_deposit_calldatas(&mut self) -> Result { let deployment = self.prepare_calldata_generation(CalldataFunction::Deposit)?; - let token_deposits = self - .get_vaults_and_deposits(&deployment) - .await? - .iter() - .enumerate() - .map(|(i, (order_io, amount))| { - let vault_id = order_io - .vault_id - .ok_or(GuiError::VaultIdNotFound(i.to_string()))?; + let vaults_and_deposits = self.get_vaults_and_deposits(&deployment).await?; + if vaults_and_deposits.is_empty() { + return Ok(DepositCalldataResult::NoDeposits); + } - if order_io.token.is_none() { - return Err(GuiError::SelectTokensNotSet); - } - let token = order_io.token.as_ref().unwrap(); + let mut calldatas = Vec::new(); + for VaultAndDeposit { + order_io, + deposit_amount, + index, + } in vaults_and_deposits + { + let token = order_io + .token + .as_ref() + .ok_or(GuiError::SelectTokensNotSet)?; + let vault_id = order_io + .vault_id + .ok_or(GuiError::VaultIdNotFound(index.to_string()))?; - Ok(((vault_id, token.address), *amount)) - }) - .collect::, GuiError>>()?; + if deposit_amount == U256::ZERO { + continue; + } - if token_deposits.is_empty() { - return Ok(DepositCalldataResult::NoDeposits); + let deposit_args = DepositArgs { + token: token.address, + amount: deposit_amount, + vault_id, + }; + let calldata = deposit_args.get_deposit_calldata().await?; + calldatas.push(Bytes::copy_from_slice(&calldata)); } - let calldatas = self - .dotrain_order - .generate_deposit_calldatas(&deployment.key, &token_deposits) - .await?; - Ok(DepositCalldataResult::Calldatas(calldatas)) } @@ -302,18 +346,22 @@ impl DotrainOrderGui { &mut self, ) -> Result { let deployment = self.prepare_calldata_generation(CalldataFunction::AddOrder)?; - let calldata = self - .dotrain_order - .generate_add_order_calldata(&deployment.key) - .await?; - Ok(AddOrderCalldataResult(calldata)) + + let calldata = AddOrderArgs::new_from_deployment( + self.dotrain_order.dotrain().to_string(), + deployment.deployment.as_ref().clone(), + ) + .await? + .get_add_order_calldata(self.get_transaction_args()?) + .await?; + Ok(AddOrderCalldataResult(Bytes::copy_from_slice(&calldata))) } #[wasm_bindgen(js_name = "generateDepositAndAddOrderCalldatas")] pub async fn generate_deposit_and_add_order_calldatas( &mut self, ) -> Result { - let deployment = self.prepare_calldata_generation(CalldataFunction::DepositAndAddOrder)?; + self.prepare_calldata_generation(CalldataFunction::DepositAndAddOrder)?; let mut calls = Vec::new(); @@ -324,12 +372,9 @@ impl DotrainOrderGui { DepositCalldataResult::NoDeposits => Vec::new(), }; - let add_order_calldata = self - .dotrain_order - .generate_add_order_calldata(&deployment.key) - .await?; + let add_order_calldata = self.generate_add_order_calldata().await?; - calls.push(Bytes::copy_from_slice(&add_order_calldata)); + calls.push(Bytes::copy_from_slice(&add_order_calldata.0)); for calldata in deposit_calldatas.iter() { calls.push(Bytes::copy_from_slice(calldata)); diff --git a/crates/js_api/src/subgraph/mod.rs b/crates/js_api/src/subgraph/mod.rs index 47ae7fa88..55139b474 100644 --- a/crates/js_api/src/subgraph/mod.rs +++ b/crates/js_api/src/subgraph/mod.rs @@ -1,8 +1,5 @@ use alloy::{hex::FromHexError, primitives::ruint::ParseError}; -use rain_orderbook_common::{ - deposit::DepositError, dotrain_order::calldata::DotrainOrderCalldataError, - transaction::WritableTransactionExecuteError, -}; +use rain_orderbook_common::{deposit::DepositError, transaction::WritableTransactionExecuteError}; use rain_orderbook_subgraph_client::OrderbookSubgraphClientError; use thiserror::Error; use wasm_bindgen_utils::prelude::*; @@ -24,8 +21,6 @@ pub enum SubgraphError { #[error(transparent)] OrderbookSubgraphClientError(#[from] OrderbookSubgraphClientError), #[error(transparent)] - DotrainOrderCalldataError(#[from] DotrainOrderCalldataError), - #[error(transparent)] WritableTransactionExecuteError(#[from] WritableTransactionExecuteError), #[error(transparent)] ParseError(#[from] ParseError), diff --git a/packages/orderbook/test/js_api/gui.test.ts b/packages/orderbook/test/js_api/gui.test.ts index 32d3a1759..695e1fce3 100644 --- a/packages/orderbook/test/js_api/gui.test.ts +++ b/packages/orderbook/test/js_api/gui.test.ts @@ -1179,10 +1179,6 @@ ${dotrainWithoutVaultIds}`; await expect(async () => testGui.checkAllowances('0x1234567890abcdef1234567890abcdef12345678') ).rejects.toThrow('Token must be selected: token1'); - await expect( - async () => - await testGui.generateApprovalCalldatas('0x1234567890abcdef1234567890abcdef12345678') - ).rejects.toThrow('Token must be selected: token1'); await expect(async () => await testGui.generateDepositCalldatas()).rejects.toThrow( 'Token must be selected: token1' );