diff --git a/src/data/routes_1_0xc186fA914353c44b2E33eBE05f21846F1048bEda.json b/src/data/routes_1_0xc186fA914353c44b2E33eBE05f21846F1048bEda.json index 430278a73..95e8dc542 100644 --- a/src/data/routes_1_0xc186fA914353c44b2E33eBE05f21846F1048bEda.json +++ b/src/data/routes_1_0xc186fA914353c44b2E33eBE05f21846F1048bEda.json @@ -1780,5 +1780,9 @@ "tokenSymbol": "BOBA", "isNative": false } - ] + ], + "spokePoolVerifier": { + "address": "0x269727F088F16E1Aea52Cf5a97B1CD41DAA3f02D", + "enabledChains": [1, 10, 137, 8453, 42161] + } } diff --git a/src/data/routes_5_0x0e2817C49698cc0874204AeDf7c72Be2Bb7fCD5d.json b/src/data/routes_5_0x0e2817C49698cc0874204AeDf7c72Be2Bb7fCD5d.json index 5d03b2bdb..07bf401ba 100644 --- a/src/data/routes_5_0x0e2817C49698cc0874204AeDf7c72Be2Bb7fCD5d.json +++ b/src/data/routes_5_0x0e2817C49698cc0874204AeDf7c72Be2Bb7fCD5d.json @@ -63,5 +63,9 @@ "l1TokenAddress": "0x07865c6E87B9F70255377e024ace6630C1Eaa37F" } ], - "pools": [] + "pools": [], + "spokePoolVerifier": { + "address": "0x269727F088F16E1Aea52Cf5a97B1CD41DAA3f02D", + "enabledChains": [] + } } diff --git a/src/hooks/useDeposits.ts b/src/hooks/useDeposits.ts index 16e1df8b3..96e1190d1 100644 --- a/src/hooks/useDeposits.ts +++ b/src/hooks/useDeposits.ts @@ -88,7 +88,9 @@ export function useUserDeposits( // To provide a better UX, we take optimistically updated local pending deposits // into account to show on the "My Transactions" page. const localPendingUserDeposits = getLocalPendingDeposits().filter( - (deposit) => deposit.depositorAddr === userAddress + (deposit) => + deposit.depositorAddr === userAddress || + deposit.recipientAddr === userAddress ); const { deposits, pagination } = await getDeposits({ address: userAddress, diff --git a/src/utils/bridge.ts b/src/utils/bridge.ts index 84a395738..7cd522b38 100644 --- a/src/utils/bridge.ts +++ b/src/utils/bridge.ts @@ -178,14 +178,32 @@ export async function sendAcrossDeposit( }: AcrossDepositArgs ): Promise { const config = getConfig(); - const spokePool = config.getSpokePool(fromChain); const provider = getProvider(fromChain); - const code = await provider.getCode(spokePool.address); - if (!code) { + + const spokePool = config.getSpokePool(fromChain); + const spokePoolVerifier = config.getSpokePoolVerifier(fromChain); + + // If the spoke pool verifier is enabled, use it for native transfers. + const shouldUseSpokePoolVerifier = Boolean(spokePoolVerifier) && isNative; + + if (shouldUseSpokePoolVerifier) { + const spokePoolVerifierCode = await provider.getCode( + spokePoolVerifier!.address + ); + if (!spokePoolVerifierCode || spokePoolVerifierCode === "0x") { + throw new Error( + `SpokePoolVerifier not deployed at ${spokePoolVerifier!.address}` + ); + } + } + + const spokePoolCode = await provider.getCode(spokePool.address); + if (!spokePoolCode || spokePoolCode === "0x") { throw new Error(`SpokePool not deployed at ${spokePool.address}`); } + const value = isNative ? amount : ethers.constants.Zero; - const tx = await spokePool.populateTransaction.deposit( + const commonArgs = [ recipient, tokenAddress, amount, @@ -194,8 +212,16 @@ export async function sendAcrossDeposit( quoteTimestamp, message, maxCount, - { value } - ); + { value }, + ] as const; + + const tx = shouldUseSpokePoolVerifier + ? await config + .getSpokePoolVerifier(fromChain)! + .populateTransaction.deposit(spokePool.address, ...commonArgs) + : await config + .getSpokePool(fromChain) + .populateTransaction.deposit(...commonArgs); // do not tag a referrer if data is not provided as a hex string. tx.data = diff --git a/src/utils/config.ts b/src/utils/config.ts index 03d423623..21d7346a2 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -10,6 +10,8 @@ import { HubPool__factory, SpokePool, SpokePool__factory, + SpokePoolVerifier, + SpokePoolVerifier__factory, AcrossMerkleDistributor, AcrossMerkleDistributor__factory, AcceleratingDistributor, @@ -34,6 +36,8 @@ export class ConfigClient { public readonly spokeChains: Set = new Set(); public readonly fromChains: Set = new Set(); public readonly toChains: Set = new Set(); + public readonly enabledChainsSpokePoolVerifier: Set = new Set(); + public readonly spokePoolVerifierAddress: string = ""; public tokenOrder: Record = {}; public chainOrder: Record = {}; public routes: constants.Routes = []; @@ -71,8 +75,11 @@ export class ConfigClient { this.tokenOrder[route.fromTokenSymbol] + this.chainOrder[route.toChain] ); }); - // prioritize routes based on token symbol and tochain. This just gives us better route prioritization when filtering a fromChain this.pools = this.config.pools; + this.enabledChainsSpokePoolVerifier = new Set( + this.config.spokePoolVerifier.enabledChains || [] + ); + this.spokePoolVerifierAddress = this.config.spokePoolVerifier.address; } getWethAddress(): string { return this.config.hubPoolWethAddress; @@ -105,6 +112,25 @@ export class ConfigClient { const provider = signer ?? providerUtils.getProvider(chainId); return SpokePool__factory.connect(address, provider); } + getSpokePoolVerifierAddress(chainId: constants.ChainId): string | undefined { + if (!this.enabledChainsSpokePoolVerifier.has(chainId)) { + return undefined; + } + return this.spokePoolVerifierAddress; + } + getSpokePoolVerifier( + chainId: constants.ChainId, + signer?: Signer + ): SpokePoolVerifier | undefined { + const address = this.getSpokePoolVerifierAddress(chainId); + + if (!address) { + return undefined; + } + + const provider = signer ?? providerUtils.getProvider(chainId); + return SpokePoolVerifier__factory.connect(address, provider); + } getHubPoolChainId(): constants.ChainId { return this.config.hubPoolChain; } diff --git a/src/utils/constants.ts b/src/utils/constants.ts index d8768ff21..cda9b1eaa 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -503,10 +503,15 @@ const PoolSS = superstruct.object({ tokenSymbol: superstruct.string(), isNative: superstruct.boolean(), }); +const SpokePoolVerifierSS = superstruct.object({ + enabledChains: superstruct.array(superstruct.number()), + address: superstruct.string(), +}); const PoolsSS = superstruct.array(PoolSS); const RouteConfigSS = superstruct.type({ routes: RoutesSS, pools: PoolsSS, + spokePoolVerifier: SpokePoolVerifierSS, hubPoolWethAddress: superstruct.string(), hubPoolChain: superstruct.number(), hubPoolAddress: superstruct.string(), @@ -520,6 +525,7 @@ export type Route = superstruct.Infer; export type Routes = superstruct.Infer; export type Pool = superstruct.Infer; export type Pools = superstruct.Infer; +export type SpokePoolVerifier = superstruct.Infer; export function getRoutes(chainId: ChainId): RouteConfig { if (chainId === ChainId.MAINNET) { superstruct.assert(MainnetRoutes, RouteConfigSS); diff --git a/src/utils/typechain.ts b/src/utils/typechain.ts index 7a3f74238..2dce307b8 100644 --- a/src/utils/typechain.ts +++ b/src/utils/typechain.ts @@ -6,6 +6,7 @@ export { AcrossMerkleDistributor__factory } from "@across-protocol/contracts-v2/dist/typechain/factories/contracts/merkle-distributor/AcrossMerkleDistributor__factory"; export { HubPool__factory } from "@across-protocol/contracts-v2/dist/typechain/factories/contracts/HubPool__factory"; export { SpokePool__factory } from "@across-protocol/contracts-v2/dist/typechain/factories/contracts/SpokePool.sol/SpokePool__factory"; +export { SpokePoolVerifier__factory } from "@across-protocol/contracts-v2/dist/typechain/factories/contracts/SpokePoolVerifier__factory"; export { ERC20__factory } from "@across-protocol/contracts-v2/dist/typechain/factories/@openzeppelin/contracts/token/ERC20/ERC20__factory"; export { AcceleratingDistributor__factory } from "@across-protocol/across-token/dist/typechain/factories/AcceleratingDistributor__factory"; export { ClaimAndStake__factory } from "@across-protocol/across-token/dist/typechain/factories/ClaimAndStake__factory"; @@ -13,6 +14,7 @@ export { ClaimAndStake__factory } from "@across-protocol/across-token/dist/typec export type { AcrossMerkleDistributor } from "@across-protocol/contracts-v2/dist/typechain/contracts/merkle-distributor/AcrossMerkleDistributor"; export type { HubPool } from "@across-protocol/contracts-v2/dist/typechain/contracts/HubPool"; export type { SpokePool } from "@across-protocol/contracts-v2/dist/typechain/contracts/SpokePool.sol/SpokePool"; +export type { SpokePoolVerifier } from "@across-protocol/contracts-v2/dist/typechain/contracts/SpokePoolVerifier"; export type { AcceleratingDistributor } from "@across-protocol/across-token/dist/typechain/AcceleratingDistributor"; export type { ClaimAndStake } from "@across-protocol/across-token/dist/typechain/ClaimAndStake"; export type {