Skip to content

Commit

Permalink
Revert "improved evm getTokenBalances"
Browse files Browse the repository at this point in the history
This reverts commit 75762e8.
  • Loading branch information
evan-gray committed Oct 4, 2023
1 parent a217bbb commit c823976
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 142 deletions.
4 changes: 2 additions & 2 deletions builder/src/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ export const DEFAULT_MAINNET_RPCS = {
aptos: "https://fullnode.mainnet.aptoslabs.com/v1",
arbitrum: "https://arb1.arbitrum.io/rpc",
optimism: "https://mainnet.optimism.io",
base: "https://base.publicnode.com",
base: "https://mainnet.base.org",
sei: "", // TODO: fill in
wormchain: "",
osmosis: "https://osmosis-rpc.polkachu.com",
Expand All @@ -194,7 +194,7 @@ export const DEFAULT_TESTNET_RPCS = {
aptos: "https://fullnode.testnet.aptoslabs.com/v1",
arbitrumgoerli: "https://arbitrum-goerli.publicnode.com",
optimismgoerli: "https://optimism-goerli.publicnode.com",
basegoerli: "https://base-goerli.publicnode.com",
basegoerli: "https://goerli.base.org",
sei: "https://rpc.atlantic-2.seinetwork.io",
wormchain: "",
osmosis: "https://rpc.osmotest5.osmosis.zone",
Expand Down
2 changes: 1 addition & 1 deletion sdk/src/config/MAINNET.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ const MAINNET_CONFIG: WormholeConfig = {
aptos: 'https://fullnode.mainnet.aptoslabs.com/v1',
arbitrum: 'https://arb1.arbitrum.io/rpc',
optimism: 'https://mainnet.optimism.io',
base: 'https://base.publicnode.com',
base: 'https://mainnet.base.org',
sei: '', // TODO: fill in
wormchain: '',
osmosis: 'https://osmosis-rpc.polkachu.com',
Expand Down
2 changes: 1 addition & 1 deletion sdk/src/config/TESTNET.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ const TESTNET_CONFIG: WormholeConfig = {
aptos: 'https://fullnode.testnet.aptoslabs.com/v1',
arbitrumgoerli: 'https://arbitrum-goerli.publicnode.com',
optimismgoerli: 'https://optimism-goerli.publicnode.com',
basegoerli: 'https://base-goerli.publicnode.com',
basegoerli: 'https://goerli.base.org',
sei: 'https://rpc.atlantic-2.seinetwork.io',
wormchain: '',
osmosis: 'https://rpc.osmotest5.osmosis.zone',
Expand Down
147 changes: 28 additions & 119 deletions sdk/src/contexts/eth/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { parseVaa } from '../../vaa';
import { RelayerAbstract } from '../abstracts/relayer';
import { SolanaContext } from '../solana';
import { arrayify } from 'ethers/lib/utils';
import { ForeignAssetCache, chunkArray } from '../../utils';
import { ForeignAssetCache } from '../../utils';

export const NO_VAA_FOUND = 'No message publish found in logs';

Expand Down Expand Up @@ -61,12 +61,10 @@ export class EthContext<
return gasUsed.mul(effectiveGasPrice);
}

// This helper is needed so that the `wrappedAsset` calls can be batched efficiently
async getForeignAssetPartiallyUnresolved(
async getForeignAsset(
tokenId: TokenId,
chain: ChainName | ChainId,
provider?: ethers.providers.Provider,
): Promise<string | (() => Promise<string | null>)> {
): Promise<string | null> {
const chainName = this.context.toChainName(chain);
if (this.foreignAssetCache.get(tokenId.chain, tokenId.address, chainName)) {
return this.foreignAssetCache.get(
Expand All @@ -81,34 +79,21 @@ export class EthContext<
// if the token is already native, return the token address
if (toChainId === chainId) return tokenId.address;
// else fetch the representation
const tokenBridge = this.contracts.mustGetBridge(chain, provider);
const tokenBridge = this.contracts.mustGetBridge(chain);
const sourceContext = this.context.getContext(tokenId.chain);
const tokenAddr = await sourceContext.formatAssetAddress(tokenId.address);
return async () => {
const foreignAddr = await tokenBridge.wrappedAsset(
chainId,
utils.arrayify(tokenAddr),
);
if (foreignAddr === constants.AddressZero) return null;
this.foreignAssetCache.set(
tokenId.chain,
tokenId.address,
chainName,
foreignAddr,
);
return foreignAddr;
};
}

async getForeignAsset(
tokenId: TokenId,
chain: ChainName | ChainId,
): Promise<string | null> {
const result = await this.getForeignAssetPartiallyUnresolved(
tokenId,
chain,
const foreignAddr = await tokenBridge.wrappedAsset(
chainId,
utils.arrayify(tokenAddr),
);
if (foreignAddr === constants.AddressZero) return null;
this.foreignAssetCache.set(
tokenId.chain,
tokenId.address,
chainName,
foreignAddr,
);
return typeof result === 'function' ? await result() : result;
return foreignAddr;
}

async mustGetForeignAsset(
Expand Down Expand Up @@ -159,95 +144,19 @@ export class EthContext<
tokenIds: TokenId[],
chain: ChainName | ChainId,
): Promise<(BigNumber | null)[]> {
// The complex chunking into maxBatchSize and reconstituting
// should not be required when using ethers v6 batch provider
const contextProvider = this.context.mustGetProvider(chain);
// attempt to batch the balance calls
// @ts-ignore connection definitely exists on provider
const rpc: string = contextProvider.connection?.url || '';
const provider =
rpc.startsWith('http://') || rpc.startsWith('https://')
? new ethers.providers.JsonRpcBatchProvider(rpc)
: contextProvider;
const maxBatchSize = 100; // 100 is the default used by ethers v6

let addresses: (string | null)[] = [];
{
const partiallyUnresolvedAddresses = await Promise.all(
tokenIds.map((tokenId) =>
this.getForeignAssetPartiallyUnresolved(tokenId, chain, provider),
),
);
// we don't want to include resolved addresses in our chunks, as we want to pack in the most per-query
const unresolvedIndexes = partiallyUnresolvedAddresses
.map((_, idx) => idx)
.filter(
(aIdx) => typeof partiallyUnresolvedAddresses[aIdx] === 'function',
);
const idxChunks = chunkArray(unresolvedIndexes, maxBatchSize);
let queriedAddresses: (string | null)[] = [];
// batch request each chunk
for (const chunk of idxChunks) {
queriedAddresses = [
...queriedAddresses,
...(await Promise.all(
chunk.map((idx) => {
const result = partiallyUnresolvedAddresses[idx];
return typeof result === 'function'
? result()
: Promise.resolve(result);
}),
)),
];
}
// re-assemble the balances array to match the input order
let queriedIdx = 0;
for (let i = 0; i < partiallyUnresolvedAddresses.length; i++) {
const maybeResult = partiallyUnresolvedAddresses[i];
if (typeof maybeResult === 'string') {
addresses.push(maybeResult);
} else {
addresses.push(queriedAddresses[queriedIdx++]);
}
}
}

let balances: (BigNumber | null)[] = [];
{
// we don't want to include nulls in our chunks, as we want to pack in the most per-query
const nonNullIndexes = addresses
.map((_, idx) => idx)
.filter((aIdx) => !!addresses[aIdx]);
const idxChunks = chunkArray(nonNullIndexes, maxBatchSize);
let queriedBalances: (BigNumber | null)[] = [];
// batch request each chunk
for (const chunk of idxChunks) {
queriedBalances = [
...queriedBalances,
...(await Promise.all(
chunk.map((idx) => {
const address = addresses[idx];
return !address
? Promise.resolve(null)
: // TODO: this connect may trigger extra requests
TokenImplementation__factory.connect(
address,
provider,
).balanceOf(walletAddr);
}),
)),
];
}
// re-assemble the balances array to match the input order
let queriedIdx = 0;
for (let i = 0; i < addresses.length; i++) {
if (i === nonNullIndexes[queriedIdx]) {
balances.push(queriedBalances[queriedIdx++]);
} else {
balances.push(null);
}
}
}
const addresses = await Promise.all(
tokenIds.map((tokenId) => this.getForeignAsset(tokenId, chain)),
);
const provider = this.context.mustGetProvider(chain);
const balances = await Promise.all(
addresses.map((address) =>
!address
? Promise.resolve(null)
: TokenImplementation__factory.connect(address, provider).balanceOf(
walletAddr,
),
),
);
return balances;
}

Expand Down
15 changes: 4 additions & 11 deletions sdk/src/contexts/eth/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { CircleRelayer__factory } from '../../abis/CircleRelayer__factory';
import { ContractsAbstract } from '../abstracts/contracts';
import { WormholeContext } from '../../wormhole';
import { filterByContext } from '../../utils';
import { ethers } from 'ethers';

/**
* @category EVM
Expand Down Expand Up @@ -74,11 +73,8 @@ export class EthContracts<
*
* @returns An interface for the bridge contract, undefined if not found
*/
getBridge(
chain: ChainName | ChainId,
provider?: ethers.providers.Provider,
): Bridge | undefined {
const connection = provider || this.context.mustGetConnection(chain);
getBridge(chain: ChainName | ChainId): Bridge | undefined {
const connection = this.context.mustGetConnection(chain);
const address = this.mustGetContracts(chain).token_bridge;
if (!address) return undefined;
return ethers_contracts.Bridge__factory.connect(address, connection);
Expand All @@ -89,11 +85,8 @@ export class EthContracts<
*
* @returns An interface for the bridge contract, errors if not found
*/
mustGetBridge(
chain: ChainName | ChainId,
provider?: ethers.providers.Provider,
): Bridge {
const bridge = this.getBridge(chain, provider);
mustGetBridge(chain: ChainName | ChainId): Bridge {
const bridge = this.getBridge(chain);
if (!bridge)
throw new Error(`Bridge contract for domain ${chain} not found`);
return bridge;
Expand Down
8 changes: 0 additions & 8 deletions sdk/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,6 @@ export function stripHexPrefix(val: string) {
return val.startsWith('0x') ? val.slice(2) : val;
}

export function chunkArray<T>(arr: T[], size: number): T[][] {
const chunks = [];
for (let i = 0; i < arr.length; i += size) {
chunks.push(arr.slice(i, i + size));
}
return chunks;
}

// (asset chain, asset address, foreign chain) => address
type ForeignAssetCacheMap = Partial<
Record<ChainName, Partial<Record<string, Partial<Record<ChainName, string>>>>>
Expand Down

0 comments on commit c823976

Please sign in to comment.