Skip to content

Commit

Permalink
Merge pull request #3426 from osmosis-labs/connor/unclaimed-orders
Browse files Browse the repository at this point in the history
feat: added claimable orders
  • Loading branch information
crnbarr93 authored Jul 1, 2024
2 parents 56cea50 + 94c960b commit 85df87c
Show file tree
Hide file tree
Showing 6 changed files with 207 additions and 95 deletions.
3 changes: 2 additions & 1 deletion packages/server/src/queries/osmosis/orderbooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export interface LimitOrder {
etas: string;
claim_bounty?: string;
placed_quantity: string;
placed_at: string;
}

interface OrderbookActiveOrdersResponse {
Expand Down Expand Up @@ -111,7 +112,7 @@ export const queryOrderbookTickUnrealizedCancelsById = createNodeQuery<
>({
path: ({ tickIds, orderbookAddress }) => {
const msg = JSON.stringify({
tick_unrealized_cancels_by_id: {
get_unrealized_cancels: {
tick_ids: tickIds,
},
});
Expand Down
215 changes: 131 additions & 84 deletions packages/trpc/src/orderbook-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { getAssetFromAssetList } from "@osmosis-labs/utils";
import { z } from "zod";

import { createTRPCRouter, publicProcedure } from "./api";
import { OsmoAddressSchema } from "./parameter-types";
import { OsmoAddressSchema, UserOsmoAddressSchema } from "./parameter-types";

const GetInfiniteLimitOrdersInputSchema = CursorPaginationSchema.merge(
z.object({
Expand All @@ -33,7 +33,7 @@ export type OrderStatus =

export type MappedLimitOrder = Omit<
LimitOrder,
"quantity" | "placed_quantity"
"quantity" | "placed_quantity" | "placed_at"
> & {
quantity: number;
placed_quantity: number;
Expand All @@ -46,18 +46,29 @@ export type MappedLimitOrder = Omit<
output: number;
quoteAsset: ReturnType<typeof getAssetFromAssetList>;
baseAsset: ReturnType<typeof getAssetFromAssetList>;
placed_at: number;
};

function mapOrderStatus(order: LimitOrder): OrderStatus {
function mapOrderStatus(order: LimitOrder, percentFilled: Dec): OrderStatus {
const quantInt = parseInt(order.quantity);
const placedQuantInt = parseInt(order.placed_quantity);
if (quantInt === 0) return "filled";
if (quantInt === 0 || percentFilled.equals(new Dec(1))) return "filled";
if (quantInt === placedQuantInt) return "open";
if (quantInt < placedQuantInt) return "partiallyFilled";

return "open";
}

function defaultSortOrders(
orderA: MappedLimitOrder,
orderB: MappedLimitOrder
): number {
if (orderA.status === orderB.status) {
return orderA.placed_at - orderB.placed_at;
}
return orderA.status === "filled" ? 1 : -1;
}

async function getTickInfoAndTransformOrders(
orderbookAddress: string,
orders: LimitOrder[],
Expand All @@ -83,88 +94,79 @@ async function getTickInfoAndTransformOrders(
unrealizedCancels: unrealizedTickCancels.find((c) => c.tick_id === tick_id),
}));

return orders
.map((o) => {
const { tickState, unrealizedCancels } = fullTickState.find(
({ tickId }) => tickId === o.tick_id
) ?? { tickState: undefined, unrealizedCancels: undefined };

const [tokenInAsset, tokenOutAsset] =
o.order_direction === "bid"
? [quoteAsset, baseAsset]
: [baseAsset, quoteAsset];
return orders.map((o) => {
const { tickState, unrealizedCancels } = fullTickState.find(
({ tickId }) => tickId === o.tick_id
) ?? { tickState: undefined, unrealizedCancels: undefined };

const quantityMin = parseInt(o.quantity);
const placedQuantityMin = parseInt(o.placed_quantity);
const placedQuantity =
placedQuantityMin / 10 ** (tokenInAsset?.decimals ?? 0);
const quantity = quantityMin / 10 ** (tokenInAsset?.decimals ?? 0);
const [tokenInAsset, tokenOutAsset] =
o.order_direction === "bid"
? [quoteAsset, baseAsset]
: [baseAsset, quoteAsset];

const percentClaimed = new Dec(
(placedQuantityMin - quantityMin) / placedQuantityMin
);
const [tickEtas, tickCumulativeCancelled, tickUnrealizedCancelled] =
o.order_direction === "bid"
? [
parseInt(
tickState?.bid_values.effective_total_amount_swapped ?? "0"
),
parseInt(
tickState?.bid_values.cumulative_realized_cancels ?? "0"
),
parseInt(
unrealizedCancels?.unrealized_cancels.bid_unrealized_cancels ??
"0"
),
]
: [
parseInt(
tickState?.ask_values.effective_total_amount_swapped ?? "0"
),
parseInt(
tickState?.ask_values.cumulative_realized_cancels ?? "0"
),
parseInt(
unrealizedCancels?.unrealized_cancels.ask_unrealized_cancels ??
"0"
),
];
const tickTotalEtas =
tickEtas + (tickUnrealizedCancelled - tickCumulativeCancelled);
const totalFilled = Math.max(
tickTotalEtas - (parseInt(o.etas) - (placedQuantityMin - quantityMin)),
0
);
const percentFilled = new Dec(totalFilled / placedQuantityMin);
const price = tickToPrice(new Int(o.tick_id));
const status = mapOrderStatus(o);
const quantityMin = parseInt(o.quantity);
const placedQuantityMin = parseInt(o.placed_quantity);
const placedQuantity =
placedQuantityMin / 10 ** (tokenInAsset?.decimals ?? 0);
const quantity = quantityMin / 10 ** (tokenInAsset?.decimals ?? 0);

const outputMin =
o.order_direction === "bid"
? new Dec(placedQuantityMin).quo(price)
: new Dec(placedQuantityMin).mul(price);
const output = parseInt(
outputMin
.quo(new Dec(10 ** (tokenOutAsset?.decimals ?? 0)))
.truncate()
.toString()
);
return {
...o,
price,
quantity,
placed_quantity: placedQuantity,
percentClaimed,
totalFilled,
percentFilled,
orderbookAddress,
status,
output,
quoteAsset,
baseAsset,
};
})
.sort((a, b) => a.order_id - b.order_id);
const percentClaimed = new Dec(
(placedQuantityMin - quantityMin) / placedQuantityMin
);
const [tickEtas, tickUnrealizedCancelled] =
o.order_direction === "bid"
? [
parseInt(
tickState?.bid_values.effective_total_amount_swapped ?? "0"
),
parseInt(
unrealizedCancels?.unrealized_cancels.bid_unrealized_cancels ??
"0"
),
]
: [
parseInt(
tickState?.ask_values.effective_total_amount_swapped ?? "0"
),
parseInt(
unrealizedCancels?.unrealized_cancels.ask_unrealized_cancels ??
"0"
),
];
const tickTotalEtas = tickEtas + tickUnrealizedCancelled;
const totalFilled = Math.max(
tickTotalEtas - (parseInt(o.etas) - (placedQuantityMin - quantityMin)),
0
);
const percentFilled = new Dec(Math.min(totalFilled / placedQuantityMin, 1));
const price = tickToPrice(new Int(o.tick_id));
const status = mapOrderStatus(o, percentFilled);
const outputMin =
o.order_direction === "bid"
? new Dec(placedQuantityMin).quo(price)
: new Dec(placedQuantityMin).mul(price);
const output = parseInt(
outputMin
.quo(new Dec(10 ** (tokenOutAsset?.decimals ?? 0)))
.truncate()
.toString()
);
return {
...o,
price,
quantity,
placed_quantity: placedQuantity,
percentClaimed,
totalFilled,
percentFilled,
orderbookAddress,
status,
output,
quoteAsset,
baseAsset,
placed_at: parseInt(o.placed_at),
};
});
}

export const orderbookRouter = createTRPCRouter({
Expand Down Expand Up @@ -268,7 +270,7 @@ export const orderbookRouter = createTRPCRouter({
quoteAsset,
baseAsset
);
return mappedOrders;
return mappedOrders.sort(defaultSortOrders);
}
);
const ordersByContracts = await Promise.all(promises);
Expand Down Expand Up @@ -304,4 +306,49 @@ export const orderbookRouter = createTRPCRouter({
});
return spotPrice;
}),
getClaimableOrders: publicProcedure
.input(
z
.object({ contractAddresses: z.array(z.string().startsWith("osmo")) })
.required()
.and(UserOsmoAddressSchema.required())
)
.query(async ({ input, ctx }) => {
const { contractAddresses, userOsmoAddress } = input;
const promises = contractAddresses.map(
async (contractOsmoAddress: string) => {
const resp = await getOrderbookActiveOrders({
orderbookAddress: contractOsmoAddress,
userOsmoAddress: userOsmoAddress,
chainList: ctx.chainList,
});

if (resp.orders.length === 0) return [];
const { base_denom } = await getOrderbookDenoms({
orderbookAddress: contractOsmoAddress,
chainList: ctx.chainList,
});
// TODO: Use actual quote denom here
const quoteAsset = getAssetFromAssetList({
assetLists: ctx.assetLists,
sourceDenom: "uusdc",
});
const baseAsset = getAssetFromAssetList({
assetLists: ctx.assetLists,
sourceDenom: base_denom,
});
const mappedOrders = await getTickInfoAndTransformOrders(
contractOsmoAddress,
resp.orders,
ctx.chainList,
quoteAsset,
baseAsset
);
return mappedOrders.filter((o) => o.percentFilled.gte(new Dec(1)));
}
);
const ordersByContracts = await Promise.all(promises);
const allOrders = ordersByContracts.flatMap((p) => p);
return allOrders;
}),
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Dec } from "@keplr-wallet/unit";
import { Dec, Int } from "@keplr-wallet/unit";
import { MappedLimitOrder } from "@osmosis-labs/trpc";
import React, { useMemo } from "react";

Expand All @@ -16,9 +16,9 @@ export const OrderProgressBar: React.FC<OrderProgressBarProps> = ({

const roundedAmountFilled = useMemo(() => {
if (percentFilled.lt(new Dec(1)) && !percentFilled.isZero()) {
return new Dec(1);
return new Int(1);
}
return percentFilled.round();
return percentFilled.round().mul(new Int(100));
}, [percentFilled]);

const progressSegments = useMemo(
Expand Down
12 changes: 10 additions & 2 deletions packages/web/components/complex/orders-history/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { Spinner } from "~/components/loaders";
import {
DisplayableLimitOrder,
useOrderbookAllActiveOrders,
useOrderbookClaimableOrders,
} from "~/hooks/limit-orders/use-orderbook";
import { useStore } from "~/stores";

Expand All @@ -34,6 +35,10 @@ export const OrderHistory = observer(() => {
getCoreRowModel: getCoreRowModel(),
});

const { count, claimAllOrders } = useOrderbookClaimableOrders({
userAddress: wallet?.address ?? "",
});

const filledOrders = useMemo(
() =>
table
Expand Down Expand Up @@ -130,7 +135,7 @@ export const OrderHistory = observer(() => {
<div className="flex items-center gap-2 pb-3">
<h6>Filled orders to claim</h6>
<div className="flex h-6 w-6 items-center justify-center rounded-full bg-[#A51399]">
<span className="caption">1</span>
<span className="caption">{count}</span>
</div>
</div>
<div className="flex h-12 w-12 items-center justify-center">
Expand All @@ -142,7 +147,10 @@ export const OrderHistory = observer(() => {
/>
</div>
</div>
<button className="flex items-center justify-center rounded-[48px] bg-wosmongton-700 py-3 px-4">
<button
className="flex items-center justify-center rounded-[48px] bg-wosmongton-700 py-3 px-4"
onClick={claimAllOrders}
>
<span className="subtitle1">Claim all</span>
</button>
</div>
Expand Down
Loading

0 comments on commit 85df87c

Please sign in to comment.