diff --git a/subgraph/tests/entities.rs b/subgraph/tests/entities.rs index e32d0355b..a96f58bc2 100644 --- a/subgraph/tests/entities.rs +++ b/subgraph/tests/entities.rs @@ -16,11 +16,11 @@ use utils::{ deploy::{deploy_erc20_mock, get_expression_deployer, get_orderbook, read_orderbook_meta}, events::{ _get_new_expression_event, get_add_order_event, get_after_clear_events, get_clear_events, - get_deposit_events, get_take_order_events, get_withdraw_events, + get_deposit_events, get_take_order_event, get_take_order_events, get_withdraw_events, }, generate_random_u256, get_wallet, h256_to_bytes, json_structs::{NewExpressionJson, OrderJson}, - numbers::{display_number, get_amount_tokens}, + numbers::{display_number, divide_decimal_strings, get_amount_tokens}, transactions::{ approve_tokens, generate_clear_config, generate_multi_add_order, generate_multi_clear, generate_multi_deposit, generate_multi_withdraw, generate_order_config, get_block_data, @@ -2597,7 +2597,7 @@ async fn order_clear_state_change_entity_clear_test() -> anyhow::Result<()> { } #[tokio::main] -#[test] +// #[test] async fn token_vault_take_order_entity_take_order_test() -> anyhow::Result<()> { let orderbook = get_orderbook().await?; @@ -2746,6 +2746,187 @@ async fn token_vault_take_order_entity_take_order_test() -> anyhow::Result<()> { Ok(()) } +#[tokio::main] +#[test] +async fn take_order_entity_take_order_test() -> anyhow::Result<()> { + let orderbook = get_orderbook().await?; + + let alice = get_wallet(1); + let bob = get_wallet(2); + + // Connect the orderbook to another wallet + let orderbook = orderbook.connect(&alice).await; + + // Vault id + let vault_id = generate_random_u256(); + + // Deploy ExpressionDeployerNP for the config + let expression_deployer = get_expression_deployer().await?; + // let expression_deployer = get_expression_deployer().await?; + + // Deploy ERC20 token contract (A) connected to Alice + let token_input = deploy_erc20_mock(None).await?; + + // Deploy ERC20 token contract (B) connected to Alice + let token_output = deploy_erc20_mock(None).await?; + + // Build OrderConfig + let order_config = generate_order_config( + expression_deployer, + &token_input, + Some(vault_id), + &token_output, + Some(vault_id), + ) + .await; + + // Add the order + let add_order_func = orderbook.add_order(order_config.clone()); + let tx_add_order = add_order_func.send().await?; + + // Decode events from the transaction + let add_order_data = get_add_order_event(&orderbook, &tx_add_order).await?; + + // Amount to deposit + let amount_b = get_amount_tokens(1000, token_output.decimals().call().await.unwrap()); + + // Fill to Alice with tokens + mint_tokens(&amount_b, &alice.address(), &token_output).await?; + + // Connect token to Alice and approve Orderbook to move tokens + approve_tokens( + &amount_b, + &orderbook.address(), + &token_output.connect(&alice).await, + ) + .await?; + + // Alice deposit tokens + let deposit_func = orderbook.deposit(token_output.address(), vault_id, amount_b); + let _ = deposit_func.send().await?; + + // BOB TAKE THE ORDER + + // Take Order configs + let minimum_input = U256::from(0); + let maximum_input = get_amount_tokens(1000, token_output.decimals().call().await.unwrap()); + let maximum_io_ratio = U256::from(10000000000000000000u64); // 10e18 + + let take_order_config = TakeOrderConfig { + order: add_order_data.order, + input_io_index: U256::zero(), + output_io_index: U256::zero(), + signed_context: Vec::new(), + }; + + let take_orders_config = TakeOrdersConfigV2 { + minimum_input, + maximum_input, + maximum_io_ratio, + orders: vec![take_order_config], + data: Bytes::new(), // Empty data + }; + + // Fill bob with token A (token input of the order) + // let amount_a = get_amount_tokens(1000, token_a.decimals().call().await.unwrap()); + let amount_a = amount_b + .saturating_mul(maximum_io_ratio) + .checked_div(U256::from(1000000000000000000u64)) // 1e18 + .unwrap(); + + mint_tokens(&amount_a, &bob.address(), &token_input).await?; + + // Connect token to Bob and approve Orderbook to move tokens + approve_tokens( + &amount_a, + &orderbook.address(), + &token_input.connect(&bob).await, + ) + .await?; + + // Take the order + let take_order_func = orderbook + .connect(&bob) + .await + .take_orders(take_orders_config); + let tx_take_order = take_order_func.send().await?; + + let tx_receipt = tx_take_order.await?.unwrap(); + + let take_order_tx_hash = &tx_receipt.transaction_hash; + let take_order_event = get_take_order_event(&orderbook, &take_order_tx_hash).await?; + + let block_data = get_block_data(&take_order_tx_hash).await?; + + // Using index 0 since only one take order was made in this tx + let take_order_entity = format!("{:?}-{}", take_order_tx_hash, 0); + let token_vault_input = format!( + "{}-{:?}-{:?}", + vault_id, + alice.address(), + token_input.address() + ); + let token_vault_output = format!( + "{}-{:?}-{:?}", + vault_id, + alice.address(), + token_output.address() + ); + + let token_vault_take_order_a_id = format!("{}-{}", take_order_entity, token_vault_input); + let token_vault_take_order_b_id = format!("{}-{}", take_order_entity, token_vault_output); + + let input_token = take_order_event + .config + .order + .valid_inputs + .first() + .unwrap() + .token; + + let output_token = take_order_event + .config + .order + .valid_outputs + .first() + .unwrap() + .token; + + let input_display = display_number(take_order_event.input, get_decimals(input_token).await?); + let output_display = display_number(take_order_event.output, get_decimals(output_token).await?); + + let io_ratio = + divide_decimal_strings(&input_display, &output_display).unwrap_or("0".to_string()); + + let resp = Query::take_order_entity(&token_vault_input).await?; + + assert_eq!(resp.sender, take_order_event.sender); + assert_eq!(resp.order, h256_to_bytes(&add_order_data.order_hash.into())); + + assert_eq!(resp.input, take_order_event.input); + assert_eq!(resp.input_display, input_display); + + assert_eq!(resp.output, take_order_event.output); + assert_eq!(resp.output_display, output_display); + + assert_eq!(resp.io_ratio, io_ratio); + + assert_eq!(resp.input_io_index, take_order_event.config.input_io_index); + assert_eq!( + resp.output_io_index, + take_order_event.config.output_io_index + ); + + assert_eq!(resp.input_token, input_token); + assert_eq!(resp.output_token, output_token); + + assert_eq!(resp.transaction, *take_order_tx_hash); + assert_eq!(resp.emitter, take_order_event.sender); + assert_eq!(resp.timestamp, block_data.timestamp); + + Ok(()) +} + // #[test] fn util_cbor_meta_test() -> anyhow::Result<()> { // Read meta from root repository (output from nix command) and convert to Bytes diff --git a/subgraph/tests/subgraph/query/mod.rs b/subgraph/tests/subgraph/query/mod.rs index 898170885..906096d13 100644 --- a/subgraph/tests/subgraph/query/mod.rs +++ b/subgraph/tests/subgraph/query/mod.rs @@ -12,6 +12,7 @@ pub(crate) mod token_vault_take_order; pub(crate) mod vault; pub(crate) mod vault_deposit; pub(crate) mod vault_withdraw; +pub(crate) mod take_order_entity; use anyhow::Result; use ethers::types::{Address, Bytes}; @@ -32,6 +33,7 @@ use token_vault_take_order::{get_token_vault_take_order, TokenVaultTakeOrderResp use vault::{get_vault, VaultResponse}; use vault_deposit::{get_vault_deposit, VaultDepositResponse}; use vault_withdraw::{get_vault_withdraw, VaultWithdrawResponse}; +use take_order_entity::{get_take_order_entity, TakeOrderEntityResponse}; pub static SG_URL: Lazy = Lazy::new(|| Url::parse("http://localhost:8000/subgraphs/name/test/test").unwrap()); @@ -94,4 +96,8 @@ impl Query { pub async fn token_vault_take_order(id: &String) -> Result { get_token_vault_take_order(id).await } + + pub async fn take_order_entity(id: &String) -> Result { + get_take_order_entity(id).await + } } diff --git a/subgraph/tests/subgraph/query/take_order_entity/mod.rs b/subgraph/tests/subgraph/query/take_order_entity/mod.rs new file mode 100644 index 000000000..34e581bd8 --- /dev/null +++ b/subgraph/tests/subgraph/query/take_order_entity/mod.rs @@ -0,0 +1,102 @@ +use self::take_order_entity::ResponseData; +use super::SG_URL; +use crate::utils::{bytes_to_h256, hex_string_to_bytes, mn_mpz_to_u256}; +use anyhow::{anyhow, Result}; +use ethers::types::TxHash; +use ethers::types::{Address, Bytes, U256}; +use graphql_client::{GraphQLQuery, Response}; +use rust_bigint::BigInt; +use serde::{Deserialize, Serialize}; + +// use bigdecimal::BigDecimal; +type BigDecimal = String; + +#[derive(GraphQLQuery)] +#[graphql( + schema_path = "tests/subgraph/query/schema.json", + query_path = "tests/subgraph/query/take_order_entity/take_order_entity.graphql", + response_derives = "Debug, Serialize, Deserialize" +)] +#[derive(Serialize, Deserialize, Debug)] +pub struct TakeOrderEntity; + +#[derive(Serialize, Deserialize, Debug)] +pub struct TakeOrderEntityResponse { + pub id: String, + pub sender: Address, + pub order: Bytes, + pub input: U256, + pub input_display: String, + pub output: U256, + pub output_display: String, + pub io_ratio: String, + pub input_io_index: U256, + pub output_io_index: U256, + pub input_token: Address, + pub output_token: Address, + pub context: Option, + pub emitter: Address, + pub transaction: TxHash, + pub timestamp: U256, +} + +impl TakeOrderEntityResponse { + pub fn from(response: ResponseData) -> TakeOrderEntityResponse { + let data = response.take_order_entity.unwrap(); + + let sender = Address::from_slice(&data.sender.id); + let input_token = Address::from_slice(&hex_string_to_bytes(&data.input_token.id).unwrap()); + let output_token = + Address::from_slice(&hex_string_to_bytes(&data.output_token.id).unwrap()); + let emitter = Address::from_slice(&data.emitter.id); + let transaction = bytes_to_h256(&hex_string_to_bytes(&data.transaction.id).unwrap()); + + let context = match data.context { + Some(value) => Some(value.id), + None => None, + }; + + TakeOrderEntityResponse { + id: data.id, + sender, + order: hex_string_to_bytes(&data.order.id).unwrap(), + input: mn_mpz_to_u256(&data.input), + input_display: data.input_display, + output: mn_mpz_to_u256(&data.input), + output_display: data.output_display, + io_ratio: data.io_ratio, + input_io_index: mn_mpz_to_u256(&data.input_io_index), + output_io_index: mn_mpz_to_u256(&data.output_io_index), + input_token, + output_token, + context, + emitter, + transaction, + timestamp: mn_mpz_to_u256(&data.timestamp), + } + } +} + +pub async fn get_vault_deposit(id: &String) -> Result { + let variables = take_order_entity::Variables { + id: id.to_string().into(), + }; + + let request_body = TakeOrderEntity::build_query(variables); + let client = reqwest::Client::new(); + let res = client + .post((*SG_URL).clone()) + .json(&request_body) + .send() + .await?; + + let response_body: Response = res.json().await?; + + match response_body.data { + Some(data) => { + let response = TakeOrderEntityResponse::from(data); + Ok(response) + } + None => Err(anyhow!("Failed to get query")), + } +} diff --git a/subgraph/tests/subgraph/query/take_order_entity/take_order_entity.graphql b/subgraph/tests/subgraph/query/take_order_entity/take_order_entity.graphql new file mode 100644 index 000000000..28d1269c3 --- /dev/null +++ b/subgraph/tests/subgraph/query/take_order_entity/take_order_entity.graphql @@ -0,0 +1,34 @@ +query TakeOrderEntity($id: String) { + takeOrderEntity(id: $id) { + id + sender { + id + } + order { + id + } + input + inputDisplay + output + outputDisplay + IORatio + inputIOIndex + outputIOIndex + inputToken { + id + } + outputToken { + id + } + transaction { + id + } + emitter { + id + } + timestamp + context { + id + } + } +} diff --git a/subgraph/tests/utils/numbers.rs b/subgraph/tests/utils/numbers.rs index 41791c8f7..a029519b8 100644 --- a/subgraph/tests/utils/numbers.rs +++ b/subgraph/tests/utils/numbers.rs @@ -1,5 +1,10 @@ +use bigdecimal::Zero; use ethers::types::U256; -use std::ops::Mul; +use std::{ops::Mul, str::FromStr}; +extern crate bigdecimal; +use bigdecimal::BigDecimal; +use bigdecimal::FromPrimitive; +use bigdecimal::ToPrimitive; pub fn get_amount_tokens(amount: u64, decimals: u8) -> U256 { let result: U256 = U256::from(amount).mul(U256::from(10).pow(U256::from(decimals))); @@ -34,3 +39,22 @@ pub fn display_number(number: U256, decimals: u8) -> String { result } + +pub fn divide_decimal_strings(value_1: &str, value_2: &str) -> Option { + // Parse the input strings into BigDecimal values + let decimal_1 = BigDecimal::from_str(value_1).expect("Invalid decimal value for value_1"); + let decimal_2 = BigDecimal::from_str(value_2).expect("Invalid decimal value for value_2"); + + // Check for division by zero + if decimal_2.is_zero() { + return None; + } + + // Perform the division + let result = decimal_1 / decimal_2; + + // Format the result as a string with a specific precision + let formatted_result = format!("{:.1}", result); // Adjust the precision as needed + + Some(formatted_result) +}