Skip to content

Commit

Permalink
WT-1808 Use blockscout to get the L1 balances if enabled (#1042)
Browse files Browse the repository at this point in the history
  • Loading branch information
imx-mikhala committed Oct 23, 2023
1 parent 5f70919 commit dc3e88b
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 25 deletions.
2 changes: 1 addition & 1 deletion packages/checkout/sdk/src/Checkout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ export class Checkout {
}

const tokenList = await tokens.getTokenAllowList(this.config, { type: TokenFilterTypes.ONRAMP });
const token = tokenList.tokens.find((t) => t.address?.toLowerCase() === params.tokenAddress?.toLowerCase());
const token = tokenList.tokens?.find((t) => t.address?.toLowerCase() === params.tokenAddress?.toLowerCase());
if (token) {
tokenAmount = params.tokenAmount;
tokenSymbol = token.symbol;
Expand Down
2 changes: 2 additions & 0 deletions packages/checkout/sdk/src/balances/balances.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
getBalance,
getBalances,
getERC20Balance,
resetBlockscoutClientMap,
} from './balances';
import {
BLOCKSCOUT_CHAIN_URL_MAP,
Expand Down Expand Up @@ -218,6 +219,7 @@ describe('balances', () => {

beforeEach(() => {
jest.restoreAllMocks();
resetBlockscoutClientMap();
getTokenAllowListMock = jest.fn().mockReturnValue({
tokens: [
{
Expand Down
65 changes: 42 additions & 23 deletions packages/checkout/sdk/src/balances/balances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { BigNumber, Contract, utils } from 'ethers';
import { HttpStatusCode } from 'axios';
import {
ChainId,
DEFAULT_TOKEN_DECIMALS,
ERC20ABI,
GetAllBalancesResult,
GetBalanceResult,
Expand All @@ -14,7 +15,7 @@ import {
import { CheckoutError, CheckoutErrorType, withCheckoutError } from '../errors';
import { getNetworkInfo } from '../network';
import { getTokenAllowList } from '../tokens';
import { CheckoutConfiguration } from '../config';
import { CheckoutConfiguration, getL1ChainId } from '../config';
import {
Blockscout,
BlockscoutTokens,
Expand Down Expand Up @@ -87,20 +88,29 @@ export async function getERC20Balance(
);
}

// Blockscout client singleton
let blockscoutClient: Blockscout;
// Blockscout client singleton per chain id
const blockscoutClientMap: Map<ChainId, Blockscout> = new Map();

// This function is a utility function that can be used to reset the
// blockscout map and therefore clear all the cache.
export const resetBlockscoutClientMap = () => blockscoutClientMap.clear();

export const getIndexerBalance = async (
walletAddress: string,
chainId: ChainId,
rename: TokenInfo[],
filterTokens: TokenInfo[],
): Promise<GetAllBalancesResult> => {
// Shuffle the mapping of the tokens configuration so it is a hashmap
// for faster access to tokens config objects.
const mapRename = Object.assign({}, ...(rename.map((t) => ({ [t.address || '']: t }))));
const shouldFilter = filterTokens.length > 0;
const mapFilterTokens = Object.assign({}, ...(filterTokens.map((t) => ({ [t.address || '']: t }))));

// Ensure singleton is present and match the selected chain
if (!blockscoutClient || blockscoutClient.chainId !== chainId) blockscoutClient = new Blockscout({ chainId });
// Get blockscout client for the given chain
let blockscoutClient = blockscoutClientMap.get(chainId);
if (!blockscoutClient) {
blockscoutClient = new Blockscout({ chainId });
blockscoutClientMap.set(chainId, blockscoutClient);
}

// Hold the items in an array for post-fetching processing
const items = [];
Expand Down Expand Up @@ -156,25 +166,28 @@ export const getIndexerBalance = async (
}
}

return {
balances: items.map((item) => {
const tokenData = item.token || {};
const balances: GetBalanceResult[] = [];
items.forEach((item) => {
if (shouldFilter && !mapFilterTokens[item.token.address]) return;

const balance = BigNumber.from(item.value);
const tokenData = item.token || {};

const renamed = (mapRename[tokenData.address] || {}) as TokenInfo;
const token = {
...tokenData,
name: renamed.name ?? tokenData.name,
symbol: renamed.symbol ?? tokenData.symbol,
decimals: parseInt(tokenData.decimals, 10),
};
const balance = BigNumber.from(item.value);

const formattedBalance = utils.formatUnits(item.value, token.decimals);
let decimals = parseInt(tokenData.decimals, 10);
if (Number.isNaN(decimals)) decimals = DEFAULT_TOKEN_DECIMALS;

const token = {
...tokenData,
decimals,
};

return { balance, formattedBalance, token } as GetBalanceResult;
}),
};
const formattedBalance = utils.formatUnits(item.value, token.decimals);

balances.push({ balance, formattedBalance, token } as GetBalanceResult);
});

return { balances };
};

export const getBalances = async (
Expand Down Expand Up @@ -233,7 +246,13 @@ export const getAllBalances = async (
}

if (flag && Blockscout.isChainSupported(chainId)) {
return await getIndexerBalance(walletAddress, chainId, tokens);
// This is a hack because the widgets are still using the tokens symbol
// to drive the conversions. If we remove all the token symbols from e.g. zkevm
// then we would not have fiat conversions.
// Please remove this hack once https://immutable.atlassian.net/browse/WT-1710
// is done.
const isL1Chain = getL1ChainId(config) === chainId;
return await getIndexerBalance(walletAddress, chainId, isL1Chain ? tokens : []);
}

// This fallback to use ERC20s calls which is a best effort solution
Expand Down
2 changes: 1 addition & 1 deletion packages/checkout/sdk/src/client/blockscout.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('Blockscout', () => {
});
});
it('not supported', () => {
expect(Blockscout.isChainSupported(ChainId.SEPOLIA)).toBe(false);
expect(Blockscout.isChainSupported('aaa' as unknown as ChainId)).toBe(false);
});
});

Expand Down
8 changes: 8 additions & 0 deletions packages/checkout/sdk/src/types/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,14 @@ export const BLOCKSCOUT_CHAIN_URL_MAP: {
url: 'https://explorer.mainnet.immutable.com',
nativeToken: PRODUCTION_CHAIN_ID_NETWORK_MAP.get(ChainId.IMTBL_ZKEVM_MAINNET)!.nativeCurrency,
},
[ChainId.SEPOLIA]: {
url: 'https://eth-sepolia.blockscout.com',
nativeToken: SANDBOX_CHAIN_ID_NETWORK_MAP.get(ChainId.SEPOLIA)!.nativeCurrency,
},
[ChainId.ETHEREUM]: {
url: 'https://eth.blockscout.com/',
nativeToken: PRODUCTION_CHAIN_ID_NETWORK_MAP.get(ChainId.ETHEREUM)!.nativeCurrency,
},
};

export const ERC20ABI = [
Expand Down

0 comments on commit dc3e88b

Please sign in to comment.