Skip to content

Commit

Permalink
improve cow orders loading time, closes #172
Browse files Browse the repository at this point in the history
  • Loading branch information
xyzseer committed Dec 19, 2024
1 parent 6651b6e commit 5da7241
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 658 deletions.
2 changes: 1 addition & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"@orbisclub/components": "https://github.com/nvm1410/social-components#custom-comments",
"@orbisclub/orbis-sdk": "^0.4.89",
"@supabase/supabase-js": "^2.46.2",
"@swapr/sdk": "https://github.com/rodsouto/swapr-sdk#fork-with-postinstall",
"@swapr/sdk": "https://github.com/rodsouto/swapr-sdk#5e3b4f7abf8411434f0a05628a23653c8d6b87a0",
"@tanstack/query-sync-storage-persister": "^5.59.9",
"@tanstack/react-query": "^5.17.19",
"@tanstack/react-query-devtools": "^5.20.5",
Expand Down
20 changes: 14 additions & 6 deletions web/src/components/Market/SwapTokens/SwapTokens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ export function SwapTokens({
isError: quoteIsError,
} = useQuoteTrade(chainId, account, amount, outcomeToken, selectedCollateral, swapType);

const isCowFastQuote =
quoteData?.trade instanceof CoWTrade && quoteData?.trade?.quote?.expiration === "1970-01-01T00:00:00Z";

const tradeTokens = useTrade(async () => {
reset();
closeConfirmSwapModal();
Expand Down Expand Up @@ -289,11 +292,10 @@ export function SwapTokens({
useFormReturn={useFormReturn}
/>
</div>

{Number(amount) > 0 && (
<div className="flex space-x-2 text-purple-primary">
Price per share ={" "}
{quoteFetchStatus === "fetching" || isFetchingSharesToAssets || isFetchingAssetsToShares ? (
{quoteIsPending || isFetchingSharesToAssets || isFetchingAssetsToShares ? (
<div className="shimmer-container ml-2 flex-grow" />
) : (
<>
Expand All @@ -307,11 +309,10 @@ export function SwapTokens({
)}
</div>
)}

{Number(amount) > 0 && (
<div className="flex space-x-2 text-purple-primary">
{swapType === "buy" ? "Expected shares" : "Expected amount"} ={" "}
{quoteFetchStatus === "fetching" || isFetchingSharesToAssets ? (
{quoteIsPending || isFetchingSharesToAssets ? (
<div className="shimmer-container ml-2 flex-grow" />
) : (
<>
Expand Down Expand Up @@ -349,8 +350,15 @@ export function SwapTokens({
</div>
</div>
</div>

{quoteData?.trade ? (
{isCowFastQuote ? (
<Button
variant="primary"
type="button"
disabled={true}
isLoading={true}
text="Calculating best price..."
/>
) : quoteData?.trade ? (
<SwapButtons
account={account}
trade={quoteData.trade}
Expand Down
3 changes: 2 additions & 1 deletion web/src/hooks/trade/executeCowTrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ export async function executeCoWTrade(trade: CoWTrade): Promise<string> {

return orderId;
}
await trade.signOrder(signer, trade.order.receiver);

await trade.signOrder(signer);
const result = await toastify(() => trade.submitOrder(), {
txSent: { title: "Confirm order..." },
txSuccess: { title: "Order placed!" },
Expand Down
29 changes: 27 additions & 2 deletions web/src/hooks/trade/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { COLLATERAL_TOKENS } from "@/lib/config";
import { queryClient } from "@/lib/query-client";
import { Token } from "@/lib/tokens";
import { NATIVE_TOKEN, isTwoStringsEqual, parseFraction } from "@/lib/utils";
import { PriceQuality } from "@cowprotocol/cow-sdk";
import {
CoWTrade,
CurrencyAmount,
Expand All @@ -26,6 +27,8 @@ import { useGlobalState } from "../useGlobalState";
import { useMissingApprovals } from "../useMissingApprovals";
import { convertToSDAI } from "./handleSDAI";

const QUOTE_REFETCH_INTERVAL = Number(import.meta.env.VITE_QUOTE_REFETCH_INTERVAL) || 30_000;

export interface QuoteTradeResult {
value: bigint;
decimals: number;
Expand Down Expand Up @@ -77,6 +80,7 @@ type QuoteTradeFn = (
outcomeToken: Token,
collateralToken: Token,
swapType: "buy" | "sell",
isFastQuery?: boolean,
) => Promise<QuoteTradeResult>;

export const getUniswapQuote: QuoteTradeFn = async (
Expand Down Expand Up @@ -153,6 +157,7 @@ export const getCowQuote: QuoteTradeFn = async (
outcomeToken: Token,
collateralToken: Token,
swapType: "buy" | "sell",
isFastQuery?: boolean,
) => {
const args = await getCowTradeArgs(chainId, amount, outcomeToken, collateralToken, swapType);

Expand All @@ -162,6 +167,7 @@ export const getCowQuote: QuoteTradeFn = async (
maximumSlippage: args.maximumSlippage,
user: account || zeroAddress,
receiver: account || zeroAddress,
priceQuality: isFastQuery ? PriceQuality.FAST : undefined,
});

if (!trade) {
Expand Down Expand Up @@ -294,9 +300,19 @@ export function useSwaprQuote(
enabled: Number(amount) > 0 && chainId === gnosis.id,
retry: false,
queryFn: async () => getSwaprQuote(chainId, account, amount, outcomeToken, collateralToken, swapType),
refetchInterval: QUOTE_REFETCH_INTERVAL,
});
}

const getUseCowQuoteQueryKey = (
chainId: number,
account: Address | undefined,
amount: string,
outcomeToken: Token,
collateralToken: Token,
swapType: "buy" | "sell",
) => ["useCowQuote", chainId, account, amount.toString(), outcomeToken, collateralToken, swapType];

export function useCowQuote(
chainId: number,
account: Address | undefined,
Expand All @@ -305,11 +321,19 @@ export function useCowQuote(
collateralToken: Token,
swapType: "buy" | "sell",
) {
const queryKey = getUseCowQuoteQueryKey(chainId, account, amount, outcomeToken, collateralToken, swapType);
// Check if we have data for this quote
// If we don't, perform an initial fetch to give the user a fast quote
// it will fill the query cache, and subsequent fetches will return the verified quote
const previousData = queryClient.getQueryData(queryKey);
const isFastQuery = previousData === undefined;
return useQuery<QuoteTradeResult | undefined, Error>({
queryKey: ["useCowQuote", chainId, account, amount.toString(), outcomeToken, collateralToken, swapType],
queryKey: queryKey,
enabled: Number(amount) > 0,
retry: false,
queryFn: async () => getCowQuote(chainId, account, amount, outcomeToken, collateralToken, swapType),
queryFn: async () => getCowQuote(chainId, account, amount, outcomeToken, collateralToken, swapType, isFastQuery),
// If we used a fast quote, refetch immediately to obtain the verified quote
refetchInterval: (query) => (query.state.dataUpdateCount <= 1 ? 1 : QUOTE_REFETCH_INTERVAL),
});
}

Expand All @@ -326,6 +350,7 @@ export function useUniswapQuote(
enabled: Number(amount) > 0 && chainId === mainnet.id,
retry: false,
queryFn: async () => getUniswapQuote(chainId, account, amount, outcomeToken, collateralToken, swapType),
refetchInterval: QUOTE_REFETCH_INTERVAL,
});
}

Expand Down
5 changes: 5 additions & 0 deletions web/src/hooks/useMissingApprovals.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { SupportedChain } from "@/lib/chains";
import { NATIVE_TOKEN, isTwoStringsEqual } from "@/lib/utils";
import { config } from "@/wagmi";
import { useQuery } from "@tanstack/react-query";
import { readContracts } from "@wagmi/core";
Expand All @@ -21,6 +22,10 @@ export async function fetchNeededApprovals(
throw new Error("Invalid tokens and amounts lengths");
}

if (tokensAddresses.length === 1 && isTwoStringsEqual(tokensAddresses[0], NATIVE_TOKEN)) {
return [];
}

const allowances = await readContracts(config, {
allowFailure: false,
contracts: tokensAddresses.map((tokenAddress) => ({
Expand Down
13 changes: 11 additions & 2 deletions web/src/hooks/useTokenInfo.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SupportedChain } from "@/lib/chains";
import { isUndefined } from "@/lib/utils";
import { SupportedChain, gnosis } from "@/lib/chains";
import { NATIVE_TOKEN, isTwoStringsEqual, isUndefined } from "@/lib/utils";
import { config } from "@/wagmi";
import { useQuery } from "@tanstack/react-query";
import { readContracts } from "@wagmi/core";
Expand All @@ -13,6 +13,15 @@ interface GetTokenResult {
}

export async function getTokenInfo(address: Address, chainId: SupportedChain): Promise<GetTokenResult> {
if (isTwoStringsEqual(address, NATIVE_TOKEN)) {
return {
address,
decimals: 18,
name: chainId === gnosis.id ? "xDAI" : "ETH",
symbol: chainId === gnosis.id ? "xDAI" : "ETH",
};
}

const [decimals, name, symbol] = await readContracts(config, {
allowFailure: false,
contracts: [
Expand Down
Loading

0 comments on commit 5da7241

Please sign in to comment.