Skip to content

Commit

Permalink
[NO CHANGELOG] [Add Funds Widget] Add funds/improve get from amount (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
jiyounglee authored Oct 3, 2024
1 parent 2d6eade commit b2bd368
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 102 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { useAnalytics, UserJourney } from '../../context/analytics-provider/Segm
import { fetchChains } from './functions/fetchChains';
import { Review } from './views/Review';
import { fetchBalances } from './functions/fetchBalances';
import { useTokens } from './hooks/useTokens';

export type AddFundsWidgetInputs = AddFundsWidgetParams & {
checkout: Checkout;
Expand Down Expand Up @@ -71,6 +72,7 @@ export default function AddFundsWidget({
);

const squidSdk = useSquid(checkout);
const tokensResponse = useTokens(checkout);

useEffect(() => {
(async () => {
Expand Down Expand Up @@ -112,6 +114,17 @@ export default function AddFundsWidget({
});
}, [squidSdk]);

useEffect(() => {
if (!tokensResponse) return;

addFundsDispatch({
payload: {
type: AddFundsActions.SET_TOKENS,
tokens: tokensResponse,
},
});
}, [tokensResponse]);

useEffect(() => {
if (!web3Provider) return;
addFundsDispatch({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export function RouteOption<RC extends ReactElement | undefined = undefined>({
/>

<MenuItem.FramedImage
use={<img src={fromToken.logoURI} alt={fromToken.name} />}
use={<img src={fromToken.iconUrl} alt={fromToken.name} />}
/>
</Sticker>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { createContext } from 'react';
import { Checkout, TokenInfo } from '@imtbl/checkout-sdk';
import { Squid } from '@0xsquid/sdk';
import { TokenBalance } from '@0xsquid/sdk/dist/types';
import { Chain } from '../types';
import { Chain, Token } from '../types';

export interface AddFundsState {
checkout: Checkout | null;
Expand All @@ -12,6 +12,7 @@ export interface AddFundsState {
squid: Squid | null;
chains: Chain[] | null;
balances: TokenBalance[] | null;
tokens: Token[] | null;
}

export const initialAddFundsState: AddFundsState = {
Expand All @@ -21,6 +22,7 @@ export const initialAddFundsState: AddFundsState = {
squid: null,
chains: null,
balances: null,
tokens: null,
};

export interface AddFundsContextState {
Expand All @@ -38,7 +40,8 @@ type ActionPayload =
| SetAllowedTokensPayload
| SetSquid
| SetChains
| SetBalances;
| SetBalances
| SetTokens;

export enum AddFundsActions {
SET_CHECKOUT = 'SET_CHECKOUT',
Expand All @@ -47,6 +50,7 @@ export enum AddFundsActions {
SET_SQUID = 'SET_SQUID',
SET_CHAINS = 'SET_CHAINS',
SET_BALANCES = 'SET_BALANCES',
SET_TOKENS = 'SET_TOKENS',
}

export interface SetCheckoutPayload {
Expand All @@ -72,11 +76,17 @@ export interface SetChains {
type: AddFundsActions.SET_CHAINS;
chains: Chain[];
}

export interface SetBalances {
type: AddFundsActions.SET_BALANCES;
balances: TokenBalance[];
}

export interface SetTokens {
type: AddFundsActions.SET_TOKENS;
tokens: Token[];
}

// eslint-disable-next-line @typescript-eslint/naming-convention
export const AddFundsContext = createContext<AddFundsContextState>({
addFundsState: initialAddFundsState,
Expand Down Expand Up @@ -122,6 +132,11 @@ export const addFundsReducer: Reducer<AddFundsState, AddFundsAction> = (
...state,
balances: action.payload.balances,
};
case AddFundsActions.SET_TOKENS:
return {
...state,
tokens: action.payload.tokens,
};
default:
return state;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Token } from '../types';
import { SQUID_SDK_BASE_URL } from '../utils/config';

type SquidTokenResponse = {
chainId: number;
address: string;
decimals: number;
symbol?: string;
name?: string;
usdPrice: number;
logoURI: string;
};

type SquidTokensResponse = {
tokens: SquidTokenResponse[];
};

export const fetchTokens = async (integratorId:string): Promise<Token[]> => {
const url = `${SQUID_SDK_BASE_URL}/v2/sdk-info`;

const response = await fetch(url, {
method: 'GET',
headers: {
// eslint-disable-next-line @typescript-eslint/naming-convention
'Content-Type': 'application/json',
// eslint-disable-next-line @typescript-eslint/naming-convention
'x-integrator-id': integratorId,
},
});

const data: SquidTokensResponse = await response.json();

return data.tokens.map((tokenResponse: SquidTokenResponse) => ({
chainId: tokenResponse.chainId.toString(),
address: tokenResponse.address,
decimals: tokenResponse.decimals,
symbol: tokenResponse.symbol,
name: tokenResponse.name,
usdPrice: tokenResponse.usdPrice,
iconUrl: tokenResponse.logoURI,
}));
};
140 changes: 51 additions & 89 deletions packages/checkout/widgets-lib/src/widgets/add-funds/hooks/useRoutes.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { TokenBalance } from '@0xsquid/sdk/dist/types';
import { RouteResponse, Token } from '@0xsquid/squid-types';
import { RouteResponse } from '@0xsquid/squid-types';
import { Squid } from '@0xsquid/sdk';
import { utils } from 'ethers';
import { useRef, useState } from 'react';
import { delay } from '../functions/delay';
import { AmountData, RouteData } from '../types';
import { AmountData, RouteData, Token } from '../types';

export const useRoutes = () => {
const [routes, setRoutes] = useState<RouteData[] | undefined>(undefined);
Expand All @@ -14,115 +14,80 @@ export const useRoutes = () => {
setRoutes(undefined);
};

const getFromAmount = async (
squid: Squid,
const findToken = (tokens: Token[], address: string, chainId: string)
: Token | undefined => tokens.find((value) => value.address.toLowerCase() === address.toLowerCase()
&& value.chainId === chainId);

const calculateFromAmount = (fromToken: Token, toToken: Token, toAmount: string) => {
const toAmountNumber = Number(toAmount);
const toAmountInUsd = toAmountNumber * toToken.usdPrice;
const baseFromAmount = toAmountInUsd / fromToken.usdPrice;
const fromAmountWithBuffer = baseFromAmount * 1.015;
return fromAmountWithBuffer.toString();
};

const getAmountData = (
tokens: Token[],
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) {
): AmountData | undefined => {
const fromToken = findToken(tokens, balance.address, balance.chainId.toString());
const toToken = findToken(tokens, toTokenAddress, toChainId);
if (!fromToken || !toToken) {
return undefined;
}
return {
fromToken,
fromAmount: calculateFromAmount(fromToken, toToken, toAmount),
toToken,
toAmount,
balance,
};
};

const getSufficientFromAmounts = async (
squid: Squid,
const getSufficientFromAmounts = (
tokens: Token[],
balances: TokenBalance[],
toChainId: string,
toTokenAddress: string,
toAmount: string,
bulkNumber: number,
delayMs: number,
): Promise<AmountData[]> => {
): AmountData[] => {
const filteredBalances = balances.filter(
(balance) => !(balance.address === toTokenAddress && balance.chainId === toChainId),
(balance) => !(balance.address.toLowerCase() === toTokenAddress.toLowerCase() && balance.chainId === toChainId),
);
const amountDataArray: AmountData[] = filteredBalances.map((balance) => getAmountData(
tokens,
balance,
toAmount,
toChainId,
toTokenAddress,
)).filter((value) => value !== 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;
return amountDataArray.filter((data: AmountData) => {
const formattedBalance = utils.formatUnits(data.balance.balance, data.balance.decimals);
return parseFloat(formattedBalance.toString()) > parseFloat(data.fromAmount);
});
};

const getRoute = async (
squid: Squid,
fromToken: Token,
toToken: Token,
toAmount: string,
toAddress: string,
fromAddress?:string,
fromAmount: string,
fromAddress?: string,
quoteOnly = true,
): 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({
return await squid.getRoute({
fromChain: fromToken.chainId,
fromToken: fromToken.address,
fromAmount: formattedFromAmount.toString(),
Expand All @@ -133,8 +98,6 @@ export const useRoutes = () => {
quoteOnly,
enableBoost: true,
});

return route;
} catch (error) {
return undefined;
}
Expand All @@ -150,8 +113,8 @@ export const useRoutes = () => {
squid,
data.fromToken,
data.toToken,
data.toAmount,
toTokenAddress,
data.fromAmount,
).then((route) => ({
amountData: data,
route,
Expand All @@ -164,23 +127,22 @@ export const useRoutes = () => {

const fetchRoutesWithRateLimit = async (
squid: Squid,
tokens: Token[],
balances: TokenBalance[],
toChanId: string,
toTokenAddress: string,
toAmount: string,
bulkNumber = 5,
delayMs = 1000,
):Promise<RouteData[]> => {
): Promise<RouteData[]> => {
const currentRequestId = ++latestRequestIdRef.current;

const amountDataArray = await getSufficientFromAmounts(
squid,
const amountDataArray = getSufficientFromAmounts(
tokens,
balances,
toChanId,
toTokenAddress,
toAmount,
10,
1000,
);

const allRoutes: RouteData[] = [];
Expand All @@ -203,6 +165,6 @@ export const useRoutes = () => {
};

return {
routes, fetchRoutesWithRateLimit, getFromAmount, getRoute, resetRoutes,
routes, fetchRoutesWithRateLimit, getAmountData, getRoute, resetRoutes,
};
};
Loading

0 comments on commit b2bd368

Please sign in to comment.