Skip to content

Commit

Permalink
[NO CHANGELOG][Add Funds Widget] Add funds update useRoutes to handle…
Browse files Browse the repository at this point in the history
… rate limiting (#2189)
  • Loading branch information
mimi-imtbl authored Sep 19, 2024
1 parent c94d48d commit a154af5
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,14 @@ export default function AddFundsWidget({
chainIds,
evmAddress: fromAddress,
});
const filteredBalances = balances?.evmBalances?.filter(
(balance) => balance.balance !== '0',
);

addFundsDispatch({
payload: {
type: AddFundsActions.SET_BALANCES,
balances: balances?.evmBalances ?? [],
balances: filteredBalances ?? [],
},
});
})();
Expand Down Expand Up @@ -140,7 +144,6 @@ export default function AddFundsWidget({
{viewState.view.type === AddFundsWidgetViews.ADD_FUNDS && (
<AddFunds
checkout={checkout}
provider={web3Provider}
toTokenAddress={toTokenAddress}
toAmount={toAmount}
showBackButton={showBackButton}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const delay = (ms: number) => new Promise((resolve) => {
setTimeout(resolve, ms);
});
250 changes: 151 additions & 99 deletions packages/checkout/widgets-lib/src/widgets/add-funds/hooks/useRoutes.ts
Original file line number Diff line number Diff line change
@@ -1,73 +1,98 @@
/* eslint-disable no-console */
import { TokenBalance } from '@0xsquid/sdk/dist/types';
import { RouteResponse, Token } from '@0xsquid/squid-types';
import { Squid } from '@0xsquid/sdk';
import { utils } from 'ethers';
import { useState } from 'react';

const delay = (ms: number) => new Promise((resolve) => {
setTimeout(resolve, ms);
});

export type AmountData = {
fromAmount: string;
fromToken: Token;
toToken: Token;
toAmount: string;
balance: TokenBalance;
};

export type RouteData = {
amountData: AmountData;
route: RouteResponse;
};
import { delay } from '../functions/delay';
import { AmountData, RouteData } from '../types';

export const useRoutes = () => {
const [routes, setRoutes] = useState<RouteData[]>([]);

const getFromAmounts = (
const getFromAmount = async (
squid: Squid,
balance: TokenBalance,
toAmount: string,
toChainId: string,
toTokenAddress: string,
): Promise<AmountData | undefined> => {
const fromTokenData = squid?.getTokenData(
balance.address,
balance.chainId.toString(),
);
const toTokenData = squid?.getTokenData(toTokenAddress, toChainId);

if (!fromTokenData || !toTokenData) {
return undefined;
}

try {
const fromAmount = await squid.getFromAmount({
fromToken: fromTokenData,
toToken: toTokenData,
toAmount,
});

return {
fromAmount,
fromToken: fromTokenData,
toToken: toTokenData,
toAmount,
balance,
};
} catch (error) {
return undefined;
}
};

const getSufficientFromAmounts = async (
squid: Squid,
balances: TokenBalance[],
toChanId: string,
toChainId: string,
toTokenAddress: string,
toAmount: string,
): Promise<AmountData | undefined>[] => {
bulkNumber: number,
delayMs: number,
): Promise<AmountData[]> => {
const filteredBalances = balances.filter(
(balance) => balance.address !== toTokenAddress,
(balance) => !(balance.address === toTokenAddress && balance.chainId === toChainId),
);

return filteredBalances.map(
(balance: TokenBalance): Promise<AmountData | undefined> => {
const fromTokenData = squid?.getTokenData(
balance.address,
balance.chainId.toString(),
);
const toTokenData = squid?.getTokenData(toTokenAddress, toChanId);

if (!fromTokenData || !toTokenData) {
console.log('tokenData not found', fromTokenData, toTokenData);
return Promise.resolve(undefined);
}

return squid
.getFromAmount({
fromToken: fromTokenData,
toToken: toTokenData,
toAmount,
})
.then((fromAmount: string) => ({
fromAmount,
fromToken: fromTokenData,
toToken: toTokenData,
toAmount,
balance,
}))
.catch((error) => {
console.log('error', error);
return Promise.resolve(undefined);
});
const result :AmountData[] = [];

for (let i = 0; i < filteredBalances.length; i += bulkNumber) {
const promises = filteredBalances.slice(i, i + bulkNumber).map(
(balance) => getFromAmount(
squid,
balance,
toAmount,
toChainId,
toTokenAddress,
),
);

// eslint-disable-next-line no-await-in-loop
const amountsData = await Promise.all(promises);

const filteredAmountsData = amountsData.filter(
(amountData): amountData is AmountData => amountData !== undefined,
);

if (filteredAmountsData.length > 0) {
result.push(...filteredAmountsData);
}

// eslint-disable-next-line no-await-in-loop
await delay(delayMs);
}

const filteredAmountData = result.filter(
(data: AmountData) => {
const formattedBalance = utils.formatUnits(data.balance.balance, data.balance.decimals);
return parseFloat(formattedBalance.toString()) > parseFloat(data.fromAmount);
},
);
return filteredAmountData;
};

const getRoute = async (
Expand All @@ -77,66 +102,93 @@ export const useRoutes = () => {
toAmount: string,
toAddress: string,
quoteOnly = true,
): Promise<RouteResponse> => {
const fromAmount = await squid.getFromAmount({
fromToken,
toToken,
toAmount,
});
): Promise<RouteResponse | undefined> => {
try {
const fromAmount = await squid.getFromAmount({
fromToken,
toToken,
toAmount,
});

const parsedFromAmount = parseFloat(fromAmount).toFixed(fromToken.decimals);
const formattedFromAmount = utils.parseUnits(
parsedFromAmount,
fromToken.decimals,
);

const route = await squid.getRoute({
fromChain: fromToken.chainId,
fromToken: fromToken.address,
fromAmount: formattedFromAmount.toString(),
toChain: toToken.chainId,
toToken: toToken.address,
toAddress,
quoteOnly,
enableBoost: true,
});

return route;
} catch (error) {
return undefined;
}
};

const parsedFromAmount = parseFloat(fromAmount).toFixed(fromToken.decimals);
const formattedFromAmount = utils.parseUnits(
parsedFromAmount,
fromToken.decimals,
const getRoutes = async (
squid: Squid,
amountDataArray: AmountData[],
toTokenAddress: string,
): Promise<RouteData[]> => {
const routePromises = amountDataArray.map(
(data) => getRoute(
squid,
data.fromToken,
data.toToken,
data.toAmount,
toTokenAddress,
).then((route) => ({
amountData: data,
route,
})),
);

return squid.getRoute({
fromChain: fromToken.chainId,
fromToken: fromToken.address,
fromAmount: formattedFromAmount.toString(),
toChain: toToken.chainId,
toToken: toToken.address,
toAddress,
quoteOnly,
enableBoost: true,
});
const routesData = await Promise.all(routePromises);
return routesData.filter((route): route is RouteData => route !== undefined);
};

const getRoutes = async (
const fetchRoutesWithRateLimit = async (
squid: Squid,
balances: TokenBalance[],
toChanId: string,
toTokenAddress: string,
toAmount: string,
) => {
const getAmountData = await Promise.all(
getFromAmounts(squid, balances, toChanId, toTokenAddress, toAmount),
bulkNumber = 5,
delayMs = 1000,
):Promise<RouteData[]> => {
const amountDataArray = await getSufficientFromAmounts(
squid,
balances,
toChanId,
toTokenAddress,
toAmount,
10,
1000,
);
const filteredAmountData = getAmountData.filter(
(amountData) => amountData !== undefined,
) as AmountData[];

delay(2000);
const allRoutes: RouteData[] = [];

const getRoutePromises = filteredAmountData.map((data: AmountData): Promise<RouteData | undefined> => getRoute(
squid,
data.fromToken,
data.toToken,
data.toAmount,
toTokenAddress,
).then((route) => ({
amountData: data,
route,
})).catch((error) => {
console.log('error', error);
return Promise.resolve(undefined);
}));

const getRouteData = await Promise.all(getRoutePromises);
const routesData = getRouteData.filter((routeData) => routeData !== undefined) as RouteData[];
setRoutes(routesData);
return routesData;
for (let i = 0; i < amountDataArray.length; i += bulkNumber) {
const slicedAmountDataArray = amountDataArray.slice(i, i + bulkNumber);

// eslint-disable-next-line no-await-in-loop
allRoutes.push(...await getRoutes(squid, slicedAmountDataArray, toTokenAddress));

// eslint-disable-next-line no-await-in-loop
await delay(delayMs);
}

setRoutes(allRoutes);
return allRoutes;
};

return { routes, getRoutes, getRoute };
return { routes, fetchRoutesWithRateLimit };
};
16 changes: 16 additions & 0 deletions packages/checkout/widgets-lib/src/widgets/add-funds/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
import { TokenBalance } from '@0xsquid/sdk/dist/types';
import { RouteResponse, Token } from '@0xsquid/squid-types';

export type Chain = {
id: string;
type: string;
name: string;
iconUrl: string;
};

export type AmountData = {
fromToken: Token;
fromAmount: string;
toToken: Token;
toAmount: string;
balance: TokenBalance;
};

export type RouteData = {
amountData: AmountData;
route: RouteResponse;
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/* eslint-disable no-console */
import { Web3Provider } from '@ethersproject/providers';
import {
Checkout,
IMTBLWidgetEvents,
Expand Down Expand Up @@ -30,7 +28,6 @@ import {

interface AddFundsProps {
checkout?: Checkout;
provider?: Web3Provider;
showBackButton?: boolean;
showOnrampOption?: boolean;
showSwapOption?: boolean;
Expand All @@ -43,7 +40,6 @@ interface AddFundsProps {

export function AddFunds({
checkout,
provider,
toAmount,
toTokenAddress,
showBackButton = false,
Expand All @@ -53,11 +49,6 @@ export function AddFunds({
onBackButtonClick,
onCloseButtonClick,
}: AddFundsProps) {
console.log('provider', provider);
console.log('showOnrampOption', showOnrampOption);
console.log('showSwapOption', showSwapOption);
console.log('showBridgeOption', showBridgeOption);

const showBack = showBackButton || !!onBackButtonClick;

const { addFundsDispatch } = useContext(AddFundsContext);
Expand Down Expand Up @@ -174,10 +165,6 @@ export function AddFunds({
// };

const onPayWithCard = (paymentType: OptionTypes) => {
console.log('paymentType', paymentType);
console.log('=== toTokenAddress', currentToTokenAddress);
console.log('=== toAmount', toAmount);

if (paymentType === OptionTypes.SWAP) {
orchestrationEvents.sendRequestSwapEvent(
eventTarget,
Expand Down

0 comments on commit a154af5

Please sign in to comment.