diff --git a/crates/js_api/src/subgraph/order.rs b/crates/js_api/src/subgraph/order.rs index 1c23ffffb..edfb80060 100644 --- a/crates/js_api/src/subgraph/order.rs +++ b/crates/js_api/src/subgraph/order.rs @@ -1,12 +1,23 @@ +use std::collections::{HashMap, HashSet}; + use cynic::Id; -use rain_orderbook_bindings::wasm_traits::prelude::*; +use rain_orderbook_bindings::{impl_all_wasm_traits, wasm_traits::prelude::*}; use rain_orderbook_common::types::OrderDetailExtended; use rain_orderbook_subgraph_client::{ - types::common::{Order, OrdersListFilterArgs}, + types::common::{Order, OrdersListFilterArgs, Vault}, MultiOrderbookSubgraphClient, MultiSubgraphArgs, OrderbookSubgraphClient, OrderbookSubgraphClientError, PaginationArgs, }; use reqwest::Url; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Clone, Tsify)] +pub struct OrderWithSortedVaults { + #[tsify(type = "OrderSubgraph")] + pub order: Order, + pub vaults: HashMap>, +} +impl_all_wasm_traits!(OrderWithSortedVaults); /// Internal function to fetch a single order /// Returns the Order struct @@ -29,12 +40,48 @@ pub async fn get_orders( Ok(to_value(&orders)?) } +fn sort_vaults(order: &Order) -> HashMap> { + let mut sorted_vaults: HashMap> = HashMap::new(); + + let input_ids: HashSet<_> = order.inputs.iter().map(|v| &v.id).collect(); + let output_ids: HashSet<_> = order.outputs.iter().map(|v| &v.id).collect(); + + sorted_vaults.insert("inputs".to_string(), Vec::new()); + sorted_vaults.insert("outputs".to_string(), Vec::new()); + sorted_vaults.insert("inputs_outputs".to_string(), Vec::new()); + + for vault in &order.inputs { + if output_ids.contains(&vault.id) { + sorted_vaults + .get_mut("inputs_outputs") + .unwrap() + .push(vault.clone()); + } else { + sorted_vaults.get_mut("inputs").unwrap().push(vault.clone()); + } + } + + for vault in &order.outputs { + if !input_ids.contains(&vault.id) { + sorted_vaults + .get_mut("outputs") + .unwrap() + .push(vault.clone()); + } + } + + sorted_vaults +} + /// Fetch a single order -/// Returns the Order struct +/// Returns the Order struct with sorted vaults #[wasm_bindgen(js_name = "getOrder")] pub async fn get_order(url: &str, id: &str) -> Result { let order = get_sg_order(url, id).await?; - Ok(to_value(&order)?) + Ok(to_value(&OrderWithSortedVaults { + order: order.clone(), + vaults: sort_vaults(&order), + })?) } /// Extend an order to include Rainlang string diff --git a/packages/orderbook/test/js_api/order.test.ts b/packages/orderbook/test/js_api/order.test.ts index 037efe826..dda2643c0 100644 --- a/packages/orderbook/test/js_api/order.test.ts +++ b/packages/orderbook/test/js_api/order.test.ts @@ -1,7 +1,13 @@ import assert from 'assert'; import { getLocal } from 'mockttp'; import { describe, it, beforeEach, afterEach } from 'vitest'; -import { Order, OrderPerformance, OrderWithSubgraphName, Trade } from '../../dist/types/js_api.js'; +import { + OrderPerformance, + OrderSubgraph, + OrderWithSortedVaults, + OrderWithSubgraphName, + Trade +} from '../../dist/types/js_api.js'; import { getOrders, getOrder, @@ -19,7 +25,7 @@ const order1 = { owner: '0x0000000000000000000000000000000000000000', outputs: [ { - id: '0x0000000000000000000000000000000000000000', + id: '0x0000000000000000000000000000000000000001', token: { id: '0x0000000000000000000000000000000000000000', address: '0x0000000000000000000000000000000000000000', @@ -28,7 +34,7 @@ const order1 = { decimals: '0' }, balance: '0', - vaultId: '0', + vaultId: '0x1', owner: '0x0000000000000000000000000000000000000000', ordersAsOutput: [], ordersAsInput: [], @@ -40,7 +46,7 @@ const order1 = { ], inputs: [ { - id: '0x0000000000000000000000000000000000000000', + id: '0x0000000000000000000000000000000000000002', token: { id: '0x0000000000000000000000000000000000000000', address: '0x0000000000000000000000000000000000000000', @@ -49,7 +55,7 @@ const order1 = { decimals: '0' }, balance: '0', - vaultId: '0', + vaultId: '0x2', owner: '0x0000000000000000000000000000000000000000', ordersAsOutput: [], ordersAsInput: [], @@ -77,7 +83,7 @@ const order1 = { }, trades: [] }; -const order2: Order = { +const order2 = { id: 'order2', orderBytes: '0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', @@ -142,7 +148,7 @@ const order2: Order = { id: '0x0000000000000000000000000000000000000000' }, trades: [] -} as unknown as Order; +} as unknown as OrderSubgraph; export const order3 = { id: 'order1', @@ -372,8 +378,8 @@ describe('Rain Orderbook JS API Package Bindgen Tests - Order', async function ( await mockServer.forPost('/sg1').thenReply(200, JSON.stringify({ data: { order: order1 } })); try { - const result: Order = await getOrder(mockServer.url + '/sg1', order1.id); - assert.equal(result.id, order1.id); + const result: OrderWithSortedVaults = await getOrder(mockServer.url + '/sg1', order1.id); + assert.equal(result.order.id, order1.id); } catch (e) { console.log(e); assert.fail('expected to resolve, but failed' + (e instanceof Error ? e.message : String(e))); @@ -617,4 +623,114 @@ describe('Rain Orderbook JS API Package Bindgen Tests - Order', async function ( mockServer.stop(); assert.deepEqual(result, expected); }); + + it('should return vaults sorted by inputs, outputs and inputs and outputs', async () => { + const inputs = [ + { + id: '0x0000000000000000000000000000000000000001', + token: { + id: '0x0000000000000000000000000000000000000000', + address: '0x0000000000000000000000000000000000000000', + name: 'T2', + symbol: 'T2', + decimals: '0' + }, + balance: '0', + vaultId: '0x1', + owner: '0x0000000000000000000000000000000000000000', + ordersAsOutput: [], + ordersAsInput: [], + balanceChanges: [], + orderbook: { + id: '0x0000000000000000000000000000000000000000' + } + }, + { + id: '0x0000000000000000000000000000000000000003', + token: { + id: '0x0000000000000000000000000000000000000000', + address: '0x0000000000000000000000000000000000000000', + name: 'T3', + symbol: 'T3', + decimals: '0' + }, + balance: '0', + vaultId: '0x3', + owner: '0x0000000000000000000000000000000000000000', + ordersAsOutput: [], + ordersAsInput: [], + balanceChanges: [], + orderbook: { + id: '0x0000000000000000000000000000000000000000' + } + } + ]; + const outputs = [ + { + id: '0x0000000000000000000000000000000000000002', + token: { + id: '0x0000000000000000000000000000000000000000', + address: '0x0000000000000000000000000000000000000000', + name: 'T2', + symbol: 'T2', + decimals: '0' + }, + balance: '0', + vaultId: '0x2', + owner: '0x0000000000000000000000000000000000000000', + ordersAsOutput: [], + ordersAsInput: [], + balanceChanges: [], + orderbook: { + id: '0x0000000000000000000000000000000000000000' + } + }, + { + id: '0x0000000000000000000000000000000000000003', + token: { + id: '0x0000000000000000000000000000000000000000', + address: '0x0000000000000000000000000000000000000000', + name: 'T3', + symbol: 'T3', + decimals: '0' + }, + balance: '0', + vaultId: '0x3', + owner: '0x0000000000000000000000000000000000000000', + ordersAsOutput: [], + ordersAsInput: [], + balanceChanges: [], + orderbook: { + id: '0x0000000000000000000000000000000000000000' + } + } + ]; + await mockServer + .forPost('/sg1') + .once() + .thenReply(200, JSON.stringify({ data: { order: { ...order1, inputs, outputs } } })); + + try { + const result: OrderWithSortedVaults = await getOrder(mockServer.url + '/sg1', order1.id); + + const inputs = result.vaults.get('inputs'); + const outputs = result.vaults.get('outputs'); + const inputsOutputs = result.vaults.get('inputs_outputs'); + + if (!inputs || !outputs || !inputsOutputs) { + assert.fail('inputs, outputs or inputsOutputs should not be null'); + } + + assert.equal(inputs.length, 1); + assert.equal(outputs.length, 1); + assert.equal(inputsOutputs.length, 1); + + assert.equal(inputs[0].id, '0x0000000000000000000000000000000000000001'); + assert.equal(outputs[0].id, '0x0000000000000000000000000000000000000002'); + assert.equal(inputsOutputs[0].id, '0x0000000000000000000000000000000000000003'); + } catch (e) { + console.log(e); + assert.fail('expected to resolve, but failed' + (e instanceof Error ? e.message : String(e))); + } + }); }); diff --git a/packages/ui-components/src/__tests__/OrderDetail.test.svelte b/packages/ui-components/src/__tests__/OrderDetail.test.svelte index 2cace9b7b..284198db7 100644 --- a/packages/ui-components/src/__tests__/OrderDetail.test.svelte +++ b/packages/ui-components/src/__tests__/OrderDetail.test.svelte @@ -1,11 +1,14 @@