diff --git a/builder/src/consts.ts b/builder/src/consts.ts index d88caa63e..11470d2d1 100644 --- a/builder/src/consts.ts +++ b/builder/src/consts.ts @@ -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", @@ -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", diff --git a/sdk/src/config/MAINNET.ts b/sdk/src/config/MAINNET.ts index 4c14c2e5d..9a3a388a8 100644 --- a/sdk/src/config/MAINNET.ts +++ b/sdk/src/config/MAINNET.ts @@ -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', diff --git a/sdk/src/config/TESTNET.ts b/sdk/src/config/TESTNET.ts index cc8b86782..4a1cdcb40 100644 --- a/sdk/src/config/TESTNET.ts +++ b/sdk/src/config/TESTNET.ts @@ -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', diff --git a/sdk/src/contexts/eth/context.ts b/sdk/src/contexts/eth/context.ts index 5bc3445f8..330bcb38b 100644 --- a/sdk/src/contexts/eth/context.ts +++ b/sdk/src/contexts/eth/context.ts @@ -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'; @@ -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 Promise)> { + ): Promise { const chainName = this.context.toChainName(chain); if (this.foreignAssetCache.get(tokenId.chain, tokenId.address, chainName)) { return this.foreignAssetCache.get( @@ -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 { - 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( @@ -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; } diff --git a/sdk/src/contexts/eth/contracts.ts b/sdk/src/contexts/eth/contracts.ts index 5edde0d64..90b26724f 100644 --- a/sdk/src/contexts/eth/contracts.ts +++ b/sdk/src/contexts/eth/contracts.ts @@ -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 @@ -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); @@ -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; diff --git a/sdk/src/utils.ts b/sdk/src/utils.ts index 0e936c904..8cc9eb506 100644 --- a/sdk/src/utils.ts +++ b/sdk/src/utils.ts @@ -8,14 +8,6 @@ export function stripHexPrefix(val: string) { return val.startsWith('0x') ? val.slice(2) : val; } -export function chunkArray(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>>>>