Skip to content

Commit

Permalink
Merge pull request #1296 from rainlanguage/2025-02-13-io-vaults
Browse files Browse the repository at this point in the history
  • Loading branch information
hardyjosh authored Feb 14, 2025
2 parents df2ea94 + f03f565 commit 671bc63
Show file tree
Hide file tree
Showing 5 changed files with 361 additions and 78 deletions.
55 changes: 51 additions & 4 deletions crates/js_api/src/subgraph/order.rs
Original file line number Diff line number Diff line change
@@ -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<String, Vec<Vault>>,
}
impl_all_wasm_traits!(OrderWithSortedVaults);

/// Internal function to fetch a single order
/// Returns the Order struct
Expand All @@ -29,12 +40,48 @@ pub async fn get_orders(
Ok(to_value(&orders)?)
}

fn sort_vaults(order: &Order) -> HashMap<String, Vec<Vault>> {
let mut sorted_vaults: HashMap<String, Vec<Vault>> = 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<JsValue, OrderbookSubgraphClientError> {
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
Expand Down
134 changes: 125 additions & 9 deletions packages/orderbook/test/js_api/order.test.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -19,7 +25,7 @@ const order1 = {
owner: '0x0000000000000000000000000000000000000000',
outputs: [
{
id: '0x0000000000000000000000000000000000000000',
id: '0x0000000000000000000000000000000000000001',
token: {
id: '0x0000000000000000000000000000000000000000',
address: '0x0000000000000000000000000000000000000000',
Expand All @@ -28,7 +34,7 @@ const order1 = {
decimals: '0'
},
balance: '0',
vaultId: '0',
vaultId: '0x1',
owner: '0x0000000000000000000000000000000000000000',
ordersAsOutput: [],
ordersAsInput: [],
Expand All @@ -40,7 +46,7 @@ const order1 = {
],
inputs: [
{
id: '0x0000000000000000000000000000000000000000',
id: '0x0000000000000000000000000000000000000002',
token: {
id: '0x0000000000000000000000000000000000000000',
address: '0x0000000000000000000000000000000000000000',
Expand All @@ -49,7 +55,7 @@ const order1 = {
decimals: '0'
},
balance: '0',
vaultId: '0',
vaultId: '0x2',
owner: '0x0000000000000000000000000000000000000000',
ordersAsOutput: [],
ordersAsInput: [],
Expand Down Expand Up @@ -77,7 +83,7 @@ const order1 = {
},
trades: []
};
const order2: Order = {
const order2 = {
id: 'order2',
orderBytes:
'0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
Expand Down Expand Up @@ -142,7 +148,7 @@ const order2: Order = {
id: '0x0000000000000000000000000000000000000000'
},
trades: []
} as unknown as Order;
} as unknown as OrderSubgraph;

export const order3 = {
id: 'order1',
Expand Down Expand Up @@ -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)));
Expand Down Expand Up @@ -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)));
}
});
});
42 changes: 35 additions & 7 deletions packages/ui-components/src/__tests__/OrderDetail.test.svelte
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
<script lang="ts">
import TanstackPageContentDetail from '../lib/components/detail/TanstackPageContentDetail.svelte';
import CardProperty from '../lib/components/CardProperty.svelte';
import ButtonVaultLink from '../lib/components/ButtonVaultLink.svelte';
import { createQuery } from '@tanstack/svelte-query';
import type { OrderSubgraph } from '@rainlanguage/orderbook/js_api';
import type { OrderSubgraph, OrderWithSortedVaults } from '@rainlanguage/orderbook/js_api';
import { getOrder } from '@rainlanguage/orderbook/js_api';
import { QKEY_ORDER } from '../lib/queries/keys';
import type { Readable } from 'svelte/store';
import { Button } from 'flowbite-svelte';
import DepositOrWithdrawButtons from '../lib/components/detail/DepositOrWithdrawButtons.svelte';
export let walletAddressMatchesOrBlank: Readable<(address: string) => boolean> | undefined =
undefined;
Expand All @@ -15,7 +18,7 @@
export let id: string;
export let subgraphUrl: string;
$: orderDetailQuery = createQuery<OrderSubgraph>({
$: orderDetailQuery = createQuery<OrderWithSortedVaults>({
queryKey: [id, QKEY_ORDER + id],
queryFn: () => getOrder(subgraphUrl, id),
enabled: !!subgraphUrl && !!id
Expand All @@ -24,12 +27,12 @@

<TanstackPageContentDetail query={orderDetailQuery} emptyMessage="Order not found">
<svelte:fragment slot="top" let:data>
<div>Order {data.orderHash}</div>
{#if data && $walletAddressMatchesOrBlank?.(data.owner) && data.active && handleOrderRemoveModal}
<div>Order {data.order.orderHash}</div>
{#if data && $walletAddressMatchesOrBlank?.(data.order.owner) && data.order.active && handleOrderRemoveModal}
<Button
data-testid="remove-button"
color="dark"
on:click={() => handleOrderRemoveModal(data, $orderDetailQuery.refetch)}
on:click={() => handleOrderRemoveModal(data.order, $orderDetailQuery.refetch)}
disabled={!handleOrderRemoveModal}
>
Remove
Expand All @@ -38,14 +41,39 @@
</svelte:fragment>

<svelte:fragment slot="card" let:data>
<div>Owner: {data.owner}</div>
<div>Owner: {data.order.owner}</div>

{#each [{ key: 'Input vaults', type: 'inputs' }, { key: 'Output vaults', type: 'outputs' }, { key: 'Input & output vaults', type: 'inputs_outputs' }] as { key, type }}
{#if data.vaults.get(type)?.length !== 0}
<CardProperty>
<svelte:fragment slot="key">{key}</svelte:fragment>
<svelte:fragment slot="value">
<div class="mt-2 space-y-2">
{#each data.vaults.get(type) || [] as vault}
<ButtonVaultLink tokenVault={vault} subgraphName="subgraphName">
<svelte:fragment slot="buttons">
<DepositOrWithdrawButtons
{vault}
chainId={1}
rpcUrl="https://example.com"
query={orderDetailQuery}
handleDepositOrWithdrawModal={() => {}}
/>
</svelte:fragment>
</ButtonVaultLink>
{/each}
</div>
</svelte:fragment>
</CardProperty>
{/if}
{/each}
</svelte:fragment>

<svelte:fragment slot="chart">
<div>Chart placeholder</div>
</svelte:fragment>

<svelte:fragment slot="below" let:data>
<div>Below content: {data.id}</div>
<div>Below content: {data.order.id}</div>
</svelte:fragment>
</TanstackPageContentDetail>
Loading

0 comments on commit 671bc63

Please sign in to comment.