From 0f6d1cac66c683e720547607547b53e2c4906def Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Mon, 10 Jul 2023 15:55:39 +0200 Subject: [PATCH 001/192] adjust genAbi script --- scripts/genAbi.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/genAbi.js b/scripts/genAbi.js index c01b38593b..d388a043a9 100644 --- a/scripts/genAbi.js +++ b/scripts/genAbi.js @@ -11,7 +11,7 @@ async function main() { const cwd = process.cwd() const nitroPath = getPackagePath('@arbitrum/nitro-contracts') - const peripheralsPath = getPackagePath('arb-bridge-peripherals') + const tokenBridgePath = getPackagePath('@arbitrum/token-bridge-contracts') console.log('Compiling paths.') @@ -27,20 +27,20 @@ async function main() { // https://yarnpkg.com/advanced/rulebook#packages-should-never-write-inside-their-own-folder-outside-of-postinstall // instead of writing in postinstall in each of those packages, we should target a local folder in sdk's postinstall - console.log('building nitro') - execSync(`${npmExec} run hardhat:prod compile`, { + console.log('building @arbitrum/nitro-contracts') + execSync(`${npmExec} run build`, { cwd: nitroPath, }) - console.log('building peripherals') - execSync(`${npmExec} run hardhat:prod compile`, { - cwd: peripheralsPath, + console.log('building @arbitrum/token-bridge-contracts') + execSync(`${npmExec} run build`, { + cwd: tokenBridgePath, }) console.log('Done compiling') const nitroFiles = glob(cwd, [ - `${peripheralsPath}/build/contracts/!(build-info)/**/+([a-zA-Z0-9_]).json`, + `${tokenBridgePath}/build/contracts/!(build-info)/**/+([a-zA-Z0-9_]).json`, `${nitroPath}/build/contracts/!(build-info)/**/+([a-zA-Z0-9_]).json`, ]) From 9e9ef4cd60a85612e6cf1f577f52b5f062c888f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Mon, 10 Jul 2023 16:53:57 +0200 Subject: [PATCH 002/192] copy bridger class --- src/index.ts | 1 + src/lib/assetBridger/NativeErc20Bridger.ts | 310 +++++++++++++++++++++ 2 files changed, 311 insertions(+) create mode 100644 src/lib/assetBridger/NativeErc20Bridger.ts diff --git a/src/index.ts b/src/index.ts index 70fa6dc5c7..bb20e6010d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,6 +18,7 @@ export { EthBridger } from './lib/assetBridger/ethBridger' export { Erc20Bridger } from './lib/assetBridger/erc20Bridger' +export { NativeErc20Bridger } from './lib/assetBridger/NativeErc20Bridger' export { L2TransactionReceipt, L2ContractTransaction, diff --git a/src/lib/assetBridger/NativeErc20Bridger.ts b/src/lib/assetBridger/NativeErc20Bridger.ts new file mode 100644 index 0000000000..465105fae3 --- /dev/null +++ b/src/lib/assetBridger/NativeErc20Bridger.ts @@ -0,0 +1,310 @@ +/* + * Copyright 2021, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* eslint-env node */ +'use strict' + +import { Signer } from '@ethersproject/abstract-signer' +import { Provider } from '@ethersproject/abstract-provider' +import { PayableOverrides, Overrides } from '@ethersproject/contracts' +import { BigNumber } from 'ethers' + +import { Inbox__factory } from '../abi/factories/Inbox__factory' +import { ArbSys__factory } from '../abi/factories/ArbSys__factory' +import { ARB_SYS_ADDRESS } from '../dataEntities/constants' +import { AssetBridger } from './assetBridger' +import { + L1EthDepositTransaction, + L1ContractCallTransaction, + L1TransactionReceipt, +} from '../message/L1Transaction' +import { + L2ContractTransaction, + L2TransactionReceipt, +} from '../message/L2Transaction' +import { L1ToL2MessageCreator } from '../message/L1ToL2MessageCreator' +import { GasOverrides } from '../message/L1ToL2MessageGasEstimator' +import { + isL1ToL2TransactionRequest, + isL2ToL1TransactionRequest, + L1ToL2TransactionRequest, + L2ToL1TransactionRequest, +} from '../dataEntities/transactionRequest' +import { OmitTyped } from '../utils/types' +import { SignerProviderUtils } from '../dataEntities/signerOrProvider' +import { MissingProviderArbSdkError } from '../dataEntities/errors' +import { getL2Network } from '../dataEntities/networks' + +export interface EthWithdrawParams { + /** + * The amount of ETH or tokens to be withdrawn + */ + amount: BigNumber + /** + * The L1 address to receive the value. + */ + destinationAddress: string + /** + * The address of the withdrawal sender + */ + from: string + /** + * Transaction overrides + */ + overrides?: PayableOverrides +} + +export type EthDepositParams = { + /** + * The L1 provider or signer + */ + l1Signer: Signer + /** + * The amount of ETH or tokens to be deposited + */ + amount: BigNumber + /** + * Transaction overrides + */ + overrides?: PayableOverrides +} + +export type EthDepositToParams = EthDepositParams & { + /** + * An L2 provider + */ + l2Provider: Provider + /** + * L2 address of the entity receiving the funds + */ + destinationAddress: string + /** + * Overrides for the retryable ticket parameters + */ + retryableGasOverrides?: GasOverrides +} + +export type L1ToL2TxReqAndSigner = L1ToL2TransactionRequest & { + l1Signer: Signer + overrides?: Overrides +} + +export type L2ToL1TxReqAndSigner = L2ToL1TransactionRequest & { + l2Signer: Signer + overrides?: Overrides +} + +type EthDepositRequestParams = OmitTyped< + EthDepositParams, + 'overrides' | 'l1Signer' +> & { from: string } + +type EthDepositToRequestParams = OmitTyped< + EthDepositToParams, + 'overrides' | 'l1Signer' +> & { + /** + * The L1 provider + */ + l1Provider: Provider + /** + * Address that is depositing the ETH + */ + from: string +} + +/** + * Bridger for moving ETH back and forth between L1 to L2 + */ +export class NativeErc20Bridger extends AssetBridger< + EthDepositParams | EthDepositToParams | L1ToL2TxReqAndSigner, + EthWithdrawParams | L2ToL1TxReqAndSigner +> { + /** + * Instantiates a new EthBridger from an L2 Provider + * @param l2Provider + * @returns + */ + public static async fromProvider(l2Provider: Provider) { + return new NativeErc20Bridger(await getL2Network(l2Provider)) + } + + /** + * Get a transaction request for an eth deposit + * @param params + * @returns + */ + public async getDepositRequest( + params: EthDepositRequestParams + ): Promise> { + const inboxInterface = Inbox__factory.createInterface() + + const functionData = ( + inboxInterface as unknown as { + encodeFunctionData( + functionFragment: 'depositEth()', + values?: undefined + ): string + } + ).encodeFunctionData('depositEth()') + + return { + txRequest: { + to: this.l2Network.ethBridge.inbox, + value: params.amount, + data: functionData, + from: params.from, + }, + isValid: async () => true, + } + } + + /** + * Deposit ETH from L1 onto L2 + * @param params + * @returns + */ + public async deposit( + params: EthDepositParams | L1ToL2TxReqAndSigner + ): Promise { + await this.checkL1Network(params.l1Signer) + + const ethDeposit = isL1ToL2TransactionRequest(params) + ? params + : await this.getDepositRequest({ + ...params, + from: await params.l1Signer.getAddress(), + }) + + const tx = await params.l1Signer.sendTransaction({ + ...ethDeposit.txRequest, + ...params.overrides, + }) + + return L1TransactionReceipt.monkeyPatchEthDepositWait(tx) + } + + /** + * Get a transaction request for an ETH deposit to a different L2 address using Retryables + * @param params + * @returns + */ + public async getDepositToRequest( + params: EthDepositToRequestParams + ): Promise { + const requestParams = { + ...params, + to: params.destinationAddress, + l2CallValue: params.amount, + callValueRefundAddress: params.destinationAddress, + data: '0x', + } + + // Gas overrides can be passed in the parameters + const gasOverrides = params.retryableGasOverrides || undefined + + return L1ToL2MessageCreator.getTicketCreationRequest( + requestParams, + params.l1Provider, + params.l2Provider, + gasOverrides + ) + } + + /** + * Deposit ETH from L1 onto a different L2 address + * @param params + * @returns + */ + public async depositTo( + params: + | EthDepositToParams + | (L1ToL2TxReqAndSigner & { l2Provider: Provider }) + ): Promise { + await this.checkL1Network(params.l1Signer) + await this.checkL2Network(params.l2Provider) + + const retryableTicketRequest = isL1ToL2TransactionRequest(params) + ? params + : await this.getDepositToRequest({ + ...params, + from: await params.l1Signer.getAddress(), + l1Provider: params.l1Signer.provider!, + }) + + const tx = await params.l1Signer.sendTransaction({ + ...retryableTicketRequest.txRequest, + ...params.overrides, + }) + + return L1TransactionReceipt.monkeyPatchContractCallWait(tx) + } + + /** + * Get a transaction request for an eth withdrawal + * @param params + * @returns + */ + public async getWithdrawalRequest( + params: EthWithdrawParams + ): Promise { + const iArbSys = ArbSys__factory.createInterface() + const functionData = iArbSys.encodeFunctionData('withdrawEth', [ + params.destinationAddress, + ]) + + return { + txRequest: { + to: ARB_SYS_ADDRESS, + data: functionData, + value: params.amount, + from: params.from, + }, + // we make this async and expect a provider since we + // in the future we want to do proper estimation here + /* eslint-disable @typescript-eslint/no-unused-vars */ + estimateL1GasLimit: async (l1Provider: Provider) => { + // measured 126998 - add some padding + return BigNumber.from(130000) + }, + } + } + + /** + * Withdraw ETH from L2 onto L1 + * @param params + * @returns + */ + public async withdraw( + params: (EthWithdrawParams & { l2Signer: Signer }) | L2ToL1TxReqAndSigner + ): Promise { + if (!SignerProviderUtils.signerHasProvider(params.l2Signer)) { + throw new MissingProviderArbSdkError('l2Signer') + } + await this.checkL2Network(params.l2Signer) + + const request = isL2ToL1TransactionRequest< + EthWithdrawParams & { l2Signer: Signer } + >(params) + ? params + : await this.getWithdrawalRequest(params) + + const tx = await params.l2Signer.sendTransaction({ + ...request.txRequest, + ...params.overrides, + }) + return L2TransactionReceipt.monkeyPatchWait(tx) + } +} From 44633d8c114e67da7876311c95393ad0b51e129e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Tue, 11 Jul 2023 12:26:39 +0200 Subject: [PATCH 003/192] add native token property to L2 networ --- src/lib/dataEntities/networks.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index db1ea89d14..51972ca98b 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -40,7 +40,12 @@ export interface L2Network extends Network { * How long to wait (ms) for a deposit to arrive on l2 before timing out a request */ depositTimeout: number + /** + * In case of a chain that uses an ERC-20 token as its native currency, this is the address of said token on the parent chain. + */ + nativeToken?: string } + export interface Network { chainID: number name: string From a88f94047d5efb772145445e4a1b67dcd11808ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Tue, 11 Jul 2023 12:27:39 +0200 Subject: [PATCH 004/192] add approval implementation and tests --- scripts/testSetup.ts | 18 ++- src/lib/assetBridger/NativeErc20Bridger.ts | 37 +++++- tests/integration/NativeErc20Bridger.test.ts | 118 +++++++++++++++++++ 3 files changed, 164 insertions(+), 9 deletions(-) create mode 100644 tests/integration/NativeErc20Bridger.test.ts diff --git a/scripts/testSetup.ts b/scripts/testSetup.ts index 5558054297..dbe6444a14 100644 --- a/scripts/testSetup.ts +++ b/scripts/testSetup.ts @@ -20,7 +20,12 @@ import { JsonRpcProvider } from '@ethersproject/providers' import { Wallet } from '@ethersproject/wallet' import dotenv from 'dotenv' -import { EthBridger, InboxTools, Erc20Bridger } from '../src' +import { + EthBridger, + InboxTools, + Erc20Bridger, + NativeErc20Bridger, +} from '../src' import { L1Network, L2Network, @@ -203,13 +208,15 @@ export const getSigner = (provider: JsonRpcProvider, key?: string) => { else return provider.getSigner(0) } -export const testSetup = async (): Promise<{ +export const testSetup = async ( + isERC20Rollup: TIsERC20Rollup = false as TIsERC20Rollup +): Promise<{ l1Network: L1Network l2Network: L2Network l1Signer: Signer l2Signer: Signer erc20Bridger: Erc20Bridger - ethBridger: EthBridger + ethBridger: TIsERC20Rollup extends true ? NativeErc20Bridger : EthBridger adminErc20Bridger: AdminErc20Bridger inboxTools: InboxTools l1Deployer: Signer @@ -264,7 +271,9 @@ export const testSetup = async (): Promise<{ const erc20Bridger = new Erc20Bridger(setL2Network) const adminErc20Bridger = new AdminErc20Bridger(setL2Network) - const ethBridger = new EthBridger(setL2Network) + const ethBridger = isERC20Rollup + ? new NativeErc20Bridger(setL2Network) + : new EthBridger(setL2Network) const inboxTools = new InboxTools(l1Signer, setL2Network) return { @@ -274,6 +283,7 @@ export const testSetup = async (): Promise<{ l2Network: setL2Network, erc20Bridger, adminErc20Bridger, + // @ts-ignore TODO(spsjvc): fix ethBridger, inboxTools, l1Deployer, diff --git a/src/lib/assetBridger/NativeErc20Bridger.ts b/src/lib/assetBridger/NativeErc20Bridger.ts index 465105fae3..50bed7088e 100644 --- a/src/lib/assetBridger/NativeErc20Bridger.ts +++ b/src/lib/assetBridger/NativeErc20Bridger.ts @@ -19,9 +19,10 @@ import { Signer } from '@ethersproject/abstract-signer' import { Provider } from '@ethersproject/abstract-provider' import { PayableOverrides, Overrides } from '@ethersproject/contracts' -import { BigNumber } from 'ethers' +import { BigNumber, constants } from 'ethers' -import { Inbox__factory } from '../abi/factories/Inbox__factory' +import { ERC20Inbox__factory } from '../abi/factories/ERC20Inbox__factory' +import { ERC20__factory } from '../abi/factories/ERC20__factory' import { ArbSys__factory } from '../abi/factories/ArbSys__factory' import { ARB_SYS_ADDRESS } from '../dataEntities/constants' import { AssetBridger } from './assetBridger' @@ -44,8 +45,8 @@ import { } from '../dataEntities/transactionRequest' import { OmitTyped } from '../utils/types' import { SignerProviderUtils } from '../dataEntities/signerOrProvider' -import { MissingProviderArbSdkError } from '../dataEntities/errors' -import { getL2Network } from '../dataEntities/networks' +import { ArbSdkError, MissingProviderArbSdkError } from '../dataEntities/errors' +import { L2Network, getL2Network } from '../dataEntities/networks' export interface EthWithdrawParams { /** @@ -133,7 +134,24 @@ export class NativeErc20Bridger extends AssetBridger< EthWithdrawParams | L2ToL1TxReqAndSigner > { /** - * Instantiates a new EthBridger from an L2 Provider + * The address of the native ERC-20 token on the parent chain. + */ + protected readonly nativeToken: string + + public constructor(public readonly l2Network: L2Network) { + super(l2Network) + + if (typeof l2Network.nativeToken === 'undefined') { + throw new ArbSdkError( + `native token is missing from the l2 network object` + ) + } + + this.nativeToken = l2Network.nativeToken + } + + /** + * Instantiates a new NativeErc20Bridger from an L2 Provider * @param l2Provider * @returns */ @@ -141,6 +159,15 @@ export class NativeErc20Bridger extends AssetBridger< return new NativeErc20Bridger(await getL2Network(l2Provider)) } + // TODO(spsjvc): clean up, support tx request and add jsdoc + public async approve(params: { amount?: BigNumber; l1Signer: Signer }) { + const token = ERC20__factory.connect(this.nativeToken, params.l1Signer) + return token.approve( + this.l2Network.ethBridge.inbox, + params.amount ?? constants.MaxUint256 + ) + } + /** * Get a transaction request for an eth deposit * @param params diff --git a/tests/integration/NativeErc20Bridger.test.ts b/tests/integration/NativeErc20Bridger.test.ts new file mode 100644 index 0000000000..de06ea0263 --- /dev/null +++ b/tests/integration/NativeErc20Bridger.test.ts @@ -0,0 +1,118 @@ +/* + * Copyright 2023, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* eslint-env node */ +'use strict' + +import { expect } from 'chai' +import { ethers, providers, constants } from 'ethers' +import dotenv from 'dotenv' + +import { Wallet } from '@ethersproject/wallet' +import { parseEther } from '@ethersproject/units' + +import { skipIfMainnet } from './testHelpers' + +import { testSetup } from '../../scripts/testSetup' +import { ERC20__factory } from '../../src/lib/abi/factories/ERC20__factory' +import { ERC20 } from '../../src/lib/abi/ERC20' + +dotenv.config() + +const l1Provider = new providers.StaticJsonRpcProvider(process.env.ETH_URL) +// create a fresh random wallet for a clean slate +const l1Signer = Wallet.createRandom().connect(l1Provider) + +const l1DeployerWallet = new ethers.Wallet( + ethers.utils.sha256(ethers.utils.toUtf8Bytes('user_l1user')), + l1Provider +) + +let nativeToken: ERC20 + +async function fundL1(account: string) { + const { ethBridger } = await testSetup(true) + + const nativeTokenAddress = ethBridger.l2Network.nativeToken! + nativeToken = ERC20__factory.connect(nativeTokenAddress, l1Provider) + + const fundEthTx = await l1DeployerWallet.sendTransaction({ + to: account, + value: parseEther('10'), + }) + await fundEthTx.wait() + + const fundTokenTx = await nativeToken + .connect(l1DeployerWallet) + .transfer(account, parseEther('10')) + await fundTokenTx.wait() +} + +describe('NativeErc20Bridger', async () => { + before(async function () { + await fundL1(await l1Signer.getAddress()) + }) + + beforeEach('skipIfMainnet', async function () { + await skipIfMainnet(this) + }) + + it('approves the erc-20 token on the parent chain for an arbitrary amount', async function () { + // using a random wallet for non-max amount approval + // the rest of the test suite will use the account with the max approval + const randomL1Signer = Wallet.createRandom().connect(l1Provider) + await fundL1(await randomL1Signer.getAddress()) + + const { ethBridger } = await testSetup(true) + + const inbox = ethBridger.l2Network.ethBridge.inbox + const amount = ethers.utils.parseEther('1') + + const approvalTx = await ethBridger.approve({ + amount, + l1Signer: randomL1Signer, + }) + await approvalTx.wait() + + const allowance = await nativeToken.allowance( + await randomL1Signer.getAddress(), + inbox + ) + + expect(allowance.toString()).to.equal( + amount.toString(), + 'allowance incorrect' + ) + }) + + it('approves the erc-20 token on the parent chain for the max amount', async function () { + const { ethBridger } = await testSetup(true) + + const inbox = ethBridger.l2Network.ethBridge.inbox + + const approvalTx = await ethBridger.approve({ l1Signer }) + await approvalTx.wait() + + const allowance = await nativeToken.allowance( + await l1Signer.getAddress(), + inbox + ) + + expect(allowance.toString()).to.equal( + constants.MaxUint256.toString(), + 'allowance incorrect' + ) + }) +}) From 8ee89e219df86fb5a6cdee8537cda5a182b59021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Tue, 11 Jul 2023 13:01:44 +0200 Subject: [PATCH 005/192] add deposit --- src/lib/assetBridger/NativeErc20Bridger.ts | 14 +++--- tests/integration/NativeErc20Bridger.test.ts | 46 +++++++++++++++++++- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/src/lib/assetBridger/NativeErc20Bridger.ts b/src/lib/assetBridger/NativeErc20Bridger.ts index 50bed7088e..37e94babfe 100644 --- a/src/lib/assetBridger/NativeErc20Bridger.ts +++ b/src/lib/assetBridger/NativeErc20Bridger.ts @@ -169,28 +169,28 @@ export class NativeErc20Bridger extends AssetBridger< } /** - * Get a transaction request for an eth deposit + * Get a transaction request for a native ERC-20 deposit * @param params * @returns */ public async getDepositRequest( params: EthDepositRequestParams ): Promise> { - const inboxInterface = Inbox__factory.createInterface() + const inboxInterface = ERC20Inbox__factory.createInterface() const functionData = ( inboxInterface as unknown as { encodeFunctionData( - functionFragment: 'depositEth()', - values?: undefined + functionFragment: 'depositERC20(uint256)', + values: [BigNumber] ): string } - ).encodeFunctionData('depositEth()') + ).encodeFunctionData('depositERC20(uint256)', [params.amount]) return { txRequest: { to: this.l2Network.ethBridge.inbox, - value: params.amount, + value: constants.Zero, data: functionData, from: params.from, }, @@ -199,7 +199,7 @@ export class NativeErc20Bridger extends AssetBridger< } /** - * Deposit ETH from L1 onto L2 + * Deposit native ERC-20 from L1 onto L2 * @param params * @returns */ diff --git a/tests/integration/NativeErc20Bridger.test.ts b/tests/integration/NativeErc20Bridger.test.ts index de06ea0263..4560e9cffb 100644 --- a/tests/integration/NativeErc20Bridger.test.ts +++ b/tests/integration/NativeErc20Bridger.test.ts @@ -23,7 +23,7 @@ import dotenv from 'dotenv' import { Wallet } from '@ethersproject/wallet' import { parseEther } from '@ethersproject/units' -import { skipIfMainnet } from './testHelpers' +import { skipIfMainnet, wait } from './testHelpers' import { testSetup } from '../../scripts/testSetup' import { ERC20__factory } from '../../src/lib/abi/factories/ERC20__factory' @@ -32,8 +32,12 @@ import { ERC20 } from '../../src/lib/abi/ERC20' dotenv.config() const l1Provider = new providers.StaticJsonRpcProvider(process.env.ETH_URL) +const l2Provider = new providers.StaticJsonRpcProvider(process.env.ARB_URL) + // create a fresh random wallet for a clean slate -const l1Signer = Wallet.createRandom().connect(l1Provider) +const wallet = Wallet.createRandom() +const l1Signer = wallet.connect(l1Provider) +const l2Signer = wallet.connect(l2Provider) const l1DeployerWallet = new ethers.Wallet( ethers.utils.sha256(ethers.utils.toUtf8Bytes('user_l1user')), @@ -115,4 +119,42 @@ describe('NativeErc20Bridger', async () => { 'allowance incorrect' ) }) + + it('deposits erc-20 token via params', async function () { + const { ethBridger } = await testSetup(true) + const bridge = ethBridger.l2Network.ethBridge.bridge + + const amount = parseEther('2') + + const initialBalanceBridge = await nativeToken.balanceOf(bridge) + const initialBalanceDepositor = await l2Signer.getBalance() + + // perform the deposit + const depositTx = await ethBridger.deposit({ + amount, + l1Signer, + }) + await depositTx.wait() + + expect( + // balance in the bridge after the deposit + (await nativeToken.balanceOf(bridge)).toString() + ).to.equal( + // balance in the bridge after the deposit should equal to the initial balance in the bridge + the amount deposited + initialBalanceBridge.add(amount).toString(), + 'incorrect balance in bridge after deposit' + ) + + // wait for minting on L2 + await wait(30 * 1000) + + expect( + // balance in the depositor account after the deposit + (await l2Signer.getBalance()).toString() + ).to.equal( + // balance in the depositor account after the deposit should equal to the initial balance in th depositor account + the amount deposited + initialBalanceDepositor.add(amount).toString(), + 'incorrect balance in depositor account after deposit' + ) + }) }) From 5f1de81e4ccc242cfed3c710a502474c8d5ec977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Tue, 11 Jul 2023 13:14:10 +0200 Subject: [PATCH 006/192] extend eth bridger --- src/lib/assetBridger/NativeErc20Bridger.ts | 159 ++----------------- src/lib/assetBridger/ethBridger.ts | 4 +- tests/integration/NativeErc20Bridger.test.ts | 2 + 3 files changed, 13 insertions(+), 152 deletions(-) diff --git a/src/lib/assetBridger/NativeErc20Bridger.ts b/src/lib/assetBridger/NativeErc20Bridger.ts index 37e94babfe..6829478e09 100644 --- a/src/lib/assetBridger/NativeErc20Bridger.ts +++ b/src/lib/assetBridger/NativeErc20Bridger.ts @@ -1,5 +1,5 @@ /* - * Copyright 2021, Offchain Labs, Inc. + * Copyright 2023, Offchain Labs, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,121 +18,36 @@ import { Signer } from '@ethersproject/abstract-signer' import { Provider } from '@ethersproject/abstract-provider' -import { PayableOverrides, Overrides } from '@ethersproject/contracts' import { BigNumber, constants } from 'ethers' import { ERC20Inbox__factory } from '../abi/factories/ERC20Inbox__factory' import { ERC20__factory } from '../abi/factories/ERC20__factory' -import { ArbSys__factory } from '../abi/factories/ArbSys__factory' -import { ARB_SYS_ADDRESS } from '../dataEntities/constants' -import { AssetBridger } from './assetBridger' import { L1EthDepositTransaction, L1ContractCallTransaction, L1TransactionReceipt, } from '../message/L1Transaction' -import { - L2ContractTransaction, - L2TransactionReceipt, -} from '../message/L2Transaction' import { L1ToL2MessageCreator } from '../message/L1ToL2MessageCreator' -import { GasOverrides } from '../message/L1ToL2MessageGasEstimator' import { isL1ToL2TransactionRequest, - isL2ToL1TransactionRequest, L1ToL2TransactionRequest, - L2ToL1TransactionRequest, } from '../dataEntities/transactionRequest' import { OmitTyped } from '../utils/types' -import { SignerProviderUtils } from '../dataEntities/signerOrProvider' -import { ArbSdkError, MissingProviderArbSdkError } from '../dataEntities/errors' +import { ArbSdkError } from '../dataEntities/errors' import { L2Network, getL2Network } from '../dataEntities/networks' - -export interface EthWithdrawParams { - /** - * The amount of ETH or tokens to be withdrawn - */ - amount: BigNumber - /** - * The L1 address to receive the value. - */ - destinationAddress: string - /** - * The address of the withdrawal sender - */ - from: string - /** - * Transaction overrides - */ - overrides?: PayableOverrides -} - -export type EthDepositParams = { - /** - * The L1 provider or signer - */ - l1Signer: Signer - /** - * The amount of ETH or tokens to be deposited - */ - amount: BigNumber - /** - * Transaction overrides - */ - overrides?: PayableOverrides -} - -export type EthDepositToParams = EthDepositParams & { - /** - * An L2 provider - */ - l2Provider: Provider - /** - * L2 address of the entity receiving the funds - */ - destinationAddress: string - /** - * Overrides for the retryable ticket parameters - */ - retryableGasOverrides?: GasOverrides -} - -export type L1ToL2TxReqAndSigner = L1ToL2TransactionRequest & { - l1Signer: Signer - overrides?: Overrides -} - -export type L2ToL1TxReqAndSigner = L2ToL1TransactionRequest & { - l2Signer: Signer - overrides?: Overrides -} - -type EthDepositRequestParams = OmitTyped< +import { + EthBridger, EthDepositParams, - 'overrides' | 'l1Signer' -> & { from: string } - -type EthDepositToRequestParams = OmitTyped< + EthDepositRequestParams, EthDepositToParams, - 'overrides' | 'l1Signer' -> & { - /** - * The L1 provider - */ - l1Provider: Provider - /** - * Address that is depositing the ETH - */ - from: string -} + EthDepositToRequestParams, + L1ToL2TxReqAndSigner, +} from './ethBridger' /** * Bridger for moving ETH back and forth between L1 to L2 */ -export class NativeErc20Bridger extends AssetBridger< - EthDepositParams | EthDepositToParams | L1ToL2TxReqAndSigner, - EthWithdrawParams | L2ToL1TxReqAndSigner -> { +export class NativeErc20Bridger extends EthBridger { /** * The address of the native ERC-20 token on the parent chain. */ @@ -278,60 +193,4 @@ export class NativeErc20Bridger extends AssetBridger< return L1TransactionReceipt.monkeyPatchContractCallWait(tx) } - - /** - * Get a transaction request for an eth withdrawal - * @param params - * @returns - */ - public async getWithdrawalRequest( - params: EthWithdrawParams - ): Promise { - const iArbSys = ArbSys__factory.createInterface() - const functionData = iArbSys.encodeFunctionData('withdrawEth', [ - params.destinationAddress, - ]) - - return { - txRequest: { - to: ARB_SYS_ADDRESS, - data: functionData, - value: params.amount, - from: params.from, - }, - // we make this async and expect a provider since we - // in the future we want to do proper estimation here - /* eslint-disable @typescript-eslint/no-unused-vars */ - estimateL1GasLimit: async (l1Provider: Provider) => { - // measured 126998 - add some padding - return BigNumber.from(130000) - }, - } - } - - /** - * Withdraw ETH from L2 onto L1 - * @param params - * @returns - */ - public async withdraw( - params: (EthWithdrawParams & { l2Signer: Signer }) | L2ToL1TxReqAndSigner - ): Promise { - if (!SignerProviderUtils.signerHasProvider(params.l2Signer)) { - throw new MissingProviderArbSdkError('l2Signer') - } - await this.checkL2Network(params.l2Signer) - - const request = isL2ToL1TransactionRequest< - EthWithdrawParams & { l2Signer: Signer } - >(params) - ? params - : await this.getWithdrawalRequest(params) - - const tx = await params.l2Signer.sendTransaction({ - ...request.txRequest, - ...params.overrides, - }) - return L2TransactionReceipt.monkeyPatchWait(tx) - } } diff --git a/src/lib/assetBridger/ethBridger.ts b/src/lib/assetBridger/ethBridger.ts index 4441f3d7f6..bc4ae673f2 100644 --- a/src/lib/assetBridger/ethBridger.ts +++ b/src/lib/assetBridger/ethBridger.ts @@ -106,12 +106,12 @@ export type L2ToL1TxReqAndSigner = L2ToL1TransactionRequest & { overrides?: Overrides } -type EthDepositRequestParams = OmitTyped< +export type EthDepositRequestParams = OmitTyped< EthDepositParams, 'overrides' | 'l1Signer' > & { from: string } -type EthDepositToRequestParams = OmitTyped< +export type EthDepositToRequestParams = OmitTyped< EthDepositToParams, 'overrides' | 'l1Signer' > & { diff --git a/tests/integration/NativeErc20Bridger.test.ts b/tests/integration/NativeErc20Bridger.test.ts index 4560e9cffb..91de9549ee 100644 --- a/tests/integration/NativeErc20Bridger.test.ts +++ b/tests/integration/NativeErc20Bridger.test.ts @@ -157,4 +157,6 @@ describe('NativeErc20Bridger', async () => { 'incorrect balance in depositor account after deposit' ) }) + + // TODO(spsjvc): add withdrawal tests }) From a06abfcf6f6b169b2dad8c3671d2608baf910355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Thu, 13 Jul 2023 15:06:08 +0200 Subject: [PATCH 007/192] clean up --- package.json | 3 +- scripts/testSetup.ts | 18 +- src/index.ts | 1 - src/lib/assetBridger/NativeErc20Bridger.ts | 196 ------------------ src/lib/assetBridger/ethBridger.ts | 74 +++++-- src/lib/dataEntities/networks.ts | 3 +- .../ethBridger.test.ts} | 26 ++- 7 files changed, 84 insertions(+), 237 deletions(-) delete mode 100644 src/lib/assetBridger/NativeErc20Bridger.ts rename tests/integration/{NativeErc20Bridger.test.ts => native-erc20/ethBridger.test.ts} (86%) diff --git a/package.json b/package.json index 4c58ebcb84..988ed2370e 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,8 @@ "test": "mocha", "test:coverage": "nyc mocha", "test:fork": "SHOULD_FORK=1 hardhat test tests/fork/*.test.ts", - "test:integration": "mocha tests/integration/ --timeout 30000000 --bail", + "test:integration": "mocha tests/integration/ --ignore tests/integration/native-erc20/ --timeout 30000000 --bail", + "test:integration:native-erc20": "mocha tests/integration/native-erc20/ --timeout 30000000 --bail", "test:unit": "mocha --parallel tests/unit/ --timeout 30000 --bail", "test:ci": "nyc --reporter=lcovonly mocha --reporter xunit", "lint": "eslint .", diff --git a/scripts/testSetup.ts b/scripts/testSetup.ts index dbe6444a14..5558054297 100644 --- a/scripts/testSetup.ts +++ b/scripts/testSetup.ts @@ -20,12 +20,7 @@ import { JsonRpcProvider } from '@ethersproject/providers' import { Wallet } from '@ethersproject/wallet' import dotenv from 'dotenv' -import { - EthBridger, - InboxTools, - Erc20Bridger, - NativeErc20Bridger, -} from '../src' +import { EthBridger, InboxTools, Erc20Bridger } from '../src' import { L1Network, L2Network, @@ -208,15 +203,13 @@ export const getSigner = (provider: JsonRpcProvider, key?: string) => { else return provider.getSigner(0) } -export const testSetup = async ( - isERC20Rollup: TIsERC20Rollup = false as TIsERC20Rollup -): Promise<{ +export const testSetup = async (): Promise<{ l1Network: L1Network l2Network: L2Network l1Signer: Signer l2Signer: Signer erc20Bridger: Erc20Bridger - ethBridger: TIsERC20Rollup extends true ? NativeErc20Bridger : EthBridger + ethBridger: EthBridger adminErc20Bridger: AdminErc20Bridger inboxTools: InboxTools l1Deployer: Signer @@ -271,9 +264,7 @@ export const testSetup = async ( const erc20Bridger = new Erc20Bridger(setL2Network) const adminErc20Bridger = new AdminErc20Bridger(setL2Network) - const ethBridger = isERC20Rollup - ? new NativeErc20Bridger(setL2Network) - : new EthBridger(setL2Network) + const ethBridger = new EthBridger(setL2Network) const inboxTools = new InboxTools(l1Signer, setL2Network) return { @@ -283,7 +274,6 @@ export const testSetup = async ( l2Network: setL2Network, erc20Bridger, adminErc20Bridger, - // @ts-ignore TODO(spsjvc): fix ethBridger, inboxTools, l1Deployer, diff --git a/src/index.ts b/src/index.ts index bb20e6010d..70fa6dc5c7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,7 +18,6 @@ export { EthBridger } from './lib/assetBridger/ethBridger' export { Erc20Bridger } from './lib/assetBridger/erc20Bridger' -export { NativeErc20Bridger } from './lib/assetBridger/NativeErc20Bridger' export { L2TransactionReceipt, L2ContractTransaction, diff --git a/src/lib/assetBridger/NativeErc20Bridger.ts b/src/lib/assetBridger/NativeErc20Bridger.ts deleted file mode 100644 index 6829478e09..0000000000 --- a/src/lib/assetBridger/NativeErc20Bridger.ts +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright 2023, Offchain Labs, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* eslint-env node */ -'use strict' - -import { Signer } from '@ethersproject/abstract-signer' -import { Provider } from '@ethersproject/abstract-provider' -import { BigNumber, constants } from 'ethers' - -import { ERC20Inbox__factory } from '../abi/factories/ERC20Inbox__factory' -import { ERC20__factory } from '../abi/factories/ERC20__factory' -import { - L1EthDepositTransaction, - L1ContractCallTransaction, - L1TransactionReceipt, -} from '../message/L1Transaction' -import { L1ToL2MessageCreator } from '../message/L1ToL2MessageCreator' -import { - isL1ToL2TransactionRequest, - L1ToL2TransactionRequest, -} from '../dataEntities/transactionRequest' -import { OmitTyped } from '../utils/types' -import { ArbSdkError } from '../dataEntities/errors' -import { L2Network, getL2Network } from '../dataEntities/networks' -import { - EthBridger, - EthDepositParams, - EthDepositRequestParams, - EthDepositToParams, - EthDepositToRequestParams, - L1ToL2TxReqAndSigner, -} from './ethBridger' - -/** - * Bridger for moving ETH back and forth between L1 to L2 - */ -export class NativeErc20Bridger extends EthBridger { - /** - * The address of the native ERC-20 token on the parent chain. - */ - protected readonly nativeToken: string - - public constructor(public readonly l2Network: L2Network) { - super(l2Network) - - if (typeof l2Network.nativeToken === 'undefined') { - throw new ArbSdkError( - `native token is missing from the l2 network object` - ) - } - - this.nativeToken = l2Network.nativeToken - } - - /** - * Instantiates a new NativeErc20Bridger from an L2 Provider - * @param l2Provider - * @returns - */ - public static async fromProvider(l2Provider: Provider) { - return new NativeErc20Bridger(await getL2Network(l2Provider)) - } - - // TODO(spsjvc): clean up, support tx request and add jsdoc - public async approve(params: { amount?: BigNumber; l1Signer: Signer }) { - const token = ERC20__factory.connect(this.nativeToken, params.l1Signer) - return token.approve( - this.l2Network.ethBridge.inbox, - params.amount ?? constants.MaxUint256 - ) - } - - /** - * Get a transaction request for a native ERC-20 deposit - * @param params - * @returns - */ - public async getDepositRequest( - params: EthDepositRequestParams - ): Promise> { - const inboxInterface = ERC20Inbox__factory.createInterface() - - const functionData = ( - inboxInterface as unknown as { - encodeFunctionData( - functionFragment: 'depositERC20(uint256)', - values: [BigNumber] - ): string - } - ).encodeFunctionData('depositERC20(uint256)', [params.amount]) - - return { - txRequest: { - to: this.l2Network.ethBridge.inbox, - value: constants.Zero, - data: functionData, - from: params.from, - }, - isValid: async () => true, - } - } - - /** - * Deposit native ERC-20 from L1 onto L2 - * @param params - * @returns - */ - public async deposit( - params: EthDepositParams | L1ToL2TxReqAndSigner - ): Promise { - await this.checkL1Network(params.l1Signer) - - const ethDeposit = isL1ToL2TransactionRequest(params) - ? params - : await this.getDepositRequest({ - ...params, - from: await params.l1Signer.getAddress(), - }) - - const tx = await params.l1Signer.sendTransaction({ - ...ethDeposit.txRequest, - ...params.overrides, - }) - - return L1TransactionReceipt.monkeyPatchEthDepositWait(tx) - } - - /** - * Get a transaction request for an ETH deposit to a different L2 address using Retryables - * @param params - * @returns - */ - public async getDepositToRequest( - params: EthDepositToRequestParams - ): Promise { - const requestParams = { - ...params, - to: params.destinationAddress, - l2CallValue: params.amount, - callValueRefundAddress: params.destinationAddress, - data: '0x', - } - - // Gas overrides can be passed in the parameters - const gasOverrides = params.retryableGasOverrides || undefined - - return L1ToL2MessageCreator.getTicketCreationRequest( - requestParams, - params.l1Provider, - params.l2Provider, - gasOverrides - ) - } - - /** - * Deposit ETH from L1 onto a different L2 address - * @param params - * @returns - */ - public async depositTo( - params: - | EthDepositToParams - | (L1ToL2TxReqAndSigner & { l2Provider: Provider }) - ): Promise { - await this.checkL1Network(params.l1Signer) - await this.checkL2Network(params.l2Provider) - - const retryableTicketRequest = isL1ToL2TransactionRequest(params) - ? params - : await this.getDepositToRequest({ - ...params, - from: await params.l1Signer.getAddress(), - l1Provider: params.l1Signer.provider!, - }) - - const tx = await params.l1Signer.sendTransaction({ - ...retryableTicketRequest.txRequest, - ...params.overrides, - }) - - return L1TransactionReceipt.monkeyPatchContractCallWait(tx) - } -} diff --git a/src/lib/assetBridger/ethBridger.ts b/src/lib/assetBridger/ethBridger.ts index bc4ae673f2..8dd2ce61db 100644 --- a/src/lib/assetBridger/ethBridger.ts +++ b/src/lib/assetBridger/ethBridger.ts @@ -19,9 +19,10 @@ import { Signer } from '@ethersproject/abstract-signer' import { Provider } from '@ethersproject/abstract-provider' import { PayableOverrides, Overrides } from '@ethersproject/contracts' -import { BigNumber } from 'ethers' +import { BigNumber, BigNumberish, BytesLike, constants } from 'ethers' import { Inbox__factory } from '../abi/factories/Inbox__factory' +import { ERC20Inbox__factory } from '../abi/factories/ERC20Inbox__factory' import { ArbSys__factory } from '../abi/factories/ArbSys__factory' import { ARB_SYS_ADDRESS } from '../dataEntities/constants' import { AssetBridger } from './assetBridger' @@ -45,7 +46,8 @@ import { import { OmitTyped } from '../utils/types' import { SignerProviderUtils } from '../dataEntities/signerOrProvider' import { MissingProviderArbSdkError } from '../dataEntities/errors' -import { getL2Network } from '../dataEntities/networks' +import { L2Network, getL2Network } from '../dataEntities/networks' +import { ERC20__factory } from '../abi/factories/ERC20__factory' export interface EthWithdrawParams { /** @@ -132,6 +134,18 @@ export class EthBridger extends AssetBridger< EthDepositParams | EthDepositToParams | L1ToL2TxReqAndSigner, EthWithdrawParams | L2ToL1TxReqAndSigner > { + /** + * In case of a chain that uses ETH as its native token, this is undefined. + * In case of a chain that uses an ERC-20 token from the parent chain as its native token, this is the address of said token on the parent chain. + */ + protected readonly nativeToken?: string + + public constructor(public readonly l2Network: L2Network) { + super(l2Network) + + this.nativeToken = l2Network.nativeToken + } + /** * Instantiates a new EthBridger from an L2 Provider * @param l2Provider @@ -141,6 +155,22 @@ export class EthBridger extends AssetBridger< return new EthBridger(await getL2Network(l2Provider)) } + // TODO(spsjvc): clean up, support tx request and add jsdoc + public async approve(params: { amount?: BigNumber; l1Signer: Signer }) { + if (typeof this.nativeToken === 'undefined') { + throw new Error( + `approve can't be called for a network that uses ETH as its native token` + ) + } + + const token = ERC20__factory.connect(this.nativeToken, params.l1Signer) + + return token.approve( + this.l2Network.ethBridge.inbox, + params.amount ?? constants.MaxUint256 + ) + } + /** * Get a transaction request for an eth deposit * @param params @@ -149,22 +179,40 @@ export class EthBridger extends AssetBridger< public async getDepositRequest( params: EthDepositRequestParams ): Promise> { - const inboxInterface = Inbox__factory.createInterface() + let data: BytesLike + let value: BigNumberish + + if (typeof this.nativeToken === 'undefined') { + const inboxInterface = Inbox__factory.createInterface() - const functionData = ( - inboxInterface as unknown as { - encodeFunctionData( - functionFragment: 'depositEth()', - values?: undefined - ): string - } - ).encodeFunctionData('depositEth()') + value = params.amount + data = ( + inboxInterface as unknown as { + encodeFunctionData( + functionFragment: 'depositEth()', + values?: undefined + ): string + } + ).encodeFunctionData('depositEth()') + } else { + const inboxInterface = ERC20Inbox__factory.createInterface() + + value = 0 + data = ( + inboxInterface as unknown as { + encodeFunctionData( + functionFragment: 'depositERC20(uint256)', + values: [BigNumber] + ): string + } + ).encodeFunctionData('depositERC20(uint256)', [params.amount]) + } return { txRequest: { to: this.l2Network.ethBridge.inbox, - value: params.amount, - data: functionData, + value, + data, from: params.from, }, isValid: async () => true, diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 51972ca98b..2d9fd98cb2 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -41,7 +41,8 @@ export interface L2Network extends Network { */ depositTimeout: number /** - * In case of a chain that uses an ERC-20 token as its native currency, this is the address of said token on the parent chain. + * In case of a chain that uses ETH as its native token, this is undefined. + * In case of a chain that uses an ERC-20 token from the parent chain as its native token, this is the address of said token on the parent chain. */ nativeToken?: string } diff --git a/tests/integration/NativeErc20Bridger.test.ts b/tests/integration/native-erc20/ethBridger.test.ts similarity index 86% rename from tests/integration/NativeErc20Bridger.test.ts rename to tests/integration/native-erc20/ethBridger.test.ts index 91de9549ee..b9bc38fb81 100644 --- a/tests/integration/NativeErc20Bridger.test.ts +++ b/tests/integration/native-erc20/ethBridger.test.ts @@ -23,11 +23,12 @@ import dotenv from 'dotenv' import { Wallet } from '@ethersproject/wallet' import { parseEther } from '@ethersproject/units' -import { skipIfMainnet, wait } from './testHelpers' +import { skipIfMainnet, wait } from '../testHelpers' -import { testSetup } from '../../scripts/testSetup' -import { ERC20__factory } from '../../src/lib/abi/factories/ERC20__factory' -import { ERC20 } from '../../src/lib/abi/ERC20' +import { testSetup as _testSetup } from '../../../scripts/testSetup' +import { ERC20__factory } from '../../../src/lib/abi/factories/ERC20__factory' +import { ERC20 } from '../../../src/lib/abi/ERC20' +import { EthBridger } from '../../../src' dotenv.config() @@ -46,8 +47,13 @@ const l1DeployerWallet = new ethers.Wallet( let nativeToken: ERC20 +async function testSetup() { + const result = await _testSetup() + return { ...result, ethBridger: new EthBridger(result.l2Network) } +} + async function fundL1(account: string) { - const { ethBridger } = await testSetup(true) + const { ethBridger } = await testSetup() const nativeTokenAddress = ethBridger.l2Network.nativeToken! nativeToken = ERC20__factory.connect(nativeTokenAddress, l1Provider) @@ -64,7 +70,7 @@ async function fundL1(account: string) { await fundTokenTx.wait() } -describe('NativeErc20Bridger', async () => { +describe('EthBridger (with erc-20 as native token)', async () => { before(async function () { await fundL1(await l1Signer.getAddress()) }) @@ -79,7 +85,7 @@ describe('NativeErc20Bridger', async () => { const randomL1Signer = Wallet.createRandom().connect(l1Provider) await fundL1(await randomL1Signer.getAddress()) - const { ethBridger } = await testSetup(true) + const { ethBridger } = await testSetup() const inbox = ethBridger.l2Network.ethBridge.inbox const amount = ethers.utils.parseEther('1') @@ -102,7 +108,7 @@ describe('NativeErc20Bridger', async () => { }) it('approves the erc-20 token on the parent chain for the max amount', async function () { - const { ethBridger } = await testSetup(true) + const { ethBridger } = await testSetup() const inbox = ethBridger.l2Network.ethBridge.inbox @@ -121,7 +127,7 @@ describe('NativeErc20Bridger', async () => { }) it('deposits erc-20 token via params', async function () { - const { ethBridger } = await testSetup(true) + const { ethBridger } = await testSetup() const bridge = ethBridger.l2Network.ethBridge.bridge const amount = parseEther('2') @@ -157,6 +163,4 @@ describe('NativeErc20Bridger', async () => { 'incorrect balance in depositor account after deposit' ) }) - - // TODO(spsjvc): add withdrawal tests }) From 718683894e4d29e60f2e1c38e8a3e8ba0b37657b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Thu, 13 Jul 2023 19:07:08 +0200 Subject: [PATCH 008/192] return providers from testSetup --- scripts/testSetup.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/testSetup.ts b/scripts/testSetup.ts index 5558054297..6066ace730 100644 --- a/scripts/testSetup.ts +++ b/scripts/testSetup.ts @@ -18,8 +18,10 @@ import { JsonRpcProvider } from '@ethersproject/providers' import { Wallet } from '@ethersproject/wallet' - +import { Signer } from 'ethers' +import { Provider } from '@ethersproject/abstract-provider' import dotenv from 'dotenv' + import { EthBridger, InboxTools, Erc20Bridger } from '../src' import { L1Network, @@ -28,7 +30,6 @@ import { getL2Network, addCustomNetwork, } from '../src/lib/dataEntities/networks' -import { Signer } from 'ethers' import { AdminErc20Bridger } from '../src/lib/assetBridger/erc20Bridger' import { execSync } from 'child_process' import { Bridge__factory } from '../src/lib/abi/factories/Bridge__factory' @@ -208,6 +209,8 @@ export const testSetup = async (): Promise<{ l2Network: L2Network l1Signer: Signer l2Signer: Signer + l1Provider: Provider + l2Provider: Provider erc20Bridger: Erc20Bridger ethBridger: EthBridger adminErc20Bridger: AdminErc20Bridger @@ -270,6 +273,8 @@ export const testSetup = async (): Promise<{ return { l1Signer, l2Signer, + l1Provider: ethProvider, + l2Provider: arbProvider, l1Network: setL1Network, l2Network: setL2Network, erc20Bridger, From ba8c7d47a8e1a5576d2df5317d7308fa39c7e548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Fri, 14 Jul 2023 12:51:57 +0200 Subject: [PATCH 009/192] clean up more --- .../native-erc20/ethBridger.test.ts | 61 +++++++++---------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/tests/integration/native-erc20/ethBridger.test.ts b/tests/integration/native-erc20/ethBridger.test.ts index b9bc38fb81..c63bb45687 100644 --- a/tests/integration/native-erc20/ethBridger.test.ts +++ b/tests/integration/native-erc20/ethBridger.test.ts @@ -17,7 +17,7 @@ 'use strict' import { expect } from 'chai' -import { ethers, providers, constants } from 'ethers' +import { ethers, constants } from 'ethers' import dotenv from 'dotenv' import { Wallet } from '@ethersproject/wallet' @@ -27,44 +27,42 @@ import { skipIfMainnet, wait } from '../testHelpers' import { testSetup as _testSetup } from '../../../scripts/testSetup' import { ERC20__factory } from '../../../src/lib/abi/factories/ERC20__factory' -import { ERC20 } from '../../../src/lib/abi/ERC20' -import { EthBridger } from '../../../src' dotenv.config() -const l1Provider = new providers.StaticJsonRpcProvider(process.env.ETH_URL) -const l2Provider = new providers.StaticJsonRpcProvider(process.env.ARB_URL) - -// create a fresh random wallet for a clean slate +// create one random wallet for the test const wallet = Wallet.createRandom() -const l1Signer = wallet.connect(l1Provider) -const l2Signer = wallet.connect(l2Provider) - -const l1DeployerWallet = new ethers.Wallet( - ethers.utils.sha256(ethers.utils.toUtf8Bytes('user_l1user')), - l1Provider -) - -let nativeToken: ERC20 async function testSetup() { const result = await _testSetup() - return { ...result, ethBridger: new EthBridger(result.l2Network) } + const { l2Network, l1Provider, l2Provider } = result + + const nativeToken = l2Network.nativeToken! + const nativeTokenContract = ERC20__factory.connect(nativeToken, l1Provider) + + const l1Signer = wallet.connect(l1Provider) + const l2Signer = wallet.connect(l2Provider) + + return { ...result, nativeTokenContract, l1Signer, l2Signer } } async function fundL1(account: string) { - const { ethBridger } = await testSetup() + const { l1Provider, nativeTokenContract } = await testSetup() - const nativeTokenAddress = ethBridger.l2Network.nativeToken! - nativeToken = ERC20__factory.connect(nativeTokenAddress, l1Provider) + const l1DeployerWallet = new ethers.Wallet( + ethers.utils.sha256(ethers.utils.toUtf8Bytes('user_l1user')), + l1Provider + ) + // send 1 eth to account const fundEthTx = await l1DeployerWallet.sendTransaction({ to: account, - value: parseEther('10'), + value: parseEther('1'), }) await fundEthTx.wait() - const fundTokenTx = await nativeToken + // send 10 erc-20 tokens to account + const fundTokenTx = await nativeTokenContract .connect(l1DeployerWallet) .transfer(account, parseEther('10')) await fundTokenTx.wait() @@ -72,6 +70,7 @@ async function fundL1(account: string) { describe('EthBridger (with erc-20 as native token)', async () => { before(async function () { + const { l1Signer } = await testSetup() await fundL1(await l1Signer.getAddress()) }) @@ -80,13 +79,13 @@ describe('EthBridger (with erc-20 as native token)', async () => { }) it('approves the erc-20 token on the parent chain for an arbitrary amount', async function () { + const { ethBridger, nativeTokenContract, l1Provider } = await testSetup() + // using a random wallet for non-max amount approval // the rest of the test suite will use the account with the max approval const randomL1Signer = Wallet.createRandom().connect(l1Provider) await fundL1(await randomL1Signer.getAddress()) - const { ethBridger } = await testSetup() - const inbox = ethBridger.l2Network.ethBridge.inbox const amount = ethers.utils.parseEther('1') @@ -96,7 +95,7 @@ describe('EthBridger (with erc-20 as native token)', async () => { }) await approvalTx.wait() - const allowance = await nativeToken.allowance( + const allowance = await nativeTokenContract.allowance( await randomL1Signer.getAddress(), inbox ) @@ -108,14 +107,13 @@ describe('EthBridger (with erc-20 as native token)', async () => { }) it('approves the erc-20 token on the parent chain for the max amount', async function () { - const { ethBridger } = await testSetup() - + const { ethBridger, nativeTokenContract, l1Signer } = await testSetup() const inbox = ethBridger.l2Network.ethBridge.inbox const approvalTx = await ethBridger.approve({ l1Signer }) await approvalTx.wait() - const allowance = await nativeToken.allowance( + const allowance = await nativeTokenContract.allowance( await l1Signer.getAddress(), inbox ) @@ -127,12 +125,13 @@ describe('EthBridger (with erc-20 as native token)', async () => { }) it('deposits erc-20 token via params', async function () { - const { ethBridger } = await testSetup() + const result = await testSetup() + const { ethBridger, nativeTokenContract, l1Signer, l2Signer } = result const bridge = ethBridger.l2Network.ethBridge.bridge const amount = parseEther('2') - const initialBalanceBridge = await nativeToken.balanceOf(bridge) + const initialBalanceBridge = await nativeTokenContract.balanceOf(bridge) const initialBalanceDepositor = await l2Signer.getBalance() // perform the deposit @@ -144,7 +143,7 @@ describe('EthBridger (with erc-20 as native token)', async () => { expect( // balance in the bridge after the deposit - (await nativeToken.balanceOf(bridge)).toString() + (await nativeTokenContract.balanceOf(bridge)).toString() ).to.equal( // balance in the bridge after the deposit should equal to the initial balance in the bridge + the amount deposited initialBalanceBridge.add(amount).toString(), From 21e0b3127cdbf3d11a1355a0c05cb9b0e61d04a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Fri, 14 Jul 2023 12:53:11 +0200 Subject: [PATCH 010/192] don't export types --- src/lib/assetBridger/ethBridger.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/assetBridger/ethBridger.ts b/src/lib/assetBridger/ethBridger.ts index 8dd2ce61db..3d2ee4f231 100644 --- a/src/lib/assetBridger/ethBridger.ts +++ b/src/lib/assetBridger/ethBridger.ts @@ -108,12 +108,12 @@ export type L2ToL1TxReqAndSigner = L2ToL1TransactionRequest & { overrides?: Overrides } -export type EthDepositRequestParams = OmitTyped< +type EthDepositRequestParams = OmitTyped< EthDepositParams, 'overrides' | 'l1Signer' > & { from: string } -export type EthDepositToRequestParams = OmitTyped< +type EthDepositToRequestParams = OmitTyped< EthDepositToParams, 'overrides' | 'l1Signer' > & { From 7c6c3dcf53b36d95f2625930fb13cfbe00f4f33b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Fri, 14 Jul 2023 13:49:47 +0200 Subject: [PATCH 011/192] extract test helpers and rename file --- ...est.ts => ethBridger.native-erc20.test.ts} | 38 +++---------------- .../native-erc20/testHelpers.native-erc20.ts | 36 ++++++++++++++++++ 2 files changed, 41 insertions(+), 33 deletions(-) rename tests/integration/native-erc20/{ethBridger.test.ts => ethBridger.native-erc20.test.ts} (78%) create mode 100644 tests/integration/native-erc20/testHelpers.native-erc20.ts diff --git a/tests/integration/native-erc20/ethBridger.test.ts b/tests/integration/native-erc20/ethBridger.native-erc20.test.ts similarity index 78% rename from tests/integration/native-erc20/ethBridger.test.ts rename to tests/integration/native-erc20/ethBridger.native-erc20.test.ts index c63bb45687..72ef6ea855 100644 --- a/tests/integration/native-erc20/ethBridger.test.ts +++ b/tests/integration/native-erc20/ethBridger.native-erc20.test.ts @@ -23,49 +23,21 @@ import dotenv from 'dotenv' import { Wallet } from '@ethersproject/wallet' import { parseEther } from '@ethersproject/units' +import { testSetup as _testSetup, fundL1 } from './testHelpers.native-erc20' import { skipIfMainnet, wait } from '../testHelpers' -import { testSetup as _testSetup } from '../../../scripts/testSetup' -import { ERC20__factory } from '../../../src/lib/abi/factories/ERC20__factory' - dotenv.config() -// create one random wallet for the test +// random wallet for the test const wallet = Wallet.createRandom() async function testSetup() { const result = await _testSetup() - const { l2Network, l1Provider, l2Provider } = result - - const nativeToken = l2Network.nativeToken! - const nativeTokenContract = ERC20__factory.connect(nativeToken, l1Provider) - - const l1Signer = wallet.connect(l1Provider) - const l2Signer = wallet.connect(l2Provider) - - return { ...result, nativeTokenContract, l1Signer, l2Signer } -} - -async function fundL1(account: string) { - const { l1Provider, nativeTokenContract } = await testSetup() - const l1DeployerWallet = new ethers.Wallet( - ethers.utils.sha256(ethers.utils.toUtf8Bytes('user_l1user')), - l1Provider - ) - - // send 1 eth to account - const fundEthTx = await l1DeployerWallet.sendTransaction({ - to: account, - value: parseEther('1'), - }) - await fundEthTx.wait() + const l1Signer = wallet.connect(result.l1Provider) + const l2Signer = wallet.connect(result.l2Provider) - // send 10 erc-20 tokens to account - const fundTokenTx = await nativeTokenContract - .connect(l1DeployerWallet) - .transfer(account, parseEther('10')) - await fundTokenTx.wait() + return { ...result, l1Signer, l2Signer } } describe('EthBridger (with erc-20 as native token)', async () => { diff --git a/tests/integration/native-erc20/testHelpers.native-erc20.ts b/tests/integration/native-erc20/testHelpers.native-erc20.ts new file mode 100644 index 0000000000..a47c998cde --- /dev/null +++ b/tests/integration/native-erc20/testHelpers.native-erc20.ts @@ -0,0 +1,36 @@ +import { ethers, utils } from 'ethers' + +import { testSetup as _testSetup } from '../../../scripts/testSetup' +import { ERC20__factory } from '../../../src/lib/abi/factories/ERC20__factory' + +export async function testSetup() { + const result = await _testSetup() + const { l2Network, l1Provider } = result + + const nativeToken = l2Network.nativeToken! + const nativeTokenContract = ERC20__factory.connect(nativeToken, l1Provider) + + return { ...result, nativeTokenContract } +} + +export async function fundL1(account: string) { + const { l1Provider, nativeTokenContract } = await testSetup() + + const l1DeployerWallet = new ethers.Wallet( + ethers.utils.sha256(ethers.utils.toUtf8Bytes('user_l1user')), + l1Provider + ) + + // send 1 eth to account + const fundEthTx = await l1DeployerWallet.sendTransaction({ + to: account, + value: utils.parseEther('1'), + }) + await fundEthTx.wait() + + // send 10 erc-20 tokens to account + const fundTokenTx = await nativeTokenContract + .connect(l1DeployerWallet) + .transfer(account, utils.parseEther('10')) + await fundTokenTx.wait() +} From bc1d82b30a19cfce051cc006240e5c081c6d95d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Fri, 14 Jul 2023 14:03:50 +0200 Subject: [PATCH 012/192] add more assertions --- .../ethBridger.native-erc20.test.ts | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/tests/integration/native-erc20/ethBridger.native-erc20.test.ts b/tests/integration/native-erc20/ethBridger.native-erc20.test.ts index 72ef6ea855..33985c6aef 100644 --- a/tests/integration/native-erc20/ethBridger.native-erc20.test.ts +++ b/tests/integration/native-erc20/ethBridger.native-erc20.test.ts @@ -24,7 +24,7 @@ import { Wallet } from '@ethersproject/wallet' import { parseEther } from '@ethersproject/units' import { testSetup as _testSetup, fundL1 } from './testHelpers.native-erc20' -import { skipIfMainnet, wait } from '../testHelpers' +import { prettyLog, skipIfMainnet, wait } from '../testHelpers' dotenv.config() @@ -43,7 +43,9 @@ async function testSetup() { describe('EthBridger (with erc-20 as native token)', async () => { before(async function () { const { l1Signer } = await testSetup() - await fundL1(await l1Signer.getAddress()) + const address = await l1Signer.getAddress() + prettyLog(`testing with account: ${address}`) + await fundL1(address) }) beforeEach('skipIfMainnet', async function () { @@ -98,7 +100,8 @@ describe('EthBridger (with erc-20 as native token)', async () => { it('deposits erc-20 token via params', async function () { const result = await testSetup() - const { ethBridger, nativeTokenContract, l1Signer, l2Signer } = result + const { ethBridger, nativeTokenContract, l1Signer, l2Signer, l2Provider } = + result const bridge = ethBridger.l2Network.ethBridge.bridge const amount = parseEther('2') @@ -111,7 +114,8 @@ describe('EthBridger (with erc-20 as native token)', async () => { amount, l1Signer, }) - await depositTx.wait() + const depositTxReceipt = await depositTx.wait() + expect(depositTxReceipt.status).to.equal(1, 'deposit tx failed') expect( // balance in the bridge after the deposit @@ -125,6 +129,13 @@ describe('EthBridger (with erc-20 as native token)', async () => { // wait for minting on L2 await wait(30 * 1000) + // check for cross-chain messages + const depositMessages = await depositTxReceipt.getEthDeposits(l2Provider) + expect(depositMessages.length).to.equal(1, 'failed to find deposit message') + const [depositMessage] = depositMessages + expect(depositMessage.value.toString()).to.equal(amount.toString()) + expect(depositMessage.to).to.equal(await l2Signer.getAddress()) + expect( // balance in the depositor account after the deposit (await l2Signer.getBalance()).toString() From b35868c61a6603f8bd115b73551d56d1a82a7cde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Fri, 14 Jul 2023 14:23:59 +0200 Subject: [PATCH 013/192] add test --- src/lib/assetBridger/ethBridger.ts | 2 +- tests/integration/eth.test.ts | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/lib/assetBridger/ethBridger.ts b/src/lib/assetBridger/ethBridger.ts index 3d2ee4f231..152dbf4e85 100644 --- a/src/lib/assetBridger/ethBridger.ts +++ b/src/lib/assetBridger/ethBridger.ts @@ -159,7 +159,7 @@ export class EthBridger extends AssetBridger< public async approve(params: { amount?: BigNumber; l1Signer: Signer }) { if (typeof this.nativeToken === 'undefined') { throw new Error( - `approve can't be called for a network that uses ETH as its native token` + `can't call "EthBridger.approve" for network that uses eth as native token` ) } diff --git a/tests/integration/eth.test.ts b/tests/integration/eth.test.ts index af8ca1c7a6..eb191aef32 100644 --- a/tests/integration/eth.test.ts +++ b/tests/integration/eth.test.ts @@ -78,6 +78,20 @@ describe('Ether', async () => { ) }) + it('"EthBridger.approve" throws when eth is native token', async () => { + const { ethBridger, l1Signer } = await testSetup() + const randomL1Signer = Wallet.createRandom().connect(l1Signer.provider!) + + try { + await ethBridger.approve({ l1Signer: randomL1Signer }) + expect.fail(`"EthBridger.approve" should have thrown`) + } catch (error: any) { + expect(error.message).to.equal( + `can't call "EthBridger.approve" for network that uses eth as native token` + ) + } + }) + it('deposits ether', async () => { const { ethBridger, l1Signer, l2Signer } = await testSetup() From 0bc5723bf73436f49c5d26557d1b356d17317939 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Tue, 18 Jul 2023 14:38:07 +0200 Subject: [PATCH 014/192] enable creating retryable tickets via erc20inbox --- src/lib/dataEntities/retryableData.ts | 4 +++ src/lib/message/L1ToL2MessageCreator.ts | 42 ++++++++++++++++++++----- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/lib/dataEntities/retryableData.ts b/src/lib/dataEntities/retryableData.ts index e1b05d1f99..d4884b7810 100644 --- a/src/lib/dataEntities/retryableData.ts +++ b/src/lib/dataEntities/retryableData.ts @@ -42,6 +42,10 @@ export interface RetryableData { * The max gas price to pay on L2 */ maxFeePerGas: BigNumber + /** + * todo(spsjvc) + */ + tokenTotalFeeAmount?: BigNumber /** * The data to call the L2 address with */ diff --git a/src/lib/message/L1ToL2MessageCreator.ts b/src/lib/message/L1ToL2MessageCreator.ts index d930e2b22f..4a98535a03 100644 --- a/src/lib/message/L1ToL2MessageCreator.ts +++ b/src/lib/message/L1ToL2MessageCreator.ts @@ -1,3 +1,4 @@ +import { BigNumber, constants } from 'ethers' import { Signer } from '@ethersproject/abstract-signer' import { Provider } from '@ethersproject/abstract-provider' @@ -7,6 +8,7 @@ import { } from './L1ToL2MessageGasEstimator' import { L1ContractTransaction, L1TransactionReceipt } from './L1Transaction' import { Inbox__factory } from '../abi/factories/Inbox__factory' +import { ERC20Inbox__factory } from '../abi/factories/ERC20Inbox__factory' import { getL2Network } from '../dataEntities/networks' import { PayableOverrides } from '@ethersproject/contracts' import { SignerProviderUtils } from '../dataEntities/signerOrProvider' @@ -97,10 +99,17 @@ export class L1ToL2MessageCreator { ) const l2Network = await getL2Network(l2Provider) - const inboxInterface = Inbox__factory.createInterface() - const functionData = inboxInterface.encodeFunctionData( - 'createRetryableTicket', - [ + + let value: BigNumber + let tokenTotalFeeAmount: BigNumber | undefined + let data: string + + if (typeof l2Network.nativeToken === 'undefined') { + const inboxInterface = Inbox__factory.createInterface() + + value = estimates.deposit + tokenTotalFeeAmount = undefined + data = inboxInterface.encodeFunctionData('createRetryableTicket', [ params.to, params.l2CallValue, estimates.maxSubmissionCost, @@ -109,14 +118,30 @@ export class L1ToL2MessageCreator { estimates.gasLimit, estimates.maxFeePerGas, params.data, - ] - ) + ]) + } else { + const inboxInterface = ERC20Inbox__factory.createInterface() + + value = constants.Zero + tokenTotalFeeAmount = estimates.deposit + data = inboxInterface.encodeFunctionData('createRetryableTicket', [ + params.to, + params.l2CallValue, + estimates.maxSubmissionCost, + excessFeeRefundAddress, + callValueRefundAddress, + estimates.gasLimit, + estimates.maxFeePerGas, + estimates.deposit, + params.data, + ]) + } return { txRequest: { to: l2Network.ethBridge.inbox, - data: functionData, - value: estimates.deposit, + data, + value, from: params.from, }, retryableData: { @@ -129,6 +154,7 @@ export class L1ToL2MessageCreator { maxSubmissionCost: estimates.maxSubmissionCost, maxFeePerGas: estimates.maxFeePerGas, gasLimit: estimates.gasLimit, + tokenTotalFeeAmount, deposit: estimates.deposit, }, isValid: async () => { From 4fedf324d8178b4a2c8f3e12074786ecbfcf23a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 19 Jul 2023 09:22:41 +0200 Subject: [PATCH 015/192] improve test setup --- .../ethBridger.native-erc20.test.ts | 18 +++-- .../native-erc20/testHelpers.native-erc20.ts | 67 +++++++++++++------ 2 files changed, 62 insertions(+), 23 deletions(-) diff --git a/tests/integration/native-erc20/ethBridger.native-erc20.test.ts b/tests/integration/native-erc20/ethBridger.native-erc20.test.ts index 33985c6aef..236d5e7e8f 100644 --- a/tests/integration/native-erc20/ethBridger.native-erc20.test.ts +++ b/tests/integration/native-erc20/ethBridger.native-erc20.test.ts @@ -23,8 +23,16 @@ import dotenv from 'dotenv' import { Wallet } from '@ethersproject/wallet' import { parseEther } from '@ethersproject/units' -import { testSetup as _testSetup, fundL1 } from './testHelpers.native-erc20' -import { prettyLog, skipIfMainnet, wait } from '../testHelpers' +import { + testSetup as _testSetup, + fundL1WithCustomFeeToken, +} from './testHelpers.native-erc20' +import { + fundL1 as fundL1WithEth, + prettyLog, + skipIfMainnet, + wait, +} from '../testHelpers' dotenv.config() @@ -45,7 +53,8 @@ describe('EthBridger (with erc-20 as native token)', async () => { const { l1Signer } = await testSetup() const address = await l1Signer.getAddress() prettyLog(`testing with account: ${address}`) - await fundL1(address) + await fundL1WithEth(l1Signer) + await fundL1WithCustomFeeToken(l1Signer) }) beforeEach('skipIfMainnet', async function () { @@ -58,7 +67,8 @@ describe('EthBridger (with erc-20 as native token)', async () => { // using a random wallet for non-max amount approval // the rest of the test suite will use the account with the max approval const randomL1Signer = Wallet.createRandom().connect(l1Provider) - await fundL1(await randomL1Signer.getAddress()) + await fundL1WithEth(randomL1Signer) + await fundL1WithCustomFeeToken(randomL1Signer) const inbox = ethBridger.l2Network.ethBridge.inbox const amount = ethers.utils.parseEther('1') diff --git a/tests/integration/native-erc20/testHelpers.native-erc20.ts b/tests/integration/native-erc20/testHelpers.native-erc20.ts index a47c998cde..0f155f52f0 100644 --- a/tests/integration/native-erc20/testHelpers.native-erc20.ts +++ b/tests/integration/native-erc20/testHelpers.native-erc20.ts @@ -1,7 +1,28 @@ -import { ethers, utils } from 'ethers' +import { utils, Signer, Wallet } from 'ethers' +import { StaticJsonRpcProvider } from '@ethersproject/providers' +import * as path from 'path' +import * as fs from 'fs' -import { testSetup as _testSetup } from '../../../scripts/testSetup' +import { testSetup as _testSetup, config } from '../../../scripts/testSetup' import { ERC20__factory } from '../../../src/lib/abi/factories/ERC20__factory' +import { EthBridger, L1Network, L2Network } from '../../../src' + +function getLocalNetworks(): { + l1Network: L1Network + l2Network: L2Network +} { + const pathToLocalNetworkFile = path.join( + __dirname, + '..', + path.sep, + '..', + path.sep, + '..', + 'localNetwork.json' + ) + + return JSON.parse(fs.readFileSync(pathToLocalNetworkFile, 'utf8')) +} export async function testSetup() { const result = await _testSetup() @@ -13,24 +34,32 @@ export async function testSetup() { return { ...result, nativeTokenContract } } -export async function fundL1(account: string) { - const { l1Provider, nativeTokenContract } = await testSetup() +export async function fundL1WithCustomFeeToken(l1Signer: Signer) { + const nativeToken = getLocalNetworks().l2Network.nativeToken + + if (typeof nativeToken === 'undefined') { + throw new Error( + `can't call "fundL1WithCustomFeeToken" for network that uses eth as native token` + ) + } + + const deployerWallet = new Wallet( + utils.sha256(utils.toUtf8Bytes('user_l1user')), + new StaticJsonRpcProvider(config.ethUrl) + ) + + const address = await l1Signer.getAddress() + const tokenContract = ERC20__factory.connect(nativeToken, deployerWallet) + + const tx = await tokenContract.transfer(address, utils.parseEther('10')) + await tx.wait() +} - const l1DeployerWallet = new ethers.Wallet( - ethers.utils.sha256(ethers.utils.toUtf8Bytes('user_l1user')), - l1Provider +export async function approveL1CustomFeeToken(l1Signer: Signer) { + const ethBridger = await EthBridger.fromProvider( + new StaticJsonRpcProvider(config.arbUrl) ) - // send 1 eth to account - const fundEthTx = await l1DeployerWallet.sendTransaction({ - to: account, - value: utils.parseEther('1'), - }) - await fundEthTx.wait() - - // send 10 erc-20 tokens to account - const fundTokenTx = await nativeTokenContract - .connect(l1DeployerWallet) - .transfer(account, utils.parseEther('10')) - await fundTokenTx.wait() + const tx = await ethBridger.approve({ l1Signer }) + await tx.wait() } From 41913f7f00ab3ebfba318e6ac8465b5c19af96bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 19 Jul 2023 09:45:59 +0200 Subject: [PATCH 016/192] cleanup --- .../ethBridger.native-erc20.test.ts | 74 ++++++------------- .../native-erc20/testHelpers.native-erc20.ts | 13 ++-- 2 files changed, 31 insertions(+), 56 deletions(-) diff --git a/tests/integration/native-erc20/ethBridger.native-erc20.test.ts b/tests/integration/native-erc20/ethBridger.native-erc20.test.ts index 236d5e7e8f..997d741a43 100644 --- a/tests/integration/native-erc20/ethBridger.native-erc20.test.ts +++ b/tests/integration/native-erc20/ethBridger.native-erc20.test.ts @@ -20,68 +20,38 @@ import { expect } from 'chai' import { ethers, constants } from 'ethers' import dotenv from 'dotenv' -import { Wallet } from '@ethersproject/wallet' import { parseEther } from '@ethersproject/units' import { - testSetup as _testSetup, - fundL1WithCustomFeeToken, + testSetup, + fundL1CustomFeeToken, + approveL1CustomFeeToken, } from './testHelpers.native-erc20' -import { - fundL1 as fundL1WithEth, - prettyLog, - skipIfMainnet, - wait, -} from '../testHelpers' +import { fundL1 as fundL1Ether, skipIfMainnet, wait } from '../testHelpers' dotenv.config() -// random wallet for the test -const wallet = Wallet.createRandom() - -async function testSetup() { - const result = await _testSetup() - - const l1Signer = wallet.connect(result.l1Provider) - const l2Signer = wallet.connect(result.l2Provider) - - return { ...result, l1Signer, l2Signer } -} - -describe('EthBridger (with erc-20 as native token)', async () => { - before(async function () { - const { l1Signer } = await testSetup() - const address = await l1Signer.getAddress() - prettyLog(`testing with account: ${address}`) - await fundL1WithEth(l1Signer) - await fundL1WithCustomFeeToken(l1Signer) - }) - +describe('EthBridger (with custom fee token)', async () => { beforeEach('skipIfMainnet', async function () { await skipIfMainnet(this) }) - it('approves the erc-20 token on the parent chain for an arbitrary amount', async function () { - const { ethBridger, nativeTokenContract, l1Provider } = await testSetup() - - // using a random wallet for non-max amount approval - // the rest of the test suite will use the account with the max approval - const randomL1Signer = Wallet.createRandom().connect(l1Provider) - await fundL1WithEth(randomL1Signer) - await fundL1WithCustomFeeToken(randomL1Signer) - - const inbox = ethBridger.l2Network.ethBridge.inbox + it('approves the custom fee token on the parent chain for an arbitrary amount', async function () { + const { ethBridger, nativeTokenContract, l1Signer } = await testSetup() const amount = ethers.utils.parseEther('1') + await fundL1Ether(l1Signer) + await fundL1CustomFeeToken(l1Signer) + const approvalTx = await ethBridger.approve({ amount, - l1Signer: randomL1Signer, + l1Signer, }) await approvalTx.wait() const allowance = await nativeTokenContract.allowance( - await randomL1Signer.getAddress(), - inbox + await l1Signer.getAddress(), + ethBridger.l2Network.ethBridge.inbox ) expect(allowance.toString()).to.equal( @@ -90,16 +60,18 @@ describe('EthBridger (with erc-20 as native token)', async () => { ) }) - it('approves the erc-20 token on the parent chain for the max amount', async function () { + it('approves the custom fee token on the parent chain for the max amount', async function () { const { ethBridger, nativeTokenContract, l1Signer } = await testSetup() - const inbox = ethBridger.l2Network.ethBridge.inbox + + await fundL1Ether(l1Signer) + await fundL1CustomFeeToken(l1Signer) const approvalTx = await ethBridger.approve({ l1Signer }) await approvalTx.wait() const allowance = await nativeTokenContract.allowance( await l1Signer.getAddress(), - inbox + ethBridger.l2Network.ethBridge.inbox ) expect(allowance.toString()).to.equal( @@ -108,14 +80,16 @@ describe('EthBridger (with erc-20 as native token)', async () => { ) }) - it('deposits erc-20 token via params', async function () { - const result = await testSetup() + it('deposits custom fee token via params', async function () { const { ethBridger, nativeTokenContract, l1Signer, l2Signer, l2Provider } = - result + await testSetup() const bridge = ethBridger.l2Network.ethBridge.bridge - const amount = parseEther('2') + await fundL1Ether(l1Signer) + await fundL1CustomFeeToken(l1Signer) + await approveL1CustomFeeToken(l1Signer) + const initialBalanceBridge = await nativeTokenContract.balanceOf(bridge) const initialBalanceDepositor = await l2Signer.getBalance() diff --git a/tests/integration/native-erc20/testHelpers.native-erc20.ts b/tests/integration/native-erc20/testHelpers.native-erc20.ts index 0f155f52f0..1e420e9faa 100644 --- a/tests/integration/native-erc20/testHelpers.native-erc20.ts +++ b/tests/integration/native-erc20/testHelpers.native-erc20.ts @@ -7,6 +7,9 @@ import { testSetup as _testSetup, config } from '../../../scripts/testSetup' import { ERC20__factory } from '../../../src/lib/abi/factories/ERC20__factory' import { EthBridger, L1Network, L2Network } from '../../../src' +const ethProvider = new StaticJsonRpcProvider(config.ethUrl) +const arbProvider = new StaticJsonRpcProvider(config.arbUrl) + function getLocalNetworks(): { l1Network: L1Network l2Network: L2Network @@ -34,18 +37,18 @@ export async function testSetup() { return { ...result, nativeTokenContract } } -export async function fundL1WithCustomFeeToken(l1Signer: Signer) { +export async function fundL1CustomFeeToken(l1Signer: Signer) { const nativeToken = getLocalNetworks().l2Network.nativeToken if (typeof nativeToken === 'undefined') { throw new Error( - `can't call "fundL1WithCustomFeeToken" for network that uses eth as native token` + `can't call "fundL1CustomFeeToken" for network that uses eth as native token` ) } const deployerWallet = new Wallet( utils.sha256(utils.toUtf8Bytes('user_l1user')), - new StaticJsonRpcProvider(config.ethUrl) + ethProvider ) const address = await l1Signer.getAddress() @@ -56,9 +59,7 @@ export async function fundL1WithCustomFeeToken(l1Signer: Signer) { } export async function approveL1CustomFeeToken(l1Signer: Signer) { - const ethBridger = await EthBridger.fromProvider( - new StaticJsonRpcProvider(config.arbUrl) - ) + const ethBridger = await EthBridger.fromProvider(arbProvider) const tx = await ethBridger.approve({ l1Signer }) await tx.wait() From 2c5fb7c884a75f4476cbc5459727e98e05c85754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 19 Jul 2023 10:24:00 +0200 Subject: [PATCH 017/192] add withdrawal test --- .../ethBridger.native-erc20.test.ts | 130 +++++++++++++++++- .../native-erc20/testHelpers.native-erc20.ts | 13 ++ 2 files changed, 141 insertions(+), 2 deletions(-) diff --git a/tests/integration/native-erc20/ethBridger.native-erc20.test.ts b/tests/integration/native-erc20/ethBridger.native-erc20.test.ts index 997d741a43..1a8287fe09 100644 --- a/tests/integration/native-erc20/ethBridger.native-erc20.test.ts +++ b/tests/integration/native-erc20/ethBridger.native-erc20.test.ts @@ -17,7 +17,7 @@ 'use strict' import { expect } from 'chai' -import { ethers, constants } from 'ethers' +import { ethers, constants, Wallet } from 'ethers' import dotenv from 'dotenv' import { parseEther } from '@ethersproject/units' @@ -25,9 +25,16 @@ import { parseEther } from '@ethersproject/units' import { testSetup, fundL1CustomFeeToken, + fundL2CustomFeeToken, approveL1CustomFeeToken, } from './testHelpers.native-erc20' -import { fundL1 as fundL1Ether, skipIfMainnet, wait } from '../testHelpers' +import { + fundL1 as fundL1Ether, + mineUntilStop, + skipIfMainnet, + wait, +} from '../testHelpers' +import { L2ToL1Message, L2ToL1MessageStatus } from '../../../src' dotenv.config() @@ -129,4 +136,123 @@ describe('EthBridger (with custom fee token)', async () => { 'incorrect balance in depositor account after deposit' ) }) + + it('withdraws custom fee token', async () => { + const { + l1Signer, + l1Provider, + l2Signer, + l2Provider, + ethBridger, + nativeTokenContract, + } = await testSetup() + const bridge = ethBridger.l2Network.ethBridge.bridge + const amount = parseEther('0.2') + + await fundL1Ether(l1Signer) + await fundL2CustomFeeToken(l2Signer) + + const from = await l2Signer.getAddress() + const destinationAddress = await l1Signer.getAddress() + + const initialBalanceBridge = await nativeTokenContract.balanceOf(bridge) + const initialBalanceDestination = await nativeTokenContract.balanceOf( + destinationAddress + ) + + const withdrawalTxRequest = await ethBridger.getWithdrawalRequest({ + amount, + destinationAddress, + from, + }) + + const l1GasEstimate = await withdrawalTxRequest.estimateL1GasLimit( + l1Provider + ) + + const withdrawalTx = await ethBridger.withdraw({ + amount, + l2Signer: l2Signer, + destinationAddress, + from, + }) + const withdrawalTxReceipt = await withdrawalTx.wait() + + expect(withdrawalTxReceipt.status).to.equal( + 1, + 'initiate withdrawal tx failed' + ) + + const messages = await withdrawalTxReceipt.getL2ToL1Messages(l1Signer) + expect(messages.length).to.equal( + 1, + 'custom fee token withdraw getWithdrawalsInL2Transaction query came back empty' + ) + + const withdrawalEvents = await L2ToL1Message.getL2ToL1Events( + l2Provider, + { fromBlock: withdrawalTxReceipt.blockNumber, toBlock: 'latest' }, + undefined, + destinationAddress + ) + + expect(withdrawalEvents.length).to.equal( + 1, + 'custom fee token withdraw getL2ToL1EventData failed' + ) + + const [message] = messages + const messageStatus = await message.status(l2Provider) + expect( + messageStatus, + `custom fee token withdraw status returned ${messageStatus}` + ).to.be.eq(L2ToL1MessageStatus.UNCONFIRMED) + + // run a miner whilst withdrawing + const miner1 = Wallet.createRandom().connect(l1Provider) + const miner2 = Wallet.createRandom().connect(l2Provider) + await fundL1Ether(miner1, parseEther('1')) + await fundL2CustomFeeToken(miner2) + const state = { mining: true } + await Promise.race([ + mineUntilStop(miner1, state), + mineUntilStop(miner2, state), + message.waitUntilReadyToExecute(l2Provider), + ]) + state.mining = false + + expect(await message.status(l2Provider), 'confirmed status') + // + .to.eq(L2ToL1MessageStatus.CONFIRMED) + + const execTx = await message.execute(l2Provider) + const execTxReceipt = await execTx.wait() + + expect( + execTxReceipt.gasUsed.toNumber(), + 'gas used greater than estimate' + ).to.be.lessThan(l1GasEstimate.toNumber()) + + expect(await message.status(l2Provider), 'executed status') + // + .to.eq(L2ToL1MessageStatus.EXECUTED) + + expect( + // balance in the bridge after the withdrawal + (await nativeTokenContract.balanceOf(bridge)).toString() + ).to.equal( + // balance in the bridge after the withdrawal should equal to the initial balance in the bridge - the amount withdrawn + initialBalanceBridge.sub(amount).toString(), + 'incorrect balance in bridge after withdrawal' + ) + + expect( + // balance in the destination after the withdrawal + (await nativeTokenContract.balanceOf(destinationAddress)).toString() + ).to.equal( + // balance in the destination after the withdrawal should equal to the initial balance in the destination + the amount withdrawn + initialBalanceDestination.add(amount).toString(), + 'incorrect balance in destination after withdrawal' + ) + }) }) diff --git a/tests/integration/native-erc20/testHelpers.native-erc20.ts b/tests/integration/native-erc20/testHelpers.native-erc20.ts index 1e420e9faa..1ecf31ecf7 100644 --- a/tests/integration/native-erc20/testHelpers.native-erc20.ts +++ b/tests/integration/native-erc20/testHelpers.native-erc20.ts @@ -64,3 +64,16 @@ export async function approveL1CustomFeeToken(l1Signer: Signer) { const tx = await ethBridger.approve({ l1Signer }) await tx.wait() } + +export async function fundL2CustomFeeToken(l2Signer: Signer) { + const deployerWallet = new Wallet( + utils.sha256(utils.toUtf8Bytes('user_l1user')), + arbProvider + ) + + const tx = await deployerWallet.sendTransaction({ + to: await l2Signer.getAddress(), + value: utils.parseEther('1'), + }) + await tx.wait() +} From d19498e72d89d4d9aa0bfd4f2628a572f492034c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 19 Jul 2023 10:24:37 +0200 Subject: [PATCH 018/192] clean up --- tests/integration/eth.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/integration/eth.test.ts b/tests/integration/eth.test.ts index eb191aef32..23e15dce0c 100644 --- a/tests/integration/eth.test.ts +++ b/tests/integration/eth.test.ts @@ -80,10 +80,9 @@ describe('Ether', async () => { it('"EthBridger.approve" throws when eth is native token', async () => { const { ethBridger, l1Signer } = await testSetup() - const randomL1Signer = Wallet.createRandom().connect(l1Signer.provider!) try { - await ethBridger.approve({ l1Signer: randomL1Signer }) + await ethBridger.approve({ l1Signer }) expect.fail(`"EthBridger.approve" should have thrown`) } catch (error: any) { expect(error.message).to.equal( From 9193a0d9b59b88d5b47d18bc0fa3036e52be9592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 19 Jul 2023 10:27:39 +0200 Subject: [PATCH 019/192] rename stuff --- package.json | 4 ++-- .../customFeeTokenEthBridger.test.ts} | 2 +- .../customFeeTokenTestHelpers.ts} | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename tests/integration/{native-erc20/ethBridger.native-erc20.test.ts => custom-fee-token/customFeeTokenEthBridger.test.ts} (99%) rename tests/integration/{native-erc20/testHelpers.native-erc20.ts => custom-fee-token/customFeeTokenTestHelpers.ts} (100%) diff --git a/package.json b/package.json index 988ed2370e..3aa805e2f8 100644 --- a/package.json +++ b/package.json @@ -31,8 +31,8 @@ "test": "mocha", "test:coverage": "nyc mocha", "test:fork": "SHOULD_FORK=1 hardhat test tests/fork/*.test.ts", - "test:integration": "mocha tests/integration/ --ignore tests/integration/native-erc20/ --timeout 30000000 --bail", - "test:integration:native-erc20": "mocha tests/integration/native-erc20/ --timeout 30000000 --bail", + "test:integration": "mocha tests/integration/ --ignore tests/integration/custom-fee-token/ --timeout 30000000 --bail", + "test:integration:custom-fee-token": "mocha tests/integration/custom-fee-token/ --timeout 30000000 --bail", "test:unit": "mocha --parallel tests/unit/ --timeout 30000 --bail", "test:ci": "nyc --reporter=lcovonly mocha --reporter xunit", "lint": "eslint .", diff --git a/tests/integration/native-erc20/ethBridger.native-erc20.test.ts b/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts similarity index 99% rename from tests/integration/native-erc20/ethBridger.native-erc20.test.ts rename to tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts index 1a8287fe09..44ed1640b4 100644 --- a/tests/integration/native-erc20/ethBridger.native-erc20.test.ts +++ b/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts @@ -27,7 +27,7 @@ import { fundL1CustomFeeToken, fundL2CustomFeeToken, approveL1CustomFeeToken, -} from './testHelpers.native-erc20' +} from './customFeeTokenTestHelpers' import { fundL1 as fundL1Ether, mineUntilStop, diff --git a/tests/integration/native-erc20/testHelpers.native-erc20.ts b/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts similarity index 100% rename from tests/integration/native-erc20/testHelpers.native-erc20.ts rename to tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts From 1c426d763357ce63b1818bbb5edb881ae8760192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 19 Jul 2023 11:04:10 +0200 Subject: [PATCH 020/192] test setup for custom fee token --- .../customFeeTokenTestHelpers.ts | 4 ++++ tests/integration/l1ToL2MessageCreator.test.ts | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts b/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts index 1ecf31ecf7..af21460840 100644 --- a/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts +++ b/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts @@ -10,6 +10,10 @@ import { EthBridger, L1Network, L2Network } from '../../../src' const ethProvider = new StaticJsonRpcProvider(config.ethUrl) const arbProvider = new StaticJsonRpcProvider(config.arbUrl) +export function isL2NetworkWithCustomFeeToken(): boolean { + return typeof getLocalNetworks().l2Network.nativeToken !== 'undefined' +} + function getLocalNetworks(): { l1Network: L1Network l2Network: L2Network diff --git a/tests/integration/l1ToL2MessageCreator.test.ts b/tests/integration/l1ToL2MessageCreator.test.ts index f7b9458420..88f3b2157c 100644 --- a/tests/integration/l1ToL2MessageCreator.test.ts +++ b/tests/integration/l1ToL2MessageCreator.test.ts @@ -23,6 +23,14 @@ import { testSetup } from '../../scripts/testSetup' import { L1ToL2MessageCreator } from '../../src/lib/message/L1ToL2MessageCreator' import { L1ToL2MessageStatus } from '../../src' +import { + fundL1CustomFeeToken, + approveL1CustomFeeToken, + isL2NetworkWithCustomFeeToken, +} from './custom-fee-token/customFeeTokenTestHelpers' + +const isCustomFeeToken = isL2NetworkWithCustomFeeToken() + describe('L1ToL2MessageCreator', () => { beforeEach('skipIfMainnet', async function () { await skipIfMainnet(this) @@ -39,6 +47,11 @@ describe('L1ToL2MessageCreator', () => { // Funding L1 wallet await fundL1(l1Signer) + if (isCustomFeeToken) { + await fundL1CustomFeeToken(l1Signer) + await approveL1CustomFeeToken(l1Signer) + } + // Instantiate the object const l1ToL2MessageCreator = new L1ToL2MessageCreator(l1Signer) @@ -92,6 +105,11 @@ describe('L1ToL2MessageCreator', () => { // Funding L1 wallet await fundL1(l1Signer) + if (isCustomFeeToken) { + await fundL1CustomFeeToken(l1Signer) + await approveL1CustomFeeToken(l1Signer) + } + // Instantiate the object const l1ToL2MessageCreator = new L1ToL2MessageCreator(l1Signer) From d149a0f5693ffb2816012fc3782d1a4cef948188 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 19 Jul 2023 14:11:16 +0200 Subject: [PATCH 021/192] update to public and update comment --- src/lib/assetBridger/ethBridger.ts | 6 +++--- src/lib/dataEntities/networks.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/assetBridger/ethBridger.ts b/src/lib/assetBridger/ethBridger.ts index 152dbf4e85..a95cd33e7f 100644 --- a/src/lib/assetBridger/ethBridger.ts +++ b/src/lib/assetBridger/ethBridger.ts @@ -135,10 +135,10 @@ export class EthBridger extends AssetBridger< EthWithdrawParams | L2ToL1TxReqAndSigner > { /** - * In case of a chain that uses ETH as its native token, this is undefined. - * In case of a chain that uses an ERC-20 token from the parent chain as its native token, this is the address of said token on the parent chain. + * In case of a chain that uses ETH as its native/fee token, this is undefined. + * In case of a chain that uses an ERC-20 token from the parent chain as its native/fee token, this is the address of said token on the parent chain. */ - protected readonly nativeToken?: string + public readonly nativeToken?: string public constructor(public readonly l2Network: L2Network) { super(l2Network) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 2d9fd98cb2..9527931d02 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -41,8 +41,8 @@ export interface L2Network extends Network { */ depositTimeout: number /** - * In case of a chain that uses ETH as its native token, this is undefined. - * In case of a chain that uses an ERC-20 token from the parent chain as its native token, this is the address of said token on the parent chain. + * In case of a chain that uses ETH as its native/fee token, this is undefined. + * In case of a chain that uses an ERC-20 token from the parent chain as its native/fee token, this is the address of said token on the parent chain. */ nativeToken?: string } From 374e8948e73d43438664713ae5d1935c73b4056f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 19 Jul 2023 14:41:53 +0200 Subject: [PATCH 022/192] improve approvals --- src/lib/assetBridger/ethBridger.ts | 91 ++++++++++++++++--- .../customFeeTokenEthBridger.test.ts | 13 ++- .../customFeeTokenTestHelpers.ts | 2 +- tests/integration/eth.test.ts | 10 +- 4 files changed, 93 insertions(+), 23 deletions(-) diff --git a/src/lib/assetBridger/ethBridger.ts b/src/lib/assetBridger/ethBridger.ts index a95cd33e7f..9805f6e780 100644 --- a/src/lib/assetBridger/ethBridger.ts +++ b/src/lib/assetBridger/ethBridger.ts @@ -17,7 +17,7 @@ 'use strict' import { Signer } from '@ethersproject/abstract-signer' -import { Provider } from '@ethersproject/abstract-provider' +import { Provider, TransactionRequest } from '@ethersproject/abstract-provider' import { PayableOverrides, Overrides } from '@ethersproject/contracts' import { BigNumber, BigNumberish, BytesLike, constants } from 'ethers' @@ -49,6 +49,36 @@ import { MissingProviderArbSdkError } from '../dataEntities/errors' import { L2Network, getL2Network } from '../dataEntities/networks' import { ERC20__factory } from '../abi/factories/ERC20__factory' +export type ApproveFeeTokenParams = { + /** + * Amount to approve. Defaults to max int. + */ + amount?: BigNumber + /** + * Transaction overrides + */ + overrides?: PayableOverrides +} + +export type ApproveFeeTokenTxRequest = { + /** + * Transaction request + */ + txRequest: Required> + /** + * Transaction overrides + */ + overrides?: Overrides +} + +export type ApproveFeeTokenParamsOrTxRequest = + | ApproveFeeTokenParams + | ApproveFeeTokenTxRequest + +type WithL1Signer = T & { + l1Signer: Signer +} + export interface EthWithdrawParams { /** * The amount of ETH or tokens to be withdrawn @@ -155,20 +185,59 @@ export class EthBridger extends AssetBridger< return new EthBridger(await getL2Network(l2Provider)) } - // TODO(spsjvc): clean up, support tx request and add jsdoc - public async approve(params: { amount?: BigNumber; l1Signer: Signer }) { + /** + * Asserts that the provided argument is of type `ApproveFeeTokenParams` and not `ApproveFeeTokenTxRequest`. + * @param params + */ + private isApproveFeeTokenParams( + params: ApproveFeeTokenParamsOrTxRequest + ): params is WithL1Signer { + return typeof (params as ApproveFeeTokenTxRequest).txRequest === 'undefined' + } + + /** + * Creates a transaction request for approving the custom fee token to be spent by the Inbox on the parent chain. + * @param params + */ + public async getApproveFeeTokenTxRequest( + params?: ApproveFeeTokenParams + ): Promise>> { if (typeof this.nativeToken === 'undefined') { - throw new Error( - `can't call "EthBridger.approve" for network that uses eth as native token` - ) + throw new Error('chain uses ETH as its native/fee token') } - const token = ERC20__factory.connect(this.nativeToken, params.l1Signer) - - return token.approve( + const erc20Interface = ERC20__factory.createInterface() + const data = erc20Interface.encodeFunctionData('approve', [ this.l2Network.ethBridge.inbox, - params.amount ?? constants.MaxUint256 - ) + params?.amount ?? constants.MaxUint256, + ]) + + return { + to: this.nativeToken, + data, + value: BigNumber.from(0), + } + } + + /** + * Approves the custom fee token to be spent by the Inbox on the parent chain. + * @param params + */ + public async approveFeeToken( + params: WithL1Signer + ) { + if (typeof this.nativeToken === 'undefined') { + throw new Error('chain uses ETH as its native/fee token') + } + + const approveFeeTokenRequest = this.isApproveFeeTokenParams(params) + ? await this.getApproveFeeTokenTxRequest(params) + : params.txRequest + + return params.l1Signer.sendTransaction({ + ...approveFeeTokenRequest, + ...params.overrides, + }) } /** diff --git a/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts b/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts index 44ed1640b4..1b87ff2c12 100644 --- a/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts +++ b/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts @@ -43,14 +43,14 @@ describe('EthBridger (with custom fee token)', async () => { await skipIfMainnet(this) }) - it('approves the custom fee token on the parent chain for an arbitrary amount', async function () { + it('approves the custom fee token to be spent by the Inbox on the parent chain (arbitrary amount, using params)', async function () { const { ethBridger, nativeTokenContract, l1Signer } = await testSetup() const amount = ethers.utils.parseEther('1') await fundL1Ether(l1Signer) await fundL1CustomFeeToken(l1Signer) - const approvalTx = await ethBridger.approve({ + const approvalTx = await ethBridger.approveFeeToken({ amount, l1Signer, }) @@ -67,13 +67,16 @@ describe('EthBridger (with custom fee token)', async () => { ) }) - it('approves the custom fee token on the parent chain for the max amount', async function () { + it('approves the custom fee token to be spent by the Inbox on the parent chain (max amount, using tx request)', async function () { const { ethBridger, nativeTokenContract, l1Signer } = await testSetup() await fundL1Ether(l1Signer) await fundL1CustomFeeToken(l1Signer) - const approvalTx = await ethBridger.approve({ l1Signer }) + const approvalTx = await ethBridger.approveFeeToken({ + txRequest: await ethBridger.getApproveFeeTokenTxRequest(), + l1Signer, + }) await approvalTx.wait() const allowance = await nativeTokenContract.allowance( @@ -87,7 +90,7 @@ describe('EthBridger (with custom fee token)', async () => { ) }) - it('deposits custom fee token via params', async function () { + it('deposits custom fee token (using params)', async function () { const { ethBridger, nativeTokenContract, l1Signer, l2Signer, l2Provider } = await testSetup() const bridge = ethBridger.l2Network.ethBridge.bridge diff --git a/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts b/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts index af21460840..3709cdecda 100644 --- a/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts +++ b/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts @@ -65,7 +65,7 @@ export async function fundL1CustomFeeToken(l1Signer: Signer) { export async function approveL1CustomFeeToken(l1Signer: Signer) { const ethBridger = await EthBridger.fromProvider(arbProvider) - const tx = await ethBridger.approve({ l1Signer }) + const tx = await ethBridger.approveFeeToken({ l1Signer }) await tx.wait() } diff --git a/tests/integration/eth.test.ts b/tests/integration/eth.test.ts index 23e15dce0c..ffde105451 100644 --- a/tests/integration/eth.test.ts +++ b/tests/integration/eth.test.ts @@ -78,16 +78,14 @@ describe('Ether', async () => { ) }) - it('"EthBridger.approve" throws when eth is native token', async () => { + it('"EthBridger.approveFeeToken" throws when eth is used as native/fee token', async () => { const { ethBridger, l1Signer } = await testSetup() try { - await ethBridger.approve({ l1Signer }) - expect.fail(`"EthBridger.approve" should have thrown`) + await ethBridger.approveFeeToken({ l1Signer }) + expect.fail(`"EthBridger.approveFeeToken" should have thrown`) } catch (error: any) { - expect(error.message).to.equal( - `can't call "EthBridger.approve" for network that uses eth as native token` - ) + expect(error.message).to.equal('chain uses ETH as its native/fee token') } }) From ff1516087d8dffd0c82468ec96ed80619601382a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 19 Jul 2023 14:44:28 +0200 Subject: [PATCH 023/192] remove async --- src/lib/assetBridger/ethBridger.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/assetBridger/ethBridger.ts b/src/lib/assetBridger/ethBridger.ts index 9805f6e780..636f7b1fc5 100644 --- a/src/lib/assetBridger/ethBridger.ts +++ b/src/lib/assetBridger/ethBridger.ts @@ -199,9 +199,9 @@ export class EthBridger extends AssetBridger< * Creates a transaction request for approving the custom fee token to be spent by the Inbox on the parent chain. * @param params */ - public async getApproveFeeTokenTxRequest( + public getApproveFeeTokenTxRequest( params?: ApproveFeeTokenParams - ): Promise>> { + ): Required> { if (typeof this.nativeToken === 'undefined') { throw new Error('chain uses ETH as its native/fee token') } @@ -231,7 +231,7 @@ export class EthBridger extends AssetBridger< } const approveFeeTokenRequest = this.isApproveFeeTokenParams(params) - ? await this.getApproveFeeTokenTxRequest(params) + ? this.getApproveFeeTokenTxRequest(params) : params.txRequest return params.l1Signer.sendTransaction({ From 65ca1f214bcfb8ab6f9b056d8a4bd13dd6112421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 19 Jul 2023 14:51:39 +0200 Subject: [PATCH 024/192] refactor --- src/lib/assetBridger/ethBridger.ts | 50 ++++++++++++++---------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/src/lib/assetBridger/ethBridger.ts b/src/lib/assetBridger/ethBridger.ts index 636f7b1fc5..eb10d15421 100644 --- a/src/lib/assetBridger/ethBridger.ts +++ b/src/lib/assetBridger/ethBridger.ts @@ -241,47 +241,45 @@ export class EthBridger extends AssetBridger< } /** - * Get a transaction request for an eth deposit + * Gets the transaction data for a tx request necessary for depositing ETH or other native/fee token * @param params * @returns */ - public async getDepositRequest( - params: EthDepositRequestParams - ): Promise> { - let data: BytesLike - let value: BigNumberish - + private getDepositRequestData(params: EthDepositRequestParams) { if (typeof this.nativeToken === 'undefined') { - const inboxInterface = Inbox__factory.createInterface() - - value = params.amount - data = ( - inboxInterface as unknown as { + return ( + Inbox__factory.createInterface() as unknown as { encodeFunctionData( functionFragment: 'depositEth()', values?: undefined ): string } ).encodeFunctionData('depositEth()') - } else { - const inboxInterface = ERC20Inbox__factory.createInterface() - - value = 0 - data = ( - inboxInterface as unknown as { - encodeFunctionData( - functionFragment: 'depositERC20(uint256)', - values: [BigNumber] - ): string - } - ).encodeFunctionData('depositERC20(uint256)', [params.amount]) } + return ( + ERC20Inbox__factory.createInterface() as unknown as { + encodeFunctionData( + functionFragment: 'depositERC20(uint256)', + values: [BigNumber] + ): string + } + ).encodeFunctionData('depositERC20(uint256)', [params.amount]) + } + + /** + * Get a transaction request for an eth deposit + * @param params + * @returns + */ + public async getDepositRequest( + params: EthDepositRequestParams + ): Promise> { return { txRequest: { to: this.l2Network.ethBridge.inbox, - value, - data, + value: typeof this.nativeToken === 'undefined' ? params.amount : 0, + data: this.getDepositRequestData(params), from: params.from, }, isValid: async () => true, From b5f747cb328d8b69a8beb5391327386c4d7880f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 19 Jul 2023 15:00:34 +0200 Subject: [PATCH 025/192] remove field --- src/lib/dataEntities/retryableData.ts | 4 ---- src/lib/message/L1ToL2MessageCreator.ts | 1 - 2 files changed, 5 deletions(-) diff --git a/src/lib/dataEntities/retryableData.ts b/src/lib/dataEntities/retryableData.ts index d4884b7810..e1b05d1f99 100644 --- a/src/lib/dataEntities/retryableData.ts +++ b/src/lib/dataEntities/retryableData.ts @@ -42,10 +42,6 @@ export interface RetryableData { * The max gas price to pay on L2 */ maxFeePerGas: BigNumber - /** - * todo(spsjvc) - */ - tokenTotalFeeAmount?: BigNumber /** * The data to call the L2 address with */ diff --git a/src/lib/message/L1ToL2MessageCreator.ts b/src/lib/message/L1ToL2MessageCreator.ts index 4a98535a03..20d90ca967 100644 --- a/src/lib/message/L1ToL2MessageCreator.ts +++ b/src/lib/message/L1ToL2MessageCreator.ts @@ -154,7 +154,6 @@ export class L1ToL2MessageCreator { maxSubmissionCost: estimates.maxSubmissionCost, maxFeePerGas: estimates.maxFeePerGas, gasLimit: estimates.gasLimit, - tokenTotalFeeAmount, deposit: estimates.deposit, }, isValid: async () => { From 78b13c1db9e11992144e16abafb7ed0a881d1bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 19 Jul 2023 15:47:23 +0200 Subject: [PATCH 026/192] refactor --- src/lib/assetBridger/ethBridger.ts | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/lib/assetBridger/ethBridger.ts b/src/lib/assetBridger/ethBridger.ts index eb10d15421..9e3cfabe02 100644 --- a/src/lib/assetBridger/ethBridger.ts +++ b/src/lib/assetBridger/ethBridger.ts @@ -176,6 +176,14 @@ export class EthBridger extends AssetBridger< this.nativeToken = l2Network.nativeToken } + /** + * Whether the chain uses ETH as its native/fee token. + * @returns + */ + private get isNativeTokenEth() { + return typeof this.nativeToken === 'undefined' + } + /** * Instantiates a new EthBridger from an L2 Provider * @param l2Provider @@ -202,7 +210,7 @@ export class EthBridger extends AssetBridger< public getApproveFeeTokenTxRequest( params?: ApproveFeeTokenParams ): Required> { - if (typeof this.nativeToken === 'undefined') { + if (this.isNativeTokenEth) { throw new Error('chain uses ETH as its native/fee token') } @@ -213,7 +221,7 @@ export class EthBridger extends AssetBridger< ]) return { - to: this.nativeToken, + to: this.nativeToken!, data, value: BigNumber.from(0), } @@ -226,7 +234,7 @@ export class EthBridger extends AssetBridger< public async approveFeeToken( params: WithL1Signer ) { - if (typeof this.nativeToken === 'undefined') { + if (this.isNativeTokenEth) { throw new Error('chain uses ETH as its native/fee token') } @@ -241,12 +249,12 @@ export class EthBridger extends AssetBridger< } /** - * Gets the transaction data for a tx request necessary for depositing ETH or other native/fee token + * Gets the transaction data for a tx request necessary for depositing ETH or other native/fee token. * @param params * @returns */ private getDepositRequestData(params: EthDepositRequestParams) { - if (typeof this.nativeToken === 'undefined') { + if (this.isNativeTokenEth) { return ( Inbox__factory.createInterface() as unknown as { encodeFunctionData( @@ -278,7 +286,7 @@ export class EthBridger extends AssetBridger< return { txRequest: { to: this.l2Network.ethBridge.inbox, - value: typeof this.nativeToken === 'undefined' ? params.amount : 0, + value: this.isNativeTokenEth ? params.amount : 0, data: this.getDepositRequestData(params), from: params.from, }, From 2ed6fcd9b29ae7f06a306aa99b6685de6f5dcc00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 19 Jul 2023 16:00:46 +0200 Subject: [PATCH 027/192] extract func --- src/lib/message/L1ToL2MessageCreator.ts | 88 ++++++++++++++----------- 1 file changed, 50 insertions(+), 38 deletions(-) diff --git a/src/lib/message/L1ToL2MessageCreator.ts b/src/lib/message/L1ToL2MessageCreator.ts index 20d90ca967..9ac02081e6 100644 --- a/src/lib/message/L1ToL2MessageCreator.ts +++ b/src/lib/message/L1ToL2MessageCreator.ts @@ -9,7 +9,7 @@ import { import { L1ContractTransaction, L1TransactionReceipt } from './L1Transaction' import { Inbox__factory } from '../abi/factories/Inbox__factory' import { ERC20Inbox__factory } from '../abi/factories/ERC20Inbox__factory' -import { getL2Network } from '../dataEntities/networks' +import { L2Network, getL2Network } from '../dataEntities/networks' import { PayableOverrides } from '@ethersproject/contracts' import { SignerProviderUtils } from '../dataEntities/signerOrProvider' import { MissingProviderArbSdkError } from '../dataEntities/errors' @@ -68,6 +68,46 @@ export class L1ToL2MessageCreator { ) } + // todo(spsjvc): jsdoc + protected static getTicketCreationRequestData( + params: L1ToL2MessageParams, + estimates: Pick, + excessFeeRefundAddress: string, + callValueRefundAddress: string, + isNativeTokenEth: boolean + ) { + if (isNativeTokenEth) { + return Inbox__factory.createInterface().encodeFunctionData( + 'createRetryableTicket', + [ + params.to, + params.l2CallValue, + estimates.maxSubmissionCost, + excessFeeRefundAddress, + callValueRefundAddress, + estimates.gasLimit, + estimates.maxFeePerGas, + params.data, + ] + ) + } + + return ERC20Inbox__factory.createInterface().encodeFunctionData( + 'createRetryableTicket', + [ + params.to, + params.l2CallValue, + estimates.maxSubmissionCost, + excessFeeRefundAddress, + callValueRefundAddress, + estimates.gasLimit, + estimates.maxFeePerGas, + estimates.deposit, // tokenTotalFeeAmount + params.data, + ] + ) + } + /** * Generate a transaction request for creating a retryable ticket * @param params @@ -99,49 +139,21 @@ export class L1ToL2MessageCreator { ) const l2Network = await getL2Network(l2Provider) + const isNativeTokenEth = typeof l2Network.nativeToken === 'undefined' - let value: BigNumber - let tokenTotalFeeAmount: BigNumber | undefined - let data: string - - if (typeof l2Network.nativeToken === 'undefined') { - const inboxInterface = Inbox__factory.createInterface() - - value = estimates.deposit - tokenTotalFeeAmount = undefined - data = inboxInterface.encodeFunctionData('createRetryableTicket', [ - params.to, - params.l2CallValue, - estimates.maxSubmissionCost, - excessFeeRefundAddress, - callValueRefundAddress, - estimates.gasLimit, - estimates.maxFeePerGas, - params.data, - ]) - } else { - const inboxInterface = ERC20Inbox__factory.createInterface() - - value = constants.Zero - tokenTotalFeeAmount = estimates.deposit - data = inboxInterface.encodeFunctionData('createRetryableTicket', [ - params.to, - params.l2CallValue, - estimates.maxSubmissionCost, - excessFeeRefundAddress, - callValueRefundAddress, - estimates.gasLimit, - estimates.maxFeePerGas, - estimates.deposit, - params.data, - ]) - } + const data = L1ToL2MessageCreator.getTicketCreationRequestData( + params, + estimates, + excessFeeRefundAddress, + callValueRefundAddress, + isNativeTokenEth + ) return { txRequest: { to: l2Network.ethBridge.inbox, data, - value, + value: isNativeTokenEth ? estimates.deposit : constants.Zero, from: params.from, }, retryableData: { From 522f852edef4b7ec2c6ecc45beabe090e2a8dd7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 19 Jul 2023 18:38:31 +0200 Subject: [PATCH 028/192] add await --- src/lib/assetBridger/ethBridger.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/assetBridger/ethBridger.ts b/src/lib/assetBridger/ethBridger.ts index 9e3cfabe02..b5c1089da1 100644 --- a/src/lib/assetBridger/ethBridger.ts +++ b/src/lib/assetBridger/ethBridger.ts @@ -242,7 +242,7 @@ export class EthBridger extends AssetBridger< ? this.getApproveFeeTokenTxRequest(params) : params.txRequest - return params.l1Signer.sendTransaction({ + return await params.l1Signer.sendTransaction({ ...approveFeeTokenRequest, ...params.overrides, }) From 363f988424e9dd9402b8f28490dfd835c6abe61c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 19 Jul 2023 18:39:16 +0200 Subject: [PATCH 029/192] unused imports --- src/lib/assetBridger/ethBridger.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/assetBridger/ethBridger.ts b/src/lib/assetBridger/ethBridger.ts index b5c1089da1..886a3697c5 100644 --- a/src/lib/assetBridger/ethBridger.ts +++ b/src/lib/assetBridger/ethBridger.ts @@ -19,7 +19,7 @@ import { Signer } from '@ethersproject/abstract-signer' import { Provider, TransactionRequest } from '@ethersproject/abstract-provider' import { PayableOverrides, Overrides } from '@ethersproject/contracts' -import { BigNumber, BigNumberish, BytesLike, constants } from 'ethers' +import { BigNumber, constants } from 'ethers' import { Inbox__factory } from '../abi/factories/Inbox__factory' import { ERC20Inbox__factory } from '../abi/factories/ERC20Inbox__factory' From 828c529dd08b527772b3cc3f9b8391e9b3546049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 19 Jul 2023 18:44:13 +0200 Subject: [PATCH 030/192] add methods for approving custom fee token for gateways --- src/lib/assetBridger/erc20Bridger.ts | 57 ++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/lib/assetBridger/erc20Bridger.ts b/src/lib/assetBridger/erc20Bridger.ts index ae504eb77d..349ad2e3a5 100644 --- a/src/lib/assetBridger/erc20Bridger.ts +++ b/src/lib/assetBridger/erc20Bridger.ts @@ -175,11 +175,27 @@ export class Erc20Bridger extends AssetBridger< public static MAX_APPROVAL = MaxUint256 public static MIN_CUSTOM_DEPOSIT_GAS_LIMIT = BigNumber.from(275000) + /** + * In case of a chain that uses ETH as its native/fee token, this is undefined. + * In case of a chain that uses an ERC-20 token from the parent chain as its native/fee token, this is the address of said token on the parent chain. + */ + public readonly nativeToken?: string + /** * Bridger for moving ERC20 tokens back and forth between L1 to L2 */ public constructor(l2Network: L2Network) { super(l2Network) + + this.nativeToken = l2Network.nativeToken + } + + /** + * Whether the chain uses ETH as its native/fee token. + * @returns + */ + private get isNativeTokenEth() { + return typeof this.nativeToken === 'undefined' } /** @@ -227,6 +243,47 @@ export class Erc20Bridger extends AssetBridger< ).getGateway(erc20L1Address) } + /** + * Creates a transaction request for approving the custom fee token to be spent by the relevant Gateway on the parent chain. + * @param params + */ + public async getApproveFeeTokenRequest( + params: ProviderTokenApproveParams + ): Promise>> { + if (this.isNativeTokenEth) { + throw new Error('chain uses ETH as its native/fee token') + } + + const txRequest = await this.getApproveTokenRequest(params) + // just reuse the approve token request but direct it towards the native token contract + return { ...txRequest, to: this.nativeToken! } + } + + /** + * Approves the custom fee token to be spent by the relevant Gateway on the parent chain. + * @param params + */ + public async approveFeeToken( + params: ApproveParamsOrTxRequest + ): Promise { + if (this.isNativeTokenEth) { + throw new Error('chain uses ETH as its native/fee token') + } + + await this.checkL1Network(params.l1Signer) + + const approveRequest = this.isApproveParams(params) + ? await this.getApproveFeeTokenRequest({ + ...params, + l1Provider: SignerProviderUtils.getProviderOrThrow(params.l1Signer), + }) + : params.txRequest + return await params.l1Signer.sendTransaction({ + ...approveRequest, + ...params.overrides, + }) + } + /** * Get a tx request to approve tokens for deposit to the bridge. * The tokens will be approved for the relevant gateway. From 2e8143c48d941ce4d01d0800ec0d10ce25db4d3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 19 Jul 2023 18:45:50 +0200 Subject: [PATCH 031/192] reverse cases --- src/lib/assetBridger/ethBridger.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/lib/assetBridger/ethBridger.ts b/src/lib/assetBridger/ethBridger.ts index 886a3697c5..623050f7d2 100644 --- a/src/lib/assetBridger/ethBridger.ts +++ b/src/lib/assetBridger/ethBridger.ts @@ -254,25 +254,25 @@ export class EthBridger extends AssetBridger< * @returns */ private getDepositRequestData(params: EthDepositRequestParams) { - if (this.isNativeTokenEth) { + if (!this.isNativeTokenEth) { return ( - Inbox__factory.createInterface() as unknown as { + ERC20Inbox__factory.createInterface() as unknown as { encodeFunctionData( - functionFragment: 'depositEth()', - values?: undefined + functionFragment: 'depositERC20(uint256)', + values: [BigNumber] ): string } - ).encodeFunctionData('depositEth()') + ).encodeFunctionData('depositERC20(uint256)', [params.amount]) } return ( - ERC20Inbox__factory.createInterface() as unknown as { + Inbox__factory.createInterface() as unknown as { encodeFunctionData( - functionFragment: 'depositERC20(uint256)', - values: [BigNumber] + functionFragment: 'depositEth()', + values?: undefined ): string } - ).encodeFunctionData('depositERC20(uint256)', [params.amount]) + ).encodeFunctionData('depositEth()') } /** From fd63bb15b280db15f5d366f891e9e7f8b705746c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 19 Jul 2023 18:46:15 +0200 Subject: [PATCH 032/192] remove unused imports --- src/lib/message/L1ToL2MessageCreator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/message/L1ToL2MessageCreator.ts b/src/lib/message/L1ToL2MessageCreator.ts index 9ac02081e6..f4f33f0a07 100644 --- a/src/lib/message/L1ToL2MessageCreator.ts +++ b/src/lib/message/L1ToL2MessageCreator.ts @@ -1,4 +1,4 @@ -import { BigNumber, constants } from 'ethers' +import { constants } from 'ethers' import { Signer } from '@ethersproject/abstract-signer' import { Provider } from '@ethersproject/abstract-provider' @@ -9,7 +9,7 @@ import { import { L1ContractTransaction, L1TransactionReceipt } from './L1Transaction' import { Inbox__factory } from '../abi/factories/Inbox__factory' import { ERC20Inbox__factory } from '../abi/factories/ERC20Inbox__factory' -import { L2Network, getL2Network } from '../dataEntities/networks' +import { getL2Network } from '../dataEntities/networks' import { PayableOverrides } from '@ethersproject/contracts' import { SignerProviderUtils } from '../dataEntities/signerOrProvider' import { MissingProviderArbSdkError } from '../dataEntities/errors' From 3033b905ae119d59834ed1d9ed6ad1f799d53667 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 19 Jul 2023 18:47:02 +0200 Subject: [PATCH 033/192] reverse cases --- src/lib/message/L1ToL2MessageCreator.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/message/L1ToL2MessageCreator.ts b/src/lib/message/L1ToL2MessageCreator.ts index f4f33f0a07..afb55760c4 100644 --- a/src/lib/message/L1ToL2MessageCreator.ts +++ b/src/lib/message/L1ToL2MessageCreator.ts @@ -76,8 +76,8 @@ export class L1ToL2MessageCreator { callValueRefundAddress: string, isNativeTokenEth: boolean ) { - if (isNativeTokenEth) { - return Inbox__factory.createInterface().encodeFunctionData( + if (!isNativeTokenEth) { + return ERC20Inbox__factory.createInterface().encodeFunctionData( 'createRetryableTicket', [ params.to, @@ -87,12 +87,13 @@ export class L1ToL2MessageCreator { callValueRefundAddress, estimates.gasLimit, estimates.maxFeePerGas, + estimates.deposit, // tokenTotalFeeAmount params.data, ] ) } - return ERC20Inbox__factory.createInterface().encodeFunctionData( + return Inbox__factory.createInterface().encodeFunctionData( 'createRetryableTicket', [ params.to, @@ -102,7 +103,6 @@ export class L1ToL2MessageCreator { callValueRefundAddress, estimates.gasLimit, estimates.maxFeePerGas, - estimates.deposit, // tokenTotalFeeAmount params.data, ] ) From 0dbdcaaaf3e33b67cab54ce4b6eca9e46da07ad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 19 Jul 2023 19:09:53 +0200 Subject: [PATCH 034/192] support erc-20 deposits paid by custom fee token --- src/lib/assetBridger/erc20Bridger.ts | 62 +++++++++++++++++++++------ src/lib/dataEntities/retryableData.ts | 2 +- 2 files changed, 50 insertions(+), 14 deletions(-) diff --git a/src/lib/assetBridger/erc20Bridger.ts b/src/lib/assetBridger/erc20Bridger.ts index 349ad2e3a5..95539995ba 100644 --- a/src/lib/assetBridger/erc20Bridger.ts +++ b/src/lib/assetBridger/erc20Bridger.ts @@ -25,7 +25,7 @@ import { import { PayableOverrides, Overrides } from '@ethersproject/contracts' import { MaxUint256 } from '@ethersproject/constants' import { ErrorCode, Logger } from '@ethersproject/logger' -import { BigNumber, BigNumberish, ethers, BytesLike } from 'ethers' +import { BigNumber, BigNumberish, ethers, BytesLike, constants } from 'ethers' import { L1GatewayRouter__factory } from '../abi/factories/L1GatewayRouter__factory' import { L2GatewayRouter__factory } from '../abi/factories/L2GatewayRouter__factory' @@ -555,6 +555,52 @@ export class Erc20Bridger extends AssetBridger< } } + /** + * Get the call value for the deposit transaction request + * @param depositParams + * @returns + */ + private getDepositRequestValue( + depositParams: OmitTyped + ) { + // the call value should be zero when paying with a custom fee token, + // as the fee amount is packed inside the last parameter (`data`) of the call to `outboundTransfer` + if (!this.isNativeTokenEth) { + return constants.Zero + } + + // we dont include the l2 call value for token deposits because + // they either have 0 call value, or their call value is withdrawn from + // a contract by the gateway (weth). So in both of these cases the l2 call value + // is not actually deposited in the value field + return depositParams.gasLimit + .mul(depositParams.maxFeePerGas) + .add(depositParams.maxSubmissionCost) + } + + // todo(spsjvc): jsdoc + private getDepositRequestOutboundTransferData( + depositParams: OmitTyped + ) { + if (!this.isNativeTokenEth) { + return defaultAbiCoder.encode( + ['uint256', 'bytes', 'uint256'], + [ + constants.Zero, + '0x', + depositParams.gasLimit + .mul(depositParams.maxFeePerGas) + .add(depositParams.maxSubmissionCost), + ] + ) + } + + return defaultAbiCoder.encode( + ['uint256', 'bytes'], + [depositParams.maxSubmissionCost, '0x'] + ) + } + /** * Get the arguments for calling the deposit function * @param params @@ -594,10 +640,6 @@ export class Erc20Bridger extends AssetBridger< const depositFunc = ( depositParams: OmitTyped ) => { - const innerData = defaultAbiCoder.encode( - ['uint256', 'bytes'], - [depositParams.maxSubmissionCost, '0x'] - ) const iGatewayRouter = L1GatewayRouter__factory.createInterface() return { @@ -607,17 +649,11 @@ export class Erc20Bridger extends AssetBridger< amount, depositParams.gasLimit, depositParams.maxFeePerGas, - innerData, + this.getDepositRequestOutboundTransferData(depositParams), ]), to: this.l2Network.tokenBridge.l1GatewayRouter, from: defaultedParams.from, - value: depositParams.gasLimit - .mul(depositParams.maxFeePerGas) - .add(depositParams.maxSubmissionCost), - // we dont include the l2 call value for token deposits because - // they either have 0 call value, or their call value is withdrawn from - // a contract by the gateway (weth). So in both of these cases the l2 call value - // is not actually deposited in the value field + value: this.getDepositRequestValue(depositParams), } } diff --git a/src/lib/dataEntities/retryableData.ts b/src/lib/dataEntities/retryableData.ts index e1b05d1f99..7db4c32593 100644 --- a/src/lib/dataEntities/retryableData.ts +++ b/src/lib/dataEntities/retryableData.ts @@ -23,7 +23,7 @@ export interface RetryableData { */ deposit: BigNumber /** - * The maximum cost to be paif for submitting the transaction + * The maximum cost to be paid for submitting the transaction */ maxSubmissionCost: BigNumber /** From 4c67e85cde6224aa9b7f3c53378624b754178aff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 19 Jul 2023 20:11:54 +0200 Subject: [PATCH 035/192] rename stuff --- src/lib/assetBridger/erc20Bridger.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/assetBridger/erc20Bridger.ts b/src/lib/assetBridger/erc20Bridger.ts index 95539995ba..f677934f64 100644 --- a/src/lib/assetBridger/erc20Bridger.ts +++ b/src/lib/assetBridger/erc20Bridger.ts @@ -560,7 +560,7 @@ export class Erc20Bridger extends AssetBridger< * @param depositParams * @returns */ - private getDepositRequestValue( + private getDepositRequestCallValue( depositParams: OmitTyped ) { // the call value should be zero when paying with a custom fee token, @@ -579,7 +579,7 @@ export class Erc20Bridger extends AssetBridger< } // todo(spsjvc): jsdoc - private getDepositRequestOutboundTransferData( + private getDepositRequestOutboundTransferDataParam( depositParams: OmitTyped ) { if (!this.isNativeTokenEth) { @@ -649,11 +649,11 @@ export class Erc20Bridger extends AssetBridger< amount, depositParams.gasLimit, depositParams.maxFeePerGas, - this.getDepositRequestOutboundTransferData(depositParams), + this.getDepositRequestOutboundTransferDataParam(depositParams), ]), to: this.l2Network.tokenBridge.l1GatewayRouter, from: defaultedParams.from, - value: this.getDepositRequestValue(depositParams), + value: this.getDepositRequestCallValue(depositParams), } } From d75bddfdccf5eef7be02d87f68a736c6ce1226a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 2 Aug 2023 13:34:21 +0200 Subject: [PATCH 036/192] just commit --- package.json | 3 +- scripts/fundCustomFeeToken.ts | 7 +++ .../customFeeTokenTestHelpers.ts | 25 +++++++- tests/integration/standarderc20.test.ts | 63 ++++++++++++++++++- 4 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 scripts/fundCustomFeeToken.ts diff --git a/package.json b/package.json index 3aa805e2f8..618342bdec 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,8 @@ "setStandard": "ts-node scripts/setStandardGateways.ts", "setCustom": "ts-node scripts/setArbCustomGateways.ts", "cancelRetryable": "ts-node scripts/cancelRetryable.ts", - "bridgeStandardToken": "ts-node scripts/deployStandard.ts" + "bridgeStandardToken": "ts-node scripts/deployStandard.ts", + "fund": "ts-node scripts/fundCustomFeeToken.ts" }, "dependencies": { "@ethersproject/address": "^5.0.8", diff --git a/scripts/fundCustomFeeToken.ts b/scripts/fundCustomFeeToken.ts new file mode 100644 index 0000000000..8d1a164dc6 --- /dev/null +++ b/scripts/fundCustomFeeToken.ts @@ -0,0 +1,7 @@ +import { fundL1CustomFeeToken } from '../tests/integration/custom-fee-token/customFeeTokenTestHelpers' + +async function main() { + await fundL1CustomFeeToken('0x3f1Eae7D46d88F08fc2F8ed27FCb2AB183EB2d0E') +} + +main() diff --git a/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts b/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts index 3709cdecda..7f87b51fc8 100644 --- a/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts +++ b/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts @@ -5,7 +5,7 @@ import * as fs from 'fs' import { testSetup as _testSetup, config } from '../../../scripts/testSetup' import { ERC20__factory } from '../../../src/lib/abi/factories/ERC20__factory' -import { EthBridger, L1Network, L2Network } from '../../../src' +import { Erc20Bridger, EthBridger, L1Network, L2Network } from '../../../src' const ethProvider = new StaticJsonRpcProvider(config.ethUrl) const arbProvider = new StaticJsonRpcProvider(config.arbUrl) @@ -41,8 +41,12 @@ export async function testSetup() { return { ...result, nativeTokenContract } } -export async function fundL1CustomFeeToken(l1Signer: Signer) { +export async function fundL1CustomFeeToken(l1SignerOrAddress: Signer | string) { const nativeToken = getLocalNetworks().l2Network.nativeToken + const address = + typeof l1SignerOrAddress === 'string' + ? l1SignerOrAddress + : await l1SignerOrAddress.getAddress() if (typeof nativeToken === 'undefined') { throw new Error( @@ -55,7 +59,6 @@ export async function fundL1CustomFeeToken(l1Signer: Signer) { ethProvider ) - const address = await l1Signer.getAddress() const tokenContract = ERC20__factory.connect(nativeToken, deployerWallet) const tx = await tokenContract.transfer(address, utils.parseEther('10')) @@ -69,6 +72,22 @@ export async function approveL1CustomFeeToken(l1Signer: Signer) { await tx.wait() } +export async function getNativeTokenAllowance(owner: string, spender: string) { + const nativeToken = getLocalNetworks().l2Network.nativeToken + const nativeTokenContract = ERC20__factory.connect(nativeToken!, ethProvider) + return nativeTokenContract.allowance(owner, spender) +} + +export async function approveL1CustomFeeTokenForErc20Deposit( + l1Signer: Signer, + erc20L1Address: string +) { + const erc20Bridger = await Erc20Bridger.fromProvider(arbProvider) + + const tx = await erc20Bridger.approveFeeToken({ erc20L1Address, l1Signer }) + await tx.wait() +} + export async function fundL2CustomFeeToken(l2Signer: Signer) { const deployerWallet = new Wallet( utils.sha256(utils.toUtf8Bytes('user_l1user')), diff --git a/tests/integration/standarderc20.test.ts b/tests/integration/standarderc20.test.ts index 4a1ac7ce83..7900991214 100644 --- a/tests/integration/standarderc20.test.ts +++ b/tests/integration/standarderc20.test.ts @@ -34,7 +34,7 @@ import { L2Network, L2TransactionReceipt, } from '../../src' -import { Signer } from 'ethers' +import { Signer, constants } from 'ethers' import { TestERC20 } from '../../src/lib/abi/TestERC20' import { testSetup } from '../../scripts/testSetup' import { ERC20__factory } from '../../src/lib/abi/factories/ERC20__factory' @@ -45,6 +45,13 @@ import { import { ArbRetryableTx__factory } from '../../src/lib/abi/factories/ArbRetryableTx__factory' import { NodeInterface__factory } from '../../src/lib/abi/factories/NodeInterface__factory' import { isDefined } from '../../src/lib/utils/lib' +import { + fundL1CustomFeeToken, + approveL1CustomFeeToken, + fundL2CustomFeeToken, + approveL1CustomFeeTokenForErc20Deposit, + getNativeTokenAllowance, +} from './custom-fee-token/customFeeTokenTestHelpers' const depositAmount = BigNumber.from(100) const withdrawalAmount = BigNumber.from(10) @@ -65,7 +72,9 @@ describe('standard ERC20', () => { before('init', async () => { const setup = await testSetup() await fundL1(setup.l1Signer) - await fundL2(setup.l2Signer) + await fundL1CustomFeeToken(setup.l1Signer) + await approveL1CustomFeeToken(setup.l1Signer) + await fundL2CustomFeeToken(setup.l2Signer) const deployErc20 = new TestERC20__factory().connect(setup.l1Signer) const testToken = await deployErc20.deploy() @@ -76,7 +85,55 @@ describe('standard ERC20', () => { testState = { ...setup, l1Token: testToken } }) - it('deposits erc20', async () => { + it('approves the thing', async () => { + const { l1Signer, l2Signer, erc20Bridger } = await testSetup() + + await fundL1(l1Signer) + await fundL1CustomFeeToken(l1Signer) + await fundL2CustomFeeToken(l2Signer) + + const gatewayAddress = await erc20Bridger.getL1GatewayAddress( + testState.l1Token.address, + l1Signer.provider! + ) + + const initialAllowance = await getNativeTokenAllowance( + await l1Signer.getAddress(), + gatewayAddress + ) + + console.log({ initialAllowance }) + + expect(initialAllowance.toString()).to.eq( + constants.Zero.toString(), + 'initial allowance is not empty' + ) + + const tx = await erc20Bridger.approveFeeToken({ + l1Signer: l1Signer, + erc20L1Address: testState.l1Token.address, + }) + await tx.wait() + + const finalAllowance = await getNativeTokenAllowance( + await l1Signer.getAddress(), + gatewayAddress + ) + + console.log({ finalAllowance }) + + expect(finalAllowance.toString()).to.eq( + constants.MaxUint256.toString(), + 'initial allowance is not empty' + ) + }) + + it.only('deposits erc20', async () => { + await approveL1CustomFeeTokenForErc20Deposit( + testState.l1Signer, + testState.l1Token.address + ) + await depositToken( depositAmount, testState.l1Token.address, From 11c1f8a5676f489a60b104d131acc43c139e1a99 Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 2 Aug 2023 13:55:44 +0200 Subject: [PATCH 037/192] support L2 parent chains --- src/lib/assetBridger/assetBridger.ts | 2 +- src/lib/dataEntities/networks.ts | 48 +++++++++++++++++++++++----- src/lib/inbox/inbox.ts | 10 ++++-- 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/lib/assetBridger/assetBridger.ts b/src/lib/assetBridger/assetBridger.ts index ea3f72162b..b7bda7547e 100644 --- a/src/lib/assetBridger/assetBridger.ts +++ b/src/lib/assetBridger/assetBridger.ts @@ -30,7 +30,7 @@ import { * Base for bridging assets from l1 to l2 and back */ export abstract class AssetBridger { - public readonly l1Network: L1Network + public readonly l1Network: L1Network | L2Network public constructor(public readonly l2Network: L2Network) { this.l1Network = l1Networks[l2Network.partnerChainID] diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index db1ea89d14..942b6cabdd 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -28,14 +28,16 @@ export interface L1Network extends Network { } export interface L2Network extends Network { + partnerChainIDs: number[] tokenBridge: TokenBridge ethBridge: EthBridge partnerChainID: number - isArbitrum: true + isArbitrum: boolean confirmPeriodBlocks: number retryableLifetimeSeconds: number nitroGenesisBlock: number nitroGenesisL1Block: number + isL3?: boolean /** * How long to wait (ms) for a deposit to arrive on l2 before timing out a request */ @@ -78,7 +80,7 @@ export interface EthBridge { } export interface L1Networks { - [id: string]: L1Network + [id: string]: L1Network | L2Network } export interface L2Networks { @@ -114,6 +116,21 @@ const mainnetETHBridge: EthBridge = { }, } +// L2 networks that are a parent chain to L3s. +function getParentL2Networks() { + const parentL2Networks: { [id: string]: L2Network } = {} + + for (const chainId in l2Networks) { + const network = l2Networks[chainId] + + if (network.partnerChainIDs.length > 0) { + parentL2Networks[chainId] = network + } + } + + return parentL2Networks +} + export const l1Networks: L1Networks = { 1: { chainID: 1, @@ -142,6 +159,8 @@ export const l1Networks: L1Networks = { partnerChainIDs: [421613], isArbitrum: false, }, + // If L2 network is a parent chain to L3 network, it's part of the L1 list. + ...getParentL2Networks(), } export const l2Networks: L2Networks = { @@ -150,6 +169,7 @@ export const l2Networks: L2Networks = { name: 'Arbitrum One', explorerUrl: 'https://arbiscan.io', partnerChainID: 1, + partnerChainIDs: [], isArbitrum: true, tokenBridge: mainnetTokenBridge, ethBridge: mainnetETHBridge, @@ -167,6 +187,9 @@ export const l2Networks: L2Networks = { }, 421613: { chainID: 421613, + partnerChainIDs: [ + // Xai Goerli goes here + ], confirmPeriodBlocks: 20, retryableLifetimeSeconds: SEVEN_DAYS_IN_SECONDS, ethBridge: { @@ -207,6 +230,7 @@ export const l2Networks: L2Networks = { }, 42170: { chainID: 42170, + partnerChainIDs: [], confirmPeriodBlocks: 45818, ethBridge: { bridge: '0xc1ebd02f738644983b6c4b2d440b8e77dde276bd', @@ -265,11 +289,19 @@ const getNetwork = async ( })() const networks = layer === 1 ? l1Networks : l2Networks - if (networks[chainID]) { - return networks[chainID] - } else { - throw new ArbSdkError(`Unrecognized network ${chainID}.`) + const network = networks[chainID] + + if (network) { + if (layer === 2) { + return network + } + // Any network that's a parent chain is a valid L1 network + if (network.partnerChainIDs.length > 0) { + return network + } } + + throw new ArbSdkError(`Unrecognized network ${chainID}.`) } export const getL1Network = ( @@ -377,6 +409,7 @@ export const addDefaultLocalNetwork = (): { const defaultLocalL2Network: L2Network = { chainID: 412346, + partnerChainIDs: [], confirmPeriodBlocks: 20, ethBridge: { bridge: '0x2b360a9881f21c3d7aa0ea6ca0de2a3341d4ef3c', @@ -426,6 +459,5 @@ export const addDefaultLocalNetwork = (): { export const isL1Network = ( network: L1Network | L2Network ): network is L1Network => { - if ((network as L1Network).partnerChainIDs) return true - else return false + return (network as L1Network).partnerChainIDs.length > 0 } diff --git a/src/lib/inbox/inbox.ts b/src/lib/inbox/inbox.ts index 240bfb61b6..64544bee96 100644 --- a/src/lib/inbox/inbox.ts +++ b/src/lib/inbox/inbox.ts @@ -28,7 +28,7 @@ import { SequencerInbox__factory } from '../abi/factories/SequencerInbox__factor import { IInbox__factory } from '../abi/factories/IInbox__factory' import { RequiredPick } from '../utils/types' import { MessageDeliveredEvent } from '../abi/Bridge' -import { l1Networks, L2Network } from '../dataEntities/networks' +import { L1Network, l1Networks, L2Network } from '../dataEntities/networks' import { SignerProviderUtils } from '../dataEntities/signerOrProvider' import { FetchedEvent, EventFetcher } from '../utils/eventFetcher' import { MultiCaller, CallInput } from '../utils/multicall' @@ -90,7 +90,13 @@ export class InboxTools { // we take a long average block time of 14s // and always move at least 10 blocks - const diffBlocks = Math.max(Math.ceil(diff / this.l1Network.blockTime), 10) + let diffBlocks = 10 + if ((this.l1Network as L1Network).blockTime) { + diffBlocks = Math.max( + Math.ceil(diff / (this.l1Network as L1Network).blockTime), + 10 + ) + } return await this.findFirstBlockBelow( blockNumber - diffBlocks, From 7f758a596d90a39c5220674cbc3a89265a4a1cf9 Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 2 Aug 2023 14:39:18 +0200 Subject: [PATCH 038/192] add partnerChainIDs --- scripts/testSetup.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/testSetup.ts b/scripts/testSetup.ts index 5558054297..9a81844846 100644 --- a/scripts/testSetup.ts +++ b/scripts/testSetup.ts @@ -110,6 +110,7 @@ export const getCustomNetworks = async ( const l2Network: Omit = { chainID: l2NetworkInfo.chainId, + partnerChainIDs: [], confirmPeriodBlocks: confirmPeriodBlocks.toNumber(), ethBridge: { bridge: parsedDeploymentData.bridge, From b477a8a16eefb43a62ae83b242115f6d7f5a2807 Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 4 Aug 2023 09:18:42 +0200 Subject: [PATCH 039/192] extra error message --- src/lib/dataEntities/networks.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 942b6cabdd..ee415e2c52 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -295,10 +295,15 @@ const getNetwork = async ( if (layer === 2) { return network } + // Any network that's a parent chain is a valid L1 network if (network.partnerChainIDs.length > 0) { return network } + + throw new ArbSdkError( + `Expected 'partnerChainIDs' to include at least one L2 network. Got empty for network ${chainID}.` + ) } throw new ArbSdkError(`Unrecognized network ${chainID}.`) From e14337bd792ee1409ac0a67eece290b9021754ff Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 9 Aug 2023 12:44:36 +0200 Subject: [PATCH 040/192] update --- scripts/testSetup.ts | 1 - src/lib/dataEntities/networks.ts | 17 ++++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/scripts/testSetup.ts b/scripts/testSetup.ts index 9a81844846..5558054297 100644 --- a/scripts/testSetup.ts +++ b/scripts/testSetup.ts @@ -110,7 +110,6 @@ export const getCustomNetworks = async ( const l2Network: Omit = { chainID: l2NetworkInfo.chainId, - partnerChainIDs: [], confirmPeriodBlocks: confirmPeriodBlocks.toNumber(), ethBridge: { bridge: parsedDeploymentData.bridge, diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index ee415e2c52..7d6c0d29f3 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -28,7 +28,7 @@ export interface L1Network extends Network { } export interface L2Network extends Network { - partnerChainIDs: number[] + partnerChainIDs?: number[] tokenBridge: TokenBridge ethBridge: EthBridge partnerChainID: number @@ -37,7 +37,6 @@ export interface L2Network extends Network { retryableLifetimeSeconds: number nitroGenesisBlock: number nitroGenesisL1Block: number - isL3?: boolean /** * How long to wait (ms) for a deposit to arrive on l2 before timing out a request */ @@ -123,7 +122,7 @@ function getParentL2Networks() { for (const chainId in l2Networks) { const network = l2Networks[chainId] - if (network.partnerChainIDs.length > 0) { + if (network.partnerChainIDs && network.partnerChainIDs.length > 0) { parentL2Networks[chainId] = network } } @@ -169,7 +168,6 @@ export const l2Networks: L2Networks = { name: 'Arbitrum One', explorerUrl: 'https://arbiscan.io', partnerChainID: 1, - partnerChainIDs: [], isArbitrum: true, tokenBridge: mainnetTokenBridge, ethBridge: mainnetETHBridge, @@ -230,7 +228,6 @@ export const l2Networks: L2Networks = { }, 42170: { chainID: 42170, - partnerChainIDs: [], confirmPeriodBlocks: 45818, ethBridge: { bridge: '0xc1ebd02f738644983b6c4b2d440b8e77dde276bd', @@ -296,8 +293,8 @@ const getNetwork = async ( return network } - // Any network that's a parent chain is a valid L1 network - if (network.partnerChainIDs.length > 0) { + // Any network that's a parent chain is valid + if (network.partnerChainIDs && network.partnerChainIDs.length > 0) { return network } @@ -388,7 +385,10 @@ export const addCustomNetwork = ({ `Network ${customL2Network.chainID}'s partner network, ${customL2Network.partnerChainID}, not recognized` ) - if (!l1PartnerChain.partnerChainIDs.includes(customL2Network.chainID)) { + if ( + l1PartnerChain.partnerChainIDs && + !l1PartnerChain.partnerChainIDs.includes(customL2Network.chainID) + ) { l1PartnerChain.partnerChainIDs.push(customL2Network.chainID) } } @@ -414,7 +414,6 @@ export const addDefaultLocalNetwork = (): { const defaultLocalL2Network: L2Network = { chainID: 412346, - partnerChainIDs: [], confirmPeriodBlocks: 20, ethBridge: { bridge: '0x2b360a9881f21c3d7aa0ea6ca0de2a3341d4ef3c', From 8936273582fa78fbeb24fd1e9526c3c5811ed8fc Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 9 Aug 2023 12:57:58 +0200 Subject: [PATCH 041/192] add jsdoc --- src/lib/dataEntities/networks.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 7d6c0d29f3..7c091fb968 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -28,6 +28,9 @@ export interface L1Network extends Network { } export interface L2Network extends Network { + /** + * An array of chain IDs representing child chains that are associated with this L2Network. + */ partnerChainIDs?: number[] tokenBridge: TokenBridge ethBridge: EthBridge From 9ba357c76d0cebcb5115a22d5870592c13feb8bc Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 9 Aug 2023 20:22:53 +0200 Subject: [PATCH 042/192] fix issues --- src/lib/dataEntities/networks.ts | 100 ++++++++++++++++--------------- 1 file changed, 52 insertions(+), 48 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 7c091fb968..ccd7e093a3 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -118,53 +118,6 @@ const mainnetETHBridge: EthBridge = { }, } -// L2 networks that are a parent chain to L3s. -function getParentL2Networks() { - const parentL2Networks: { [id: string]: L2Network } = {} - - for (const chainId in l2Networks) { - const network = l2Networks[chainId] - - if (network.partnerChainIDs && network.partnerChainIDs.length > 0) { - parentL2Networks[chainId] = network - } - } - - return parentL2Networks -} - -export const l1Networks: L1Networks = { - 1: { - chainID: 1, - name: 'Mainnet', - explorerUrl: 'https://etherscan.io', - partnerChainIDs: [42161, 42170], - blockTime: 14, - isCustom: false, - isArbitrum: false, - }, - 1338: { - chainID: 1338, - name: 'Hardhat_Mainnet_Fork', - explorerUrl: 'https://etherscan.io', - partnerChainIDs: [42161], - blockTime: 1, - isCustom: false, - isArbitrum: false, - }, - 5: { - blockTime: 15, - chainID: 5, - explorerUrl: 'https://goerli.etherscan.io', - isCustom: false, - name: 'Goerli', - partnerChainIDs: [421613], - isArbitrum: false, - }, - // If L2 network is a parent chain to L3 network, it's part of the L1 list. - ...getParentL2Networks(), -} - export const l2Networks: L2Networks = { 42161: { chainID: 42161, @@ -272,6 +225,53 @@ export const l2Networks: L2Networks = { }, } +// L2 networks that are a parent chain to L3s. +function getParentL2Networks(networks: L2Networks) { + const parentL2Networks: { [id: string]: L2Network } = {} + + Object.keys(networks).map(chainId => { + const network = l2Networks[Number(chainId)] + + if (network.partnerChainIDs && network.partnerChainIDs.length > 0) { + parentL2Networks[chainId] = network + } + }) + + return parentL2Networks +} + +export const l1Networks: L1Networks = { + 1: { + chainID: 1, + name: 'Mainnet', + explorerUrl: 'https://etherscan.io', + partnerChainIDs: [42161, 42170], + blockTime: 14, + isCustom: false, + isArbitrum: false, + }, + 1338: { + chainID: 1338, + name: 'Hardhat_Mainnet_Fork', + explorerUrl: 'https://etherscan.io', + partnerChainIDs: [42161], + blockTime: 1, + isCustom: false, + isArbitrum: false, + }, + 5: { + blockTime: 15, + chainID: 5, + explorerUrl: 'https://goerli.etherscan.io', + isCustom: false, + name: 'Goerli', + partnerChainIDs: [421613], + isArbitrum: false, + }, + // If L2 network is a parent chain to L3 network, it's part of the L1 list. + ...getParentL2Networks(l2Networks), +} + const getNetwork = async ( signerOrProviderOrChainID: SignerOrProvider | number, layer: 1 | 2 @@ -306,7 +306,11 @@ const getNetwork = async ( ) } - throw new ArbSdkError(`Unrecognized network ${chainID}.`) + throw new ArbSdkError( + `Unrecognized L${layer} network ${chainID}. L1 Networks: ${Object.keys( + l1Networks + )}` + ) } export const getL1Network = ( From 50820b061f9d9c9d5b590559329f52ed7db4a09d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Thu, 10 Aug 2023 13:21:12 +0200 Subject: [PATCH 043/192] try thing --- hardhat.nitro-contracts-abigen.ts | 18 +++ hardhat.token-bridge-contracts-abigen.ts | 18 +++ package.json | 4 +- scripts/genAbi.js | 13 +- yarn.lock | 145 ++++++++++++----------- 5 files changed, 123 insertions(+), 75 deletions(-) create mode 100644 hardhat.nitro-contracts-abigen.ts create mode 100644 hardhat.token-bridge-contracts-abigen.ts diff --git a/hardhat.nitro-contracts-abigen.ts b/hardhat.nitro-contracts-abigen.ts new file mode 100644 index 0000000000..247cefbbce --- /dev/null +++ b/hardhat.nitro-contracts-abigen.ts @@ -0,0 +1,18 @@ +/** + * @type import('hardhat/config').HardhatUserConfig + */ +module.exports = { + solidity: { + version: '0.8.9', + settings: { + optimizer: { + enabled: true, + runs: 100, + }, + }, + }, + paths: { + sources: './src', + artifacts: './build/contracts', + }, +} diff --git a/hardhat.token-bridge-contracts-abigen.ts b/hardhat.token-bridge-contracts-abigen.ts new file mode 100644 index 0000000000..49b79a2a4c --- /dev/null +++ b/hardhat.token-bridge-contracts-abigen.ts @@ -0,0 +1,18 @@ +/** + * @type import('hardhat/config').HardhatUserConfig + */ +module.exports = { + solidity: { + version: '0.8.17', + settings: { + optimizer: { + enabled: true, + runs: 100, + }, + }, + }, + paths: { + sources: './contracts', + artifacts: './build/contracts', + }, +} diff --git a/package.json b/package.json index 618342bdec..90cd062b01 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,8 @@ "ethers": "^5.1.0" }, "devDependencies": { - "@arbitrum/nitro-contracts": "1.0.1", + "@arbitrum/nitro-contracts": "1.1.0-alpha.3", + "@arbitrum/token-bridge-contracts": "1.1.0-alpha.1", "@nomiclabs/hardhat-ethers": "^2.0.4", "@typechain/ethers-v5": "9.0.0", "@types/chai": "^4.2.11", @@ -64,7 +65,6 @@ "@typescript-eslint/eslint-plugin": "^5.14.0", "@typescript-eslint/eslint-plugin-tslint": "^5.27.1", "@typescript-eslint/parser": "^5.14.0", - "arb-bridge-peripherals": "1.0.10", "audit-ci": "^6.3.0", "axios": "^0.21.3", "chai": "^4.2.0", diff --git a/scripts/genAbi.js b/scripts/genAbi.js index d388a043a9..bb9ede52ab 100644 --- a/scripts/genAbi.js +++ b/scripts/genAbi.js @@ -27,13 +27,22 @@ async function main() { // https://yarnpkg.com/advanced/rulebook#packages-should-never-write-inside-their-own-folder-outside-of-postinstall // instead of writing in postinstall in each of those packages, we should target a local folder in sdk's postinstall + // copy the hardhat config to nitro-contracts + execSync( + `cp ${cwd}/hardhat.nitro-contracts-abigen.ts ${nitroPath}/hardhat-abigen.ts` + ) + // copy the hardhat config to token-bridge-contracts + execSync( + `cp ${cwd}/hardhat.token-bridge-contracts-abigen.ts ${tokenBridgePath}/hardhat-abigen.ts` + ) + console.log('building @arbitrum/nitro-contracts') - execSync(`${npmExec} run build`, { + execSync(`${npmExec} run build --config hardhat-abigen.ts`, { cwd: nitroPath, }) console.log('building @arbitrum/token-bridge-contracts') - execSync(`${npmExec} run build`, { + const x = execSync(`${npmExec} run build --config hardhat-abigen.ts`, { cwd: tokenBridgePath, }) diff --git a/yarn.lock b/yarn.lock index 0e161db0b4..20424d2d76 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,18 +15,51 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" -"@arbitrum/nitro-contracts@1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@arbitrum/nitro-contracts/-/nitro-contracts-1.0.1.tgz#c9b4e6fba7375f8a2e7cacbda8248870bef2d61b" - integrity sha512-gNbtcWM1QkLPYs4nxvC1RnoHqcg+uk6acs11C4XRcp77vC72izpQ1+4GqMVXV6viHCNquz8posG9rButW8nHFw== +"@arbitrum/nitro-contracts@1.1.0-alpha.3": + version "1.1.0-alpha.3" + resolved "https://registry.yarnpkg.com/@arbitrum/nitro-contracts/-/nitro-contracts-1.1.0-alpha.3.tgz#4aeedcc5f3861df7ecb439406286a93edd2e0775" + integrity sha512-2f25dVcLP2kOr+MJkKBlQ1sQeZ++Hj1uoUWcGD+kI8Dr7BMO5vgIQR0bdYNXG1Eat8omW0y9mKVtRoXzxPGqJQ== + dependencies: + "@arbitrum/sdk" "^3.1.3" + "@ethersproject/providers" "^5.7.2" + "@openzeppelin/contracts" "4.5.0" + "@openzeppelin/contracts-upgradeable" "4.5.2" + patch-package "^6.4.7" + optionalDependencies: + sol2uml "2.2.0" + +"@arbitrum/nitro-contracts@^1.0.0-beta.8": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@arbitrum/nitro-contracts/-/nitro-contracts-1.0.2.tgz#c73dce72b8f969a5909d5aaef6da80681c667475" + integrity sha512-Y+cXIQNsy9UNANnFrDGKhHzNmOWttxpXP0/uJFTRmS+rgS4ozlr81/UmBo3tX6uWjziDtquhYRuG3wx17talOQ== dependencies: "@openzeppelin/contracts" "4.5.0" "@openzeppelin/contracts-upgradeable" "4.5.2" - hardhat "^2.6.6" patch-package "^6.4.7" optionalDependencies: sol2uml "2.2.0" +"@arbitrum/sdk@^3.1.3": + version "3.1.6" + resolved "https://registry.yarnpkg.com/@arbitrum/sdk/-/sdk-3.1.6.tgz#0a64728040bd1ca4d6d8024946991308d248f303" + integrity sha512-wY7RHmvY26yc/OuwpJY+kjgAmUJZDGDXaQxfSQTp2t6sSFO+8oFFVsKIthBCY2RpDGFUVTGpRjCUEXiuJ6/SFA== + dependencies: + "@ethersproject/address" "^5.0.8" + "@ethersproject/bignumber" "^5.1.1" + "@ethersproject/bytes" "^5.0.8" + ethers "^5.1.0" + +"@arbitrum/token-bridge-contracts@1.1.0-alpha.1": + version "1.1.0-alpha.1" + resolved "https://registry.yarnpkg.com/@arbitrum/token-bridge-contracts/-/token-bridge-contracts-1.1.0-alpha.1.tgz#35ef4101445f510787352c4aea42939376ead951" + integrity sha512-NZiIrpozPc0ZhkEG2+RNqWdNH56qAaZau5WGhoIkazLhlRL6R/iLx2HGtcYzq0RrorD0KagDn7voX4rOumL5fQ== + dependencies: + "@arbitrum/nitro-contracts" "^1.0.0-beta.8" + "@openzeppelin/contracts" "4.8.3" + "@openzeppelin/contracts-upgradeable" "4.8.3" + optionalDependencies: + "@openzeppelin/upgrades-core" "^1.24.1" + "@babel/code-frame@7.12.11": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" @@ -711,7 +744,7 @@ bech32 "1.1.4" ws "7.4.6" -"@ethersproject/providers@5.7.2": +"@ethersproject/providers@5.7.2", "@ethersproject/providers@^5.7.2": version "5.7.2" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb" integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg== @@ -1085,44 +1118,39 @@ resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.0.5.tgz#131b0da1b71680d5a01569f916ae878229d326d3" integrity sha512-A2gZAGB6kUvLx+kzM92HKuUF33F1FSe90L0TmkXkT2Hh0OKRpvWZURUSU2nghD2yC4DzfEZ3DftfeHGvZ2JTUw== -"@openzeppelin/contracts-0.8@npm:@openzeppelin/contracts@^4.3.2": - version "4.6.0" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.6.0.tgz#c91cf64bc27f573836dba4122758b4743418c1b3" - integrity sha512-8vi4d50NNya/bQqCmaVzvHNmwHvS0OBKb7HNtuNwEE3scXWrP31fKQoGxNMT+KbzmrNZzatE3QK5p2gFONI/hg== - -"@openzeppelin/contracts-upgradeable@3.4.2": - version "3.4.2" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-3.4.2.tgz#2c2a1b0fa748235a1f495b6489349776365c51b3" - integrity sha512-mDlBS17ymb2wpaLcrqRYdnBAmP1EwqhOXMvqWk2c5Q1N1pm5TkiCtXM9Xzznh4bYsQBq0aIWEkFFE2+iLSN1Tw== - "@openzeppelin/contracts-upgradeable@4.5.2": version "4.5.2" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.5.2.tgz#90d9e47bacfd8693bfad0ac8a394645575528d05" integrity sha512-xgWZYaPlrEOQo3cBj97Ufiuv79SPd8Brh4GcFYhPgb6WvAq4ppz8dWKL6h+jLAK01rUqMRp/TS9AdXgAeNvCLA== -"@openzeppelin/contracts@3.4.2": - version "3.4.2" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.2.tgz#d81f786fda2871d1eb8a8c5a73e455753ba53527" - integrity sha512-z0zMCjyhhp4y7XKAcDAi3Vgms4T2PstwBdahiO0+9NaGICQKjynK3wduSRplTgk4LXmoO1yfDGO5RbjKYxtuxA== +"@openzeppelin/contracts-upgradeable@4.8.3": + version "4.8.3" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.8.3.tgz#6b076a7b751811b90fe3a172a7faeaa603e13a3f" + integrity sha512-SXDRl7HKpl2WDoJpn7CK/M9U4Z8gNXDHHChAKh0Iz+Wew3wu6CmFYBeie3je8V0GSXZAIYYwUktSrnW/kwVPtg== "@openzeppelin/contracts@4.5.0": version "4.5.0" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.5.0.tgz#3fd75d57de172b3743cdfc1206883f56430409cc" integrity sha512-fdkzKPYMjrRiPK6K4y64e6GzULR7R7RwxSigHS8DDp7aWDeoReqsQI+cxHV1UuhAqX69L1lAaWDxenfP+xiqzA== -"@openzeppelin/upgrades-core@^1.7.6": - version "1.14.2" - resolved "https://registry.yarnpkg.com/@openzeppelin/upgrades-core/-/upgrades-core-1.14.2.tgz#1acf6560dbe42b5e68fdffe6e6979b90fa33bb56" - integrity sha512-JkrMcsB0v6vwX+fObY+y51L3tD3BcLjNpPnKkgtsEOC1Umwt6WzvI8Gq2brmNOzFLNQqeX2xySiJTGvypqUQow== +"@openzeppelin/contracts@4.8.3": + version "4.8.3" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.8.3.tgz#cbef3146bfc570849405f59cba18235da95a252a" + integrity sha512-bQHV8R9Me8IaJoJ2vPG4rXcL7seB7YVuskr4f+f5RyOStSZetwzkWtoqDMl5erkBJy0lDRUnIR2WIkPiC0GJlg== + +"@openzeppelin/upgrades-core@^1.24.1": + version "1.28.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/upgrades-core/-/upgrades-core-1.28.0.tgz#19405f272dc09e766c756d9d149cbd680168aef7" + integrity sha512-8RKlyg98Adv+46GxDaR0awL3R8bVCcQ27DcSEwrgWOp6siHh8sZg4a2l+2dhPl1510S6uBfhHSydMH5VX2BV5g== dependencies: - bn.js "^5.1.2" - cbor "^8.0.0" + cbor "^9.0.0" chalk "^4.1.0" - compare-versions "^4.0.0" + compare-versions "^6.0.0" debug "^4.1.1" ethereumjs-util "^7.0.3" + minimist "^1.2.7" proper-lockfile "^4.1.1" - solidity-ast "^0.4.15" + solidity-ast "^0.4.26" "@sentry/core@5.30.0": version "5.30.0" @@ -1639,36 +1667,6 @@ append-transform@^2.0.0: dependencies: default-require-extensions "^3.0.0" -arb-bridge-eth@0.7.7: - version "0.7.7" - resolved "https://registry.yarnpkg.com/arb-bridge-eth/-/arb-bridge-eth-0.7.7.tgz#9a6251c3000701b94770f97a33d5074bd8587536" - integrity sha512-2acKHyMF40G668Tz3Akqw2ERiur5BDbUxQ8ZG31can/HA0cMpU14wCP7vSPIO3BkD0x9wPhqG1kxTrSrkRYqzA== - dependencies: - "@openzeppelin/contracts" "3.4.2" - "@openzeppelin/contracts-0.8" "npm:@openzeppelin/contracts@^4.3.2" - "@openzeppelin/contracts-upgradeable" "3.4.2" - hardhat "^2.6.1" - -arb-bridge-peripherals@1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/arb-bridge-peripherals/-/arb-bridge-peripherals-1.0.10.tgz#9b400331352b626d4fdec074d46a9e1cef5c5fcf" - integrity sha512-iNhpbwN22Bco3DM5y8NJDMiDAqoGJ0VgUD7GSPvGk6ox8otp1aYX92ZEbbIm414qS5+dhi9kv5tZrvVO6tpfZQ== - dependencies: - "@openzeppelin/contracts" "3.4.2" - "@openzeppelin/contracts-upgradeable" "3.4.2" - arb-bridge-eth "0.7.7" - arbos-precompiles "^1.0.2" - hardhat "^2.6.1" - optionalDependencies: - "@openzeppelin/upgrades-core" "^1.7.6" - -arbos-precompiles@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/arbos-precompiles/-/arbos-precompiles-1.0.2.tgz#7bebd5963aef972cd259eb41f3116ea065013ea6" - integrity sha512-1dOFYFJUN0kKoofh6buZJ8qCqTs+oLGSsGzHI0trA/Pka/TCERflCRsNVxez2lihOvK7MT/a2RA8AepKtBXdPQ== - dependencies: - hardhat "^2.6.4" - archy@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" @@ -1979,10 +1977,10 @@ caniuse-lite@^1.0.30001332: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001334.tgz#892e9965b35285033fc2b8a8eff499fe02f13d8b" integrity sha512-kbaCEBRRVSoeNs74sCuq92MJyGrMtjWVfhltoHUCW4t4pXFvGjUBrfo47weBRViHkiV3eBYyIsfl956NtHGazw== -cbor@^8.0.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.1.0.tgz#cfc56437e770b73417a2ecbfc9caf6b771af60d5" - integrity sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg== +cbor@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-9.0.1.tgz#b16e393d4948d44758cd54ac6151379d443b37ae" + integrity sha512-/TQOWyamDxvVIv+DY9cOLNuABkoyz8K/F3QE56539pGVYohx0+MEA1f4lChFTX79dBTBS7R1PF6ovH7G+VtBfQ== dependencies: nofilter "^3.1.0" @@ -2178,10 +2176,10 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= -compare-versions@^4.0.0: - version "4.1.3" - resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-4.1.3.tgz#8f7b8966aef7dc4282b45dfa6be98434fc18a1a4" - integrity sha512-WQfnbDcrYnGr55UwbxKiQKASnTtNnaAWVi8jZyy8NTpVAXWACSne8lMD1iaIo9AiU6mnuLvSVshCzewVuWxHUg== +compare-versions@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-6.1.0.tgz#3f2131e3ae93577df111dba133e6db876ffe127a" + integrity sha512-LNZQXhqUvqUTotpZ00qLSaify3b4VFD588aRr8MKFw4CMUr98ytzCW5wDH5qx/DEY5kCDXcbcRuCqL0szEf2tg== concat-map@0.0.1: version "0.0.1" @@ -3209,7 +3207,7 @@ handlebars@^4.7.7: optionalDependencies: uglify-js "^3.1.4" -hardhat@^2.6.1, hardhat@^2.6.4, hardhat@^2.6.6, hardhat@^2.8.3: +hardhat@^2.8.3: version "2.9.3" resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.9.3.tgz#4759dc3c468c7d15f34334ca1be7d59b04e47b1e" integrity sha512-7Vw99RbYbMZ15UzegOR/nqIYIqddZXvLwJGaX5sX4G5bydILnbjmDU6g3jMKJSiArEixS3vHAEaOs5CW1JQ3hg== @@ -4036,6 +4034,11 @@ minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== +minimist@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + mkdirp-classic@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" @@ -4899,10 +4902,10 @@ solc@0.7.3: semver "^5.5.0" tmp "0.0.33" -solidity-ast@^0.4.15: - version "0.4.32" - resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.32.tgz#ba613ca24c7c79007798033e8a0f32a71285f09e" - integrity sha512-vCx17410X+NMnpLVyg6ix4NMCHFIkvWrJb1rPBBeQYEQChX93Zgb9WB9NaIY4zpsr3Q8IvAfohw+jmuBzGf8OQ== +solidity-ast@^0.4.26: + version "0.4.49" + resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.49.tgz#ecba89d10c0067845b7848c3a3e8cc61a4fc5b82" + integrity sha512-Pr5sCAj1SFqzwFZw1HPKSq0PehlQNdM8GwKyAVYh2DOn7/cCK8LUKD1HeHnKtTgBW7hi9h4nnnan7hpAg5RhWQ== solidity-comments-extractor@^0.0.7: version "0.0.7" From f408fadeaaaed46d9656e79d644b9dd5a1f3e3d3 Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 11 Aug 2023 11:16:11 +0200 Subject: [PATCH 044/192] add parentChains, chains --- src/lib/dataEntities/networks.ts | 143 ++++++++++++++++++++++++------- 1 file changed, 113 insertions(+), 30 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index ccd7e093a3..7d81aa2d1c 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -28,14 +28,11 @@ export interface L1Network extends Network { } export interface L2Network extends Network { - /** - * An array of chain IDs representing child chains that are associated with this L2Network. - */ partnerChainIDs?: number[] tokenBridge: TokenBridge ethBridge: EthBridge partnerChainID: number - isArbitrum: boolean + isArbitrum: true confirmPeriodBlocks: number retryableLifetimeSeconds: number nitroGenesisBlock: number @@ -45,6 +42,15 @@ export interface L2Network extends Network { */ depositTimeout: number } + +export type ParentChain = + | L1Network + | (L2Network & { + partnerChainIDs: number[] + }) + +export type Chain = Omit + export interface Network { chainID: number name: string @@ -82,13 +88,21 @@ export interface EthBridge { } export interface L1Networks { - [id: string]: L1Network | L2Network + [id: string]: L1Network } export interface L2Networks { [id: string]: L2Network } +export interface ParentChains { + [id: string]: ParentChain +} + +export interface Chains { + [id: string]: Chain +} + const mainnetTokenBridge: TokenBridge = { l1GatewayRouter: '0x72Ce9c846789fdB6fC1f34aC4AD25Dd9ef7031ef', l2GatewayRouter: '0x5288c571Fd7aD117beA99bF60FE0846C4E84F933', @@ -225,19 +239,40 @@ export const l2Networks: L2Networks = { }, } -// L2 networks that are a parent chain to L3s. -function getParentL2Networks(networks: L2Networks) { - const parentL2Networks: { [id: string]: L2Network } = {} +// L2 networks that are a parent chain to Orbit chains. +function getL2ParentChains(_l2Networks: L2Networks) { + const _parentChains: ParentChains = {} - Object.keys(networks).map(chainId => { + Object.keys(_l2Networks).map(chainId => { const network = l2Networks[Number(chainId)] if (network.partnerChainIDs && network.partnerChainIDs.length > 0) { - parentL2Networks[chainId] = network + _parentChains[chainId] = network as ParentChain } }) - return parentL2Networks + return _parentChains +} + +// L2 or Orbit chains that are not a parent chain to other Orbit chains. +function getChains(_l2Networks: L2Networks) { + const _chains: Chains = {} + + Object.keys(_l2Networks).map(chainId => { + const network = l2Networks[Number(chainId)] + + if (network.partnerChainIDs && network.partnerChainIDs.length === 0) { + // If we get an empty array, it means it's a valid Chain. + // We don't store it to keep it consistent with the Chain type. + delete network.partnerChainIDs + } + + if (!network.partnerChainIDs) { + _chains[chainId] = network + } + }) + + return _chains } export const l1Networks: L1Networks = { @@ -268,10 +303,15 @@ export const l1Networks: L1Networks = { partnerChainIDs: [421613], isArbitrum: false, }, - // If L2 network is a parent chain to L3 network, it's part of the L1 list. - ...getParentL2Networks(l2Networks), } +export const parentChains: ParentChains = { + ...l1Networks, + ...getL2ParentChains(l2Networks), +} + +export const chains: Chains = getChains(l2Networks) + const getNetwork = async ( signerOrProviderOrChainID: SignerOrProvider | number, layer: 1 | 2 @@ -292,25 +332,10 @@ const getNetwork = async ( const network = networks[chainID] if (network) { - if (layer === 2) { - return network - } - - // Any network that's a parent chain is valid - if (network.partnerChainIDs && network.partnerChainIDs.length > 0) { - return network - } - - throw new ArbSdkError( - `Expected 'partnerChainIDs' to include at least one L2 network. Got empty for network ${chainID}.` - ) + return network } - throw new ArbSdkError( - `Unrecognized L${layer} network ${chainID}. L1 Networks: ${Object.keys( - l1Networks - )}` - ) + throw new ArbSdkError(`Unrecognized network ${chainID}`) } export const getL1Network = ( @@ -324,6 +349,64 @@ export const getL2Network = ( return getNetwork(signerOrProviderOrChainID, 2) as Promise } +/** + * Returns a chain that is associated with at least one child chain + * @param signerOrProviderOrChainID + * @returns Chain that is associated with at least one child chain + */ +export const getParentChain = async ( + signerOrProviderOrChainID: SignerOrProvider | number +): Promise => { + const chainID = await (async () => { + if (typeof signerOrProviderOrChainID === 'number') { + return signerOrProviderOrChainID + } + const provider = SignerProviderUtils.getProviderOrThrow( + signerOrProviderOrChainID + ) + + const { chainId } = await provider.getNetwork() + return chainId + })() + + const chain = parentChains[chainID] + + if (chain) { + return chain + } + + throw new ArbSdkError(`Unrecognized parent chain ${chainID}`) +} + +/** + * Returns a chain that is associated with a parent chain + * @param signerOrProviderOrChainID + * @returns Chain that is associated with a parent chain + */ +export const getChain = async ( + signerOrProviderOrChainID: SignerOrProvider | number +): Promise => { + const chainID = await (async () => { + if (typeof signerOrProviderOrChainID === 'number') { + return signerOrProviderOrChainID + } + const provider = SignerProviderUtils.getProviderOrThrow( + signerOrProviderOrChainID + ) + + const { chainId } = await provider.getNetwork() + return chainId + })() + + const chain = chains[chainID] + + if (chain) { + return chain + } + + throw new ArbSdkError(`Unrecognized chain ${chainID}`) +} + /** * Returns the addresses of all contracts that make up the ETH bridge * @param rollupContractAddress Address of the Rollup contract From 0b030c291b3270ba81be194feb06cb12515988d1 Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 11 Aug 2023 11:18:41 +0200 Subject: [PATCH 045/192] reduce diff --- src/lib/dataEntities/networks.ts | 60 ++++++++++++++++---------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 7d81aa2d1c..19d5a24f27 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -132,6 +132,36 @@ const mainnetETHBridge: EthBridge = { }, } +export const l1Networks: L1Networks = { + 1: { + chainID: 1, + name: 'Mainnet', + explorerUrl: 'https://etherscan.io', + partnerChainIDs: [42161, 42170], + blockTime: 14, + isCustom: false, + isArbitrum: false, + }, + 1338: { + chainID: 1338, + name: 'Hardhat_Mainnet_Fork', + explorerUrl: 'https://etherscan.io', + partnerChainIDs: [42161], + blockTime: 1, + isCustom: false, + isArbitrum: false, + }, + 5: { + blockTime: 15, + chainID: 5, + explorerUrl: 'https://goerli.etherscan.io', + isCustom: false, + name: 'Goerli', + partnerChainIDs: [421613], + isArbitrum: false, + }, +} + export const l2Networks: L2Networks = { 42161: { chainID: 42161, @@ -275,36 +305,6 @@ function getChains(_l2Networks: L2Networks) { return _chains } -export const l1Networks: L1Networks = { - 1: { - chainID: 1, - name: 'Mainnet', - explorerUrl: 'https://etherscan.io', - partnerChainIDs: [42161, 42170], - blockTime: 14, - isCustom: false, - isArbitrum: false, - }, - 1338: { - chainID: 1338, - name: 'Hardhat_Mainnet_Fork', - explorerUrl: 'https://etherscan.io', - partnerChainIDs: [42161], - blockTime: 1, - isCustom: false, - isArbitrum: false, - }, - 5: { - blockTime: 15, - chainID: 5, - explorerUrl: 'https://goerli.etherscan.io', - isCustom: false, - name: 'Goerli', - partnerChainIDs: [421613], - isArbitrum: false, - }, -} - export const parentChains: ParentChains = { ...l1Networks, ...getL2ParentChains(l2Networks), From 2fd86ecc2103a682f736cfe0d0b2f6b716a02ef4 Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 11 Aug 2023 11:21:11 +0200 Subject: [PATCH 046/192] reduce diff --- src/lib/dataEntities/networks.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 19d5a24f27..f93be6a8ee 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -329,13 +329,11 @@ const getNetwork = async ( })() const networks = layer === 1 ? l1Networks : l2Networks - const network = networks[chainID] - - if (network) { - return network + if (networks[chainID]) { + return networks[chainID] + } else { + throw new ArbSdkError(`Unrecognized network ${chainID}.`) } - - throw new ArbSdkError(`Unrecognized network ${chainID}`) } export const getL1Network = ( From 8205daa4c0da0af51f2c8a822ed85141db42a37a Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 11 Aug 2023 11:25:32 +0200 Subject: [PATCH 047/192] clean up --- src/lib/assetBridger/assetBridger.ts | 4 ++-- src/lib/dataEntities/networks.ts | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/lib/assetBridger/assetBridger.ts b/src/lib/assetBridger/assetBridger.ts index b7bda7547e..8dec112f9f 100644 --- a/src/lib/assetBridger/assetBridger.ts +++ b/src/lib/assetBridger/assetBridger.ts @@ -20,7 +20,7 @@ import { ArbSdkError } from '../dataEntities/errors' import { L1ContractTransaction } from '../message/L1Transaction' import { L2ContractTransaction } from '../message/L2Transaction' -import { l1Networks, L1Network, L2Network } from '../dataEntities/networks' +import { l1Networks, L2Network, ParentChain } from '../dataEntities/networks' import { SignerOrProvider, SignerProviderUtils, @@ -30,7 +30,7 @@ import { * Base for bridging assets from l1 to l2 and back */ export abstract class AssetBridger { - public readonly l1Network: L1Network | L2Network + public readonly l1Network: ParentChain public constructor(public readonly l2Network: L2Network) { this.l1Network = l1Networks[l2Network.partnerChainID] diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index f93be6a8ee..f86139fade 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -551,5 +551,8 @@ export const addDefaultLocalNetwork = (): { export const isL1Network = ( network: L1Network | L2Network ): network is L1Network => { - return (network as L1Network).partnerChainIDs.length > 0 + if (!network.partnerChainIDs) { + return false + } + return network.partnerChainIDs.length > 0 } From 2954097c07077ca63eb8f10a801f0ce58e246c8a Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 11 Aug 2023 11:31:59 +0200 Subject: [PATCH 048/192] clean up --- src/lib/dataEntities/networks.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index f86139fade..db0a749a54 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -45,9 +45,7 @@ export interface L2Network extends Network { export type ParentChain = | L1Network - | (L2Network & { - partnerChainIDs: number[] - }) + | (L2Network & Required>) export type Chain = Omit From 96b7c23ccdb69176d4264a6089dfdafa8ef17d23 Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 11 Aug 2023 12:16:00 +0200 Subject: [PATCH 049/192] fix addCustomNetwork --- src/lib/dataEntities/networks.ts | 75 ++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index db0a749a54..7f873abfb6 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -434,49 +434,68 @@ export const getEthBridgeInformation = async ( } } +/** + * Adds custom L1 and L2 networks. + * @param customL1Network Custom L1 network that can also be an Arbitrum chain. + * If `isArbitrum` is set to `true`, this will be added to `l1Networks` and can be fetched with `getL1Network`. + * If `isArbitrum` is set to `false`, this will be added to `parentChains` and can be fetched with `getParentChain`. + * @param customL2Network Custom L2 network, Arbitrum chain. + */ export const addCustomNetwork = ({ customL1Network, customL2Network, }: { - customL1Network?: L1Network - customL2Network: L2Network + customL1Network?: ParentChain + customL2Network: Chain }): void => { - if (customL1Network) { - if (l1Networks[customL1Network.chainID]) { - throw new ArbSdkError( - `Network ${customL1Network.chainID} already included` - ) - } else if (!customL1Network.isCustom) { - throw new ArbSdkError( - `Custom network ${customL1Network.chainID} must have isCustom flag set to true` - ) - } else { - l1Networks[customL1Network.chainID] = customL1Network - } + if (!customL2Network.isCustom) { + throw new ArbSdkError( + `Custom network ${customL2Network.chainID} must have isCustom flag set to true.` + ) } - if (l2Networks[customL2Network.chainID]) + const isAddedToL2Networks = Boolean(l2Networks[customL2Network.chainID]) + const isAddedToChains = Boolean(chains[customL2Network.chainID]) + + if (isAddedToL2Networks && isAddedToChains) { throw new ArbSdkError(`Network ${customL2Network.chainID} already included`) - else if (!customL2Network.isCustom) { - throw new ArbSdkError( - `Custom network ${customL2Network.chainID} must have isCustom flag set to true` - ) } - l2Networks[customL2Network.chainID] = customL2Network + const l1PartnerChain = parentChains[customL2Network.partnerChainID] + const isCustomL1ParentToCustomL2 = + customL2Network.partnerChainID === customL1Network?.chainID - const l1PartnerChain = l1Networks[customL2Network.partnerChainID] - if (!l1PartnerChain) + if (typeof l1PartnerChain === 'undefined' && !isCustomL1ParentToCustomL2) { throw new ArbSdkError( - `Network ${customL2Network.chainID}'s partner network, ${customL2Network.partnerChainID}, not recognized` + `Network ${customL2Network.chainID}'s partner network, ${customL2Network.partnerChainID}, not recognized.` ) + } - if ( - l1PartnerChain.partnerChainIDs && - !l1PartnerChain.partnerChainIDs.includes(customL2Network.chainID) - ) { - l1PartnerChain.partnerChainIDs.push(customL2Network.chainID) + if (customL1Network) { + const isAddedToL1Networks = + typeof l1Networks[customL1Network.chainID] !== 'undefined' + const isAddedToParentChains = + typeof parentChains[customL1Network.chainID] !== 'undefined' + const { isArbitrum } = customL1Network + + if (isArbitrum) { + if (isAddedToParentChains) { + throw new ArbSdkError( + `Network ${customL1Network.chainID} is already registered as a ParentChain. If you intended to add it as an L1Network, please set 'isArbitrum' to false.` + ) + } + parentChains[customL1Network.chainID] = customL1Network + } else { + if (isAddedToL1Networks) { + throw new ArbSdkError( + `Network ${customL1Network.chainID} is already registered as an L1Network. If you intended to add it as a ParentChain, please set 'isArbitrum' to true.` + ) + } + l1Networks[customL1Network.chainID] = customL1Network + } } + + l2Networks[customL2Network.chainID] = customL2Network } /** From 636572822969391bb7029d79f7797adbc46468c7 Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 11 Aug 2023 12:21:16 +0200 Subject: [PATCH 050/192] comment --- src/lib/dataEntities/networks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 7f873abfb6..6565c6e6d3 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -291,7 +291,7 @@ function getChains(_l2Networks: L2Networks) { if (network.partnerChainIDs && network.partnerChainIDs.length === 0) { // If we get an empty array, it means it's a valid Chain. - // We don't store it to keep it consistent with the Chain type. + // We remove the property to make it consistent with the Chain type. delete network.partnerChainIDs } From 2100d07c92b6f8315594d5fcf04cbd0dc78045c5 Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 11 Aug 2023 12:35:05 +0200 Subject: [PATCH 051/192] fix comments and addCustomNetwork --- src/lib/dataEntities/networks.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 6565c6e6d3..6048742015 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -437,9 +437,9 @@ export const getEthBridgeInformation = async ( /** * Adds custom L1 and L2 networks. * @param customL1Network Custom L1 network that can also be an Arbitrum chain. - * If `isArbitrum` is set to `true`, this will be added to `l1Networks` and can be fetched with `getL1Network`. - * If `isArbitrum` is set to `false`, this will be added to `parentChains` and can be fetched with `getParentChain`. - * @param customL2Network Custom L2 network, Arbitrum chain. + * If `isArbitrum` is set to `false`, this will be added to both `l1Networks` and `parentChains`, and can be fetched with either `getL1Network` or `getParentChain`. + * If `isArbitrum` is set to `true`, this will be only added to `parentChains`, and can be fetched with `getParentChain`. + * @param customL2Network Custom L2 network, an Arbitrum chain. */ export const addCustomNetwork = ({ customL1Network, @@ -486,12 +486,19 @@ export const addCustomNetwork = ({ } parentChains[customL1Network.chainID] = customL1Network } else { - if (isAddedToL1Networks) { + if (isAddedToL1Networks && isAddedToParentChains) { throw new ArbSdkError( - `Network ${customL1Network.chainID} is already registered as an L1Network. If you intended to add it as a ParentChain, please set 'isArbitrum' to true.` + `Network ${customL1Network.chainID} is already registered as an L1Network and a ParentChain.` ) } - l1Networks[customL1Network.chainID] = customL1Network + // Make sure not to do any 'return' and 'throw' statements after adding an L1 network. + // Otherwise it could lead to an edge case where L1 is added but L2 throws. + if (!isAddedToL1Networks) { + l1Networks[customL1Network.chainID] = customL1Network + } + if (!isAddedToParentChains) { + parentChains[customL1Network.chainID] = customL1Network + } } } From 413f9e5813d5d9873ffde04caf83abaf3da9cede Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 11 Aug 2023 12:37:32 +0200 Subject: [PATCH 052/192] clean up --- src/lib/dataEntities/networks.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 6048742015..9dff415851 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -454,8 +454,9 @@ export const addCustomNetwork = ({ ) } - const isAddedToL2Networks = Boolean(l2Networks[customL2Network.chainID]) - const isAddedToChains = Boolean(chains[customL2Network.chainID]) + const isAddedToL2Networks = + typeof l2Networks[customL2Network.chainID] !== 'undefined' + const isAddedToChains = typeof chains[customL2Network.chainID] !== 'undefined' if (isAddedToL2Networks && isAddedToChains) { throw new ArbSdkError(`Network ${customL2Network.chainID} already included`) From 67e49c5960d1f4ed999f10dc0520e3cebc07c586 Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 11 Aug 2023 12:48:47 +0200 Subject: [PATCH 053/192] var name --- src/lib/dataEntities/networks.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 9dff415851..9826aef27a 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -462,11 +462,12 @@ export const addCustomNetwork = ({ throw new ArbSdkError(`Network ${customL2Network.chainID} already included`) } - const l1PartnerChain = parentChains[customL2Network.partnerChainID] + const hasParentChain = + typeof parentChains[customL2Network.partnerChainID] !== 'undefined' const isCustomL1ParentToCustomL2 = customL2Network.partnerChainID === customL1Network?.chainID - if (typeof l1PartnerChain === 'undefined' && !isCustomL1ParentToCustomL2) { + if (!hasParentChain && !isCustomL1ParentToCustomL2) { throw new ArbSdkError( `Network ${customL2Network.chainID}'s partner network, ${customL2Network.partnerChainID}, not recognized.` ) From 7571156fb1963171cd3067b67c13e60cc991b5b8 Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 11 Aug 2023 13:03:38 +0200 Subject: [PATCH 054/192] fix --- src/lib/dataEntities/networks.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 9826aef27a..b23d93a670 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -504,7 +504,12 @@ export const addCustomNetwork = ({ } } - l2Networks[customL2Network.chainID] = customL2Network + if (!isAddedToL2Networks) { + l2Networks[customL2Network.chainID] = customL2Network + } + if (!isAddedToChains) { + chains[customL2Network.chainID] = customL2Network + } } /** From c086850ddf8538970a39ecc9b43a204b1920f357 Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 11 Aug 2023 14:33:46 +0200 Subject: [PATCH 055/192] export types and methods --- src/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/index.ts b/src/index.ts index 70fa6dc5c7..8c13ea781b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -47,8 +47,12 @@ export { L2Networks, L1Network, L2Network, + Chain, + ParentChain, getL1Network, getL2Network, + getChain, + getParentChain, addCustomNetwork, addDefaultLocalNetwork, } from './lib/dataEntities/networks' From 6f78538908abd5a6df4a86306e061b1da73f167e Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 11 Aug 2023 15:45:44 +0200 Subject: [PATCH 056/192] fixes --- src/lib/dataEntities/networks.ts | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index b23d93a670..7624e330b3 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -47,7 +47,7 @@ export type ParentChain = | L1Network | (L2Network & Required>) -export type Chain = Omit +export type Chain = L2Network export interface Network { chainID: number @@ -183,9 +183,6 @@ export const l2Networks: L2Networks = { }, 421613: { chainID: 421613, - partnerChainIDs: [ - // Xai Goerli goes here - ], confirmPeriodBlocks: 20, retryableLifetimeSeconds: SEVEN_DAYS_IN_SECONDS, ethBridge: { @@ -282,33 +279,12 @@ function getL2ParentChains(_l2Networks: L2Networks) { return _parentChains } -// L2 or Orbit chains that are not a parent chain to other Orbit chains. -function getChains(_l2Networks: L2Networks) { - const _chains: Chains = {} - - Object.keys(_l2Networks).map(chainId => { - const network = l2Networks[Number(chainId)] - - if (network.partnerChainIDs && network.partnerChainIDs.length === 0) { - // If we get an empty array, it means it's a valid Chain. - // We remove the property to make it consistent with the Chain type. - delete network.partnerChainIDs - } - - if (!network.partnerChainIDs) { - _chains[chainId] = network - } - }) - - return _chains -} - export const parentChains: ParentChains = { ...l1Networks, ...getL2ParentChains(l2Networks), } -export const chains: Chains = getChains(l2Networks) +export const chains: Chains = l2Networks const getNetwork = async ( signerOrProviderOrChainID: SignerOrProvider | number, From 796c64b248bdda8d5c00e0deb25791b4b111f948 Mon Sep 17 00:00:00 2001 From: Bartek Date: Fri, 11 Aug 2023 16:26:30 +0200 Subject: [PATCH 057/192] change bridger type for l1network --- src/lib/assetBridger/assetBridger.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/assetBridger/assetBridger.ts b/src/lib/assetBridger/assetBridger.ts index 8dec112f9f..ea3f72162b 100644 --- a/src/lib/assetBridger/assetBridger.ts +++ b/src/lib/assetBridger/assetBridger.ts @@ -20,7 +20,7 @@ import { ArbSdkError } from '../dataEntities/errors' import { L1ContractTransaction } from '../message/L1Transaction' import { L2ContractTransaction } from '../message/L2Transaction' -import { l1Networks, L2Network, ParentChain } from '../dataEntities/networks' +import { l1Networks, L1Network, L2Network } from '../dataEntities/networks' import { SignerOrProvider, SignerProviderUtils, @@ -30,7 +30,7 @@ import { * Base for bridging assets from l1 to l2 and back */ export abstract class AssetBridger { - public readonly l1Network: ParentChain + public readonly l1Network: L1Network public constructor(public readonly l2Network: L2Network) { this.l1Network = l1Networks[l2Network.partnerChainID] From fbc4df7d0322371c0a09fa45d964d757fe4b41ba Mon Sep 17 00:00:00 2001 From: gzeon Date: Mon, 14 Aug 2023 22:02:21 +0800 Subject: [PATCH 058/192] chore: bump contracts to 1.1.0-alpha.4 --- package.json | 2 +- yarn.lock | 22 +++++----------------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index 90cd062b01..1d0f7c6955 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "ethers": "^5.1.0" }, "devDependencies": { - "@arbitrum/nitro-contracts": "1.1.0-alpha.3", + "@arbitrum/nitro-contracts": "1.1.0-alpha.4", "@arbitrum/token-bridge-contracts": "1.1.0-alpha.1", "@nomiclabs/hardhat-ethers": "^2.0.4", "@typechain/ethers-v5": "9.0.0", diff --git a/yarn.lock b/yarn.lock index 20424d2d76..a43d681193 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,13 +15,11 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" -"@arbitrum/nitro-contracts@1.1.0-alpha.3": - version "1.1.0-alpha.3" - resolved "https://registry.yarnpkg.com/@arbitrum/nitro-contracts/-/nitro-contracts-1.1.0-alpha.3.tgz#4aeedcc5f3861df7ecb439406286a93edd2e0775" - integrity sha512-2f25dVcLP2kOr+MJkKBlQ1sQeZ++Hj1uoUWcGD+kI8Dr7BMO5vgIQR0bdYNXG1Eat8omW0y9mKVtRoXzxPGqJQ== +"@arbitrum/nitro-contracts@1.1.0-alpha.4": + version "1.1.0-alpha.4" + resolved "https://registry.yarnpkg.com/@arbitrum/nitro-contracts/-/nitro-contracts-1.1.0-alpha.4.tgz#4edc59ac9b8d6e57c73e80d21a702fa8ed6c43f7" + integrity sha512-gmMA0rAwCJx4bdDj4gqoDOq8uDyG3TnJrbwHktdPZzye0Q0e0xr1kTc6JtDEhxgEZiOoXYYVx0JnN3TU5aSwZQ== dependencies: - "@arbitrum/sdk" "^3.1.3" - "@ethersproject/providers" "^5.7.2" "@openzeppelin/contracts" "4.5.0" "@openzeppelin/contracts-upgradeable" "4.5.2" patch-package "^6.4.7" @@ -39,16 +37,6 @@ optionalDependencies: sol2uml "2.2.0" -"@arbitrum/sdk@^3.1.3": - version "3.1.6" - resolved "https://registry.yarnpkg.com/@arbitrum/sdk/-/sdk-3.1.6.tgz#0a64728040bd1ca4d6d8024946991308d248f303" - integrity sha512-wY7RHmvY26yc/OuwpJY+kjgAmUJZDGDXaQxfSQTp2t6sSFO+8oFFVsKIthBCY2RpDGFUVTGpRjCUEXiuJ6/SFA== - dependencies: - "@ethersproject/address" "^5.0.8" - "@ethersproject/bignumber" "^5.1.1" - "@ethersproject/bytes" "^5.0.8" - ethers "^5.1.0" - "@arbitrum/token-bridge-contracts@1.1.0-alpha.1": version "1.1.0-alpha.1" resolved "https://registry.yarnpkg.com/@arbitrum/token-bridge-contracts/-/token-bridge-contracts-1.1.0-alpha.1.tgz#35ef4101445f510787352c4aea42939376ead951" @@ -744,7 +732,7 @@ bech32 "1.1.4" ws "7.4.6" -"@ethersproject/providers@5.7.2", "@ethersproject/providers@^5.7.2": +"@ethersproject/providers@5.7.2": version "5.7.2" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb" integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg== From eb15634152b4d93d85016b312aab9dde55707f95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Mon, 14 Aug 2023 16:07:53 +0200 Subject: [PATCH 059/192] remove thing --- scripts/genAbi.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/genAbi.js b/scripts/genAbi.js index bb9ede52ab..87d285eb5e 100644 --- a/scripts/genAbi.js +++ b/scripts/genAbi.js @@ -42,7 +42,7 @@ async function main() { }) console.log('building @arbitrum/token-bridge-contracts') - const x = execSync(`${npmExec} run build --config hardhat-abigen.ts`, { + execSync(`${npmExec} run build --config hardhat-abigen.ts`, { cwd: tokenBridgePath, }) From b850c2e3065dcdc32e2ce072693360869ce39905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Mon, 14 Aug 2023 16:10:30 +0200 Subject: [PATCH 060/192] remove unused imporT --- tests/integration/standarderc20.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/integration/standarderc20.test.ts b/tests/integration/standarderc20.test.ts index 7900991214..55aeaf4a54 100644 --- a/tests/integration/standarderc20.test.ts +++ b/tests/integration/standarderc20.test.ts @@ -21,7 +21,6 @@ import { BigNumber } from '@ethersproject/bignumber' import { TestERC20__factory } from '../../src/lib/abi/factories/TestERC20__factory' import { fundL1, - fundL2, skipIfMainnet, depositToken, GatewayType, From 3b4364d2cb9934e0b9141ef9c314286ed07022cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Mon, 14 Aug 2023 16:15:58 +0200 Subject: [PATCH 061/192] v3.1.6-orbit-custom-fee-token.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1d0f7c6955..8fba60a785 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@arbitrum/sdk", - "version": "3.1.6", + "version": "3.1.6-orbit-custom-fee-token.0", "description": "Typescript library client-side interactions with Arbitrum", "author": "Offchain Labs, Inc.", "license": "Apache-2.0", From 100c3a8911987dc8a62dc7c81f620f24dd9c5ee7 Mon Sep 17 00:00:00 2001 From: gzeon Date: Mon, 14 Aug 2023 22:38:12 +0800 Subject: [PATCH 062/192] chore: bump contracts to 1.1.0-alpha.5 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 1d0f7c6955..24facf2231 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "ethers": "^5.1.0" }, "devDependencies": { - "@arbitrum/nitro-contracts": "1.1.0-alpha.4", + "@arbitrum/nitro-contracts": "1.1.0-alpha.5", "@arbitrum/token-bridge-contracts": "1.1.0-alpha.1", "@nomiclabs/hardhat-ethers": "^2.0.4", "@typechain/ethers-v5": "9.0.0", diff --git a/yarn.lock b/yarn.lock index a43d681193..5cefc75e9e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,10 +15,10 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" -"@arbitrum/nitro-contracts@1.1.0-alpha.4": - version "1.1.0-alpha.4" - resolved "https://registry.yarnpkg.com/@arbitrum/nitro-contracts/-/nitro-contracts-1.1.0-alpha.4.tgz#4edc59ac9b8d6e57c73e80d21a702fa8ed6c43f7" - integrity sha512-gmMA0rAwCJx4bdDj4gqoDOq8uDyG3TnJrbwHktdPZzye0Q0e0xr1kTc6JtDEhxgEZiOoXYYVx0JnN3TU5aSwZQ== +"@arbitrum/nitro-contracts@1.1.0-alpha.5": + version "1.1.0-alpha.5" + resolved "https://registry.yarnpkg.com/@arbitrum/nitro-contracts/-/nitro-contracts-1.1.0-alpha.5.tgz#2a2759d3de2c5ff04ee2f354d55b5abd78cc3c40" + integrity sha512-c70n5OM7kxkUR7tosKtIlvlDfdghJe4sxIFgUIyp9DngkZlsEm/t9I6XIVNuYRSxdlFFemRPLyzpLr/P2UNvPA== dependencies: "@openzeppelin/contracts" "4.5.0" "@openzeppelin/contracts-upgradeable" "4.5.2" From 2e76d7ed601504bea5fd03639343d919b63ab176 Mon Sep 17 00:00:00 2001 From: gzeon Date: Mon, 14 Aug 2023 22:59:13 +0800 Subject: [PATCH 063/192] chore: bump contracts to 1.1.0-alpha.7 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 24facf2231..9423c6d8b0 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "ethers": "^5.1.0" }, "devDependencies": { - "@arbitrum/nitro-contracts": "1.1.0-alpha.5", + "@arbitrum/nitro-contracts": "1.1.0-alpha.7", "@arbitrum/token-bridge-contracts": "1.1.0-alpha.1", "@nomiclabs/hardhat-ethers": "^2.0.4", "@typechain/ethers-v5": "9.0.0", diff --git a/yarn.lock b/yarn.lock index 5cefc75e9e..98c065d3d4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,10 +15,10 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" -"@arbitrum/nitro-contracts@1.1.0-alpha.5": - version "1.1.0-alpha.5" - resolved "https://registry.yarnpkg.com/@arbitrum/nitro-contracts/-/nitro-contracts-1.1.0-alpha.5.tgz#2a2759d3de2c5ff04ee2f354d55b5abd78cc3c40" - integrity sha512-c70n5OM7kxkUR7tosKtIlvlDfdghJe4sxIFgUIyp9DngkZlsEm/t9I6XIVNuYRSxdlFFemRPLyzpLr/P2UNvPA== +"@arbitrum/nitro-contracts@1.1.0-alpha.7": + version "1.1.0-alpha.7" + resolved "https://registry.yarnpkg.com/@arbitrum/nitro-contracts/-/nitro-contracts-1.1.0-alpha.7.tgz#5d215540bd2c820b79c9e3afd701238bb2adc9dd" + integrity sha512-H/wnkxg6ZVuc8wyiPSsL9T3vKE5iDhlB3TaBlAhtBFZPyyuBJBIlmDdk3PtgrzNqHrCrQloTT1kT5b9DXC21Aw== dependencies: "@openzeppelin/contracts" "4.5.0" "@openzeppelin/contracts-upgradeable" "4.5.2" From 5a864e1a5477f8d3ade24f62f3576e8dd4b46117 Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 21 Aug 2023 10:52:34 +0200 Subject: [PATCH 064/192] separate method for adding custom chains --- src/lib/assetBridger/assetBridger.ts | 16 +++- src/lib/dataEntities/networks.ts | 135 ++++++++++++++++----------- 2 files changed, 92 insertions(+), 59 deletions(-) diff --git a/src/lib/assetBridger/assetBridger.ts b/src/lib/assetBridger/assetBridger.ts index ea3f72162b..ee04e2dca9 100644 --- a/src/lib/assetBridger/assetBridger.ts +++ b/src/lib/assetBridger/assetBridger.ts @@ -20,7 +20,13 @@ import { ArbSdkError } from '../dataEntities/errors' import { L1ContractTransaction } from '../message/L1Transaction' import { L2ContractTransaction } from '../message/L2Transaction' -import { l1Networks, L1Network, L2Network } from '../dataEntities/networks' +import { + l1Networks, + L1Network, + L2Network, + ParentChain, + parentChains, +} from '../dataEntities/networks' import { SignerOrProvider, SignerProviderUtils, @@ -31,10 +37,12 @@ import { */ export abstract class AssetBridger { public readonly l1Network: L1Network + public readonly parentChain: ParentChain public constructor(public readonly l2Network: L2Network) { this.l1Network = l1Networks[l2Network.partnerChainID] - if (!this.l1Network) { + this.parentChain = parentChains[l2Network.partnerChainID] + if (!this.parentChain) { throw new ArbSdkError( `Unknown l1 network chain id: ${l2Network.partnerChainID}` ) @@ -42,11 +50,11 @@ export abstract class AssetBridger { } /** - * Check the signer/provider matches the l1Network, throws if not + * Check the signer/provider matches the parentChain, throws if not * @param sop */ protected async checkL1Network(sop: SignerOrProvider): Promise { - await SignerProviderUtils.checkNetworkMatches(sop, this.l1Network.chainID) + await SignerProviderUtils.checkNetworkMatches(sop, this.parentChain.chainID) } /** diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 7b11611bc6..66e2411953 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -456,80 +456,105 @@ export const getEthBridgeInformation = async ( } /** - * Adds custom L1 and L2 networks. - * @param customL1Network Custom L1 network that can also be an Arbitrum chain. - * If `isArbitrum` is set to `false`, this will be added to both `l1Networks` and `parentChains`, and can be fetched with either `getL1Network` or `getParentChain`. - * If `isArbitrum` is set to `true`, this will be only added to `parentChains`, and can be fetched with `getParentChain`. - * @param customL2Network Custom L2 network, an Arbitrum chain. + * Adds custom Parent Chain and custom Chain. These chains will be returned in `getParentChain` and `getChain`. + * @param customParentChain Custom Parent Chain. This can be either an L1 or an Arbitrum network. + * @param customChain Custom Chain. This is an Arbitrum network. */ -export const addCustomNetwork = ({ - customL1Network, - customL2Network, +export const addCustomChain = ({ + customParentChain, + customChain, }: { - customL1Network?: ParentChain - customL2Network: Chain + customParentChain?: ParentChain + customChain: Chain }): void => { - if (!customL2Network.isCustom) { + if (customParentChain) { + if (parentChains[customParentChain.chainID]) { + throw new ArbSdkError( + `Parent chain ${customParentChain.chainID} already included` + ) + } + if (!customParentChain.isCustom) { + throw new ArbSdkError( + `Custom parent chain ${customParentChain.chainID} must have isCustom flag set to true` + ) + } + } + + if (chains[customChain.chainID]) { + throw new ArbSdkError(`Network ${customChain.chainID} already included`) + } + if (!customChain.isCustom) { throw new ArbSdkError( - `Custom network ${customL2Network.chainID} must have isCustom flag set to true.` + `Custom chain ${customChain.chainID} must have isCustom flag set to true.` ) } - const isAddedToL2Networks = - typeof l2Networks[customL2Network.chainID] !== 'undefined' - const isAddedToChains = typeof chains[customL2Network.chainID] !== 'undefined' - - if (isAddedToL2Networks && isAddedToChains) { - throw new ArbSdkError(`Network ${customL2Network.chainID} already included`) + let parentPartnerChain = parentChains[customChain.partnerChainID] + if ( + !parentPartnerChain && + customParentChain?.chainID === customChain.partnerChainID + ) { + // No existing ParentChain found as a partner to our customChain. + // And the newly added ParentChain is a partner to our customChain. + parentPartnerChain = customParentChain } - const hasParentChain = - typeof parentChains[customL2Network.partnerChainID] !== 'undefined' - const isCustomL1ParentToCustomL2 = - customL2Network.partnerChainID === customL1Network?.chainID - - if (!hasParentChain && !isCustomL1ParentToCustomL2) { + if (!parentPartnerChain) { throw new ArbSdkError( - `Network ${customL2Network.chainID}'s partner network, ${customL2Network.partnerChainID}, not recognized.` + `Chain ${customChain.chainID}'s partner parent chain, ${parentChains.partnerChainID}, not recognized.` ) } + if (customParentChain) { + parentChains[customParentChain?.chainID] = customParentChain + } + chains[customChain.chainID] = customChain +} + +/** + * Adds custom L1 and L2 networks. These networks will be returned in `getL1Network` and `getL2Network`. + * @param customL1Network + * @param customL2Network + */ +export const addCustomNetwork = ({ + customL1Network, + customL2Network, +}: { + customL1Network?: L1Network + customL2Network: L2Network +}): void => { if (customL1Network) { - const isAddedToL1Networks = - typeof l1Networks[customL1Network.chainID] !== 'undefined' - const isAddedToParentChains = - typeof parentChains[customL1Network.chainID] !== 'undefined' - const { isArbitrum } = customL1Network - - if (isArbitrum) { - if (isAddedToParentChains) { - throw new ArbSdkError( - `Network ${customL1Network.chainID} is already registered as a ParentChain. If you intended to add it as an L1Network, please set 'isArbitrum' to false.` - ) - } - parentChains[customL1Network.chainID] = customL1Network + if (l1Networks[customL1Network.chainID]) { + throw new ArbSdkError( + `Network ${customL1Network.chainID} already included` + ) + } else if (!customL1Network.isCustom) { + throw new ArbSdkError( + `Custom network ${customL1Network.chainID} must have isCustom flag set to true` + ) } else { - if (isAddedToL1Networks && isAddedToParentChains) { - throw new ArbSdkError( - `Network ${customL1Network.chainID} is already registered as an L1Network and a ParentChain.` - ) - } - // Make sure not to do any 'return' and 'throw' statements after adding an L1 network. - // Otherwise it could lead to an edge case where L1 is added but L2 throws. - if (!isAddedToL1Networks) { - l1Networks[customL1Network.chainID] = customL1Network - } - if (!isAddedToParentChains) { - parentChains[customL1Network.chainID] = customL1Network - } + l1Networks[customL1Network.chainID] = customL1Network } } - if (!isAddedToL2Networks) { - l2Networks[customL2Network.chainID] = customL2Network + if (l2Networks[customL2Network.chainID]) + throw new ArbSdkError(`Network ${customL2Network.chainID} already included`) + else if (!customL2Network.isCustom) { + throw new ArbSdkError( + `Custom network ${customL2Network.chainID} must have isCustom flag set to true` + ) } - if (!isAddedToChains) { - chains[customL2Network.chainID] = customL2Network + + l2Networks[customL2Network.chainID] = customL2Network + + const l1PartnerChain = l1Networks[customL2Network.partnerChainID] + if (!l1PartnerChain) + throw new ArbSdkError( + `Network ${customL2Network.chainID}'s partner network, ${customL2Network.partnerChainID}, not recognized` + ) + + if (!l1PartnerChain.partnerChainIDs.includes(customL2Network.chainID)) { + l1PartnerChain.partnerChainIDs.push(customL2Network.chainID) } } From 7de3b79e96c97a9d0f7b1fb53000a7313b767edf Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 21 Aug 2023 10:56:10 +0200 Subject: [PATCH 065/192] spread l2networks --- src/lib/dataEntities/networks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 66e2411953..c6c10390f5 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -329,7 +329,7 @@ export const parentChains: ParentChains = { ...getL2ParentChains(l2Networks), } -export const chains: Chains = l2Networks +export const chains: Chains = { ...l2Networks } const getNetwork = async ( signerOrProviderOrChainID: SignerOrProvider | number, From af42cc1d30f47c5151802298c8bc7d457beb98c3 Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 21 Aug 2023 11:02:59 +0200 Subject: [PATCH 066/192] clean up --- src/lib/dataEntities/networks.ts | 54 +++++++++++++++----------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index c6c10390f5..dbba438f3a 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -366,14 +366,10 @@ export const getL2Network = ( return getNetwork(signerOrProviderOrChainID, 2) as Promise } -/** - * Returns a chain that is associated with at least one child chain - * @param signerOrProviderOrChainID - * @returns Chain that is associated with at least one child chain - */ -export const getParentChain = async ( - signerOrProviderOrChainID: SignerOrProvider | number -): Promise => { +const getParentChainOrChain = async ( + signerOrProviderOrChainID: SignerOrProvider | number, + type: 'ParentChain' | 'Chain' +) => { const chainID = await (async () => { if (typeof signerOrProviderOrChainID === 'number') { return signerOrProviderOrChainID @@ -386,13 +382,28 @@ export const getParentChain = async ( return chainId })() - const chain = parentChains[chainID] + const _chains = type === 'ParentChain' ? parentChains : chains + const chain = _chains[chainID] if (chain) { return chain } - throw new ArbSdkError(`Unrecognized parent chain ${chainID}`) + throw new ArbSdkError(`Unrecognized ${type} ${chainID}.`) +} + +/** + * Returns a chain that is associated with at least one child chain + * @param signerOrProviderOrChainID + * @returns Chain that is associated with at least one child chain + */ +export const getParentChain = async ( + signerOrProviderOrChainID: SignerOrProvider | number +): Promise => { + return getParentChainOrChain( + signerOrProviderOrChainID, + 'ParentChain' + ) as Promise } /** @@ -403,25 +414,10 @@ export const getParentChain = async ( export const getChain = async ( signerOrProviderOrChainID: SignerOrProvider | number ): Promise => { - const chainID = await (async () => { - if (typeof signerOrProviderOrChainID === 'number') { - return signerOrProviderOrChainID - } - const provider = SignerProviderUtils.getProviderOrThrow( - signerOrProviderOrChainID - ) - - const { chainId } = await provider.getNetwork() - return chainId - })() - - const chain = chains[chainID] - - if (chain) { - return chain - } - - throw new ArbSdkError(`Unrecognized chain ${chainID}`) + return getParentChainOrChain( + signerOrProviderOrChainID, + 'Chain' + ) as Promise } /** From 0f67d96a265f7dc74ac10762096635e99a637930 Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 21 Aug 2023 11:16:25 +0200 Subject: [PATCH 067/192] default blocktime to 12sec --- src/lib/inbox/inbox.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/inbox/inbox.ts b/src/lib/inbox/inbox.ts index 64544bee96..74519399b7 100644 --- a/src/lib/inbox/inbox.ts +++ b/src/lib/inbox/inbox.ts @@ -93,7 +93,7 @@ export class InboxTools { let diffBlocks = 10 if ((this.l1Network as L1Network).blockTime) { diffBlocks = Math.max( - Math.ceil(diff / (this.l1Network as L1Network).blockTime), + Math.ceil(diff / (this.l1Network as L1Network).blockTime) ?? 12, 10 ) } From d716532a9b5aee5a1c527d5f9e40fe3315a1c172 Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 21 Aug 2023 11:41:11 +0200 Subject: [PATCH 068/192] clean up --- src/lib/dataEntities/networks.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index dbba438f3a..5568ae764b 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -466,18 +466,18 @@ export const addCustomChain = ({ if (customParentChain) { if (parentChains[customParentChain.chainID]) { throw new ArbSdkError( - `Parent chain ${customParentChain.chainID} already included` + `Parent chain ${customParentChain.chainID} already included.` ) } if (!customParentChain.isCustom) { throw new ArbSdkError( - `Custom parent chain ${customParentChain.chainID} must have isCustom flag set to true` + `Custom parent chain ${customParentChain.chainID} must have isCustom flag set to true.` ) } } if (chains[customChain.chainID]) { - throw new ArbSdkError(`Network ${customChain.chainID} already included`) + throw new ArbSdkError(`Network ${customChain.chainID} already included.`) } if (!customChain.isCustom) { throw new ArbSdkError( From b2f0c8b9b416031686616250f086b95543d2337d Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 21 Aug 2023 11:52:08 +0200 Subject: [PATCH 069/192] clean up --- src/lib/dataEntities/networks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 5568ae764b..952f9e830d 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -477,7 +477,7 @@ export const addCustomChain = ({ } if (chains[customChain.chainID]) { - throw new ArbSdkError(`Network ${customChain.chainID} already included.`) + throw new ArbSdkError(`Chain ${customChain.chainID} already included.`) } if (!customChain.isCustom) { throw new ArbSdkError( From d2e3502f4eefe1f024b83796a829b4646de99ce6 Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 21 Aug 2023 16:28:37 +0200 Subject: [PATCH 070/192] fix assetBridger --- src/index.ts | 1 + src/lib/assetBridger/assetBridger.ts | 17 ++++++++++++----- src/lib/assetBridger/erc20Bridger.ts | 10 ++++++++-- src/lib/assetBridger/ethBridger.ts | 10 ++++++++-- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/index.ts b/src/index.ts index 8c13ea781b..787d30979c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -54,6 +54,7 @@ export { getChain, getParentChain, addCustomNetwork, + addCustomChain, addDefaultLocalNetwork, } from './lib/dataEntities/networks' export { InboxTools } from './lib/inbox/inbox' diff --git a/src/lib/assetBridger/assetBridger.ts b/src/lib/assetBridger/assetBridger.ts index ee04e2dca9..644494296e 100644 --- a/src/lib/assetBridger/assetBridger.ts +++ b/src/lib/assetBridger/assetBridger.ts @@ -24,6 +24,7 @@ import { l1Networks, L1Network, L2Network, + Chain, ParentChain, parentChains, } from '../dataEntities/networks' @@ -38,23 +39,29 @@ import { export abstract class AssetBridger { public readonly l1Network: L1Network public readonly parentChain: ParentChain + private readonly l1NetworkOrParentChain: L1Network | ParentChain - public constructor(public readonly l2Network: L2Network) { + public constructor(public readonly l2Network: L2Network | Chain) { this.l1Network = l1Networks[l2Network.partnerChainID] this.parentChain = parentChains[l2Network.partnerChainID] - if (!this.parentChain) { + this.l1NetworkOrParentChain = this.l1Network || this.parentChain + + if (!this.l1NetworkOrParentChain) { throw new ArbSdkError( - `Unknown l1 network chain id: ${l2Network.partnerChainID}` + `Unknown parent network chain id: ${l2Network.partnerChainID}` ) } } /** - * Check the signer/provider matches the parentChain, throws if not + * Check the signer/provider matches the L1 network or the Parent Chain, throws if not * @param sop */ protected async checkL1Network(sop: SignerOrProvider): Promise { - await SignerProviderUtils.checkNetworkMatches(sop, this.parentChain.chainID) + await SignerProviderUtils.checkNetworkMatches( + sop, + this.l1NetworkOrParentChain.chainID + ) } /** diff --git a/src/lib/assetBridger/erc20Bridger.ts b/src/lib/assetBridger/erc20Bridger.ts index d4883ac22f..41ec565267 100644 --- a/src/lib/assetBridger/erc20Bridger.ts +++ b/src/lib/assetBridger/erc20Bridger.ts @@ -45,7 +45,7 @@ import { L1ToL2MessageGasEstimator, } from '../message/L1ToL2MessageGasEstimator' import { SignerProviderUtils } from '../dataEntities/signerOrProvider' -import { L2Network, getL2Network } from '../dataEntities/networks' +import { L2Network, getChain, getL2Network } from '../dataEntities/networks' import { ArbSdkError, MissingProviderArbSdkError } from '../dataEntities/errors' import { DISABLED_GATEWAY } from '../dataEntities/constants' import { EventFetcher } from '../utils/eventFetcher' @@ -188,7 +188,13 @@ export class Erc20Bridger extends AssetBridger< * @returns */ public static async fromProvider(l2Provider: Provider) { - return new Erc20Bridger(await getL2Network(l2Provider)) + let l2Network + try { + l2Network = await getL2Network(l2Provider) + } catch (e) { + l2Network = await getChain(l2Provider) + } + return new Erc20Bridger(l2Network) } /** diff --git a/src/lib/assetBridger/ethBridger.ts b/src/lib/assetBridger/ethBridger.ts index 4441f3d7f6..a7e4f93c31 100644 --- a/src/lib/assetBridger/ethBridger.ts +++ b/src/lib/assetBridger/ethBridger.ts @@ -45,7 +45,7 @@ import { import { OmitTyped } from '../utils/types' import { SignerProviderUtils } from '../dataEntities/signerOrProvider' import { MissingProviderArbSdkError } from '../dataEntities/errors' -import { getL2Network } from '../dataEntities/networks' +import { getChain, getL2Network } from '../dataEntities/networks' export interface EthWithdrawParams { /** @@ -138,7 +138,13 @@ export class EthBridger extends AssetBridger< * @returns */ public static async fromProvider(l2Provider: Provider) { - return new EthBridger(await getL2Network(l2Provider)) + let l2Network + try { + l2Network = await getL2Network(l2Provider) + } catch (e) { + l2Network = await getChain(l2Provider) + } + return new EthBridger(l2Network) } /** From 649dca4a89685dc702e102317046aea01ba95e48 Mon Sep 17 00:00:00 2001 From: Bartek Date: Tue, 22 Aug 2023 16:02:03 +0200 Subject: [PATCH 071/192] read l2network from l2Networks or chains --- src/lib/dataEntities/networks.ts | 44 +++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 952f9e830d..af238c5595 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -347,9 +347,14 @@ const getNetwork = async ( return chainId })() - const networks = layer === 1 ? l1Networks : l2Networks - if (networks[chainID]) { - return networks[chainID] + let network + if (layer === 1) { + network = l1Networks[chainID] + } else { + network = l2Networks[chainID] || chains[chainID] + } + if (network) { + return network } else { throw new ArbSdkError(`Unrecognized network ${chainID}.`) } @@ -363,7 +368,13 @@ export const getL1Network = ( export const getL2Network = ( signerOrProviderOrChainID: SignerOrProvider | number ): Promise => { - return getNetwork(signerOrProviderOrChainID, 2) as Promise + let l2Network + try { + l2Network = getNetwork(signerOrProviderOrChainID, 2) as Promise + } catch { + l2Network = getChain(signerOrProviderOrChainID) as Promise + } + return l2Network } const getParentChainOrChain = async ( @@ -485,10 +496,12 @@ export const addCustomChain = ({ ) } - let parentPartnerChain = parentChains[customChain.partnerChainID] + let parentPartnerChain: ParentChain | undefined = { + ...parentChains[customChain.partnerChainID], + } if ( !parentPartnerChain && - customParentChain?.chainID === customChain.partnerChainID + Number(customParentChain?.chainID) === Number(customChain.partnerChainID) ) { // No existing ParentChain found as a partner to our customChain. // And the newly added ParentChain is a partner to our customChain. @@ -505,6 +518,25 @@ export const addCustomChain = ({ parentChains[customParentChain?.chainID] = customParentChain } chains[customChain.chainID] = customChain + + // if parent chain doesn't exist, we need to add it from L2 Networks + if (!parentChains[customChain.partnerChainID]) { + parentChains[customChain.partnerChainID] = { + ...l2Networks[customChain.partnerChainID], + partnerChainIDs: [], + } + } + + // add partner chain ID to the parent network if it doesn't exist + if ( + !parentChains[customChain.partnerChainID].partnerChainIDs.includes( + customChain.chainID + ) + ) { + parentChains[customChain.partnerChainID].partnerChainIDs.push( + customChain.chainID + ) + } } /** From 0119fd9b279c9f8893660b97b0146dc59ba78d91 Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 23 Aug 2023 10:43:44 +0200 Subject: [PATCH 072/192] clean up --- src/lib/assetBridger/erc20Bridger.ts | 8 +------- src/lib/assetBridger/ethBridger.ts | 8 +------- src/lib/inbox/inbox.ts | 8 +------- 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/src/lib/assetBridger/erc20Bridger.ts b/src/lib/assetBridger/erc20Bridger.ts index 41ec565267..a31286eb77 100644 --- a/src/lib/assetBridger/erc20Bridger.ts +++ b/src/lib/assetBridger/erc20Bridger.ts @@ -188,13 +188,7 @@ export class Erc20Bridger extends AssetBridger< * @returns */ public static async fromProvider(l2Provider: Provider) { - let l2Network - try { - l2Network = await getL2Network(l2Provider) - } catch (e) { - l2Network = await getChain(l2Provider) - } - return new Erc20Bridger(l2Network) + return new Erc20Bridger(await getL2Network(l2Provider)) } /** diff --git a/src/lib/assetBridger/ethBridger.ts b/src/lib/assetBridger/ethBridger.ts index a7e4f93c31..9049edb25b 100644 --- a/src/lib/assetBridger/ethBridger.ts +++ b/src/lib/assetBridger/ethBridger.ts @@ -138,13 +138,7 @@ export class EthBridger extends AssetBridger< * @returns */ public static async fromProvider(l2Provider: Provider) { - let l2Network - try { - l2Network = await getL2Network(l2Provider) - } catch (e) { - l2Network = await getChain(l2Provider) - } - return new EthBridger(l2Network) + return new EthBridger(await getL2Network(l2Provider)) } /** diff --git a/src/lib/inbox/inbox.ts b/src/lib/inbox/inbox.ts index 74519399b7..f8d25c168a 100644 --- a/src/lib/inbox/inbox.ts +++ b/src/lib/inbox/inbox.ts @@ -90,13 +90,7 @@ export class InboxTools { // we take a long average block time of 14s // and always move at least 10 blocks - let diffBlocks = 10 - if ((this.l1Network as L1Network).blockTime) { - diffBlocks = Math.max( - Math.ceil(diff / (this.l1Network as L1Network).blockTime) ?? 12, - 10 - ) - } + const diffBlocks = Math.max(Math.ceil(diff / this.l1Network.blockTime), 10) return await this.findFirstBlockBelow( blockNumber - diffBlocks, From 31ab5d33f41a8126232b2ae7f2a765f96c9354d4 Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 23 Aug 2023 10:46:02 +0200 Subject: [PATCH 073/192] unused import --- src/lib/inbox/inbox.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/inbox/inbox.ts b/src/lib/inbox/inbox.ts index f8d25c168a..240bfb61b6 100644 --- a/src/lib/inbox/inbox.ts +++ b/src/lib/inbox/inbox.ts @@ -28,7 +28,7 @@ import { SequencerInbox__factory } from '../abi/factories/SequencerInbox__factor import { IInbox__factory } from '../abi/factories/IInbox__factory' import { RequiredPick } from '../utils/types' import { MessageDeliveredEvent } from '../abi/Bridge' -import { L1Network, l1Networks, L2Network } from '../dataEntities/networks' +import { l1Networks, L2Network } from '../dataEntities/networks' import { SignerProviderUtils } from '../dataEntities/signerOrProvider' import { FetchedEvent, EventFetcher } from '../utils/eventFetcher' import { MultiCaller, CallInput } from '../utils/multicall' From 4582b544f0da8ad2410215164db9041dd25eed94 Mon Sep 17 00:00:00 2001 From: Bartek Date: Wed, 23 Aug 2023 10:48:18 +0200 Subject: [PATCH 074/192] unused import --- src/lib/assetBridger/erc20Bridger.ts | 2 +- src/lib/assetBridger/ethBridger.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/assetBridger/erc20Bridger.ts b/src/lib/assetBridger/erc20Bridger.ts index a31286eb77..d4883ac22f 100644 --- a/src/lib/assetBridger/erc20Bridger.ts +++ b/src/lib/assetBridger/erc20Bridger.ts @@ -45,7 +45,7 @@ import { L1ToL2MessageGasEstimator, } from '../message/L1ToL2MessageGasEstimator' import { SignerProviderUtils } from '../dataEntities/signerOrProvider' -import { L2Network, getChain, getL2Network } from '../dataEntities/networks' +import { L2Network, getL2Network } from '../dataEntities/networks' import { ArbSdkError, MissingProviderArbSdkError } from '../dataEntities/errors' import { DISABLED_GATEWAY } from '../dataEntities/constants' import { EventFetcher } from '../utils/eventFetcher' diff --git a/src/lib/assetBridger/ethBridger.ts b/src/lib/assetBridger/ethBridger.ts index 9049edb25b..4441f3d7f6 100644 --- a/src/lib/assetBridger/ethBridger.ts +++ b/src/lib/assetBridger/ethBridger.ts @@ -45,7 +45,7 @@ import { import { OmitTyped } from '../utils/types' import { SignerProviderUtils } from '../dataEntities/signerOrProvider' import { MissingProviderArbSdkError } from '../dataEntities/errors' -import { getChain, getL2Network } from '../dataEntities/networks' +import { getL2Network } from '../dataEntities/networks' export interface EthWithdrawParams { /** From d3597cb614e797a1e4ffb339b2e58553c929ebb9 Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 24 Aug 2023 14:47:30 +0200 Subject: [PATCH 075/192] clean up, unit tests --- src/lib/dataEntities/networks.ts | 6 +- tests/unit/network.test.ts | 119 +++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 tests/unit/network.test.ts diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index af238c5595..0aff943d1c 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -396,11 +396,11 @@ const getParentChainOrChain = async ( const _chains = type === 'ParentChain' ? parentChains : chains const chain = _chains[chainID] - if (chain) { - return chain + if (!chain) { + throw new ArbSdkError(`Unrecognized ${type} ${chainID}.`) } - throw new ArbSdkError(`Unrecognized ${type} ${chainID}.`) + return chain } /** diff --git a/tests/unit/network.test.ts b/tests/unit/network.test.ts new file mode 100644 index 0000000000..8ccea157e8 --- /dev/null +++ b/tests/unit/network.test.ts @@ -0,0 +1,119 @@ +import { + getL1Network, + addCustomChain, + getL2Network, + getParentChain, + getChain, +} from '../../src' + +import { expect } from 'chai' + +const mainnetId = 1 +const arbOneId = 42161 +const mockOrbitChainId = 99999999 + +describe('Network', () => { + const fetchErrorMessage = + 'Network fetched successfully but the chain ID is invalid.' + + it('Adds a custom Orbit chain', async function () { + const arbOneNetwork = await getL2Network(arbOneId) + + addCustomChain({ + customChain: { + // we partially copy Arbitrum One network because we only want to mimic a custom chain + ...arbOneNetwork, + chainID: mockOrbitChainId, + partnerChainID: arbOneId, + isArbitrum: true, + isCustom: true, + }, + }) + }) + + it('Successfully fetches an L1 network with `getL1Network`', async function () { + const network = await getL1Network(mainnetId) + expect(network.chainID, fetchErrorMessage).to.be.eq(mainnetId) + }) + + it('Fails to fetch an L2 network with `getL1Network`', async function () { + let network + try { + network = await getL1Network(arbOneId) + } catch (err) { + // should fail + expect(err).to.be.an('error') + expect((err as Error).message).to.be.eq( + `Unrecognized network ${arbOneId}.` + ) + } finally { + expect(network, '`getL1Network` returned a result for an L2 network.').to + .be.undefined + } + }) + + it('Successfully fetches an L2 network with `getL2Network`', async function () { + const network = await getL2Network(arbOneId) + expect(network.chainID, fetchErrorMessage).to.be.eq(arbOneId) + }) + + it('Fails to fetch an L1 network with `getL2Network`', async function () { + let network + try { + network = await getL2Network(mainnetId) + } catch (err) { + // should fail + expect(err).to.be.an('error') + expect((err as Error).message).to.be.eq( + `Unrecognized network ${mainnetId}.` + ) + } finally { + expect(network, '`getL2Network` returned a result for an L1 network.').to + .be.undefined + } + }) + + it('Successfully fetches a parent chain with `getParentChain`', async function () { + const parentChain = await getParentChain(arbOneId) + expect(parentChain.chainID, fetchErrorMessage).to.be.eq(arbOneId) + }) + + it('Fails to fetch an Orbit chain with `getParentChain`', async function () { + let parentChain + try { + parentChain = await getParentChain(mockOrbitChainId) + } catch (err) { + // should fail + expect(err).to.be.an('error') + expect((err as Error).message).to.be.eq( + `Unrecognized ParentChain ${mockOrbitChainId}.` + ) + } finally { + expect( + parentChain, + '`getParentChain` returned a result for an Orbit chain.' + ).to.be.undefined + } + }) + + it('Successfully fetches an Orbit chain with `getChain`', async function () { + const chain = await getChain(mockOrbitChainId) + expect(chain.chainID, fetchErrorMessage).to.be.eq(mockOrbitChainId) + }) + + it('Fails to fetch a parent chain with `getChain`', async function () { + let chain + try { + chain = await getChain(mainnetId) + } catch (err) { + // should fail + expect(err).to.be.an('error') + expect((err as Error).message).to.be.eq( + `Unrecognized Chain ${mainnetId}.` + ) + } finally { + expect(chain, '`getChain` returned a result for a parent chain.').to.be + .undefined + } + }) +}) From 3d22c146cad0ea84e1c28b16563c69c894815320 Mon Sep 17 00:00:00 2001 From: gzeon Date: Mon, 28 Aug 2023 17:40:36 +0800 Subject: [PATCH 076/192] v3.1.10-beta.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 15c24d8e2c..fcd7a56ad5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@arbitrum/sdk", - "version": "3.1.9", + "version": "3.1.10-beta.0", "description": "Typescript library client-side interactions with Arbitrum", "author": "Offchain Labs, Inc.", "license": "Apache-2.0", From cf3cfac27a3a617f29d3e37217e09b1160afdc54 Mon Sep 17 00:00:00 2001 From: Bartek Date: Mon, 28 Aug 2023 15:28:04 +0200 Subject: [PATCH 077/192] fix multicall --- src/lib/utils/multicall.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/utils/multicall.ts b/src/lib/utils/multicall.ts index 187b170c1f..6f8fc0a84e 100644 --- a/src/lib/utils/multicall.ts +++ b/src/lib/utils/multicall.ts @@ -24,6 +24,7 @@ import { Multicall2 } from '../abi/Multicall2' import { Multicall2__factory } from '../abi/factories/Multicall2__factory' import { ArbSdkError } from '../dataEntities/errors' import { + chains, isL1Network, L1Network, l1Networks, @@ -131,7 +132,9 @@ export class MultiCaller { */ public static async fromProvider(provider: Provider): Promise { const chainId = (await provider.getNetwork()).chainId - const l2Network = l2Networks[chainId] as L2Network | undefined + const l2Network = (l2Networks[chainId] || chains[chainId]) as + | L2Network + | undefined const l1Network = l1Networks[chainId] as L1Network | undefined const network = l2Network || l1Network From a58988886f67e758a10de19a5fccab150ae01e10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Mon, 2 Oct 2023 09:23:33 +0200 Subject: [PATCH 078/192] v --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0efce7ade5..2ab43ebca1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@arbitrum/sdk", - "version": "3.1.6-orbit-custom-fee-token.0", + "version": "3.1.10-orbit-custom-fee-token.0", "description": "Typescript library client-side interactions with Arbitrum", "author": "Offchain Labs, Inc.", "license": "Apache-2.0", From 158b8d4835988e075d7a24e5b58f4b785d0aec2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Thu, 5 Oct 2023 11:32:14 +0200 Subject: [PATCH 079/192] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 136df7d431..2b9f8e340d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@arbitrum/sdk", - "version": "3.1.12-beta.0", + "version": "3.1.12-orbit", "description": "Typescript library client-side interactions with Arbitrum", "author": "Offchain Labs, Inc.", "license": "Apache-2.0", From cc48da25e94f690012f3ac10a1be5d4b2fa75ce2 Mon Sep 17 00:00:00 2001 From: Bartek Date: Thu, 5 Oct 2023 13:23:12 +0200 Subject: [PATCH 080/192] add stylus to partner chains for arb sepolia --- src/lib/dataEntities/networks.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 8dbb6aa5c1..554cd296cd 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -286,6 +286,7 @@ export const l2Networks: L2Networks = { isCustom: false, name: 'Arbitrum Rollup Sepolia Testnet', partnerChainID: 11155111, + partnerChainIDs: [23011913], retryableLifetimeSeconds: SEVEN_DAYS_IN_SECONDS, tokenBridge: { l1CustomGateway: '0xba2F7B6eAe1F9d174199C5E4867b563E0eaC40F3', From 980ca7a74cec35a77376a6fb795c7bad58bc18d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Thu, 5 Oct 2023 14:03:11 +0200 Subject: [PATCH 081/192] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2b9f8e340d..e84c971fff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@arbitrum/sdk", - "version": "3.1.12-orbit", + "version": "3.1.12-orbit.1", "description": "Typescript library client-side interactions with Arbitrum", "author": "Offchain Labs, Inc.", "license": "Apache-2.0", From 9615ca83dee50848cee12b227ea0f03666af075f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 1 Nov 2023 18:05:04 +0100 Subject: [PATCH 082/192] make consistent --- src/lib/assetBridger/ethBridger.ts | 4 ++-- .../custom-fee-token/customFeeTokenEthBridger.test.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/assetBridger/ethBridger.ts b/src/lib/assetBridger/ethBridger.ts index 623050f7d2..b9f4279d95 100644 --- a/src/lib/assetBridger/ethBridger.ts +++ b/src/lib/assetBridger/ethBridger.ts @@ -207,7 +207,7 @@ export class EthBridger extends AssetBridger< * Creates a transaction request for approving the custom fee token to be spent by the Inbox on the parent chain. * @param params */ - public getApproveFeeTokenTxRequest( + public getApproveFeeTokenRequest( params?: ApproveFeeTokenParams ): Required> { if (this.isNativeTokenEth) { @@ -239,7 +239,7 @@ export class EthBridger extends AssetBridger< } const approveFeeTokenRequest = this.isApproveFeeTokenParams(params) - ? this.getApproveFeeTokenTxRequest(params) + ? this.getApproveFeeTokenRequest(params) : params.txRequest return await params.l1Signer.sendTransaction({ diff --git a/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts b/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts index 1b87ff2c12..2a058fa77b 100644 --- a/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts +++ b/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts @@ -74,7 +74,7 @@ describe('EthBridger (with custom fee token)', async () => { await fundL1CustomFeeToken(l1Signer) const approvalTx = await ethBridger.approveFeeToken({ - txRequest: await ethBridger.getApproveFeeTokenTxRequest(), + txRequest: await ethBridger.getApproveFeeTokenRequest(), l1Signer, }) await approvalTx.wait() From b150be05d6e3aad695b3be505428a491d7789df5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 1 Nov 2023 18:06:54 +0100 Subject: [PATCH 083/192] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index aba51e74d0..e7c120bdef 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@arbitrum/sdk", - "version": "3.1.10-orbit-custom-fee-token.0", + "version": "3.1.12-orbit-custom-fee-token.0", "description": "Typescript library client-side interactions with Arbitrum", "author": "Offchain Labs, Inc.", "license": "Apache-2.0", From d35e6d60be42f72a22d98a4890ee5f6e49e2868e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Fri, 3 Nov 2023 12:43:57 +0100 Subject: [PATCH 084/192] handle zero address native token as eth --- src/lib/assetBridger/erc20Bridger.ts | 4 ++-- src/lib/assetBridger/ethBridger.ts | 4 ++-- src/lib/dataEntities/networks.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/assetBridger/erc20Bridger.ts b/src/lib/assetBridger/erc20Bridger.ts index 5b86e8637a..ea3ede4989 100644 --- a/src/lib/assetBridger/erc20Bridger.ts +++ b/src/lib/assetBridger/erc20Bridger.ts @@ -176,7 +176,7 @@ export class Erc20Bridger extends AssetBridger< public static MIN_CUSTOM_DEPOSIT_GAS_LIMIT = BigNumber.from(275000) /** - * In case of a chain that uses ETH as its native/fee token, this is undefined. + * In case of a chain that uses ETH as its native/fee token, this is either undefined or the zero address. * In case of a chain that uses an ERC-20 token from the parent chain as its native/fee token, this is the address of said token on the parent chain. */ public readonly nativeToken?: string @@ -195,7 +195,7 @@ export class Erc20Bridger extends AssetBridger< * @returns */ private get isNativeTokenEth() { - return typeof this.nativeToken === 'undefined' + return !this.nativeToken || this.nativeToken === constants.AddressZero } /** diff --git a/src/lib/assetBridger/ethBridger.ts b/src/lib/assetBridger/ethBridger.ts index b9f4279d95..a0540ef580 100644 --- a/src/lib/assetBridger/ethBridger.ts +++ b/src/lib/assetBridger/ethBridger.ts @@ -165,7 +165,7 @@ export class EthBridger extends AssetBridger< EthWithdrawParams | L2ToL1TxReqAndSigner > { /** - * In case of a chain that uses ETH as its native/fee token, this is undefined. + * In case of a chain that uses ETH as its native/fee token, this is either undefined or the zero address. * In case of a chain that uses an ERC-20 token from the parent chain as its native/fee token, this is the address of said token on the parent chain. */ public readonly nativeToken?: string @@ -181,7 +181,7 @@ export class EthBridger extends AssetBridger< * @returns */ private get isNativeTokenEth() { - return typeof this.nativeToken === 'undefined' + return !this.nativeToken || this.nativeToken === constants.AddressZero } /** diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index daef48294b..8fc30fc154 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -42,7 +42,7 @@ export interface L2Network extends Network { */ depositTimeout: number /** - * In case of a chain that uses ETH as its native/fee token, this is undefined. + * In case of a chain that uses ETH as its native/fee token, this is either undefined or the zero address. * In case of a chain that uses an ERC-20 token from the parent chain as its native/fee token, this is the address of said token on the parent chain. */ nativeToken?: string From d31e2efdb1051a36fa7c82ddef27727261624891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Fri, 3 Nov 2023 12:44:11 +0100 Subject: [PATCH 085/192] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e7c120bdef..1df491c5e1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@arbitrum/sdk", - "version": "3.1.12-orbit-custom-fee-token.0", + "version": "3.1.12-orbit-custom-fee-token.1", "description": "Typescript library client-side interactions with Arbitrum", "author": "Offchain Labs, Inc.", "license": "Apache-2.0", From 8551202994faa20e79c9375a9c122a899971c9e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Fri, 3 Nov 2023 16:50:55 +0100 Subject: [PATCH 086/192] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2eab86d5fe..e0ab1f260f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@arbitrum/sdk", - "version": "3.1.13", + "version": "3.1.13-orbit.0", "description": "Typescript library client-side interactions with Arbitrum", "author": "Offchain Labs, Inc.", "license": "Apache-2.0", From 611c48a1b55e57f2acfd918d0b8bc9083530f61c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Fri, 3 Nov 2023 17:42:41 +0100 Subject: [PATCH 087/192] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2e2a33cfd4..ba62aee067 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@arbitrum/sdk", - "version": "3.1.13-orbit.0", + "version": "3.1.13-orbit-custom-fee-token.1", "description": "Typescript library client-side interactions with Arbitrum", "author": "Offchain Labs, Inc.", "license": "Apache-2.0", From cf61bb8eae3ec73debae0462e7d334d187a06f76 Mon Sep 17 00:00:00 2001 From: Doug Lance Date: Sat, 18 Nov 2023 10:46:50 -0500 Subject: [PATCH 088/192] Testing the flat chains option --- src/index.ts | 1 - src/lib/assetBridger/assetBridger.ts | 4 +- src/lib/dataEntities/networks.ts | 205 +++++++++++++-------------- src/lib/utils/multicall.ts | 4 +- 4 files changed, 105 insertions(+), 109 deletions(-) diff --git a/src/index.ts b/src/index.ts index 70e217e15f..f4dacf8a0a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -54,7 +54,6 @@ export { getChain, getParentChain, addCustomNetwork, - addCustomChain, addDefaultLocalNetwork, } from './lib/dataEntities/networks' export { InboxTools } from './lib/inbox/inbox' diff --git a/src/lib/assetBridger/assetBridger.ts b/src/lib/assetBridger/assetBridger.ts index 644494296e..f76ceab073 100644 --- a/src/lib/assetBridger/assetBridger.ts +++ b/src/lib/assetBridger/assetBridger.ts @@ -26,7 +26,7 @@ import { L2Network, Chain, ParentChain, - parentChains, + getParentChains, } from '../dataEntities/networks' import { SignerOrProvider, @@ -43,7 +43,7 @@ export abstract class AssetBridger { public constructor(public readonly l2Network: L2Network | Chain) { this.l1Network = l1Networks[l2Network.partnerChainID] - this.parentChain = parentChains[l2Network.partnerChainID] + this.parentChain = getParentChains()[l2Network.partnerChainID] this.l1NetworkOrParentChain = this.l1Network || this.parentChain if (!this.l1NetworkOrParentChain) { diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 554cd296cd..0999566c15 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -130,7 +130,9 @@ const mainnetETHBridge: EthBridge = { }, } -export const l1Networks: L1Networks = { +type ChainOrNetwork = L1Network | L2Network | ParentChain | Chain + +export const CHAINS: { [key: string]: ChainOrNetwork } = { 1: { chainID: 1, name: 'Mainnet', @@ -167,9 +169,6 @@ export const l1Networks: L1Networks = { isCustom: false, isArbitrum: false, }, -} - -export const l2Networks: L2Networks = { 42161: { chainID: 42161, name: 'Arbitrum One', @@ -346,27 +345,100 @@ export const l2Networks: L2Networks = { }, } -// L2 networks that are a parent chain to Orbit chains. -function getL2ParentChains(_l2Networks: L2Networks) { - const _parentChains: ParentChains = {} +const isParentChain = (network: ChainOrNetwork): network is ParentChain => { + return 'partnerChainIDs' in network +} +const isChildChain = (network: ChainOrNetwork): network is Chain => { + return 'partnerChainID' in network +} - Object.keys(_l2Networks).map(chainId => { - const network = l2Networks[Number(chainId)] +const isOrbitChain = (network: ChainOrNetwork): network is Chain => { + return ( + 'partnerChainID' in network && + network.isCustom && + !('partnerChainIDS' in network) + // todo: not fully implemented yet + // todo: should we add `isOrbit` boolean value when adding chain? + ) +} - if (network.partnerChainIDs && network.partnerChainIDs.length > 0) { - _parentChains[chainId] = network as ParentChain +export const getParentChains = () => { + return Object.entries(CHAINS).reduce((acc, [key, value]) => { + if (isParentChain(value)) { + acc[key] = value } - }) + return acc + }, {} as ParentChains) +} - return _parentChains +const getChildChains = () => { + return Object.entries(CHAINS).reduce((acc, [key, value]) => { + if (isChildChain(value)) { + acc[key] = value + } + return acc + }, {} as Chains) } -export const parentChains: ParentChains = { - ...l1Networks, - ...getL2ParentChains(l2Networks), +const getOrbitChains = () => { + return Object.entries(CHAINS).reduce((acc, [key, value]) => { + if (isOrbitChain(value)) { + acc[key] = value + } + return acc + }, {} as Chains) } -export const chains: Chains = { ...l2Networks } +const getParentOfNetwork = (chain: ChainOrNetwork) => { + if (!isChildChain(chain)) { + return undefined + } + const parentChains = getParentChains() + const parentChain = parentChains[chain.partnerChainID] + if (!parentChain) { + throw new ArbSdkError( + `ParentChain ${chain.partnerChainID} not recognized for Chain ${chain.chainID}.` + ) + } + return parentChain +} + +const getChildrenOfNetwork = (chain: ChainOrNetwork) => { + if (!isParentChain(chain)) { + return [] + } + const childChains = getChildChains() + const children = Object.values(childChains).filter( + childChain => childChain.partnerChainID === chain.chainID + ) + return children +} + +// L2 networks that are a parent chain to Orbit chains. +function getL2ParentChains() { + const parentChains = getParentChains() + const childChains = getChildChains() + + const parentChildChains: typeof CHAINS = {} + + for (const key in parentChains) { + if (key in childChains) { + parentChildChains[key] = parentChains[key] + } + } + + return parentChildChains +} + +export let l1Networks: L1Networks = getParentChains() as L1Networks +export let l2Networks: L2Networks = getChildChains() as L2Networks + +const addNetwork = (network: ChainOrNetwork) => { + CHAINS[network.chainID] = network + + l1Networks = getParentChains() as L1Networks + l2Networks = getChildChains() as L2Networks +} const getNetwork = async ( signerOrProviderOrChainID: SignerOrProvider | number, @@ -386,9 +458,9 @@ const getNetwork = async ( let network if (layer === 1) { - network = l1Networks[chainID] + network = CHAINS[chainID] } else { - network = l2Networks[chainID] || chains[chainID] + network = CHAINS[chainID] } if (network) { return network @@ -430,8 +502,7 @@ const getParentChainOrChain = async ( return chainId })() - const _chains = type === 'ParentChain' ? parentChains : chains - const chain = _chains[chainID] + const chain = CHAINS[chainID] if (!chain) { throw new ArbSdkError(`Unrecognized ${type} ${chainID}.`) @@ -499,83 +570,6 @@ export const getEthBridgeInformation = async ( } } -/** - * Adds custom Parent Chain and custom Chain. These chains will be returned in `getParentChain` and `getChain`. - * @param customParentChain Custom Parent Chain. This can be either an L1 or an Arbitrum network. - * @param customChain Custom Chain. This is an Arbitrum network. - */ -export const addCustomChain = ({ - customParentChain, - customChain, -}: { - customParentChain?: ParentChain - customChain: Chain -}): void => { - if (customParentChain) { - if (parentChains[customParentChain.chainID]) { - throw new ArbSdkError( - `Parent chain ${customParentChain.chainID} already included.` - ) - } - if (!customParentChain.isCustom) { - throw new ArbSdkError( - `Custom parent chain ${customParentChain.chainID} must have isCustom flag set to true.` - ) - } - } - - if (chains[customChain.chainID]) { - throw new ArbSdkError(`Chain ${customChain.chainID} already included.`) - } - if (!customChain.isCustom) { - throw new ArbSdkError( - `Custom chain ${customChain.chainID} must have isCustom flag set to true.` - ) - } - - let parentPartnerChain: ParentChain | undefined = { - ...parentChains[customChain.partnerChainID], - } - if ( - !parentPartnerChain && - Number(customParentChain?.chainID) === Number(customChain.partnerChainID) - ) { - // No existing ParentChain found as a partner to our customChain. - // And the newly added ParentChain is a partner to our customChain. - parentPartnerChain = customParentChain - } - - if (!parentPartnerChain) { - throw new ArbSdkError( - `Chain ${customChain.chainID}'s partner parent chain, ${parentChains.partnerChainID}, not recognized.` - ) - } - - if (customParentChain) { - parentChains[customParentChain?.chainID] = customParentChain - } - chains[customChain.chainID] = customChain - - // if parent chain doesn't exist, we need to add it from L2 Networks - if (!parentChains[customChain.partnerChainID]) { - parentChains[customChain.partnerChainID] = { - ...l2Networks[customChain.partnerChainID], - partnerChainIDs: [], - } - } - - // add partner chain ID to the parent network if it doesn't exist - if ( - !parentChains[customChain.partnerChainID].partnerChainIDs.includes( - customChain.chainID - ) - ) { - parentChains[customChain.partnerChainID].partnerChainIDs.push( - customChain.chainID - ) - } -} - /** * Adds custom L1 and L2 networks. These networks will be returned in `getL1Network` and `getL2Network`. * @param customL1Network @@ -589,7 +583,7 @@ export const addCustomNetwork = ({ customL2Network: L2Network }): void => { if (customL1Network) { - if (l1Networks[customL1Network.chainID]) { + if (CHAINS[customL1Network.chainID]) { throw new ArbSdkError( `Network ${customL1Network.chainID} already included` ) @@ -598,7 +592,7 @@ export const addCustomNetwork = ({ `Custom network ${customL1Network.chainID} must have isCustom flag set to true` ) } else { - l1Networks[customL1Network.chainID] = customL1Network + addNetwork(customL1Network) } } @@ -610,16 +604,19 @@ export const addCustomNetwork = ({ ) } - l2Networks[customL2Network.chainID] = customL2Network + addNetwork(customL2Network) - const l1PartnerChain = l1Networks[customL2Network.partnerChainID] + const l1PartnerChain = getParentOfNetwork(customL2Network) if (!l1PartnerChain) throw new ArbSdkError( `Network ${customL2Network.chainID}'s partner network, ${customL2Network.partnerChainID}, not recognized` ) - if (!l1PartnerChain.partnerChainIDs.includes(customL2Network.chainID)) { - l1PartnerChain.partnerChainIDs.push(customL2Network.chainID) + if ( + isParentChain(l1PartnerChain) && + !l1PartnerChain.partnerChainIDs.includes(customL2Network.chainID) + ) { + l1PartnerChain?.partnerChainIDs.push(customL2Network.chainID) } } diff --git a/src/lib/utils/multicall.ts b/src/lib/utils/multicall.ts index 6f8fc0a84e..8ee3b407f8 100644 --- a/src/lib/utils/multicall.ts +++ b/src/lib/utils/multicall.ts @@ -24,7 +24,7 @@ import { Multicall2 } from '../abi/Multicall2' import { Multicall2__factory } from '../abi/factories/Multicall2__factory' import { ArbSdkError } from '../dataEntities/errors' import { - chains, + CHAINS, isL1Network, L1Network, l1Networks, @@ -132,7 +132,7 @@ export class MultiCaller { */ public static async fromProvider(provider: Provider): Promise { const chainId = (await provider.getNetwork()).chainId - const l2Network = (l2Networks[chainId] || chains[chainId]) as + const l2Network = (l2Networks[chainId] || CHAINS[chainId]) as | L2Network | undefined const l1Network = l1Networks[chainId] as L1Network | undefined From 814acf9004e006a734fffd008725f2f67ad5b7d9 Mon Sep 17 00:00:00 2001 From: Doug Lance Date: Mon, 20 Nov 2023 15:05:31 -0500 Subject: [PATCH 089/192] cleans up flat chains implementation --- src/index.ts | 2 - src/lib/assetBridger/assetBridger.ts | 10 +- src/lib/dataEntities/networks.ts | 215 +++++++++++---------------- src/lib/utils/multicall.ts | 5 +- tests/unit/network.test.ts | 42 +++--- 5 files changed, 115 insertions(+), 159 deletions(-) diff --git a/src/index.ts b/src/index.ts index f4dacf8a0a..c1cf37fd65 100644 --- a/src/index.ts +++ b/src/index.ts @@ -51,8 +51,6 @@ export { ParentChain, getL1Network, getL2Network, - getChain, - getParentChain, addCustomNetwork, addDefaultLocalNetwork, } from './lib/dataEntities/networks' diff --git a/src/lib/assetBridger/assetBridger.ts b/src/lib/assetBridger/assetBridger.ts index f76ceab073..ec0c5b3984 100644 --- a/src/lib/assetBridger/assetBridger.ts +++ b/src/lib/assetBridger/assetBridger.ts @@ -26,7 +26,7 @@ import { L2Network, Chain, ParentChain, - getParentChains, + getParentOfNetwork, } from '../dataEntities/networks' import { SignerOrProvider, @@ -43,7 +43,13 @@ export abstract class AssetBridger { public constructor(public readonly l2Network: L2Network | Chain) { this.l1Network = l1Networks[l2Network.partnerChainID] - this.parentChain = getParentChains()[l2Network.partnerChainID] + const parentChain = getParentOfNetwork(l2Network) + if (!parentChain) { + throw new ArbSdkError( + `Unknown parent network chain id: ${l2Network.partnerChainID}` + ) + } + this.parentChain = parentChain this.l1NetworkOrParentChain = this.l1Network || this.parentChain if (!this.l1NetworkOrParentChain) { diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 0999566c15..7d6928cd87 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -48,6 +48,7 @@ export type ParentChain = | (L2Network & Required>) export type Chain = L2Network +export type OrbitChain = Chain & { isOrbit: boolean } export interface Network { chainID: number @@ -101,6 +102,10 @@ export interface Chains { [id: string]: Chain } +export interface OrbitChains { + [id: string]: OrbitChain +} + const mainnetTokenBridge: TokenBridge = { l1GatewayRouter: '0x72Ce9c846789fdB6fC1f34aC4AD25Dd9ef7031ef', l2GatewayRouter: '0x5288c571Fd7aD117beA99bF60FE0846C4E84F933', @@ -130,9 +135,9 @@ const mainnetETHBridge: EthBridge = { }, } -type ChainOrNetwork = L1Network | L2Network | ParentChain | Chain +type ChainOrNetwork = L1Network | L2Network | ParentChain | Chain | OrbitChain -export const CHAINS: { [key: string]: ChainOrNetwork } = { +export const Networks: { [key: string]: ChainOrNetwork } = { 1: { chainID: 1, name: 'Mainnet', @@ -346,24 +351,44 @@ export const CHAINS: { [key: string]: ChainOrNetwork } = { } const isParentChain = (network: ChainOrNetwork): network is ParentChain => { - return 'partnerChainIDs' in network + return network && 'partnerChainIDs' in network } const isChildChain = (network: ChainOrNetwork): network is Chain => { - return 'partnerChainID' in network + return network && 'partnerChainID' in network } -const isOrbitChain = (network: ChainOrNetwork): network is Chain => { - return ( - 'partnerChainID' in network && - network.isCustom && - !('partnerChainIDS' in network) - // todo: not fully implemented yet - // todo: should we add `isOrbit` boolean value when adding chain? - ) +const isL1Chain = (network: ChainOrNetwork): network is L1Network => { + return network && isParentChain(network) && !isChildChain(network) +} + +const isL2Chain = (network: ChainOrNetwork): network is L2Network => { + return network && isChildChain(network) && !isOrbitChain(network) +} + +const isOrbitChain = (network: ChainOrNetwork): network is OrbitChain => { + return network && 'isOrbit' in network && network.isOrbit +} + +const getL1Chains = () => { + return Object.entries(Networks).reduce((acc, [key, value]) => { + if (isL1Chain(value)) { + acc[key] = value + } + return acc + }, {} as L1Networks) +} + +const getL2Chains = () => { + return Object.entries(Networks).reduce((acc, [key, value]) => { + if (isL2Chain(value)) { + acc[key] = value + } + return acc + }, {} as L2Networks) } -export const getParentChains = () => { - return Object.entries(CHAINS).reduce((acc, [key, value]) => { +const getParentChains = () => { + return Object.entries(Networks).reduce((acc, [key, value]) => { if (isParentChain(value)) { acc[key] = value } @@ -372,7 +397,7 @@ export const getParentChains = () => { } const getChildChains = () => { - return Object.entries(CHAINS).reduce((acc, [key, value]) => { + return Object.entries(Networks).reduce((acc, [key, value]) => { if (isChildChain(value)) { acc[key] = value } @@ -381,21 +406,20 @@ const getChildChains = () => { } const getOrbitChains = () => { - return Object.entries(CHAINS).reduce((acc, [key, value]) => { + return Object.entries(Networks).reduce((acc, [key, value]) => { if (isOrbitChain(value)) { acc[key] = value } return acc - }, {} as Chains) + }, {} as OrbitChains) } -const getParentOfNetwork = (chain: ChainOrNetwork) => { +export const getParentOfNetwork = (chain: ChainOrNetwork) => { if (!isChildChain(chain)) { return undefined } - const parentChains = getParentChains() - const parentChain = parentChains[chain.partnerChainID] - if (!parentChain) { + const parentChain = Networks[chain.partnerChainID] + if (!parentChain || !isParentChain(parentChain)) { throw new ArbSdkError( `ParentChain ${chain.partnerChainID} not recognized for Chain ${chain.chainID}.` ) @@ -405,7 +429,7 @@ const getParentOfNetwork = (chain: ChainOrNetwork) => { const getChildrenOfNetwork = (chain: ChainOrNetwork) => { if (!isParentChain(chain)) { - return [] + return undefined } const childChains = getChildChains() const children = Object.values(childChains).filter( @@ -414,35 +438,12 @@ const getChildrenOfNetwork = (chain: ChainOrNetwork) => { return children } -// L2 networks that are a parent chain to Orbit chains. -function getL2ParentChains() { - const parentChains = getParentChains() - const childChains = getChildChains() +export let l1Networks: L1Networks = getL1Chains() +export let l2Networks: L2Networks = getL2Chains() - const parentChildChains: typeof CHAINS = {} - - for (const key in parentChains) { - if (key in childChains) { - parentChildChains[key] = parentChains[key] - } - } - - return parentChildChains -} - -export let l1Networks: L1Networks = getParentChains() as L1Networks -export let l2Networks: L2Networks = getChildChains() as L2Networks - -const addNetwork = (network: ChainOrNetwork) => { - CHAINS[network.chainID] = network - - l1Networks = getParentChains() as L1Networks - l2Networks = getChildChains() as L2Networks -} - -const getNetwork = async ( +export const getNetwork = async ( signerOrProviderOrChainID: SignerOrProvider | number, - layer: 1 | 2 + layer?: 1 | 2 ) => { const chainID = await (async () => { if (typeof signerOrProviderOrChainID === 'number') { @@ -457,11 +458,14 @@ const getNetwork = async ( })() let network - if (layer === 1) { - network = CHAINS[chainID] + if (!layer) { + network = Networks[chainID] + } else if (layer === 1) { + network = l1Networks[chainID] } else { - network = CHAINS[chainID] + network = l2Networks[chainID] } + if (network) { return network } else { @@ -477,66 +481,7 @@ export const getL1Network = ( export const getL2Network = ( signerOrProviderOrChainID: SignerOrProvider | number ): Promise => { - let l2Network - try { - l2Network = getNetwork(signerOrProviderOrChainID, 2) as Promise - } catch { - l2Network = getChain(signerOrProviderOrChainID) as Promise - } - return l2Network -} - -const getParentChainOrChain = async ( - signerOrProviderOrChainID: SignerOrProvider | number, - type: 'ParentChain' | 'Chain' -) => { - const chainID = await (async () => { - if (typeof signerOrProviderOrChainID === 'number') { - return signerOrProviderOrChainID - } - const provider = SignerProviderUtils.getProviderOrThrow( - signerOrProviderOrChainID - ) - - const { chainId } = await provider.getNetwork() - return chainId - })() - - const chain = CHAINS[chainID] - - if (!chain) { - throw new ArbSdkError(`Unrecognized ${type} ${chainID}.`) - } - - return chain -} - -/** - * Returns a chain that is associated with at least one child chain - * @param signerOrProviderOrChainID - * @returns Chain that is associated with at least one child chain - */ -export const getParentChain = async ( - signerOrProviderOrChainID: SignerOrProvider | number -): Promise => { - return getParentChainOrChain( - signerOrProviderOrChainID, - 'ParentChain' - ) as Promise -} - -/** - * Returns a chain that is associated with a parent chain - * @param signerOrProviderOrChainID - * @returns Chain that is associated with a parent chain - */ -export const getChain = async ( - signerOrProviderOrChainID: SignerOrProvider | number -): Promise => { - return getParentChainOrChain( - signerOrProviderOrChainID, - 'Chain' - ) as Promise + return getNetwork(signerOrProviderOrChainID, 2) as Promise } /** @@ -570,6 +515,35 @@ export const getEthBridgeInformation = async ( } } +const addNetwork = (network: ChainOrNetwork) => { + Networks[network.chainID] = network + if (isParentChain(network)) { + const children = getChildrenOfNetwork(network) + children?.forEach(child => { + if (child) { + child.partnerChainID = network.chainID + } + }) + } + + if (isChildChain(network)) { + const parent = Networks[network.partnerChainID] + if (parent) { + parent.partnerChainIDs = [ + ...(parent.partnerChainIDs ?? []), + network.chainID, + ] + } else { + throw new ArbSdkError( + `Network ${network.chainID}'s partner network, ${network.partnerChainID}, not recognized` + ) + } + } + + l1Networks = getL1Chains() + l2Networks = getL2Chains() +} + /** * Adds custom L1 and L2 networks. These networks will be returned in `getL1Network` and `getL2Network`. * @param customL1Network @@ -580,10 +554,10 @@ export const addCustomNetwork = ({ customL2Network, }: { customL1Network?: L1Network - customL2Network: L2Network + customL2Network: L2Network | OrbitChain }): void => { if (customL1Network) { - if (CHAINS[customL1Network.chainID]) { + if (l1Networks[customL1Network.chainID]) { throw new ArbSdkError( `Network ${customL1Network.chainID} already included` ) @@ -605,19 +579,6 @@ export const addCustomNetwork = ({ } addNetwork(customL2Network) - - const l1PartnerChain = getParentOfNetwork(customL2Network) - if (!l1PartnerChain) - throw new ArbSdkError( - `Network ${customL2Network.chainID}'s partner network, ${customL2Network.partnerChainID}, not recognized` - ) - - if ( - isParentChain(l1PartnerChain) && - !l1PartnerChain.partnerChainIDs.includes(customL2Network.chainID) - ) { - l1PartnerChain?.partnerChainIDs.push(customL2Network.chainID) - } } /** diff --git a/src/lib/utils/multicall.ts b/src/lib/utils/multicall.ts index 8ee3b407f8..187b170c1f 100644 --- a/src/lib/utils/multicall.ts +++ b/src/lib/utils/multicall.ts @@ -24,7 +24,6 @@ import { Multicall2 } from '../abi/Multicall2' import { Multicall2__factory } from '../abi/factories/Multicall2__factory' import { ArbSdkError } from '../dataEntities/errors' import { - CHAINS, isL1Network, L1Network, l1Networks, @@ -132,9 +131,7 @@ export class MultiCaller { */ public static async fromProvider(provider: Provider): Promise { const chainId = (await provider.getNetwork()).chainId - const l2Network = (l2Networks[chainId] || CHAINS[chainId]) as - | L2Network - | undefined + const l2Network = l2Networks[chainId] as L2Network | undefined const l1Network = l1Networks[chainId] as L1Network | undefined const network = l2Network || l1Network diff --git a/tests/unit/network.test.ts b/tests/unit/network.test.ts index 8ccea157e8..ff742acac1 100644 --- a/tests/unit/network.test.ts +++ b/tests/unit/network.test.ts @@ -1,12 +1,7 @@ -import { - getL1Network, - addCustomChain, - getL2Network, - getParentChain, - getChain, -} from '../../src' +import { getL1Network, addCustomNetwork, getL2Network } from '../../src' import { expect } from 'chai' +import { getNetwork } from '../../src/lib/dataEntities/networks' const mainnetId = 1 const arbOneId = 42161 @@ -19,14 +14,15 @@ describe('Network', () => { it('Adds a custom Orbit chain', async function () { const arbOneNetwork = await getL2Network(arbOneId) - addCustomChain({ - customChain: { + addCustomNetwork({ + customL2Network: { // we partially copy Arbitrum One network because we only want to mimic a custom chain ...arbOneNetwork, chainID: mockOrbitChainId, partnerChainID: arbOneId, isArbitrum: true, isCustom: true, + isOrbit: true, }, }) }) @@ -73,46 +69,44 @@ describe('Network', () => { } }) - it('Successfully fetches a parent chain with `getParentChain`', async function () { - const parentChain = await getParentChain(arbOneId) + it('Successfully fetches a parent chain with `getNetwork`', async function () { + const parentChain = await getNetwork(arbOneId) expect(parentChain.chainID, fetchErrorMessage).to.be.eq(arbOneId) }) - it('Fails to fetch an Orbit chain with `getParentChain`', async function () { + it('Fails to fetch an Orbit chain with `getNetwork`', async function () { let parentChain try { - parentChain = await getParentChain(mockOrbitChainId) + parentChain = await getNetwork(mockOrbitChainId, 1) } catch (err) { // should fail expect(err).to.be.an('error') expect((err as Error).message).to.be.eq( - `Unrecognized ParentChain ${mockOrbitChainId}.` + `Unrecognized network ${mockOrbitChainId}.` ) } finally { - expect( - parentChain, - '`getParentChain` returned a result for an Orbit chain.' - ).to.be.undefined + expect(parentChain, '`getNetwork` returned a result for an Orbit chain.') + .to.be.undefined } }) - it('Successfully fetches an Orbit chain with `getChain`', async function () { - const chain = await getChain(mockOrbitChainId) + it('Successfully fetches an Orbit chain with `getNetwork`', async function () { + const chain = await getNetwork(mockOrbitChainId) expect(chain.chainID, fetchErrorMessage).to.be.eq(mockOrbitChainId) }) - it('Fails to fetch a parent chain with `getChain`', async function () { + it('Fails to fetch a parent chain with `getNetwork`', async function () { let chain try { - chain = await getChain(mainnetId) + chain = await getNetwork(mainnetId, 2) } catch (err) { // should fail expect(err).to.be.an('error') expect((err as Error).message).to.be.eq( - `Unrecognized Chain ${mainnetId}.` + `Unrecognized network ${mainnetId}.` ) } finally { - expect(chain, '`getChain` returned a result for a parent chain.').to.be + expect(chain, '`getNetwork` returned a result for a parent chain.').to.be .undefined } }) From 500ced0f0a340ec188198a5e0833f75f03ba2ee3 Mon Sep 17 00:00:00 2001 From: Doug Lance Date: Mon, 20 Nov 2023 15:45:04 -0500 Subject: [PATCH 090/192] exports getters to fix unused function error --- src/lib/dataEntities/networks.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 7d6928cd87..98ad554961 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -369,7 +369,7 @@ const isOrbitChain = (network: ChainOrNetwork): network is OrbitChain => { return network && 'isOrbit' in network && network.isOrbit } -const getL1Chains = () => { +export const getL1Chains = () => { return Object.entries(Networks).reduce((acc, [key, value]) => { if (isL1Chain(value)) { acc[key] = value @@ -378,7 +378,7 @@ const getL1Chains = () => { }, {} as L1Networks) } -const getL2Chains = () => { +export const getL2Chains = () => { return Object.entries(Networks).reduce((acc, [key, value]) => { if (isL2Chain(value)) { acc[key] = value @@ -387,7 +387,7 @@ const getL2Chains = () => { }, {} as L2Networks) } -const getParentChains = () => { +export const getParentChains = () => { return Object.entries(Networks).reduce((acc, [key, value]) => { if (isParentChain(value)) { acc[key] = value @@ -396,7 +396,7 @@ const getParentChains = () => { }, {} as ParentChains) } -const getChildChains = () => { +export const getChildChains = () => { return Object.entries(Networks).reduce((acc, [key, value]) => { if (isChildChain(value)) { acc[key] = value @@ -405,7 +405,7 @@ const getChildChains = () => { }, {} as Chains) } -const getOrbitChains = () => { +export const getOrbitChains = () => { return Object.entries(Networks).reduce((acc, [key, value]) => { if (isOrbitChain(value)) { acc[key] = value @@ -427,7 +427,7 @@ export const getParentOfNetwork = (chain: ChainOrNetwork) => { return parentChain } -const getChildrenOfNetwork = (chain: ChainOrNetwork) => { +export const getChildrenOfNetwork = (chain: ChainOrNetwork) => { if (!isParentChain(chain)) { return undefined } From a4999a64e82f6448b1cbdcea47d54f4b5ffa0150 Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Tue, 21 Nov 2023 09:15:09 -0500 Subject: [PATCH 091/192] sets type isOrbit to true rather than boolean Co-authored-by: spsjvc --- src/lib/dataEntities/networks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 98ad554961..e68ed1f5d7 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -48,7 +48,7 @@ export type ParentChain = | (L2Network & Required>) export type Chain = L2Network -export type OrbitChain = Chain & { isOrbit: boolean } +export type OrbitChain = L2Network & { isOrbit: true } export interface Network { chainID: number From c72eaac45af8685239810bbd630003952445fa34 Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Tue, 21 Nov 2023 09:15:34 -0500 Subject: [PATCH 092/192] swap getParentOf Network to getParentForNetwork Co-authored-by: spsjvc --- src/lib/dataEntities/networks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index e68ed1f5d7..b0737fc83d 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -414,7 +414,7 @@ export const getOrbitChains = () => { }, {} as OrbitChains) } -export const getParentOfNetwork = (chain: ChainOrNetwork) => { +export const getParentForNetwork = (chain: ChainOrNetwork) => { if (!isChildChain(chain)) { return undefined } From 08c27b326b7981449aa6289ec24177809893132f Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Tue, 21 Nov 2023 09:15:51 -0500 Subject: [PATCH 093/192] swap getChildrenOf Network to getChildrenForNetwork Co-authored-by: spsjvc --- src/lib/dataEntities/networks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index b0737fc83d..43908c85c0 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -427,7 +427,7 @@ export const getParentForNetwork = (chain: ChainOrNetwork) => { return parentChain } -export const getChildrenOfNetwork = (chain: ChainOrNetwork) => { +export const getChildrenForNetwork = (chain: ChainOrNetwork) => { if (!isParentChain(chain)) { return undefined } From ca6f1dc7f94e999e1cc65719421b61788df1ebbc Mon Sep 17 00:00:00 2001 From: Doug Lance Date: Tue, 21 Nov 2023 10:24:19 -0500 Subject: [PATCH 094/192] responds to PR feedback --- src/index.ts | 2 - src/lib/assetBridger/assetBridger.ts | 7 ++-- src/lib/dataEntities/networks.ts | 57 ++++++++++++++-------------- 3 files changed, 31 insertions(+), 35 deletions(-) diff --git a/src/index.ts b/src/index.ts index c1cf37fd65..99056afc27 100644 --- a/src/index.ts +++ b/src/index.ts @@ -47,8 +47,6 @@ export { L2Networks, L1Network, L2Network, - Chain, - ParentChain, getL1Network, getL2Network, addCustomNetwork, diff --git a/src/lib/assetBridger/assetBridger.ts b/src/lib/assetBridger/assetBridger.ts index ec0c5b3984..72b0fd6941 100644 --- a/src/lib/assetBridger/assetBridger.ts +++ b/src/lib/assetBridger/assetBridger.ts @@ -24,9 +24,8 @@ import { l1Networks, L1Network, L2Network, - Chain, ParentChain, - getParentOfNetwork, + getParentForNetwork, } from '../dataEntities/networks' import { SignerOrProvider, @@ -41,9 +40,9 @@ export abstract class AssetBridger { public readonly parentChain: ParentChain private readonly l1NetworkOrParentChain: L1Network | ParentChain - public constructor(public readonly l2Network: L2Network | Chain) { + public constructor(public readonly l2Network: L2Network) { this.l1Network = l1Networks[l2Network.partnerChainID] - const parentChain = getParentOfNetwork(l2Network) + const parentChain = getParentForNetwork(l2Network) if (!parentChain) { throw new ArbSdkError( `Unknown parent network chain id: ${l2Network.partnerChainID}` diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 43908c85c0..f36f24060f 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -21,6 +21,14 @@ import { ArbSdkError } from '../dataEntities/errors' import { SEVEN_DAYS_IN_SECONDS } from './constants' import { RollupAdminLogic__factory } from '../abi/factories/RollupAdminLogic__factory' +export interface Network { + chainID: number + name: string + explorerUrl: string + gif?: string + isCustom: boolean +} + export interface L1Network extends Network { partnerChainIDs: number[] blockTime: number //seconds @@ -47,16 +55,10 @@ export type ParentChain = | L1Network | (L2Network & Required>) -export type Chain = L2Network export type OrbitChain = L2Network & { isOrbit: true } +export type ChildChain = L2Network | OrbitChain -export interface Network { - chainID: number - name: string - explorerUrl: string - gif?: string - isCustom: boolean -} +type Chain = L1Network | L2Network | ParentChain | ChildChain | OrbitChain export interface TokenBridge { l1GatewayRouter: string @@ -97,9 +99,8 @@ export interface L2Networks { export interface ParentChains { [id: string]: ParentChain } - -export interface Chains { - [id: string]: Chain +export interface ChildChains { + [id: string]: ChildChain } export interface OrbitChains { @@ -135,9 +136,7 @@ const mainnetETHBridge: EthBridge = { }, } -type ChainOrNetwork = L1Network | L2Network | ParentChain | Chain | OrbitChain - -export const Networks: { [key: string]: ChainOrNetwork } = { +export const Networks: { [key: string]: Chain } = { 1: { chainID: 1, name: 'Mainnet', @@ -350,23 +349,23 @@ export const Networks: { [key: string]: ChainOrNetwork } = { }, } -const isParentChain = (network: ChainOrNetwork): network is ParentChain => { - return network && 'partnerChainIDs' in network +const isParentChain = (chain: Chain): chain is ParentChain => { + return chain && 'partnerChainIDs' in chain } -const isChildChain = (network: ChainOrNetwork): network is Chain => { - return network && 'partnerChainID' in network +const isChildChain = (chain: Chain): chain is ChildChain => { + return chain && 'partnerChainID' in chain } -const isL1Chain = (network: ChainOrNetwork): network is L1Network => { - return network && isParentChain(network) && !isChildChain(network) +const isL1Chain = (chain: Chain): chain is L1Network => { + return chain && isParentChain(chain) && !isChildChain(chain) } -const isL2Chain = (network: ChainOrNetwork): network is L2Network => { - return network && isChildChain(network) && !isOrbitChain(network) +const isL2Chain = (chain: Chain): chain is L2Network => { + return chain && isChildChain(chain) && !isOrbitChain(chain) } -const isOrbitChain = (network: ChainOrNetwork): network is OrbitChain => { - return network && 'isOrbit' in network && network.isOrbit +const isOrbitChain = (chain: Chain): chain is OrbitChain => { + return chain && 'isOrbit' in chain && chain.isOrbit } export const getL1Chains = () => { @@ -402,7 +401,7 @@ export const getChildChains = () => { acc[key] = value } return acc - }, {} as Chains) + }, {} as ChildChains) } export const getOrbitChains = () => { @@ -414,7 +413,7 @@ export const getOrbitChains = () => { }, {} as OrbitChains) } -export const getParentForNetwork = (chain: ChainOrNetwork) => { +export const getParentForNetwork = (chain: Chain) => { if (!isChildChain(chain)) { return undefined } @@ -427,7 +426,7 @@ export const getParentForNetwork = (chain: ChainOrNetwork) => { return parentChain } -export const getChildrenForNetwork = (chain: ChainOrNetwork) => { +export const getChildrenForNetwork = (chain: Chain) => { if (!isParentChain(chain)) { return undefined } @@ -515,10 +514,10 @@ export const getEthBridgeInformation = async ( } } -const addNetwork = (network: ChainOrNetwork) => { +const addNetwork = (network: Chain) => { Networks[network.chainID] = network if (isParentChain(network)) { - const children = getChildrenOfNetwork(network) + const children = getChildrenForNetwork(network) children?.forEach(child => { if (child) { child.partnerChainID = network.chainID From 21cbc1d143e70da874cb5287ee073789724d5b43 Mon Sep 17 00:00:00 2001 From: Doug Lance Date: Tue, 28 Nov 2023 14:38:13 -0500 Subject: [PATCH 095/192] abstracts out getting chains by type --- src/lib/dataEntities/networks.ts | 68 ++++++++++++-------------------- 1 file changed, 25 insertions(+), 43 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index f36f24060f..512df5b1f2 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -136,7 +136,7 @@ const mainnetETHBridge: EthBridge = { }, } -export const Networks: { [key: string]: Chain } = { +export const Networks: Record = { 1: { chainID: 1, name: 'Mainnet', @@ -352,6 +352,7 @@ export const Networks: { [key: string]: Chain } = { const isParentChain = (chain: Chain): chain is ParentChain => { return chain && 'partnerChainIDs' in chain } + const isChildChain = (chain: Chain): chain is ChildChain => { return chain && 'partnerChainID' in chain } @@ -368,50 +369,31 @@ const isOrbitChain = (chain: Chain): chain is OrbitChain => { return chain && 'isOrbit' in chain && chain.isOrbit } -export const getL1Chains = () => { - return Object.entries(Networks).reduce((acc, [key, value]) => { - if (isL1Chain(value)) { - acc[key] = value - } - return acc - }, {} as L1Networks) -} - -export const getL2Chains = () => { - return Object.entries(Networks).reduce((acc, [key, value]) => { - if (isL2Chain(value)) { - acc[key] = value - } - return acc - }, {} as L2Networks) -} - -export const getParentChains = () => { - return Object.entries(Networks).reduce((acc, [key, value]) => { - if (isParentChain(value)) { - acc[key] = value - } - return acc - }, {} as ParentChains) -} - -export const getChildChains = () => { - return Object.entries(Networks).reduce((acc, [key, value]) => { - if (isChildChain(value)) { - acc[key] = value - } - return acc - }, {} as ChildChains) +/** + * Builds an object that is a list of chains filtered by the provided predicate function indexed by their chain id + * @param filterFn - A predicate function to determine if a chain should be included. + * @return An object with only the filtered chains. + */ +const getChainsByType = ( + filterFn: (chain: Chain) => boolean +): T => { + return Object.entries(Networks).reduce( + (accumulator, [chainId, chainData]) => { + if (filterFn(chainData)) { + accumulator[chainId] = chainData + } + return accumulator + }, + {} + ) as T } -export const getOrbitChains = () => { - return Object.entries(Networks).reduce((acc, [key, value]) => { - if (isOrbitChain(value)) { - acc[key] = value - } - return acc - }, {} as OrbitChains) -} +export const getL1Chains = () => getChainsByType(isL1Chain) +export const getL2Chains = () => getChainsByType(isL2Chain) +export const getParentChains = () => + getChainsByType(isParentChain) +export const getChildChains = () => getChainsByType(isChildChain) +export const getOrbitChains = () => getChainsByType(isOrbitChain) export const getParentForNetwork = (chain: Chain) => { if (!isChildChain(chain)) { From 49e7aead48d86202114dcd8256ce4289538bd7c4 Mon Sep 17 00:00:00 2001 From: Doug Lance Date: Thu, 30 Nov 2023 09:01:02 -0500 Subject: [PATCH 096/192] responds to pr feedback --- src/lib/dataEntities/networks.ts | 7 ++++++- tests/unit/network.test.ts | 26 +++++++++++++++----------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 512df5b1f2..c713dc22b9 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -350,7 +350,12 @@ export const Networks: Record = { } const isParentChain = (chain: Chain): chain is ParentChain => { - return chain && 'partnerChainIDs' in chain + return ( + chain && + 'partnerChainIDs' in chain && + !!chain.partnerChainIDs && + chain.partnerChainIDs.length > 0 + ) } const isChildChain = (chain: Chain): chain is ChildChain => { diff --git a/tests/unit/network.test.ts b/tests/unit/network.test.ts index ff742acac1..5a7a69ee8e 100644 --- a/tests/unit/network.test.ts +++ b/tests/unit/network.test.ts @@ -11,10 +11,10 @@ describe('Network', () => { const fetchErrorMessage = 'Network fetched successfully but the chain ID is invalid.' - it('Adds a custom Orbit chain', async function () { + it('adds a custom Orbit chain', async function () { const arbOneNetwork = await getL2Network(arbOneId) - addCustomNetwork({ + const mockCustomNetwork = { customL2Network: { // we partially copy Arbitrum One network because we only want to mimic a custom chain ...arbOneNetwork, @@ -24,15 +24,19 @@ describe('Network', () => { isCustom: true, isOrbit: true, }, - }) + } as const + + addCustomNetwork(mockCustomNetwork) + + expect(await getNetwork(mockOrbitChainId)).to.be.ok }) - it('Successfully fetches an L1 network with `getL1Network`', async function () { + it('successfully fetches an L1 network with `getL1Network`', async function () { const network = await getL1Network(mainnetId) expect(network.chainID, fetchErrorMessage).to.be.eq(mainnetId) }) - it('Fails to fetch an L2 network with `getL1Network`', async function () { + it('fails to fetch an L2 network with `getL1Network`', async function () { let network try { network = await getL1Network(arbOneId) @@ -48,12 +52,12 @@ describe('Network', () => { } }) - it('Successfully fetches an L2 network with `getL2Network`', async function () { + it('successfully fetches an L2 network with `getL2Network`', async function () { const network = await getL2Network(arbOneId) expect(network.chainID, fetchErrorMessage).to.be.eq(arbOneId) }) - it('Fails to fetch an L1 network with `getL2Network`', async function () { + it('fails to fetch an L1 network with `getL2Network`', async function () { let network try { network = await getL2Network(mainnetId) @@ -69,12 +73,12 @@ describe('Network', () => { } }) - it('Successfully fetches a parent chain with `getNetwork`', async function () { + it('successfully fetches a parent chain with `getNetwork`', async function () { const parentChain = await getNetwork(arbOneId) expect(parentChain.chainID, fetchErrorMessage).to.be.eq(arbOneId) }) - it('Fails to fetch an Orbit chain with `getNetwork`', async function () { + it('fails to fetch an Orbit chain with `getNetwork` because it is in the wrong layer', async function () { let parentChain try { parentChain = await getNetwork(mockOrbitChainId, 1) @@ -90,12 +94,12 @@ describe('Network', () => { } }) - it('Successfully fetches an Orbit chain with `getNetwork`', async function () { + it('successfully fetches an Orbit chain with `getNetwork`', async function () { const chain = await getNetwork(mockOrbitChainId) expect(chain.chainID, fetchErrorMessage).to.be.eq(mockOrbitChainId) }) - it('Fails to fetch a parent chain with `getNetwork`', async function () { + it('fails to fetch a parent chain with `getNetwork` because it is the wrong layer', async function () { let chain try { chain = await getNetwork(mainnetId, 2) From 769bb6c357d511861af67265ae4c147bb38c1048 Mon Sep 17 00:00:00 2001 From: Doug Lance Date: Thu, 30 Nov 2023 09:53:45 -0500 Subject: [PATCH 097/192] Adds jsdocs for parent/child predicates --- src/lib/dataEntities/networks.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index c713dc22b9..fd4b21d3ba 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -349,6 +349,7 @@ export const Networks: Record = { }, } +/** Determines if a chain is a parent of *any* other chain. */ const isParentChain = (chain: Chain): chain is ParentChain => { return ( chain && @@ -358,6 +359,7 @@ const isParentChain = (chain: Chain): chain is ParentChain => { ) } +/** Determines if a chain is a child of *any* other chain. */ const isChildChain = (chain: Chain): chain is ChildChain => { return chain && 'partnerChainID' in chain } From b3737b6f991c850b3c1aa1dfa6746494f281d6c8 Mon Sep 17 00:00:00 2001 From: Doug Lance Date: Thu, 30 Nov 2023 10:00:26 -0500 Subject: [PATCH 098/192] fix lint issue --- src/lib/dataEntities/networks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index fd4b21d3ba..10666b4ad0 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -354,7 +354,7 @@ const isParentChain = (chain: Chain): chain is ParentChain => { return ( chain && 'partnerChainIDs' in chain && - !!chain.partnerChainIDs && + Boolean(chain.partnerChainIDs) && chain.partnerChainIDs.length > 0 ) } From 6645ce828b68182bdc6549ab92cd9286927f7025 Mon Sep 17 00:00:00 2001 From: Doug Lance Date: Thu, 30 Nov 2023 10:05:00 -0500 Subject: [PATCH 099/192] actually fix lint issue --- src/lib/dataEntities/networks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 10666b4ad0..7c99384f2f 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -354,7 +354,7 @@ const isParentChain = (chain: Chain): chain is ParentChain => { return ( chain && 'partnerChainIDs' in chain && - Boolean(chain.partnerChainIDs) && + Array.isArray(chain.partnerChainIDs) && chain.partnerChainIDs.length > 0 ) } From 5f3737a03ada5fd3f04eba455f54c5abaaef47d9 Mon Sep 17 00:00:00 2001 From: Doug Lance Date: Thu, 30 Nov 2023 10:55:57 -0500 Subject: [PATCH 100/192] updates `getNetwork` to return all parent chains for layer 1 --- src/lib/dataEntities/networks.ts | 23 ++--------------------- tests/unit/network.test.ts | 7 ++++--- 2 files changed, 6 insertions(+), 24 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 7c99384f2f..1fd6d9329b 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -395,27 +395,8 @@ const getChainsByType = ( ) as T } -export const getL1Chains = () => getChainsByType(isL1Chain) -export const getL2Chains = () => getChainsByType(isL2Chain) -export const getParentChains = () => - getChainsByType(isParentChain) -export const getChildChains = () => getChainsByType(isChildChain) -export const getOrbitChains = () => getChainsByType(isOrbitChain) - -export const getParentForNetwork = (chain: Chain) => { - if (!isChildChain(chain)) { - return undefined - } - const parentChain = Networks[chain.partnerChainID] - if (!parentChain || !isParentChain(parentChain)) { - throw new ArbSdkError( - `ParentChain ${chain.partnerChainID} not recognized for Chain ${chain.chainID}.` - ) - } - return parentChain -} +const getParentChains = () => getChainsByType(isParentChain) -export const getChildrenForNetwork = (chain: Chain) => { if (!isParentChain(chain)) { return undefined } @@ -449,7 +430,7 @@ export const getNetwork = async ( if (!layer) { network = Networks[chainID] } else if (layer === 1) { - network = l1Networks[chainID] + network = getParentChains()[chainID] } else { network = l2Networks[chainID] } diff --git a/tests/unit/network.test.ts b/tests/unit/network.test.ts index 5a7a69ee8e..3230e8ead9 100644 --- a/tests/unit/network.test.ts +++ b/tests/unit/network.test.ts @@ -5,6 +5,7 @@ import { getNetwork } from '../../src/lib/dataEntities/networks' const mainnetId = 1 const arbOneId = 42161 +const arbNovaId = 42170 const mockOrbitChainId = 99999999 describe('Network', () => { @@ -36,15 +37,15 @@ describe('Network', () => { expect(network.chainID, fetchErrorMessage).to.be.eq(mainnetId) }) - it('fails to fetch an L2 network with `getL1Network`', async function () { + it('fails to fetch an L2 network that is not a parent with `getL1Network`', async function () { let network try { - network = await getL1Network(arbOneId) + network = await getL1Network(arbNovaId) } catch (err) { // should fail expect(err).to.be.an('error') expect((err as Error).message).to.be.eq( - `Unrecognized network ${arbOneId}.` + `Unrecognized network ${arbNovaId}.` ) } finally { expect(network, '`getL1Network` returned a result for an L2 network.').to From 26538b433ec9542b06bbd0ee201c575adfcaea9e Mon Sep 17 00:00:00 2001 From: Doug Lance Date: Thu, 30 Nov 2023 10:57:05 -0500 Subject: [PATCH 101/192] removes exports from networks.ts --- src/lib/dataEntities/networks.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 1fd6d9329b..5eb73bcd85 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -395,8 +395,25 @@ const getChainsByType = ( ) as T } +const getL1Chains = () => getChainsByType(isL1Chain) +const getL2Chains = () => getChainsByType(isL2Chain) +const getChildChains = () => getChainsByType(isChildChain) const getParentChains = () => getChainsByType(isParentChain) +export const getParentForNetwork = (chain: Chain) => { + if (!isChildChain(chain)) { + return undefined + } + const parentChain = networks[chain.partnerChainID] + if (!parentChain || !isParentChain(parentChain)) { + throw new ArbSdkError( + `ParentChain ${chain.partnerChainID} not recognized for Chain ${chain.chainID}.` + ) + } + return parentChain +} + +const getChildrenForNetwork = (chain: Chain) => { if (!isParentChain(chain)) { return undefined } From 290cf612f9dce0e62fdcf86cfb3af6ec384c10ab Mon Sep 17 00:00:00 2001 From: Doug Lance Date: Thu, 30 Nov 2023 10:57:34 -0500 Subject: [PATCH 102/192] renames network object --- src/lib/dataEntities/networks.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 5eb73bcd85..da00675d58 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -136,7 +136,7 @@ const mainnetETHBridge: EthBridge = { }, } -export const Networks: Record = { +export const networks: Record = { 1: { chainID: 1, name: 'Mainnet', @@ -381,10 +381,10 @@ const isOrbitChain = (chain: Chain): chain is OrbitChain => { * @param filterFn - A predicate function to determine if a chain should be included. * @return An object with only the filtered chains. */ -const getChainsByType = ( +const getChainsByType = ( filterFn: (chain: Chain) => boolean ): T => { - return Object.entries(Networks).reduce( + return Object.entries(networks).reduce( (accumulator, [chainId, chainData]) => { if (filterFn(chainData)) { accumulator[chainId] = chainData @@ -445,7 +445,7 @@ export const getNetwork = async ( let network if (!layer) { - network = Networks[chainID] + network = networks[chainID] } else if (layer === 1) { network = getParentChains()[chainID] } else { @@ -502,7 +502,7 @@ export const getEthBridgeInformation = async ( } const addNetwork = (network: Chain) => { - Networks[network.chainID] = network + networks[network.chainID] = network if (isParentChain(network)) { const children = getChildrenForNetwork(network) children?.forEach(child => { @@ -513,7 +513,7 @@ const addNetwork = (network: Chain) => { } if (isChildChain(network)) { - const parent = Networks[network.partnerChainID] + const parent = networks[network.partnerChainID] if (parent) { parent.partnerChainIDs = [ ...(parent.partnerChainIDs ?? []), From 74a9f53ef3cfb0584bf04a8249a139dd1367bd20 Mon Sep 17 00:00:00 2001 From: Doug Lance Date: Thu, 30 Nov 2023 11:06:24 -0500 Subject: [PATCH 103/192] removes parentChain and l1NetworkOrParentChain from asset bridger --- src/lib/assetBridger/assetBridger.ts | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/lib/assetBridger/assetBridger.ts b/src/lib/assetBridger/assetBridger.ts index 72b0fd6941..495ba07da5 100644 --- a/src/lib/assetBridger/assetBridger.ts +++ b/src/lib/assetBridger/assetBridger.ts @@ -24,7 +24,6 @@ import { l1Networks, L1Network, L2Network, - ParentChain, getParentForNetwork, } from '../dataEntities/networks' import { @@ -37,8 +36,6 @@ import { */ export abstract class AssetBridger { public readonly l1Network: L1Network - public readonly parentChain: ParentChain - private readonly l1NetworkOrParentChain: L1Network | ParentChain public constructor(public readonly l2Network: L2Network) { this.l1Network = l1Networks[l2Network.partnerChainID] @@ -48,10 +45,10 @@ export abstract class AssetBridger { `Unknown parent network chain id: ${l2Network.partnerChainID}` ) } - this.parentChain = parentChain - this.l1NetworkOrParentChain = this.l1Network || this.parentChain - if (!this.l1NetworkOrParentChain) { + const l1NetworkOrParentChain = this.l1Network || parentChain + + if (!l1NetworkOrParentChain) { throw new ArbSdkError( `Unknown parent network chain id: ${l2Network.partnerChainID}` ) @@ -63,10 +60,7 @@ export abstract class AssetBridger { * @param sop */ protected async checkL1Network(sop: SignerOrProvider): Promise { - await SignerProviderUtils.checkNetworkMatches( - sop, - this.l1NetworkOrParentChain.chainID - ) + await SignerProviderUtils.checkNetworkMatches(sop, this.l1Network.chainID) } /** From 34b7e28ac179a3af0705b15e5c9f4531be370b7c Mon Sep 17 00:00:00 2001 From: Doug Lance Date: Fri, 1 Dec 2023 12:19:40 -0500 Subject: [PATCH 104/192] adds generic customNetwork param to `addCustomNetwork` --- src/lib/dataEntities/networks.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index da00675d58..cdc76e5d02 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -538,9 +538,11 @@ const addNetwork = (network: Chain) => { export const addCustomNetwork = ({ customL1Network, customL2Network, + customNetwork, }: { customL1Network?: L1Network customL2Network: L2Network | OrbitChain + customNetwork?: L1Network | L2Network | OrbitChain }): void => { if (customL1Network) { if (l1Networks[customL1Network.chainID]) { @@ -565,6 +567,19 @@ export const addCustomNetwork = ({ } addNetwork(customL2Network) + + if (customNetwork) { + if (networks[customNetwork.chainID]) { + throw new ArbSdkError(`Network ${customNetwork.chainID} already included`) + } + if (!customNetwork.isCustom) { + throw new ArbSdkError( + `Custom network ${customNetwork.chainID} must have isCustom flag set to true` + ) + } + + addNetwork(customNetwork) + } } /** From 5b4b50b14d8e824afce24bb23079af54c93b7e3e Mon Sep 17 00:00:00 2001 From: Doug Lance Date: Fri, 1 Dec 2023 14:58:08 -0500 Subject: [PATCH 105/192] fix add custom network param type --- src/lib/dataEntities/networks.ts | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index cdc76e5d02..e6a1d6ad8a 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -539,11 +539,11 @@ export const addCustomNetwork = ({ customL1Network, customL2Network, customNetwork, -}: { - customL1Network?: L1Network +}: Partial<{ + customL1Network: L1Network customL2Network: L2Network | OrbitChain - customNetwork?: L1Network | L2Network | OrbitChain -}): void => { + customNetwork: L1Network | L2Network | OrbitChain +}>): void => { if (customL1Network) { if (l1Networks[customL1Network.chainID]) { throw new ArbSdkError( @@ -558,15 +558,19 @@ export const addCustomNetwork = ({ } } - if (l2Networks[customL2Network.chainID]) - throw new ArbSdkError(`Network ${customL2Network.chainID} already included`) - else if (!customL2Network.isCustom) { - throw new ArbSdkError( - `Custom network ${customL2Network.chainID} must have isCustom flag set to true` - ) - } + if (customL2Network) { + if (l2Networks[customL2Network.chainID]) + throw new ArbSdkError( + `Network ${customL2Network.chainID} already included` + ) + else if (!customL2Network.isCustom) { + throw new ArbSdkError( + `Custom network ${customL2Network.chainID} must have isCustom flag set to true` + ) + } - addNetwork(customL2Network) + addNetwork(customL2Network) + } if (customNetwork) { if (networks[customNetwork.chainID]) { From f72e011b3dfeee7554aa93ac9cc3c234beabd398 Mon Sep 17 00:00:00 2001 From: Doug Lance Date: Mon, 4 Dec 2023 10:01:13 -0500 Subject: [PATCH 106/192] reverts change to addCustomNetwork --- src/lib/dataEntities/networks.ts | 39 ++++++++------------------------ 1 file changed, 10 insertions(+), 29 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index e6a1d6ad8a..da00675d58 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -538,12 +538,10 @@ const addNetwork = (network: Chain) => { export const addCustomNetwork = ({ customL1Network, customL2Network, - customNetwork, -}: Partial<{ - customL1Network: L1Network +}: { + customL1Network?: L1Network customL2Network: L2Network | OrbitChain - customNetwork: L1Network | L2Network | OrbitChain -}>): void => { +}): void => { if (customL1Network) { if (l1Networks[customL1Network.chainID]) { throw new ArbSdkError( @@ -558,32 +556,15 @@ export const addCustomNetwork = ({ } } - if (customL2Network) { - if (l2Networks[customL2Network.chainID]) - throw new ArbSdkError( - `Network ${customL2Network.chainID} already included` - ) - else if (!customL2Network.isCustom) { - throw new ArbSdkError( - `Custom network ${customL2Network.chainID} must have isCustom flag set to true` - ) - } - - addNetwork(customL2Network) + if (l2Networks[customL2Network.chainID]) + throw new ArbSdkError(`Network ${customL2Network.chainID} already included`) + else if (!customL2Network.isCustom) { + throw new ArbSdkError( + `Custom network ${customL2Network.chainID} must have isCustom flag set to true` + ) } - if (customNetwork) { - if (networks[customNetwork.chainID]) { - throw new ArbSdkError(`Network ${customNetwork.chainID} already included`) - } - if (!customNetwork.isCustom) { - throw new ArbSdkError( - `Custom network ${customNetwork.chainID} must have isCustom flag set to true` - ) - } - - addNetwork(customNetwork) - } + addNetwork(customL2Network) } /** From e2a2dc9f6981b75a3625384ff354db4619f88ec9 Mon Sep 17 00:00:00 2001 From: Doug Date: Tue, 12 Dec 2023 13:21:38 -0500 Subject: [PATCH 107/192] implement pr changes --- src/lib/dataEntities/networks.ts | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index da00675d58..d2c3da6672 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -429,7 +429,7 @@ export let l2Networks: L2Networks = getL2Chains() export const getNetwork = async ( signerOrProviderOrChainID: SignerOrProvider | number, - layer?: 1 | 2 + layer: 1 | 2 ) => { const chainID = await (async () => { if (typeof signerOrProviderOrChainID === 'number') { @@ -444,12 +444,10 @@ export const getNetwork = async ( })() let network - if (!layer) { - network = networks[chainID] - } else if (layer === 1) { + if (layer === 1) { network = getParentChains()[chainID] } else { - network = l2Networks[chainID] + network = getChildChains()[chainID] } if (network) { @@ -538,10 +536,15 @@ const addNetwork = (network: Chain) => { export const addCustomNetwork = ({ customL1Network, customL2Network, -}: { - customL1Network?: L1Network - customL2Network: L2Network | OrbitChain -}): void => { +}: + | { + customL1Network?: L1Network + customL2Network: L2Network + } + | { + customL1Network?: L2Network + customL2Network: OrbitChain + }): void => { if (customL1Network) { if (l1Networks[customL1Network.chainID]) { throw new ArbSdkError( From a67a6785644bfc92f7740dfda3828325d1f9ee3d Mon Sep 17 00:00:00 2001 From: Doug Date: Fri, 15 Dec 2023 06:52:00 -0500 Subject: [PATCH 108/192] improves tests by resetting network state between tests --- src/lib/dataEntities/networks.ts | 19 +- tests/unit/network.test.ts | 403 ++++++++++++++++++++++++------- 2 files changed, 327 insertions(+), 95 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index d2c3da6672..3229995ab1 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -397,8 +397,8 @@ const getChainsByType = ( const getL1Chains = () => getChainsByType(isL1Chain) const getL2Chains = () => getChainsByType(isL2Chain) -const getChildChains = () => getChainsByType(isChildChain) const getParentChains = () => getChainsByType(isParentChain) +const getChildChains = () => getChainsByType(isChildChain) export const getParentForNetwork = (chain: Chain) => { if (!isChildChain(chain)) { @@ -645,3 +645,20 @@ export const isL1Network = ( } return network.partnerChainIDs.length > 0 } + +const createNetworkStateHandler = () => { + const initialState = JSON.parse(JSON.stringify(networks)) + + return { + resetNetworksToDefault: () => { + Object.keys(networks).forEach(key => delete networks[key]) + Object.assign(networks, JSON.parse(JSON.stringify(initialState))) + l1Networks = getL1Chains() + l2Networks = getL2Chains() + }, + } +} + +const { resetNetworksToDefault } = createNetworkStateHandler() + +export { resetNetworksToDefault } diff --git a/tests/unit/network.test.ts b/tests/unit/network.test.ts index 3230e8ead9..3f904c9fb0 100644 --- a/tests/unit/network.test.ts +++ b/tests/unit/network.test.ts @@ -1,118 +1,333 @@ -import { getL1Network, addCustomNetwork, getL2Network } from '../../src' - import { expect } from 'chai' -import { getNetwork } from '../../src/lib/dataEntities/networks' +import { + getNetwork, + resetNetworksToDefault, + addCustomNetwork, + getL1Network, + getL2Network, +} from '../../src/lib/dataEntities/networks' const mainnetId = 1 const arbOneId = 42161 const arbNovaId = 42170 +const mockL1ChainId = 111111 +const mockL2ChainId = 222222 const mockOrbitChainId = 99999999 -describe('Network', () => { +describe('Networks', () => { const fetchErrorMessage = 'Network fetched successfully but the chain ID is invalid.' - it('adds a custom Orbit chain', async function () { - const arbOneNetwork = await getL2Network(arbOneId) + beforeEach(async () => { + resetNetworksToDefault() + }) - const mockCustomNetwork = { - customL2Network: { - // we partially copy Arbitrum One network because we only want to mimic a custom chain - ...arbOneNetwork, - chainID: mockOrbitChainId, - partnerChainID: arbOneId, - isArbitrum: true, - isCustom: true, - isOrbit: true, - }, - } as const + describe('adding networks', () => { + it('adds a custom L2 network', async function () { + const arbOneNetwork = await getL2Network(arbOneId) + const mockCustomNetwork = { + customL2Network: { + ...arbOneNetwork, + chainID: mockL2ChainId, + isArbitrum: true, + isCustom: true, + isOrbit: false, + }, + } as const - addCustomNetwork(mockCustomNetwork) + addCustomNetwork(mockCustomNetwork) - expect(await getNetwork(mockOrbitChainId)).to.be.ok - }) + expect(await getNetwork(mockL2ChainId, 2)).to.be.ok + }) - it('successfully fetches an L1 network with `getL1Network`', async function () { - const network = await getL1Network(mainnetId) - expect(network.chainID, fetchErrorMessage).to.be.eq(mainnetId) - }) + it('adds a custom L1 and L2 network', async function () { + const ethNetwork = await getL1Network(mainnetId) + const arbOneNetwork = await getL2Network(arbOneId) + const mockCustomNetwork = { + customL1Network: { + ...ethNetwork, + chainID: mockL1ChainId, + isArbitrum: false, + isCustom: true, + isOrbit: false, + }, + customL2Network: { + ...arbOneNetwork, + chainID: mockL2ChainId, + isArbitrum: true, + isCustom: true, + isOrbit: false, + }, + } as const - it('fails to fetch an L2 network that is not a parent with `getL1Network`', async function () { - let network - try { - network = await getL1Network(arbNovaId) - } catch (err) { - // should fail - expect(err).to.be.an('error') - expect((err as Error).message).to.be.eq( - `Unrecognized network ${arbNovaId}.` - ) - } finally { - expect(network, '`getL1Network` returned a result for an L2 network.').to - .be.undefined - } - }) + addCustomNetwork(mockCustomNetwork) - it('successfully fetches an L2 network with `getL2Network`', async function () { - const network = await getL2Network(arbOneId) - expect(network.chainID, fetchErrorMessage).to.be.eq(arbOneId) - }) + expect(await getNetwork(mockL1ChainId, 1)).to.be.ok + expect(await getNetwork(mockL2ChainId, 2)).to.be.ok + }) - it('fails to fetch an L1 network with `getL2Network`', async function () { - let network - try { - network = await getL2Network(mainnetId) - } catch (err) { - // should fail - expect(err).to.be.an('error') - expect((err as Error).message).to.be.eq( - `Unrecognized network ${mainnetId}.` - ) - } finally { - expect(network, '`getL2Network` returned a result for an L1 network.').to - .be.undefined - } - }) + it('adds a custom Orbit chain', async function () { + const arbOneNetwork = await getL2Network(arbOneId) - it('successfully fetches a parent chain with `getNetwork`', async function () { - const parentChain = await getNetwork(arbOneId) - expect(parentChain.chainID, fetchErrorMessage).to.be.eq(arbOneId) - }) + const mockCustomNetwork = { + customL2Network: { + ...arbOneNetwork, + chainID: mockOrbitChainId, + partnerChainID: arbOneId, + isArbitrum: true, + isCustom: true, + isOrbit: true, + }, + } as const - it('fails to fetch an Orbit chain with `getNetwork` because it is in the wrong layer', async function () { - let parentChain - try { - parentChain = await getNetwork(mockOrbitChainId, 1) - } catch (err) { - // should fail - expect(err).to.be.an('error') - expect((err as Error).message).to.be.eq( - `Unrecognized network ${mockOrbitChainId}.` - ) - } finally { - expect(parentChain, '`getNetwork` returned a result for an Orbit chain.') - .to.be.undefined - } - }) + addCustomNetwork(mockCustomNetwork) + + expect(await getNetwork(mockOrbitChainId, 2)).to.be.ok + }) + + it('adds a custom L1 and Orbit chain', async function () { + const ethNetwork = await getL1Network(mainnetId) + const arbOneNetwork = await getL2Network(arbOneId) + + const mockCustomNetwork = { + customL1Network: { + ...ethNetwork, + chainID: mockL1ChainId, + isArbitrum: false, + isCustom: true, + isOrbit: false, + }, + customL2Network: { + ...arbOneNetwork, + chainID: mockL2ChainId, + isArbitrum: true, + isCustom: true, + isOrbit: true, + }, + } as const + + addCustomNetwork(mockCustomNetwork) + + expect(await getNetwork(mockL1ChainId, 1)).to.be.ok + expect(await getNetwork(mockL2ChainId, 2)).to.be.ok + }) + + it('adds a custom L1, L2, and L3', async function () { + const ethNetwork = await getL1Network(mainnetId) + const arbOneNetwork = await getL2Network(arbOneId) + + const mockCustomNetwork = { + customL1Network: { + ...ethNetwork, + chainID: mockL1ChainId, + isArbitrum: false, + isCustom: true, + isOrbit: false, + }, + customL2Network: { + ...arbOneNetwork, + chainID: mockL2ChainId, + isArbitrum: true, + isCustom: true, + isOrbit: false, + }, + } as const + + addCustomNetwork(mockCustomNetwork) + + const mockOrbitNetwork = { + customL2Network: { + ...arbOneNetwork, + chainID: mockOrbitChainId, + partnerChainID: mockL2ChainId, + isArbitrum: true, + isCustom: true, + isOrbit: true, + }, + } as const - it('successfully fetches an Orbit chain with `getNetwork`', async function () { - const chain = await getNetwork(mockOrbitChainId) - expect(chain.chainID, fetchErrorMessage).to.be.eq(mockOrbitChainId) + addCustomNetwork(mockOrbitNetwork) + + expect(await getNetwork(mockL1ChainId, 1)).to.be.ok + expect(await getNetwork(mockL2ChainId, 2)).to.be.ok + expect(await getNetwork(mockOrbitChainId, 2)).to.be.ok + }) }) - it('fails to fetch a parent chain with `getNetwork` because it is the wrong layer', async function () { - let chain - try { - chain = await getNetwork(mainnetId, 2) - } catch (err) { - // should fail - expect(err).to.be.an('error') - expect((err as Error).message).to.be.eq( - `Unrecognized network ${mainnetId}.` - ) - } finally { - expect(chain, '`getNetwork` returned a result for a parent chain.').to.be - .undefined - } + describe('fetching networks', () => { + it('successfully fetches an L1 network with `getL1Network`', async function () { + const network = await getL1Network(mainnetId) + expect(network.chainID, fetchErrorMessage).to.be.eq(mainnetId) + }) + + it('fails to fetch an L2 network that is not a parent with `getL1Network`', async function () { + let network + try { + network = await getL1Network(arbNovaId) + } catch (err) { + // should fail + expect(err).to.be.an('error') + expect((err as Error).message).to.be.eq( + `Unrecognized network ${arbNovaId}.` + ) + } finally { + expect(network, '`getL1Network` returned a result for an L2 network.') + .to.be.undefined + } + }) + + it('successfully fetches an L2 network with `getL2Network`', async function () { + const network = await getL2Network(arbOneId) + expect(network.chainID, fetchErrorMessage).to.be.eq(arbOneId) + }) + + it('fails to fetch an L1 network with `getL2Network`', async function () { + let network + try { + network = await getL2Network(mainnetId) + } catch (err) { + // should fail + expect(err).to.be.an('error') + expect((err as Error).message).to.be.eq( + `Unrecognized network ${mainnetId}.` + ) + } finally { + expect(network, '`getL2Network` returned a result for an L1 network.') + .to.be.undefined + } + }) + + it('successfully fetches a parent chain with `getNetwork`', async function () { + const arbOneNetwork = await getL2Network(arbOneId) + + const mockCustomNetwork = { + customL2Network: { + ...arbOneNetwork, + chainID: mockOrbitChainId, + partnerChainID: arbOneId, + isArbitrum: true, + isCustom: true, + isOrbit: true, + }, + } as const + + addCustomNetwork(mockCustomNetwork) + const parentChain = await getNetwork(arbOneId, 1) + expect(parentChain.chainID, fetchErrorMessage).to.be.eq(arbOneId) + }) + + it('fails to fetch an Orbit chain with `getNetwork` because it is in the wrong layer', async function () { + let parentChain + try { + parentChain = await getNetwork(mockOrbitChainId, 1) + } catch (err) { + // should fail + expect(err).to.be.an('error') + expect((err as Error).message).to.be.eq( + `Unrecognized network ${mockOrbitChainId}.` + ) + } finally { + expect( + parentChain, + '`getNetwork` returned a result for an Orbit chain.' + ).to.be.undefined + } + }) + + it('successfully fetches an Orbit chain with `getNetwork`', async function () { + const arbOneNetwork = await getL2Network(arbOneId) + + const mockCustomNetwork = { + customL2Network: { + ...arbOneNetwork, + chainID: mockOrbitChainId, + partnerChainID: arbOneId, + isArbitrum: true, + isCustom: true, + isOrbit: true, + }, + } as const + + addCustomNetwork(mockCustomNetwork) + const chain = await getNetwork(mockOrbitChainId, 2) + expect(chain.chainID, fetchErrorMessage).to.be.eq(mockOrbitChainId) + }) + + it('fails to fetch a parent chain with `getNetwork` because it is the wrong layer', async function () { + let chain + try { + chain = await getNetwork(mainnetId, 2) + } catch (err) { + // should fail + expect(err).to.be.an('error') + expect((err as Error).message).to.be.eq( + `Unrecognized network ${mainnetId}.` + ) + } finally { + expect(chain, '`getNetwork` returned a result for a parent chain.').to + .be.undefined + } + }) + + it('fails to fetch an unrecognized L1 network', async () => { + const chainId = 9999 + try { + await getNetwork(chainId, 1) + expect.fail('Expected error was not thrown') + } catch (err) { + expect(err).to.be.instanceOf(Error) + expect((err as Error).message).to.be.eq( + `Unrecognized network ${chainId}.` + ) + } + }) + + it('fails to fetch an unrecognized L2 network', async () => { + const chainId = 9999 + try { + await getNetwork(chainId, 2) + expect.fail('Expected error was not thrown') + } catch (err) { + expect(err).to.be.instanceOf(Error) + expect((err as Error).message).to.be.eq( + `Unrecognized network ${chainId}.` + ) + } + }) + + it('fails to fetch a network in L1 until a child is added', async function () { + let parentChain + try { + parentChain = await getNetwork(arbOneId, 1) + } catch (err) { + // should fail + expect(err).to.be.an('error') + expect((err as Error).message).to.be.eq( + `Unrecognized network ${arbOneId}.` + ) + } finally { + expect( + parentChain, + '`getNetwork` returned a result for an Orbit chain.' + ).to.be.undefined + } + + const arbOneNetwork = await getL2Network(arbOneId) + + const mockCustomNetwork = { + customL2Network: { + ...arbOneNetwork, + chainID: mockOrbitChainId, + partnerChainID: arbOneId, + isArbitrum: true, + isCustom: true, + isOrbit: true, + }, + } as const + + addCustomNetwork(mockCustomNetwork) + parentChain = await getNetwork(arbOneId, 1) + expect(parentChain.chainID, fetchErrorMessage).to.be.eq(arbOneId) + }) }) }) From 842edcc16d5dff9aeb87a04a200894d9c09df9f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Mon, 18 Dec 2023 14:12:29 +0100 Subject: [PATCH 109/192] get rid of extra type --- src/lib/dataEntities/networks.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 3229995ab1..74526a372c 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -41,6 +41,7 @@ export interface L2Network extends Network { ethBridge: EthBridge partnerChainID: number isArbitrum: true + isOrbit?: boolean confirmPeriodBlocks: number retryableLifetimeSeconds: number nitroGenesisBlock: number @@ -55,10 +56,9 @@ export type ParentChain = | L1Network | (L2Network & Required>) -export type OrbitChain = L2Network & { isOrbit: true } -export type ChildChain = L2Network | OrbitChain +export type ChildChain = L2Network -type Chain = L1Network | L2Network | ParentChain | ChildChain | OrbitChain +type Chain = L1Network | L2Network | ParentChain | ChildChain export interface TokenBridge { l1GatewayRouter: string @@ -104,7 +104,7 @@ export interface ChildChains { } export interface OrbitChains { - [id: string]: OrbitChain + [id: string]: L2Network & { isOrbit: true } } const mainnetTokenBridge: TokenBridge = { @@ -372,8 +372,8 @@ const isL2Chain = (chain: Chain): chain is L2Network => { return chain && isChildChain(chain) && !isOrbitChain(chain) } -const isOrbitChain = (chain: Chain): chain is OrbitChain => { - return chain && 'isOrbit' in chain && chain.isOrbit +const isOrbitChain = (chain: any): chain is L2Network & { isOrbit: true } => { + return chain && typeof chain.isOrbit !== 'undefined' && chain.isOrbit } /** @@ -543,7 +543,7 @@ export const addCustomNetwork = ({ } | { customL1Network?: L2Network - customL2Network: OrbitChain + customL2Network: L2Network & { isOrbit: true } }): void => { if (customL1Network) { if (l1Networks[customL1Network.chainID]) { From bffe1f459a252612fce3c701add157adb33d9c23 Mon Sep 17 00:00:00 2001 From: Doug Date: Mon, 18 Dec 2023 15:12:15 -0500 Subject: [PATCH 110/192] improves tests and simplifies AssetBridger constructor --- src/lib/assetBridger/assetBridger.ts | 18 +--- src/lib/dataEntities/networks.ts | 9 +- tests/unit/network.test.ts | 155 +++++++++++++++++---------- 3 files changed, 108 insertions(+), 74 deletions(-) diff --git a/src/lib/assetBridger/assetBridger.ts b/src/lib/assetBridger/assetBridger.ts index 495ba07da5..24682a4460 100644 --- a/src/lib/assetBridger/assetBridger.ts +++ b/src/lib/assetBridger/assetBridger.ts @@ -16,12 +16,10 @@ /* eslint-env node */ 'use strict' -import { ArbSdkError } from '../dataEntities/errors' import { L1ContractTransaction } from '../message/L1Transaction' import { L2ContractTransaction } from '../message/L2Transaction' import { - l1Networks, L1Network, L2Network, getParentForNetwork, @@ -38,21 +36,7 @@ export abstract class AssetBridger { public readonly l1Network: L1Network public constructor(public readonly l2Network: L2Network) { - this.l1Network = l1Networks[l2Network.partnerChainID] - const parentChain = getParentForNetwork(l2Network) - if (!parentChain) { - throw new ArbSdkError( - `Unknown parent network chain id: ${l2Network.partnerChainID}` - ) - } - - const l1NetworkOrParentChain = this.l1Network || parentChain - - if (!l1NetworkOrParentChain) { - throw new ArbSdkError( - `Unknown parent network chain id: ${l2Network.partnerChainID}` - ) - } + this.l1Network = getParentForNetwork(l2Network) as L1Network } /** diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 74526a372c..69e6b8d3ff 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -52,6 +52,8 @@ export interface L2Network extends Network { depositTimeout: number } +//TODO: simplify + export type ParentChain = | L1Network | (L2Network & Required>) @@ -402,7 +404,7 @@ const getChildChains = () => getChainsByType(isChildChain) export const getParentForNetwork = (chain: Chain) => { if (!isChildChain(chain)) { - return undefined + throw new ArbSdkError(`Chain ${chain.chainID} is not a child chain.`) } const parentChain = networks[chain.partnerChainID] if (!parentChain || !isParentChain(parentChain)) { @@ -565,6 +567,11 @@ export const addCustomNetwork = ({ throw new ArbSdkError( `Custom network ${customL2Network.chainID} must have isCustom flag set to true` ) + } else if ( + customL2Network.isOrbit && + !isL2Chain(networks[customL2Network.partnerChainID]) + ) { + throw new ArbSdkError('Orbit chains must be paired with an L2 chain') } addNetwork(customL2Network) diff --git a/tests/unit/network.test.ts b/tests/unit/network.test.ts index 3f904c9fb0..ad4bdec81e 100644 --- a/tests/unit/network.test.ts +++ b/tests/unit/network.test.ts @@ -37,7 +37,7 @@ describe('Networks', () => { addCustomNetwork(mockCustomNetwork) - expect(await getNetwork(mockL2ChainId, 2)).to.be.ok + expect(await getL2Network(mockL2ChainId)).to.be.ok }) it('adds a custom L1 and L2 network', async function () { @@ -62,8 +62,8 @@ describe('Networks', () => { addCustomNetwork(mockCustomNetwork) - expect(await getNetwork(mockL1ChainId, 1)).to.be.ok - expect(await getNetwork(mockL2ChainId, 2)).to.be.ok + expect(await getL1Network(mockL1ChainId)).to.be.ok + expect(await getL2Network(mockL2ChainId)).to.be.ok }) it('adds a custom Orbit chain', async function () { @@ -85,21 +85,22 @@ describe('Networks', () => { expect(await getNetwork(mockOrbitChainId, 2)).to.be.ok }) - it('adds a custom L1 and Orbit chain', async function () { - const ethNetwork = await getL1Network(mainnetId) + it('adds a custom L2 and Orbit chain', async function () { const arbOneNetwork = await getL2Network(arbOneId) const mockCustomNetwork = { customL1Network: { - ...ethNetwork, - chainID: mockL1ChainId, - isArbitrum: false, + ...arbOneNetwork, + partnerChainID: mainnetId, + chainID: mockL2ChainId, + isArbitrum: true, isCustom: true, isOrbit: false, }, customL2Network: { ...arbOneNetwork, - chainID: mockL2ChainId, + chainID: mockOrbitChainId, + partnerChainID: mockL2ChainId, isArbitrum: true, isCustom: true, isOrbit: true, @@ -108,8 +109,8 @@ describe('Networks', () => { addCustomNetwork(mockCustomNetwork) - expect(await getNetwork(mockL1ChainId, 1)).to.be.ok - expect(await getNetwork(mockL2ChainId, 2)).to.be.ok + expect(await getL2Network(mockL2ChainId)).to.be.ok + expect(await getL2Network(mockOrbitChainId)).to.be.ok }) it('adds a custom L1, L2, and L3', async function () { @@ -148,9 +149,67 @@ describe('Networks', () => { addCustomNetwork(mockOrbitNetwork) - expect(await getNetwork(mockL1ChainId, 1)).to.be.ok - expect(await getNetwork(mockL2ChainId, 2)).to.be.ok - expect(await getNetwork(mockOrbitChainId, 2)).to.be.ok + expect(await getL1Network(mockL1ChainId)).to.be.ok + expect(await getL2Network(mockL2ChainId)).to.be.ok + expect(await getL2Network(mockOrbitChainId)).to.be.ok + }) + + it('fails to add an Orbit chain paired with default L1', async function () { + const arbOneNetwork = await getL2Network(arbOneId) + + const mockCustomNetwork = { + customL2Network: { + ...arbOneNetwork, + chainID: mockOrbitChainId, + partnerChainID: mainnetId, + isArbitrum: true, + isCustom: true, + isOrbit: true, + }, + } as const + + try { + addCustomNetwork(mockCustomNetwork) + } catch (err) { + // should fail + expect(err).to.be.an('error') + expect((err as Error).message).to.be.eq( + 'Orbit chains must be paired with an L2 chain' + ) + } + }) + + it('fails to add a custom L1 paired with an L3', async function () { + const ethNetwork = await getL1Network(mainnetId) + const arbOneNetwork = await getL2Network(arbOneId) + + const mockCustomNetwork = { + customL1Network: { + ...ethNetwork, + chainID: mockL1ChainId, + isArbitrum: false, + isCustom: true, + isOrbit: false, + }, + customL2Network: { + ...arbOneNetwork, + chainID: mockOrbitChainId, + partnerChainID: mockL1ChainId, + isArbitrum: true, + isCustom: true, + isOrbit: true, + }, + } as const + + try { + addCustomNetwork(mockCustomNetwork) + } catch (err) { + // should fail + expect(err).to.be.an('error') + expect((err as Error).message).to.be.eq( + 'Orbit chains must be paired with an L2 chain' + ) + } }) }) @@ -176,6 +235,25 @@ describe('Networks', () => { } }) + it('successfully fetches a parent chain with `getNetwork`', async function () { + const arbOneNetwork = await getL2Network(arbOneId) + + const mockCustomNetwork = { + customL2Network: { + ...arbOneNetwork, + chainID: mockOrbitChainId, + partnerChainID: arbOneId, + isArbitrum: true, + isCustom: true, + isOrbit: true, + }, + } as const + + addCustomNetwork(mockCustomNetwork) + const parentChain = await getL1Network(arbOneId) + expect(parentChain.chainID, fetchErrorMessage).to.be.eq(arbOneId) + }) + it('successfully fetches an L2 network with `getL2Network`', async function () { const network = await getL2Network(arbOneId) expect(network.chainID, fetchErrorMessage).to.be.eq(arbOneId) @@ -197,29 +275,10 @@ describe('Networks', () => { } }) - it('successfully fetches a parent chain with `getNetwork`', async function () { - const arbOneNetwork = await getL2Network(arbOneId) - - const mockCustomNetwork = { - customL2Network: { - ...arbOneNetwork, - chainID: mockOrbitChainId, - partnerChainID: arbOneId, - isArbitrum: true, - isCustom: true, - isOrbit: true, - }, - } as const - - addCustomNetwork(mockCustomNetwork) - const parentChain = await getNetwork(arbOneId, 1) - expect(parentChain.chainID, fetchErrorMessage).to.be.eq(arbOneId) - }) - - it('fails to fetch an Orbit chain with `getNetwork` because it is in the wrong layer', async function () { + it('fails to fetch an Orbit chain with `getL1Network`', async function () { let parentChain try { - parentChain = await getNetwork(mockOrbitChainId, 1) + parentChain = await getL1Network(mockOrbitChainId) } catch (err) { // should fail expect(err).to.be.an('error') @@ -234,7 +293,7 @@ describe('Networks', () => { } }) - it('successfully fetches an Orbit chain with `getNetwork`', async function () { + it('successfully fetches an Orbit chain with `getL2Network`', async function () { const arbOneNetwork = await getL2Network(arbOneId) const mockCustomNetwork = { @@ -253,26 +312,10 @@ describe('Networks', () => { expect(chain.chainID, fetchErrorMessage).to.be.eq(mockOrbitChainId) }) - it('fails to fetch a parent chain with `getNetwork` because it is the wrong layer', async function () { - let chain - try { - chain = await getNetwork(mainnetId, 2) - } catch (err) { - // should fail - expect(err).to.be.an('error') - expect((err as Error).message).to.be.eq( - `Unrecognized network ${mainnetId}.` - ) - } finally { - expect(chain, '`getNetwork` returned a result for a parent chain.').to - .be.undefined - } - }) - it('fails to fetch an unrecognized L1 network', async () => { const chainId = 9999 try { - await getNetwork(chainId, 1) + await getL1Network(chainId) expect.fail('Expected error was not thrown') } catch (err) { expect(err).to.be.instanceOf(Error) @@ -285,7 +328,7 @@ describe('Networks', () => { it('fails to fetch an unrecognized L2 network', async () => { const chainId = 9999 try { - await getNetwork(chainId, 2) + await getL2Network(chainId) expect.fail('Expected error was not thrown') } catch (err) { expect(err).to.be.instanceOf(Error) @@ -298,7 +341,7 @@ describe('Networks', () => { it('fails to fetch a network in L1 until a child is added', async function () { let parentChain try { - parentChain = await getNetwork(arbOneId, 1) + parentChain = await getL1Network(arbOneId) } catch (err) { // should fail expect(err).to.be.an('error') @@ -326,7 +369,7 @@ describe('Networks', () => { } as const addCustomNetwork(mockCustomNetwork) - parentChain = await getNetwork(arbOneId, 1) + parentChain = await getL1Network(arbOneId) expect(parentChain.chainID, fetchErrorMessage).to.be.eq(arbOneId) }) }) From d63c7b675148932bc0e6e492b6fa6511e337a622 Mon Sep 17 00:00:00 2001 From: Doug Date: Mon, 18 Dec 2023 15:40:11 -0500 Subject: [PATCH 111/192] Adds L1/L2Network types to asset bridger --- src/lib/assetBridger/assetBridger.ts | 4 ++-- src/lib/dataEntities/networks.ts | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib/assetBridger/assetBridger.ts b/src/lib/assetBridger/assetBridger.ts index 24682a4460..a48ef648c9 100644 --- a/src/lib/assetBridger/assetBridger.ts +++ b/src/lib/assetBridger/assetBridger.ts @@ -33,10 +33,10 @@ import { * Base for bridging assets from l1 to l2 and back */ export abstract class AssetBridger { - public readonly l1Network: L1Network + public readonly l1Network: L1Network | L2Network public constructor(public readonly l2Network: L2Network) { - this.l1Network = getParentForNetwork(l2Network) as L1Network + this.l1Network = getParentForNetwork(l2Network) } /** diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 69e6b8d3ff..c8addf99b5 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -412,6 +412,9 @@ export const getParentForNetwork = (chain: Chain) => { `ParentChain ${chain.partnerChainID} not recognized for Chain ${chain.chainID}.` ) } + if (isL2Chain(parentChain)) { + return parentChain as L2Network + } return parentChain } From c6a0efa078a1a7ee0f183e5f4c37dc983c695c32 Mon Sep 17 00:00:00 2001 From: Doug Date: Tue, 9 Jan 2024 11:42:55 -0500 Subject: [PATCH 112/192] update build script for l3 testing --- .github/workflows/build-test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 91cf33c4b6..7e73452a1e 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -129,7 +129,10 @@ jobs: strategy: matrix: node-version: [16, 18, 20] + orbit-test: ['1', '0'] needs: install + env: + ORBIT_TEST: ${{ matrix.orbit-test }} steps: - name: Checkout uses: actions/checkout@v3 @@ -146,6 +149,7 @@ jobs: uses: OffchainLabs/actions/run-nitro-test-node@main with: no-token-bridge: true + l3-node: ${{ matrix.orbit-test == '1' }} - name: Copy .env run: cp ./.env-sample ./.env From 626ce8f253f0e9b66410b0ae78bda322d8840f3c Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Tue, 9 Jan 2024 13:11:58 -0500 Subject: [PATCH 113/192] sets up tests to be orbit compatible --- .env-sample | 5 + scripts/testSetup.ts | 217 +++++++++++++++++++++++++++---- src/lib/dataEntities/networks.ts | 20 ++- src/lib/inbox/inbox.ts | 16 +-- 4 files changed, 217 insertions(+), 41 deletions(-) diff --git a/.env-sample b/.env-sample index a6987bd61f..94aace9990 100644 --- a/.env-sample +++ b/.env-sample @@ -1,7 +1,10 @@ ARB_URL="http://127.0.0.1:8547" ETH_URL="http://127.0.0.1:8545" +ORBIT_URL="http://127.0.0.1:3347" + ARB_KEY="b6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659" ETH_KEY="b6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659" +ORBIT_KEY="b6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659" INFURA_KEY="2323232323232323232" SHOULD_FORK="0" @@ -13,3 +16,5 @@ GOERLI_RPC="https://goerli.infura.io/v3/2323" GOERLI_ROLLUP_TESTNET_RPC="https://goerli-rollup.arbitrum.io/rpc" NOVA_RPC="soontm" + +ORBIT_TEST="0" diff --git a/scripts/testSetup.ts b/scripts/testSetup.ts index 5558054297..21a76a6fa2 100644 --- a/scripts/testSetup.ts +++ b/scripts/testSetup.ts @@ -27,8 +27,11 @@ import { getL1Network, getL2Network, addCustomNetwork, + isL1Chain, + isL2Chain, + isOrbitChain, } from '../src/lib/dataEntities/networks' -import { Signer } from 'ethers' +import { Signer, providers } from 'ethers' import { AdminErc20Bridger } from '../src/lib/assetBridger/erc20Bridger' import { execSync } from 'child_process' import { Bridge__factory } from '../src/lib/abi/factories/Bridge__factory' @@ -40,11 +43,27 @@ import { ArbSdkError } from '../src/lib/dataEntities/errors' dotenv.config() -export const config = { - arbUrl: process.env['ARB_URL'] as string, - ethUrl: process.env['ETH_URL'] as string, - arbKey: process.env['ARB_KEY'] as string, - ethKey: process.env['ETH_KEY'] as string, +const isTestingOrbitChains = process.env.ORBIT_TEST === '1' + +export const config = isTestingOrbitChains + ? { + arbUrl: process.env['ORBIT_URL'] as string, + ethUrl: process.env['ARB_URL'] as string, + arbKey: process.env['ORBIT_KEY'] as string, + ethKey: process.env['ARB_KEY'] as string, + } + : { + arbUrl: process.env['ARB_URL'] as string, + ethUrl: process.env['ETH_URL'] as string, + arbKey: process.env['ARB_KEY'] as string, + ethKey: process.env['ETH_KEY'] as string, + } + +type DeploymentData = { + bridge: string + inbox: string + ['sequencer-inbox']: string + rollup: string } function getDeploymentData(): string { @@ -66,22 +85,31 @@ function getDeploymentData(): string { throw new Error('nitro-testnode sequencer not found') } +function getL3DeploymentData() { + const dockerNames = ['nitro-testnode-l3node-1'] + for (const dockerName of dockerNames) { + try { + return execSync( + 'docker exec ' + dockerName + ' cat /config/l3deployment.json' + ).toString() + } catch { + // empty on purpose + } + } + throw new Error('nitro-testnode sequencer not found') +} + export const getCustomNetworks = async ( l1Url: string, l2Url: string ): Promise<{ l1Network: L1Network - l2Network: Omit + l2Network: L2Network }> => { const l1Provider = new JsonRpcProvider(l1Url) const l2Provider = new JsonRpcProvider(l2Url) const deploymentData = getDeploymentData() - const parsedDeploymentData = JSON.parse(deploymentData) as { - bridge: string - inbox: string - ['sequencer-inbox']: string - rollup: string - } + const parsedDeploymentData = JSON.parse(deploymentData) as DeploymentData const rollup = RollupAdminLogic__factory.connect( parsedDeploymentData.rollup, @@ -108,7 +136,7 @@ export const getCustomNetworks = async ( isArbitrum: false, } - const l2Network: Omit = { + const l2Network: L2Network = { chainID: l2NetworkInfo.chainId, confirmPeriodBlocks: confirmPeriodBlocks.toNumber(), ethBridge: { @@ -127,6 +155,22 @@ export const getCustomNetworks = async ( nitroGenesisBlock: 0, nitroGenesisL1Block: 0, depositTimeout: 900000, + tokenBridge: { + l1CustomGateway: '', + l1ERC20Gateway: '', + l1GatewayRouter: '', + l1MultiCall: '', + l1ProxyAdmin: '', + l1Weth: '', + l1WethGateway: '', + l2CustomGateway: '', + l2ERC20Gateway: '', + l2GatewayRouter: '', + l2Multicall: '', + l2ProxyAdmin: '', + l2Weth: '', + l2WethGateway: '', + }, } return { l1Network, @@ -134,22 +178,133 @@ export const getCustomNetworks = async ( } } +const setupOrbitNetworks = async (): Promise<{ + l1Network: L2Network + l2Network: L2Network & { isOrbit: true } +}> => { + const l1Provider = new JsonRpcProvider(process.env['ETH_URL']) + const l2Provider = new JsonRpcProvider(process.env['ARB_URL']) + const l3Provider = new JsonRpcProvider(process.env['ORBIT_URL']) + + const deploymentData = getDeploymentData() + const parsedDeploymentData = JSON.parse(deploymentData) as DeploymentData + + const l1NetworkInfo = await l1Provider.getNetwork() + const l2NetworkInfo = await l2Provider.getNetwork() + + const l1Network: L1Network = { + blockTime: 10, + chainID: l1NetworkInfo.chainId, + explorerUrl: '', + isCustom: true, + name: 'EthLocal', + partnerChainIDs: [l2NetworkInfo.chainId], + isArbitrum: false, + } + + const l2Network = await getCustomOrbitNetwork( + parsedDeploymentData, + l1Provider, + l2Provider + ) + + addCustomNetwork({ + customL1Network: l1Network, + customL2Network: l2Network, + }) + + const l3DeploymentData = getL3DeploymentData() + const parsedL3DeploymentData = JSON.parse(l3DeploymentData) as DeploymentData + const l3Network = (await getCustomOrbitNetwork( + parsedL3DeploymentData, + l2Provider, + l3Provider, + true + )) as L2Network & { isOrbit: true } + + return { + l1Network: l2Network, + l2Network: l3Network, + } +} + +async function getCustomOrbitNetwork( + deploymentData: DeploymentData, + l1Provider: providers.Provider, + l2Provider: providers.Provider, + isOrbit = false +) { + const rollup = RollupAdminLogic__factory.connect( + deploymentData.rollup, + l1Provider + ) + const confirmPeriodBlocks = await rollup.confirmPeriodBlocks() + + const bridge = Bridge__factory.connect(deploymentData.bridge, l1Provider) + const outboxAddr = await bridge.allowedOutboxList(0) + + const l1NetworkInfo = await l1Provider.getNetwork() + const l2NetworkInfo = await l2Provider.getNetwork() + + const l2Network: L2Network = { + chainID: l2NetworkInfo.chainId, + confirmPeriodBlocks: confirmPeriodBlocks.toNumber(), + ethBridge: { + bridge: deploymentData.bridge, + inbox: deploymentData.inbox, + outbox: outboxAddr, + rollup: deploymentData.rollup, + sequencerInbox: deploymentData['sequencer-inbox'], + }, + explorerUrl: '', + isArbitrum: true, + isCustom: true, + name: 'ArbLocal', + partnerChainID: l1NetworkInfo.chainId, + retryableLifetimeSeconds: 7 * 24 * 60 * 60, + nitroGenesisBlock: 0, + nitroGenesisL1Block: 0, + depositTimeout: 900000, + tokenBridge: { + l1CustomGateway: '', + l1ERC20Gateway: '', + l1GatewayRouter: '', + l1MultiCall: '', + l1ProxyAdmin: '', + l1Weth: '', + l1WethGateway: '', + l2CustomGateway: '', + l2ERC20Gateway: '', + l2GatewayRouter: '', + l2Multicall: '', + l2ProxyAdmin: '', + l2Weth: '', + l2WethGateway: '', + }, + isOrbit, + blockTime: 0.25, + } + + return l2Network +} + export const setupNetworks = async ( l1Deployer: Signer, l2Deployer: Signer, l1Url: string, l2Url: string ) => { - const { l1Network, l2Network: coreL2Network } = await getCustomNetworks( - l1Url, - l2Url - ) + const { l1Network, l2Network: coreL2Network } = isTestingOrbitChains + ? await setupOrbitNetworks() + : await getCustomNetworks(l1Url, l2Url) + const { l1: l1Contracts, l2: l2Contracts } = await deployErc20AndInit( l1Deployer, l2Deployer, coreL2Network.ethBridge.inbox ) - const l2Network: L2Network = { + + const l2Network: L2Network | (L2Network & { isOrbit: true }) = { ...coreL2Network, tokenBridge: { l1CustomGateway: l1Contracts.customGateway.address, @@ -170,10 +325,19 @@ export const setupNetworks = async ( }, } - addCustomNetwork({ - customL1Network: l1Network, - customL2Network: l2Network, - }) + if (isL1Chain(l1Network) && isL2Chain(l2Network)) { + addCustomNetwork({ + customL1Network: l1Network, + customL2Network: l2Network, + }) + } else if (isL2Chain(l1Network) && isOrbitChain(l2Network)) { + addCustomNetwork({ + customL1Network: l1Network, + customL2Network: l2Network, + }) + } else { + throw new Error('Invalid network pair') + } // also register the weth gateway // we add it here rather than in deployBridge because @@ -204,8 +368,8 @@ export const getSigner = (provider: JsonRpcProvider, key?: string) => { } export const testSetup = async (): Promise<{ - l1Network: L1Network - l2Network: L2Network + l1Network: L1Network | L2Network + l2Network: L2Network | (L2Network & { isOrbit: true }) l1Signer: Signer l2Signer: Signer erc20Bridger: Erc20Bridger @@ -225,7 +389,8 @@ export const testSetup = async (): Promise<{ const l1Signer = seed.connect(ethProvider) const l2Signer = seed.connect(arbProvider) - let setL1Network: L1Network, setL2Network: L2Network + let setL1Network: L1Network | L2Network, + setL2Network: L2Network | (L2Network & { isOrbit: true }) try { const l1Network = await getL1Network(l1Deployer) const l2Network = await getL2Network(l2Deployer) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index c86dcd02c0..ba5df4ee26 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -27,11 +27,11 @@ export interface Network { explorerUrl: string gif?: string isCustom: boolean + blockTime: number //seconds } export interface L1Network extends Network { partnerChainIDs: number[] - blockTime: number //seconds isArbitrum: false } @@ -52,8 +52,6 @@ export interface L2Network extends Network { depositTimeout: number } -//TODO: simplify - export type ParentChain = | L1Network | (L2Network & Required>) @@ -194,6 +192,7 @@ export const networks: Record = { * (Total timeout: 30 minutes) */ depositTimeout: 1800000, + blockTime: 0.25, }, 421613: { chainID: 421613, @@ -234,6 +233,7 @@ export const networks: Record = { * Wait 10 epochs there on goerli = 320 blocks. Each block is 12 seconds. */ depositTimeout: 3960000, + blockTime: 0.25, }, 42170: { chainID: 42170, @@ -275,6 +275,7 @@ export const networks: Record = { * (Total timeout: 30 minutes) */ depositTimeout: 1800000, + blockTime: 0.25, }, 421614: { chainID: 421614, @@ -312,6 +313,7 @@ export const networks: Record = { nitroGenesisBlock: 0, nitroGenesisL1Block: 0, depositTimeout: 1800000, + blockTime: 0.25, }, 23011913: { chainID: 23011913, @@ -348,6 +350,7 @@ export const networks: Record = { nitroGenesisBlock: 0, nitroGenesisL1Block: 0, depositTimeout: 900000, + blockTime: 0.25, }, } @@ -366,15 +369,17 @@ const isChildChain = (chain: Chain): chain is ChildChain => { return chain && 'partnerChainID' in chain } -const isL1Chain = (chain: Chain): chain is L1Network => { +export const isL1Chain = (chain: Chain): chain is L1Network => { return chain && isParentChain(chain) && !isChildChain(chain) } -const isL2Chain = (chain: Chain): chain is L2Network => { +export const isL2Chain = (chain: Chain): chain is L2Network => { return chain && isChildChain(chain) && !isOrbitChain(chain) } -const isOrbitChain = (chain: any): chain is L2Network & { isOrbit: true } => { +export const isOrbitChain = ( + chain: any +): chain is L2Network & { isOrbit: true } => { return chain && typeof chain.isOrbit !== 'undefined' && chain.isOrbit } @@ -464,7 +469,7 @@ export const getNetwork = async ( export const getL1Network = ( signerOrProviderOrChainID: SignerOrProvider | number -): Promise => { +): Promise => { return getNetwork(signerOrProviderOrChainID, 1) as Promise } export const getL2Network = ( @@ -634,6 +639,7 @@ export const addDefaultLocalNetwork = (): { l2Weth: '0x408Da76E87511429485C32E4Ad647DD14823Fdc4', l2WethGateway: '0x4A2bA922052bA54e29c5417bC979Daaf7D5Fe4f4', }, + blockTime: 0.25, } addCustomNetwork({ diff --git a/src/lib/inbox/inbox.ts b/src/lib/inbox/inbox.ts index 240bfb61b6..c73e1cd531 100644 --- a/src/lib/inbox/inbox.ts +++ b/src/lib/inbox/inbox.ts @@ -28,7 +28,7 @@ import { SequencerInbox__factory } from '../abi/factories/SequencerInbox__factor import { IInbox__factory } from '../abi/factories/IInbox__factory' import { RequiredPick } from '../utils/types' import { MessageDeliveredEvent } from '../abi/Bridge' -import { l1Networks, L2Network } from '../dataEntities/networks' +import { L2Network } from '../dataEntities/networks' import { SignerProviderUtils } from '../dataEntities/signerOrProvider' import { FetchedEvent, EventFetcher } from '../utils/eventFetcher' import { MultiCaller, CallInput } from '../utils/multicall' @@ -58,18 +58,18 @@ type RequiredTransactionRequestType = RequiredPick< */ export class InboxTools { private readonly l1Provider - private readonly l1Network + // private readonly l1Network constructor( private readonly l1Signer: Signer, private readonly l2Network: L2Network ) { this.l1Provider = SignerProviderUtils.getProviderOrThrow(this.l1Signer) - this.l1Network = l1Networks[l2Network.partnerChainID] - if (!this.l1Network) - throw new ArbSdkError( - `L1Network not found for chain id: ${l2Network.partnerChainID}.` - ) + // this.l1Network = l1Networks[l2Network.partnerChainID] + // if (!this.l1Network) + // throw new ArbSdkError( + // `L1Network not found for chain id: ${l2Network.partnerChainID}.` + // ) } /** @@ -90,7 +90,7 @@ export class InboxTools { // we take a long average block time of 14s // and always move at least 10 blocks - const diffBlocks = Math.max(Math.ceil(diff / this.l1Network.blockTime), 10) + const diffBlocks = Math.max(Math.ceil(diff / 0.25), 10) return await this.findFirstBlockBelow( blockNumber - diffBlocks, From 058337a16314d97f705138b3277f5cf9b5a9c7a1 Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Tue, 9 Jan 2024 13:21:59 -0500 Subject: [PATCH 114/192] reverts getL1Network type change --- src/lib/dataEntities/networks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index ba5df4ee26..1a3e55a36d 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -469,7 +469,7 @@ export const getNetwork = async ( export const getL1Network = ( signerOrProviderOrChainID: SignerOrProvider | number -): Promise => { +): Promise => { return getNetwork(signerOrProviderOrChainID, 1) as Promise } export const getL2Network = ( From 5f50b5ff894e9a838127a4d8dbb0fa1580b5942a Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Tue, 9 Jan 2024 13:27:39 -0500 Subject: [PATCH 115/192] fix blocktime --- scripts/testSetup.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/testSetup.ts b/scripts/testSetup.ts index 21a76a6fa2..a0451fab1c 100644 --- a/scripts/testSetup.ts +++ b/scripts/testSetup.ts @@ -171,7 +171,9 @@ export const getCustomNetworks = async ( l2Weth: '', l2WethGateway: '', }, + blockTime: 0.25, } + return { l1Network, l2Network, From d259c2ce945701b5f401c6532733c5e7dfcdaed6 Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Tue, 9 Jan 2024 14:05:26 -0500 Subject: [PATCH 116/192] testing with restored build file --- .github/workflows/build-test.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 7e73452a1e..db32980120 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -129,10 +129,10 @@ jobs: strategy: matrix: node-version: [16, 18, 20] - orbit-test: ['1', '0'] + # orbit-test: ['1', '0'] needs: install - env: - ORBIT_TEST: ${{ matrix.orbit-test }} + # env: + # ORBIT_TEST: ${{ matrix.orbit-test }} steps: - name: Checkout uses: actions/checkout@v3 @@ -149,7 +149,7 @@ jobs: uses: OffchainLabs/actions/run-nitro-test-node@main with: no-token-bridge: true - l3-node: ${{ matrix.orbit-test == '1' }} + # l3-node: ${{ matrix.orbit-test == '1' }} - name: Copy .env run: cp ./.env-sample ./.env From c32084ed30fa8a8a98ea5b7bf2800b56be0b27fc Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Tue, 9 Jan 2024 14:20:35 -0500 Subject: [PATCH 117/192] test with just l3-node --- .github/workflows/build-test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index db32980120..6f34647c38 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -150,6 +150,7 @@ jobs: with: no-token-bridge: true # l3-node: ${{ matrix.orbit-test == '1' }} + l3-node: true - name: Copy .env run: cp ./.env-sample ./.env From e26e0f0a8cb10412c6c82d57af2a2a62ec951036 Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Tue, 9 Jan 2024 14:49:16 -0500 Subject: [PATCH 118/192] run only orbit tests --- .github/workflows/build-test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 6f34647c38..2830f4a8dd 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -129,10 +129,10 @@ jobs: strategy: matrix: node-version: [16, 18, 20] - # orbit-test: ['1', '0'] + orbit-test: ['1', '0'] needs: install - # env: - # ORBIT_TEST: ${{ matrix.orbit-test }} + env: + ORBIT_TEST: ${{ matrix.orbit-test }} steps: - name: Checkout uses: actions/checkout@v3 From c60f510d49a062c4120f76f5efa3a701022a3875 Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Tue, 9 Jan 2024 15:23:00 -0500 Subject: [PATCH 119/192] fix testnode ref --- .github/workflows/build-test.yml | 4 ++-- scripts/testSetup.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 2830f4a8dd..4221fd04a6 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -148,9 +148,9 @@ jobs: - name: Set up the local node uses: OffchainLabs/actions/run-nitro-test-node@main with: + nitro-testnode-ref: use-tokenbridge-creator no-token-bridge: true - # l3-node: ${{ matrix.orbit-test == '1' }} - l3-node: true + l3-node: ${{ matrix.orbit-test == '1' }} - name: Copy .env run: cp ./.env-sample ./.env diff --git a/scripts/testSetup.ts b/scripts/testSetup.ts index a0451fab1c..eb2349af79 100644 --- a/scripts/testSetup.ts +++ b/scripts/testSetup.ts @@ -96,7 +96,7 @@ function getL3DeploymentData() { // empty on purpose } } - throw new Error('nitro-testnode sequencer not found') + throw new Error('nitro-testnode-l3node sequencer not found') } export const getCustomNetworks = async ( From 584bb429813e028b355e888777eb278884aa3a5d Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Tue, 9 Jan 2024 15:58:34 -0500 Subject: [PATCH 120/192] test without local network --- scripts/testSetup.ts | 50 ++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/scripts/testSetup.ts b/scripts/testSetup.ts index eb2349af79..912f597e0a 100644 --- a/scripts/testSetup.ts +++ b/scripts/testSetup.ts @@ -402,31 +402,31 @@ export const testSetup = async (): Promise<{ // the networks havent been added yet // check if theres an existing network available - const localNetworkFile = path.join(__dirname, '..', 'localNetwork.json') - if (fs.existsSync(localNetworkFile)) { - const { l1Network, l2Network } = JSON.parse( - fs.readFileSync(localNetworkFile).toString() - ) as { - l1Network: L1Network - l2Network: L2Network - } - addCustomNetwork({ - customL1Network: l1Network, - customL2Network: l2Network, - }) - setL1Network = l1Network - setL2Network = l2Network - } else { - // deploy a new network - const { l1Network, l2Network } = await setupNetworks( - l1Deployer, - l2Deployer, - config.ethUrl, - config.arbUrl - ) - setL1Network = l1Network - setL2Network = l2Network - } + // const localNetworkFile = path.join(__dirname, '..', 'localNetwork.json') + // if (fs.existsSync(localNetworkFile)) { + // const { l1Network, l2Network } = JSON.parse( + // fs.readFileSync(localNetworkFile).toString() + // ) as { + // l1Network: L1Network + // l2Network: L2Network + // } + // addCustomNetwork({ + // customL1Network: l1Network, + // customL2Network: l2Network, + // }) + // setL1Network = l1Network + // setL2Network = l2Network + // } else { + // deploy a new network + const { l1Network, l2Network } = await setupNetworks( + l1Deployer, + l2Deployer, + config.ethUrl, + config.arbUrl + ) + setL1Network = l1Network + setL2Network = l2Network + // } } const erc20Bridger = new Erc20Bridger(setL2Network) From 2cd6add438b8b99d335d15f5970478443242b60b Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Tue, 9 Jan 2024 16:08:57 -0500 Subject: [PATCH 121/192] sets up l1 before localnetwork.json for orbit --- scripts/testSetup.ts | 71 ++++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/scripts/testSetup.ts b/scripts/testSetup.ts index 912f597e0a..88488e1a9e 100644 --- a/scripts/testSetup.ts +++ b/scripts/testSetup.ts @@ -180,13 +180,12 @@ export const getCustomNetworks = async ( } } -const setupOrbitNetworks = async (): Promise<{ - l1Network: L2Network - l2Network: L2Network & { isOrbit: true } +const setupL1NetworkForOrbit = async (): Promise<{ + l2Network: L2Network + l2Provider: providers.Provider }> => { const l1Provider = new JsonRpcProvider(process.env['ETH_URL']) const l2Provider = new JsonRpcProvider(process.env['ARB_URL']) - const l3Provider = new JsonRpcProvider(process.env['ORBIT_URL']) const deploymentData = getDeploymentData() const parsedDeploymentData = JSON.parse(deploymentData) as DeploymentData @@ -215,6 +214,16 @@ const setupOrbitNetworks = async (): Promise<{ customL2Network: l2Network, }) + return { l2Network, l2Provider } +} + +const setupOrbitNetworks = async (): Promise<{ + l1Network: L2Network + l2Network: L2Network & { isOrbit: true } +}> => { + const { l2Network, l2Provider } = await setupL1NetworkForOrbit() + const l3Provider = new JsonRpcProvider(process.env['ORBIT_URL']) + const l3DeploymentData = getL3DeploymentData() const parsedL3DeploymentData = JSON.parse(l3DeploymentData) as DeploymentData const l3Network = (await getCustomOrbitNetwork( @@ -402,31 +411,35 @@ export const testSetup = async (): Promise<{ // the networks havent been added yet // check if theres an existing network available - // const localNetworkFile = path.join(__dirname, '..', 'localNetwork.json') - // if (fs.existsSync(localNetworkFile)) { - // const { l1Network, l2Network } = JSON.parse( - // fs.readFileSync(localNetworkFile).toString() - // ) as { - // l1Network: L1Network - // l2Network: L2Network - // } - // addCustomNetwork({ - // customL1Network: l1Network, - // customL2Network: l2Network, - // }) - // setL1Network = l1Network - // setL2Network = l2Network - // } else { - // deploy a new network - const { l1Network, l2Network } = await setupNetworks( - l1Deployer, - l2Deployer, - config.ethUrl, - config.arbUrl - ) - setL1Network = l1Network - setL2Network = l2Network - // } + const localNetworkFile = path.join(__dirname, '..', 'localNetwork.json') + if (fs.existsSync(localNetworkFile)) { + const { l1Network, l2Network } = JSON.parse( + fs.readFileSync(localNetworkFile).toString() + ) as { + l1Network: L1Network + l2Network: L2Network + } + if (isTestingOrbitChains) { + await setupL1NetworkForOrbit() + } + + addCustomNetwork({ + customL1Network: l1Network, + customL2Network: l2Network, + }) + setL1Network = l1Network + setL2Network = l2Network + } else { + // deploy a new network + const { l1Network, l2Network } = await setupNetworks( + l1Deployer, + l2Deployer, + config.ethUrl, + config.arbUrl + ) + setL1Network = l1Network + setL2Network = l2Network + } } const erc20Bridger = new Erc20Bridger(setL2Network) From e0bdd3477c34df3b9c5abe12850722cbfbf446af Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Tue, 9 Jan 2024 16:22:08 -0500 Subject: [PATCH 122/192] improve ci labels --- .github/workflows/build-test.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 4221fd04a6..1b33ff7131 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -130,6 +130,25 @@ jobs: matrix: node-version: [16, 18, 20] orbit-test: ['1', '0'] + include: + - node-version: 16 + orbit-test: 1 + label: 'Test (Integration) - Orbit - Node 16' + - node-version: 18 + orbit-test: 1 + label: 'Test (Integration) - Orbit - Node 18' + - node-version: 20 + orbit-test: 1 + label: 'Test (Integration) - Orbit - Node 20' + - node-version: 16 + orbit-test: 0 + label: 'Test (Integration) - No Orbit - Node 16' + - node-version: 18 + orbit-test: 0 + label: 'Test (Integration) - No Orbit - Node 18' + - node-version: 20 + orbit-test: 0 + label: 'Test (Integration) - No Orbit - Node 20' needs: install env: ORBIT_TEST: ${{ matrix.orbit-test }} From 743d168e653aa2cea5209a6ca45404988eda2b68 Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Tue, 9 Jan 2024 16:25:31 -0500 Subject: [PATCH 123/192] Update name to use matrix label --- .github/workflows/build-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 1b33ff7131..acb9224371 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -124,7 +124,7 @@ jobs: run: CI=true yarn test:unit test-integration: - name: Test (Integration) + name: ${{ matrix.label }} runs-on: ubuntu-latest strategy: matrix: From e2d9ba4951d3c5d82dcf9249ccbd0a8cf084b831 Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Tue, 9 Jan 2024 16:41:47 -0500 Subject: [PATCH 124/192] simplify name gen --- .github/workflows/build-test.yml | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index acb9224371..7218a8959e 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -124,31 +124,12 @@ jobs: run: CI=true yarn test:unit test-integration: - name: ${{ matrix.label }} + name: Test (Integration) - ${{ matrix.orbit-test == '1' && 'Orbit' || 'No Orbit' }} - Node ${{ matrix.node-version }} runs-on: ubuntu-latest strategy: matrix: node-version: [16, 18, 20] orbit-test: ['1', '0'] - include: - - node-version: 16 - orbit-test: 1 - label: 'Test (Integration) - Orbit - Node 16' - - node-version: 18 - orbit-test: 1 - label: 'Test (Integration) - Orbit - Node 18' - - node-version: 20 - orbit-test: 1 - label: 'Test (Integration) - Orbit - Node 20' - - node-version: 16 - orbit-test: 0 - label: 'Test (Integration) - No Orbit - Node 16' - - node-version: 18 - orbit-test: 0 - label: 'Test (Integration) - No Orbit - Node 18' - - node-version: 20 - orbit-test: 0 - label: 'Test (Integration) - No Orbit - Node 20' needs: install env: ORBIT_TEST: ${{ matrix.orbit-test }} From 8d11b9cc9b9739a37d7f3c5065cb816b21f916f6 Mon Sep 17 00:00:00 2001 From: spsjvc Date: Wed, 10 Jan 2024 15:25:41 +0100 Subject: [PATCH 125/192] clean up types (#389) --- scripts/testSetup.ts | 43 +++++++++++--------------------- src/lib/dataEntities/networks.ts | 10 +++++--- tests/unit/network.test.ts | 1 - 3 files changed, 22 insertions(+), 32 deletions(-) diff --git a/scripts/testSetup.ts b/scripts/testSetup.ts index 88488e1a9e..30ad469f67 100644 --- a/scripts/testSetup.ts +++ b/scripts/testSetup.ts @@ -27,9 +27,6 @@ import { getL1Network, getL2Network, addCustomNetwork, - isL1Chain, - isL2Chain, - isOrbitChain, } from '../src/lib/dataEntities/networks' import { Signer, providers } from 'ethers' import { AdminErc20Bridger } from '../src/lib/assetBridger/erc20Bridger' @@ -103,8 +100,8 @@ export const getCustomNetworks = async ( l1Url: string, l2Url: string ): Promise<{ - l1Network: L1Network - l2Network: L2Network + customL1Network: L1Network + customL2Network: L2Network }> => { const l1Provider = new JsonRpcProvider(l1Url) const l2Provider = new JsonRpcProvider(l2Url) @@ -172,11 +169,12 @@ export const getCustomNetworks = async ( l2WethGateway: '', }, blockTime: 0.25, + partnerChainIDs: [], } return { - l1Network, - l2Network, + customL1Network: l1Network, + customL2Network: l2Network, } } @@ -218,8 +216,8 @@ const setupL1NetworkForOrbit = async (): Promise<{ } const setupOrbitNetworks = async (): Promise<{ - l1Network: L2Network - l2Network: L2Network & { isOrbit: true } + customL1Network: L2Network + customL2Network: L2Network & { isOrbit: true } }> => { const { l2Network, l2Provider } = await setupL1NetworkForOrbit() const l3Provider = new JsonRpcProvider(process.env['ORBIT_URL']) @@ -234,8 +232,8 @@ const setupOrbitNetworks = async (): Promise<{ )) as L2Network & { isOrbit: true } return { - l1Network: l2Network, - l2Network: l3Network, + customL1Network: l2Network, + customL2Network: l3Network, } } @@ -294,6 +292,7 @@ async function getCustomOrbitNetwork( }, isOrbit, blockTime: 0.25, + partnerChainIDs: [], } return l2Network @@ -305,18 +304,18 @@ export const setupNetworks = async ( l1Url: string, l2Url: string ) => { - const { l1Network, l2Network: coreL2Network } = isTestingOrbitChains + const customNetworks = isTestingOrbitChains ? await setupOrbitNetworks() : await getCustomNetworks(l1Url, l2Url) const { l1: l1Contracts, l2: l2Contracts } = await deployErc20AndInit( l1Deployer, l2Deployer, - coreL2Network.ethBridge.inbox + customNetworks.customL2Network.ethBridge.inbox ) const l2Network: L2Network | (L2Network & { isOrbit: true }) = { - ...coreL2Network, + ...customNetworks.customL2Network, tokenBridge: { l1CustomGateway: l1Contracts.customGateway.address, l1ERC20Gateway: l1Contracts.standardGateway.address, @@ -336,19 +335,7 @@ export const setupNetworks = async ( }, } - if (isL1Chain(l1Network) && isL2Chain(l2Network)) { - addCustomNetwork({ - customL1Network: l1Network, - customL2Network: l2Network, - }) - } else if (isL2Chain(l1Network) && isOrbitChain(l2Network)) { - addCustomNetwork({ - customL1Network: l1Network, - customL2Network: l2Network, - }) - } else { - throw new Error('Invalid network pair') - } + addCustomNetwork(customNetworks) // also register the weth gateway // we add it here rather than in deployBridge because @@ -366,7 +353,7 @@ export const setupNetworks = async ( ).waitForL2(l2Deployer) return { - l1Network, + l1Network: customNetworks.customL1Network, l2Network, } } diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 1a3e55a36d..a5599bbe76 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -28,15 +28,14 @@ export interface Network { gif?: string isCustom: boolean blockTime: number //seconds + partnerChainIDs: number[] } export interface L1Network extends Network { - partnerChainIDs: number[] isArbitrum: false } export interface L2Network extends Network { - partnerChainIDs?: number[] tokenBridge: TokenBridge ethBridge: EthBridge partnerChainID: number @@ -193,6 +192,7 @@ export const networks: Record = { */ depositTimeout: 1800000, blockTime: 0.25, + partnerChainIDs: [], }, 421613: { chainID: 421613, @@ -234,6 +234,7 @@ export const networks: Record = { */ depositTimeout: 3960000, blockTime: 0.25, + partnerChainIDs: [], }, 42170: { chainID: 42170, @@ -276,6 +277,7 @@ export const networks: Record = { */ depositTimeout: 1800000, blockTime: 0.25, + partnerChainIDs: [], }, 421614: { chainID: 421614, @@ -351,6 +353,7 @@ export const networks: Record = { nitroGenesisL1Block: 0, depositTimeout: 900000, blockTime: 0.25, + partnerChainIDs: [], }, } @@ -469,7 +472,7 @@ export const getNetwork = async ( export const getL1Network = ( signerOrProviderOrChainID: SignerOrProvider | number -): Promise => { +): Promise => { return getNetwork(signerOrProviderOrChainID, 1) as Promise } export const getL2Network = ( @@ -640,6 +643,7 @@ export const addDefaultLocalNetwork = (): { l2WethGateway: '0x4A2bA922052bA54e29c5417bC979Daaf7D5Fe4f4', }, blockTime: 0.25, + partnerChainIDs: [], } addCustomNetwork({ diff --git a/tests/unit/network.test.ts b/tests/unit/network.test.ts index ad4bdec81e..f632fae7cd 100644 --- a/tests/unit/network.test.ts +++ b/tests/unit/network.test.ts @@ -123,7 +123,6 @@ describe('Networks', () => { chainID: mockL1ChainId, isArbitrum: false, isCustom: true, - isOrbit: false, }, customL2Network: { ...arbOneNetwork, From 0b5d1003ddb51b6a7dfe343469d848cb7b841624 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 10 Jan 2024 15:43:52 +0100 Subject: [PATCH 126/192] clean up plus docs --- src/lib/dataEntities/constants.ts | 2 ++ src/lib/dataEntities/networks.ts | 28 ++++++++++++++++++++-------- src/lib/inbox/inbox.ts | 19 ++++++++++--------- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/lib/dataEntities/constants.ts b/src/lib/dataEntities/constants.ts index b4dda70fb2..4fea86d645 100644 --- a/src/lib/dataEntities/constants.ts +++ b/src/lib/dataEntities/constants.ts @@ -33,6 +33,8 @@ export const ARB_GAS_INFO = '0x000000000000000000000000000000000000006C' export const ARB_STATISTICS = '0x000000000000000000000000000000000000006F' +export const ARB_MINIMUM_BLOCK_TIME_IN_SECONDS = 0.25 + /** * The offset added to an L1 address to get the corresponding L2 address */ diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index a5599bbe76..bebfb2d8d5 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -18,7 +18,10 @@ import { SignerOrProvider, SignerProviderUtils } from './signerOrProvider' import { ArbSdkError } from '../dataEntities/errors' -import { SEVEN_DAYS_IN_SECONDS } from './constants' +import { + SEVEN_DAYS_IN_SECONDS, + ARB_MINIMUM_BLOCK_TIME_IN_SECONDS, +} from './constants' import { RollupAdminLogic__factory } from '../abi/factories/RollupAdminLogic__factory' export interface Network { @@ -27,7 +30,13 @@ export interface Network { explorerUrl: string gif?: string isCustom: boolean - blockTime: number //seconds + /** + * Minimum possible block time for the chain (in seconds). + */ + blockTime: number + /** + * Chain ids of children chains. + */ partnerChainIDs: number[] } @@ -38,6 +47,9 @@ export interface L1Network extends Network { export interface L2Network extends Network { tokenBridge: TokenBridge ethBridge: EthBridge + /** + * Chain id of the parent chain. + */ partnerChainID: number isArbitrum: true isOrbit?: boolean @@ -191,7 +203,7 @@ export const networks: Record = { * (Total timeout: 30 minutes) */ depositTimeout: 1800000, - blockTime: 0.25, + blockTime: ARB_MINIMUM_BLOCK_TIME_IN_SECONDS, partnerChainIDs: [], }, 421613: { @@ -233,7 +245,7 @@ export const networks: Record = { * Wait 10 epochs there on goerli = 320 blocks. Each block is 12 seconds. */ depositTimeout: 3960000, - blockTime: 0.25, + blockTime: ARB_MINIMUM_BLOCK_TIME_IN_SECONDS, partnerChainIDs: [], }, 42170: { @@ -276,7 +288,7 @@ export const networks: Record = { * (Total timeout: 30 minutes) */ depositTimeout: 1800000, - blockTime: 0.25, + blockTime: ARB_MINIMUM_BLOCK_TIME_IN_SECONDS, partnerChainIDs: [], }, 421614: { @@ -315,7 +327,7 @@ export const networks: Record = { nitroGenesisBlock: 0, nitroGenesisL1Block: 0, depositTimeout: 1800000, - blockTime: 0.25, + blockTime: ARB_MINIMUM_BLOCK_TIME_IN_SECONDS, }, 23011913: { chainID: 23011913, @@ -352,7 +364,7 @@ export const networks: Record = { nitroGenesisBlock: 0, nitroGenesisL1Block: 0, depositTimeout: 900000, - blockTime: 0.25, + blockTime: ARB_MINIMUM_BLOCK_TIME_IN_SECONDS, partnerChainIDs: [], }, } @@ -642,7 +654,7 @@ export const addDefaultLocalNetwork = (): { l2Weth: '0x408Da76E87511429485C32E4Ad647DD14823Fdc4', l2WethGateway: '0x4A2bA922052bA54e29c5417bC979Daaf7D5Fe4f4', }, - blockTime: 0.25, + blockTime: ARB_MINIMUM_BLOCK_TIME_IN_SECONDS, partnerChainIDs: [], } diff --git a/src/lib/inbox/inbox.ts b/src/lib/inbox/inbox.ts index c73e1cd531..77af736d26 100644 --- a/src/lib/inbox/inbox.ts +++ b/src/lib/inbox/inbox.ts @@ -28,7 +28,11 @@ import { SequencerInbox__factory } from '../abi/factories/SequencerInbox__factor import { IInbox__factory } from '../abi/factories/IInbox__factory' import { RequiredPick } from '../utils/types' import { MessageDeliveredEvent } from '../abi/Bridge' -import { L2Network } from '../dataEntities/networks' +import { + L1Network, + L2Network, + getParentForNetwork, +} from '../dataEntities/networks' import { SignerProviderUtils } from '../dataEntities/signerOrProvider' import { FetchedEvent, EventFetcher } from '../utils/eventFetcher' import { MultiCaller, CallInput } from '../utils/multicall' @@ -57,19 +61,16 @@ type RequiredTransactionRequestType = RequiredPick< * Tools for interacting with the inbox and bridge contracts */ export class InboxTools { - private readonly l1Provider - // private readonly l1Network + // by L1 we really mean "parent chain", the misnomer will be fixed in the next major version + private readonly l1Provider: Provider + private readonly l1Network: L1Network | L2Network constructor( private readonly l1Signer: Signer, private readonly l2Network: L2Network ) { this.l1Provider = SignerProviderUtils.getProviderOrThrow(this.l1Signer) - // this.l1Network = l1Networks[l2Network.partnerChainID] - // if (!this.l1Network) - // throw new ArbSdkError( - // `L1Network not found for chain id: ${l2Network.partnerChainID}.` - // ) + this.l1Network = getParentForNetwork(l2Network) } /** @@ -90,7 +91,7 @@ export class InboxTools { // we take a long average block time of 14s // and always move at least 10 blocks - const diffBlocks = Math.max(Math.ceil(diff / 0.25), 10) + const diffBlocks = Math.max(Math.ceil(diff / this.l1Network.blockTime), 10) return await this.findFirstBlockBelow( blockNumber - diffBlocks, From 83790456dcdadab7ec4c7117894dd89319240785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Thu, 11 Jan 2024 09:49:11 +0100 Subject: [PATCH 127/192] add test for l1Networks and l2Networks --- tests/unit/network.test.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/unit/network.test.ts b/tests/unit/network.test.ts index f632fae7cd..5a87034dc1 100644 --- a/tests/unit/network.test.ts +++ b/tests/unit/network.test.ts @@ -5,6 +5,8 @@ import { addCustomNetwork, getL1Network, getL2Network, + l1Networks, + l2Networks, } from '../../src/lib/dataEntities/networks' const mainnetId = 1 @@ -372,4 +374,28 @@ describe('Networks', () => { expect(parentChain.chainID, fetchErrorMessage).to.be.eq(arbOneId) }) }) + + // todo: this could be a snapshot test + it('returns correct L1 networks', () => { + const l1NetworksEntries = Object.entries(l1Networks) + const l1NetworksKeys = l1NetworksEntries.map(([key]) => key) + + const expected = [1, 1338, 5, 11155111].map(id => id.toString()) + + expect(l1NetworksKeys).to.have.length(expected.length) + expect(l1NetworksKeys).to.have.members(expected) + }) + + // todo: this could be a snapshot test + it('returns correct L2 networks', () => { + const l2NetworksEntries = Object.entries(l2Networks) + const l2NetworksKeys = l2NetworksEntries.map(([key]) => key) + + const expected = [42161, 421613, 42170, 421614, 23011913] + // + .map(id => id.toString()) + + expect(l2NetworksKeys).to.have.length(expected.length) + expect(l2NetworksKeys).to.have.members(expected) + }) }) From 0c97ade9abfed6ed9c679b67d645ffd59d5d3e73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Thu, 11 Jan 2024 10:13:07 +0100 Subject: [PATCH 128/192] wrap in describe --- tests/unit/network.test.ts | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/tests/unit/network.test.ts b/tests/unit/network.test.ts index 5a87034dc1..b8ac2b0f1f 100644 --- a/tests/unit/network.test.ts +++ b/tests/unit/network.test.ts @@ -375,27 +375,29 @@ describe('Networks', () => { }) }) - // todo: this could be a snapshot test - it('returns correct L1 networks', () => { - const l1NetworksEntries = Object.entries(l1Networks) - const l1NetworksKeys = l1NetworksEntries.map(([key]) => key) + describe('returns correct networks', () => { + // todo: this could be a snapshot test + it('returns correct L1 networks', () => { + const l1NetworksEntries = Object.entries(l1Networks) + const l1NetworksKeys = l1NetworksEntries.map(([key]) => key) - const expected = [1, 1338, 5, 11155111].map(id => id.toString()) + const expected = [1, 1338, 5, 11155111].map(id => id.toString()) - expect(l1NetworksKeys).to.have.length(expected.length) - expect(l1NetworksKeys).to.have.members(expected) - }) + expect(l1NetworksKeys).to.have.length(expected.length) + expect(l1NetworksKeys).to.have.members(expected) + }) - // todo: this could be a snapshot test - it('returns correct L2 networks', () => { - const l2NetworksEntries = Object.entries(l2Networks) - const l2NetworksKeys = l2NetworksEntries.map(([key]) => key) + // todo: this could be a snapshot test + it('returns correct L2 networks', () => { + const l2NetworksEntries = Object.entries(l2Networks) + const l2NetworksKeys = l2NetworksEntries.map(([key]) => key) - const expected = [42161, 421613, 42170, 421614, 23011913] - // - .map(id => id.toString()) + const expected = [42161, 421613, 42170, 421614, 23011913] + // + .map(id => id.toString()) - expect(l2NetworksKeys).to.have.length(expected.length) - expect(l2NetworksKeys).to.have.members(expected) + expect(l2NetworksKeys).to.have.length(expected.length) + expect(l2NetworksKeys).to.have.members(expected) + }) }) }) From 18f6d851b78cc52ec5562bd072787889ca4a444d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Thu, 11 Jan 2024 10:25:03 +0100 Subject: [PATCH 129/192] docs --- src/lib/assetBridger/assetBridger.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/lib/assetBridger/assetBridger.ts b/src/lib/assetBridger/assetBridger.ts index a48ef648c9..9e1079b613 100644 --- a/src/lib/assetBridger/assetBridger.ts +++ b/src/lib/assetBridger/assetBridger.ts @@ -30,9 +30,12 @@ import { } from '../dataEntities/signerOrProvider' /** - * Base for bridging assets from l1 to l2 and back + * Base for bridging assets from a parent chain to a child chain and back. */ export abstract class AssetBridger { + /** + * The parent chain. Could be an L1 or an L2. + */ public readonly l1Network: L1Network | L2Network public constructor(public readonly l2Network: L2Network) { @@ -40,7 +43,7 @@ export abstract class AssetBridger { } /** - * Check the signer/provider matches the L1 network or the Parent Chain, throws if not + * Check the signer/provider matches the parent chain, throws if not. * @param sop */ protected async checkL1Network(sop: SignerOrProvider): Promise { @@ -48,7 +51,7 @@ export abstract class AssetBridger { } /** - * Check the signer/provider matches the l2Network, throws if not + * Check the signer/provider matches the child chain, throws if not. * @param sop */ protected async checkL2Network(sop: SignerOrProvider): Promise { @@ -56,13 +59,13 @@ export abstract class AssetBridger { } /** - * Transfer assets from L1 to L2 + * Transfer assets from parent chain to child chain. * @param params */ public abstract deposit(params: DepositParams): Promise /** - * Transfer assets from L2 to L1 + * Transfer assets from child chain to parent chain. * @param params */ public abstract withdraw( From b775779cb4cb3c5769261ebde60b18f89f4b0a5f Mon Sep 17 00:00:00 2001 From: spsjvc Date: Thu, 11 Jan 2024 19:52:28 +0100 Subject: [PATCH 130/192] refactor: clean up networks (#390) --- scripts/testSetup.ts | 39 +++--- src/lib/dataEntities/networks.ts | 205 +++++++++++++++---------------- src/lib/utils/multicall.ts | 4 +- tests/unit/network.test.ts | 14 --- 4 files changed, 122 insertions(+), 140 deletions(-) diff --git a/scripts/testSetup.ts b/scripts/testSetup.ts index 30ad469f67..5a891d5509 100644 --- a/scripts/testSetup.ts +++ b/scripts/testSetup.ts @@ -217,19 +217,18 @@ const setupL1NetworkForOrbit = async (): Promise<{ const setupOrbitNetworks = async (): Promise<{ customL1Network: L2Network - customL2Network: L2Network & { isOrbit: true } + customL2Network: L2Network }> => { const { l2Network, l2Provider } = await setupL1NetworkForOrbit() const l3Provider = new JsonRpcProvider(process.env['ORBIT_URL']) const l3DeploymentData = getL3DeploymentData() const parsedL3DeploymentData = JSON.parse(l3DeploymentData) as DeploymentData - const l3Network = (await getCustomOrbitNetwork( + const l3Network = await getCustomOrbitNetwork( parsedL3DeploymentData, l2Provider, - l3Provider, - true - )) as L2Network & { isOrbit: true } + l3Provider + ) return { customL1Network: l2Network, @@ -240,8 +239,7 @@ const setupOrbitNetworks = async (): Promise<{ async function getCustomOrbitNetwork( deploymentData: DeploymentData, l1Provider: providers.Provider, - l2Provider: providers.Provider, - isOrbit = false + l2Provider: providers.Provider ) { const rollup = RollupAdminLogic__factory.connect( deploymentData.rollup, @@ -290,7 +288,6 @@ async function getCustomOrbitNetwork( l2Weth: '', l2WethGateway: '', }, - isOrbit, blockTime: 0.25, partnerChainIDs: [], } @@ -314,7 +311,7 @@ export const setupNetworks = async ( customNetworks.customL2Network.ethBridge.inbox ) - const l2Network: L2Network | (L2Network & { isOrbit: true }) = { + const l2Network: L2Network = { ...customNetworks.customL2Network, tokenBridge: { l1CustomGateway: l1Contracts.customGateway.address, @@ -335,7 +332,13 @@ export const setupNetworks = async ( }, } - addCustomNetwork(customNetworks) + // in case of L3, we only need to add the L3, as L1 and L2 were registered inside "setupL1NetworkForOrbit" + // register the network with the newly deployed token bridge contracts + if (isTestingOrbitChains) { + addCustomNetwork({ customL2Network: l2Network }) + } else { + addCustomNetwork({ ...customNetworks, customL2Network: l2Network }) + } // also register the weth gateway // we add it here rather than in deployBridge because @@ -367,7 +370,7 @@ export const getSigner = (provider: JsonRpcProvider, key?: string) => { export const testSetup = async (): Promise<{ l1Network: L1Network | L2Network - l2Network: L2Network | (L2Network & { isOrbit: true }) + l2Network: L2Network l1Signer: Signer l2Signer: Signer erc20Bridger: Erc20Bridger @@ -387,8 +390,7 @@ export const testSetup = async (): Promise<{ const l1Signer = seed.connect(ethProvider) const l2Signer = seed.connect(arbProvider) - let setL1Network: L1Network | L2Network, - setL2Network: L2Network | (L2Network & { isOrbit: true }) + let setL1Network: L1Network | L2Network, setL2Network: L2Network try { const l1Network = await getL1Network(l1Deployer) const l2Network = await getL2Network(l2Deployer) @@ -406,14 +408,17 @@ export const testSetup = async (): Promise<{ l1Network: L1Network l2Network: L2Network } + if (isTestingOrbitChains) { await setupL1NetworkForOrbit() + addCustomNetwork({ customL2Network: l2Network }) + } else { + addCustomNetwork({ + customL1Network: l1Network, + customL2Network: l2Network, + }) } - addCustomNetwork({ - customL1Network: l1Network, - customL2Network: l2Network, - }) setL1Network = l1Network setL2Network = l2Network } else { diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index bebfb2d8d5..e3e4f6e31f 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -40,10 +40,16 @@ export interface Network { partnerChainIDs: number[] } +/** + * Represents an L1 chain, e.g. Ethereum Mainnet or Sepolia. + */ export interface L1Network extends Network { isArbitrum: false } +/** + * Represents an Arbitrum chain, e.g. Arbitrum One, Arbitrum Sepolia, or an L3 chain. + */ export interface L2Network extends Network { tokenBridge: TokenBridge ethBridge: EthBridge @@ -52,7 +58,6 @@ export interface L2Network extends Network { */ partnerChainID: number isArbitrum: true - isOrbit?: boolean confirmPeriodBlocks: number retryableLifetimeSeconds: number nitroGenesisBlock: number @@ -63,14 +68,6 @@ export interface L2Network extends Network { depositTimeout: number } -export type ParentChain = - | L1Network - | (L2Network & Required>) - -export type ChildChain = L2Network - -type Chain = L1Network | L2Network | ParentChain | ChildChain - export interface TokenBridge { l1GatewayRouter: string l2GatewayRouter: string @@ -107,15 +104,8 @@ export interface L2Networks { [id: string]: L2Network } -export interface ParentChains { - [id: string]: ParentChain -} -export interface ChildChains { - [id: string]: ChildChain -} - -export interface OrbitChains { - [id: string]: L2Network & { isOrbit: true } +export interface Networks { + [id: string]: L1Network | L2Network } const mainnetTokenBridge: TokenBridge = { @@ -147,7 +137,10 @@ const mainnetETHBridge: EthBridge = { }, } -export const networks: Record = { +/** + * Storage for all networks, either L1, L2 or L3. + */ +export const networks: Networks = { 1: { chainID: 1, name: 'Mainnet', @@ -369,33 +362,25 @@ export const networks: Record = { }, } -/** Determines if a chain is a parent of *any* other chain. */ -const isParentChain = (chain: Chain): chain is ParentChain => { - return ( - chain && - 'partnerChainIDs' in chain && - Array.isArray(chain.partnerChainIDs) && - chain.partnerChainIDs.length > 0 - ) -} - -/** Determines if a chain is a child of *any* other chain. */ -const isChildChain = (chain: Chain): chain is ChildChain => { - return chain && 'partnerChainID' in chain -} - -export const isL1Chain = (chain: Chain): chain is L1Network => { - return chain && isParentChain(chain) && !isChildChain(chain) +/** + * Determines if a chain is a parent of *any* other chain. Could be an L1 or an L2 chain. + */ +const isParentChain = (chain: L1Network | L2Network): boolean => { + return chain.partnerChainIDs.length > 0 } -export const isL2Chain = (chain: Chain): chain is L2Network => { - return chain && isChildChain(chain) && !isOrbitChain(chain) +/** + * Determines if a chain is an Arbitrum chain. Could be an L2 or an L3 chain. + */ +const isArbitrumChain = (chain: L1Network | L2Network): chain is L2Network => { + return chain.isArbitrum } -export const isOrbitChain = ( - chain: any -): chain is L2Network & { isOrbit: true } => { - return chain && typeof chain.isOrbit !== 'undefined' && chain.isOrbit +/** + * Determines if a chain is specifically an L1 chain (not L2 or L3). + */ +export const isL1Chain = (chain: L1Network | L2Network): chain is L1Network => { + return !chain.isArbitrum } /** @@ -404,7 +389,7 @@ export const isOrbitChain = ( * @return An object with only the filtered chains. */ const getChainsByType = ( - filterFn: (chain: Chain) => boolean + filterFn: (chain: L1Network | L2Network) => boolean ): T => { return Object.entries(networks).reduce( (accumulator, [chainId, chainData]) => { @@ -418,39 +403,42 @@ const getChainsByType = ( } const getL1Chains = () => getChainsByType(isL1Chain) -const getL2Chains = () => getChainsByType(isL2Chain) -const getParentChains = () => getChainsByType(isParentChain) -const getChildChains = () => getChainsByType(isChildChain) +const getArbitrumChains = () => getChainsByType(isArbitrumChain) +const getParentChains = () => getChainsByType(isParentChain) -export const getParentForNetwork = (chain: Chain) => { - if (!isChildChain(chain)) { - throw new ArbSdkError(`Chain ${chain.chainID} is not a child chain.`) +/** + * Returns the parent chain for the given chain. + */ +export const getParentForNetwork = (chain: L1Network | L2Network) => { + if (!isArbitrumChain(chain)) { + throw new ArbSdkError(`Chain ${chain.chainID} is not an Arbitrum chain.`) } - const parentChain = networks[chain.partnerChainID] + + const parentChain: L1Network | L2Network | undefined = + networks[chain.partnerChainID] + if (!parentChain || !isParentChain(parentChain)) { throw new ArbSdkError( - `ParentChain ${chain.partnerChainID} not recognized for Chain ${chain.chainID}.` + `Parent chain ${chain.partnerChainID} not recognized for chain ${chain.chainID}.` ) } - if (isL2Chain(parentChain)) { - return parentChain as L2Network - } + return parentChain } -const getChildrenForNetwork = (chain: Chain) => { - if (!isParentChain(chain)) { - return undefined - } - const childChains = getChildChains() - const children = Object.values(childChains).filter( - childChain => childChain.partnerChainID === chain.chainID +/** + * Returns a list of children chains for the given chain. + */ +const getChildrenForNetwork = (chain: L1Network | L2Network): L2Network[] => { + const arbitrumChains = getArbitrumChains() + + return Object.values(arbitrumChains).filter( + arbitrumChain => arbitrumChain.partnerChainID === chain.chainID ) - return children } export let l1Networks: L1Networks = getL1Chains() -export let l2Networks: L2Networks = getL2Chains() +export let l2Networks: L2Networks = getArbitrumChains() export const getNetwork = async ( signerOrProviderOrChainID: SignerOrProvider | number, @@ -468,25 +456,35 @@ export const getNetwork = async ( return chainId })() - let network + let network: L1Network | L2Network | undefined = undefined + if (layer === 1) { network = getParentChains()[chainID] } else { - network = getChildChains()[chainID] + network = getArbitrumChains()[chainID] } - if (network) { - return network - } else { + if (!network) { throw new ArbSdkError(`Unrecognized network ${chainID}.`) } + + return network } +/** + * Returns the parent chain associated with the given signer, provider or chain id. Could be an L1 chain or an Arbitrum chain. + * Throws if the chain is not a parent chain. + */ export const getL1Network = ( signerOrProviderOrChainID: SignerOrProvider | number ): Promise => { return getNetwork(signerOrProviderOrChainID, 1) as Promise } + +/** + * Returns the Arbitrum chain associated with the given signer, provider or chain id. + * Throws if the chain is not an Arbitrum chain. + */ export const getL2Network = ( signerOrProviderOrChainID: SignerOrProvider | number ): Promise => { @@ -524,39 +522,42 @@ export const getEthBridgeInformation = async ( } } -const addNetwork = (network: Chain) => { +const addNetwork = (network: L1Network | L2Network) => { + // store the network with the rest of the networks networks[network.chainID] = network + + // if it's a parent chain (L1 or L2), assign it as parent to all the children if (isParentChain(network)) { const children = getChildrenForNetwork(network) - children?.forEach(child => { - if (child) { - child.partnerChainID = network.chainID - } + + children.forEach(child => { + child.partnerChainID = network.chainID }) } - if (isChildChain(network)) { - const parent = networks[network.partnerChainID] - if (parent) { - parent.partnerChainIDs = [ - ...(parent.partnerChainIDs ?? []), - network.chainID, - ] - } else { + // if it's an arbitrum chain, add it to the parent's list of children + if (isArbitrumChain(network)) { + const parent: L1Network | L2Network | undefined = + networks[network.partnerChainID] + + if (!parent) { throw new ArbSdkError( - `Network ${network.chainID}'s partner network, ${network.partnerChainID}, not recognized` + `Network ${network.chainID}'s parent network ${network.partnerChainID} is not recognized` ) } + + parent.partnerChainIDs = [...parent.partnerChainIDs, network.chainID] } l1Networks = getL1Chains() - l2Networks = getL2Chains() + l2Networks = getArbitrumChains() } /** - * Adds custom L1 and L2 networks. These networks will be returned in `getL1Network` and `getL2Network`. - * @param customL1Network - * @param customL2Network + * Registers a pair of custom chains (parent and child). These networks will be returned in `getL1Network` and `getL2Network`, respectively. + * + * @param customL1Network the parent chain (could be an L1 or an L2) + * @param customL2Network the child chain (could be an L2 or an L3) */ export const addCustomNetwork = ({ customL1Network, @@ -568,10 +569,14 @@ export const addCustomNetwork = ({ } | { customL1Network?: L2Network - customL2Network: L2Network & { isOrbit: true } + customL2Network: L2Network }): void => { if (customL1Network) { - if (l1Networks[customL1Network.chainID]) { + // check the if the parent chain is in any of the lists + if ( + l1Networks[customL1Network.chainID] || + l2Networks[customL1Network.chainID] + ) { throw new ArbSdkError( `Network ${customL1Network.chainID} already included` ) @@ -579,22 +584,17 @@ export const addCustomNetwork = ({ throw new ArbSdkError( `Custom network ${customL1Network.chainID} must have isCustom flag set to true` ) - } else { - addNetwork(customL1Network) } + + addNetwork(customL1Network) } - if (l2Networks[customL2Network.chainID]) + if (l2Networks[customL2Network.chainID]) { throw new ArbSdkError(`Network ${customL2Network.chainID} already included`) - else if (!customL2Network.isCustom) { + } else if (!customL2Network.isCustom) { throw new ArbSdkError( `Custom network ${customL2Network.chainID} must have isCustom flag set to true` ) - } else if ( - customL2Network.isOrbit && - !isL2Chain(networks[customL2Network.partnerChainID]) - ) { - throw new ArbSdkError('Orbit chains must be paired with an L2 chain') } addNetwork(customL2Network) @@ -669,15 +669,6 @@ export const addDefaultLocalNetwork = (): { } } -export const isL1Network = ( - network: L1Network | L2Network -): network is L1Network => { - if (!network.partnerChainIDs) { - return false - } - return network.partnerChainIDs.length > 0 -} - const createNetworkStateHandler = () => { const initialState = JSON.parse(JSON.stringify(networks)) @@ -686,7 +677,7 @@ const createNetworkStateHandler = () => { Object.keys(networks).forEach(key => delete networks[key]) Object.assign(networks, JSON.parse(JSON.stringify(initialState))) l1Networks = getL1Chains() - l2Networks = getL2Chains() + l2Networks = getArbitrumChains() }, } } diff --git a/src/lib/utils/multicall.ts b/src/lib/utils/multicall.ts index 187b170c1f..42b33aef06 100644 --- a/src/lib/utils/multicall.ts +++ b/src/lib/utils/multicall.ts @@ -24,7 +24,7 @@ import { Multicall2 } from '../abi/Multicall2' import { Multicall2__factory } from '../abi/factories/Multicall2__factory' import { ArbSdkError } from '../dataEntities/errors' import { - isL1Network, + isL1Chain, L1Network, l1Networks, L2Network, @@ -142,7 +142,7 @@ export class MultiCaller { } let multiCallAddr: string - if (isL1Network(network)) { + if (isL1Chain(network)) { const firstL2 = l2Networks[network.partnerChainIDs[0]] if (!firstL2) throw new ArbSdkError( diff --git a/tests/unit/network.test.ts b/tests/unit/network.test.ts index b8ac2b0f1f..ad578e9f3f 100644 --- a/tests/unit/network.test.ts +++ b/tests/unit/network.test.ts @@ -33,7 +33,6 @@ describe('Networks', () => { chainID: mockL2ChainId, isArbitrum: true, isCustom: true, - isOrbit: false, }, } as const @@ -51,14 +50,12 @@ describe('Networks', () => { chainID: mockL1ChainId, isArbitrum: false, isCustom: true, - isOrbit: false, }, customL2Network: { ...arbOneNetwork, chainID: mockL2ChainId, isArbitrum: true, isCustom: true, - isOrbit: false, }, } as const @@ -78,7 +75,6 @@ describe('Networks', () => { partnerChainID: arbOneId, isArbitrum: true, isCustom: true, - isOrbit: true, }, } as const @@ -97,7 +93,6 @@ describe('Networks', () => { chainID: mockL2ChainId, isArbitrum: true, isCustom: true, - isOrbit: false, }, customL2Network: { ...arbOneNetwork, @@ -105,7 +100,6 @@ describe('Networks', () => { partnerChainID: mockL2ChainId, isArbitrum: true, isCustom: true, - isOrbit: true, }, } as const @@ -131,7 +125,6 @@ describe('Networks', () => { chainID: mockL2ChainId, isArbitrum: true, isCustom: true, - isOrbit: false, }, } as const @@ -144,7 +137,6 @@ describe('Networks', () => { partnerChainID: mockL2ChainId, isArbitrum: true, isCustom: true, - isOrbit: true, }, } as const @@ -165,7 +157,6 @@ describe('Networks', () => { partnerChainID: mainnetId, isArbitrum: true, isCustom: true, - isOrbit: true, }, } as const @@ -190,7 +181,6 @@ describe('Networks', () => { chainID: mockL1ChainId, isArbitrum: false, isCustom: true, - isOrbit: false, }, customL2Network: { ...arbOneNetwork, @@ -198,7 +188,6 @@ describe('Networks', () => { partnerChainID: mockL1ChainId, isArbitrum: true, isCustom: true, - isOrbit: true, }, } as const @@ -246,7 +235,6 @@ describe('Networks', () => { partnerChainID: arbOneId, isArbitrum: true, isCustom: true, - isOrbit: true, }, } as const @@ -304,7 +292,6 @@ describe('Networks', () => { partnerChainID: arbOneId, isArbitrum: true, isCustom: true, - isOrbit: true, }, } as const @@ -365,7 +352,6 @@ describe('Networks', () => { partnerChainID: arbOneId, isArbitrum: true, isCustom: true, - isOrbit: true, }, } as const From 2b7c643124c9f12e28af94f2be0032417ed19bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Thu, 11 Jan 2024 19:54:10 +0100 Subject: [PATCH 131/192] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d78e56592f..d08c8276c3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@arbitrum/sdk", - "version": "3.1.13-orbit.0", + "version": "3.2.0-beta.0", "description": "Typescript library client-side interactions with Arbitrum", "author": "Offchain Labs, Inc.", "license": "Apache-2.0", From 024ff031028d800fb716e12ef29958ae62b2d431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Thu, 11 Jan 2024 20:10:50 +0100 Subject: [PATCH 132/192] bump packages --- hardhat.token-bridge-contracts-abigen.ts | 2 +- package.json | 4 +-- yarn.lock | 44 +++++++++++++++++------- 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/hardhat.token-bridge-contracts-abigen.ts b/hardhat.token-bridge-contracts-abigen.ts index 49b79a2a4c..140d1da329 100644 --- a/hardhat.token-bridge-contracts-abigen.ts +++ b/hardhat.token-bridge-contracts-abigen.ts @@ -3,7 +3,7 @@ */ module.exports = { solidity: { - version: '0.8.17', + version: '0.8.16', settings: { optimizer: { enabled: true, diff --git a/package.json b/package.json index 714e769aee..8da10693e8 100644 --- a/package.json +++ b/package.json @@ -55,8 +55,8 @@ "ethers": "^5.1.0" }, "devDependencies": { - "@arbitrum/nitro-contracts": "1.1.0-alpha.7", - "@arbitrum/token-bridge-contracts": "1.1.0-alpha.1", + "@arbitrum/nitro-contracts": "^1.1.0", + "@arbitrum/token-bridge-contracts": "^1.2.0", "@nomiclabs/hardhat-ethers": "^2.0.4", "@typechain/ethers-v5": "9.0.0", "@types/chai": "^4.2.11", diff --git a/yarn.lock b/yarn.lock index c7029ea395..383879981e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,10 +15,10 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@arbitrum/nitro-contracts@1.1.0-alpha.7": - version "1.1.0-alpha.7" - resolved "https://registry.yarnpkg.com/@arbitrum/nitro-contracts/-/nitro-contracts-1.1.0-alpha.7.tgz#5d215540bd2c820b79c9e3afd701238bb2adc9dd" - integrity sha512-H/wnkxg6ZVuc8wyiPSsL9T3vKE5iDhlB3TaBlAhtBFZPyyuBJBIlmDdk3PtgrzNqHrCrQloTT1kT5b9DXC21Aw== +"@arbitrum/nitro-contracts@^1.0.0-beta.8": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@arbitrum/nitro-contracts/-/nitro-contracts-1.0.2.tgz#c73dce72b8f969a5909d5aaef6da80681c667475" + integrity sha512-Y+cXIQNsy9UNANnFrDGKhHzNmOWttxpXP0/uJFTRmS+rgS4ozlr81/UmBo3tX6uWjziDtquhYRuG3wx17talOQ== dependencies: "@openzeppelin/contracts" "4.5.0" "@openzeppelin/contracts-upgradeable" "4.5.2" @@ -26,23 +26,25 @@ optionalDependencies: sol2uml "2.2.0" -"@arbitrum/nitro-contracts@^1.0.0-beta.8": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@arbitrum/nitro-contracts/-/nitro-contracts-1.0.2.tgz#c73dce72b8f969a5909d5aaef6da80681c667475" - integrity sha512-Y+cXIQNsy9UNANnFrDGKhHzNmOWttxpXP0/uJFTRmS+rgS4ozlr81/UmBo3tX6uWjziDtquhYRuG3wx17talOQ== +"@arbitrum/nitro-contracts@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@arbitrum/nitro-contracts/-/nitro-contracts-1.1.0.tgz#37ca26e026306ca9cc9fbeb183841fd8666ac7bb" + integrity sha512-/tLlU++IFdaD9Bn+RYzQ6+6k+0iDPuqi/cNf9kv5N1I9NAApNx1qfsIHoHMEQAvLuY+gj+raH7TAESBbzTAuuw== dependencies: + "@offchainlabs/upgrade-executor" "1.1.0-beta.0" "@openzeppelin/contracts" "4.5.0" "@openzeppelin/contracts-upgradeable" "4.5.2" patch-package "^6.4.7" optionalDependencies: sol2uml "2.2.0" -"@arbitrum/token-bridge-contracts@1.1.0-alpha.1": - version "1.1.0-alpha.1" - resolved "https://registry.yarnpkg.com/@arbitrum/token-bridge-contracts/-/token-bridge-contracts-1.1.0-alpha.1.tgz#35ef4101445f510787352c4aea42939376ead951" - integrity sha512-NZiIrpozPc0ZhkEG2+RNqWdNH56qAaZau5WGhoIkazLhlRL6R/iLx2HGtcYzq0RrorD0KagDn7voX4rOumL5fQ== +"@arbitrum/token-bridge-contracts@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@arbitrum/token-bridge-contracts/-/token-bridge-contracts-1.2.0.tgz#b1dc02e123393848d0d8e5c167028bafa0ac8229" + integrity sha512-GdPn6nIlhkuacgvXZkmYLmNdmOcxVoHEfkEHtNlaBnMqS+HPXEA8keqZDesBHkQ96b9PCwwCegNEx/ZjM+Rq+g== dependencies: "@arbitrum/nitro-contracts" "^1.0.0-beta.8" + "@offchainlabs/upgrade-executor" "1.1.0-beta.0" "@openzeppelin/contracts" "4.8.3" "@openzeppelin/contracts-upgradeable" "4.8.3" optionalDependencies: @@ -1309,11 +1311,24 @@ resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.0.5.tgz#131b0da1b71680d5a01569f916ae878229d326d3" integrity sha512-A2gZAGB6kUvLx+kzM92HKuUF33F1FSe90L0TmkXkT2Hh0OKRpvWZURUSU2nghD2yC4DzfEZ3DftfeHGvZ2JTUw== +"@offchainlabs/upgrade-executor@1.1.0-beta.0": + version "1.1.0-beta.0" + resolved "https://registry.yarnpkg.com/@offchainlabs/upgrade-executor/-/upgrade-executor-1.1.0-beta.0.tgz#c4b1375176546a18aaef01a43956abfb58250e0a" + integrity sha512-mpn6PHjH/KDDjNX0pXHEKdyv8m6DVGQiI2nGzQn0JbM1nOSHJpWx6fvfjtH7YxHJ6zBZTcsKkqGkFKDtCfoSLw== + dependencies: + "@openzeppelin/contracts" "4.7.3" + "@openzeppelin/contracts-upgradeable" "4.7.3" + "@openzeppelin/contracts-upgradeable@4.5.2": version "4.5.2" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.5.2.tgz#90d9e47bacfd8693bfad0ac8a394645575528d05" integrity sha512-xgWZYaPlrEOQo3cBj97Ufiuv79SPd8Brh4GcFYhPgb6WvAq4ppz8dWKL6h+jLAK01rUqMRp/TS9AdXgAeNvCLA== +"@openzeppelin/contracts-upgradeable@4.7.3": + version "4.7.3" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.7.3.tgz#f1d606e2827d409053f3e908ba4eb8adb1dd6995" + integrity sha512-+wuegAMaLcZnLCJIvrVUDzA9z/Wp93f0Dla/4jJvIhijRrPabjQbZe6fWiECLaJyfn5ci9fqf9vTw3xpQOad2A== + "@openzeppelin/contracts-upgradeable@4.8.3": version "4.8.3" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.8.3.tgz#6b076a7b751811b90fe3a172a7faeaa603e13a3f" @@ -1324,6 +1339,11 @@ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.5.0.tgz#3fd75d57de172b3743cdfc1206883f56430409cc" integrity sha512-fdkzKPYMjrRiPK6K4y64e6GzULR7R7RwxSigHS8DDp7aWDeoReqsQI+cxHV1UuhAqX69L1lAaWDxenfP+xiqzA== +"@openzeppelin/contracts@4.7.3": + version "4.7.3" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.7.3.tgz#939534757a81f8d69cc854c7692805684ff3111e" + integrity sha512-dGRS0agJzu8ybo44pCIf3xBaPQN/65AIXNgK8+4gzKd5kbvlqyxryUYVLJv7fK98Seyd2hDZzVEHSWAh0Bt1Yw== + "@openzeppelin/contracts@4.8.3": version "4.8.3" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.8.3.tgz#cbef3146bfc570849405f59cba18235da95a252a" From aaa59970518cae1e9487c6f627fcced762ac55c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Thu, 11 Jan 2024 20:11:20 +0100 Subject: [PATCH 133/192] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8da10693e8..e93cbbf5e7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@arbitrum/sdk", - "version": "3.2.0-beta.0", + "version": "3.2.0-beta.0-custom-fee-token.0", "description": "Typescript library client-side interactions with Arbitrum", "author": "Offchain Labs, Inc.", "license": "Apache-2.0", From b68b6ca7c2a4523f9d0437a8e28825b485caa39b Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Fri, 12 Jan 2024 12:44:28 -0500 Subject: [PATCH 134/192] chore: updates jsdoc comments on new functions/values --- src/lib/dataEntities/networks.ts | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index e3e4f6e31f..2689c068b6 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -437,9 +437,20 @@ const getChildrenForNetwork = (chain: L1Network | L2Network): L2Network[] => { ) } +/** + * Index of *only* L1 chains that have been added. + */ export let l1Networks: L1Networks = getL1Chains() + +/** + * Index of all Arbitrum chains that have been added. + */ export let l2Networks: L2Networks = getArbitrumChains() +/** + * Returns the network associated with the given Signer, Provider or chain id. + * @note Throws if the chain is not recognized. + */ export const getNetwork = async ( signerOrProviderOrChainID: SignerOrProvider | number, layer: 1 | 2 @@ -472,8 +483,10 @@ export const getNetwork = async ( } /** - * Returns the parent chain associated with the given signer, provider or chain id. Could be an L1 chain or an Arbitrum chain. - * Throws if the chain is not a parent chain. + * Returns the parent chain **(could be an L1 chain or an Arbitrum chain)** associated with the + * given Signer, Provider or chain id. + * + * @note Throws if the chain is not a parent chain. */ export const getL1Network = ( signerOrProviderOrChainID: SignerOrProvider | number @@ -483,7 +496,8 @@ export const getL1Network = ( /** * Returns the Arbitrum chain associated with the given signer, provider or chain id. - * Throws if the chain is not an Arbitrum chain. + * + * @note Throws if the chain is not an Arbitrum chain. */ export const getL2Network = ( signerOrProviderOrChainID: SignerOrProvider | number @@ -522,6 +536,9 @@ export const getEthBridgeInformation = async ( } } +/** + * Adds any chain to the global index of networks and updates the parent/child relationships. + */ const addNetwork = (network: L1Network | L2Network) => { // store the network with the rest of the networks networks[network.chainID] = network @@ -556,8 +573,8 @@ const addNetwork = (network: L1Network | L2Network) => { /** * Registers a pair of custom chains (parent and child). These networks will be returned in `getL1Network` and `getL2Network`, respectively. * - * @param customL1Network the parent chain (could be an L1 or an L2) - * @param customL2Network the child chain (could be an L2 or an L3) + * @param customL1Network the parent chain **(could be an L1 or L2 chain)** + * @param customL2Network the child chain **(must be an Arbitrum chain)** */ export const addCustomNetwork = ({ customL1Network, @@ -669,6 +686,9 @@ export const addDefaultLocalNetwork = (): { } } +/** + * Creates a function that resets the networks index to default. Useful in development. + */ const createNetworkStateHandler = () => { const initialState = JSON.parse(JSON.stringify(networks)) From e8b29f7d2f6b097867cc229665b9026e07007b10 Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:45:51 -0500 Subject: [PATCH 135/192] swaps test setup to use arb_min_block_time --- scripts/testSetup.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/testSetup.ts b/scripts/testSetup.ts index 5a891d5509..1f2b3cf224 100644 --- a/scripts/testSetup.ts +++ b/scripts/testSetup.ts @@ -37,6 +37,7 @@ import { deployErc20AndInit } from './deployBridge' import * as path from 'path' import * as fs from 'fs' import { ArbSdkError } from '../src/lib/dataEntities/errors' +import { ARB_MINIMUM_BLOCK_TIME_IN_SECONDS } from '../src/lib/dataEntities/constants' dotenv.config() @@ -168,7 +169,7 @@ export const getCustomNetworks = async ( l2Weth: '', l2WethGateway: '', }, - blockTime: 0.25, + blockTime: ARB_MINIMUM_BLOCK_TIME_IN_SECONDS, partnerChainIDs: [], } @@ -288,7 +289,7 @@ async function getCustomOrbitNetwork( l2Weth: '', l2WethGateway: '', }, - blockTime: 0.25, + blockTime: ARB_MINIMUM_BLOCK_TIME_IN_SECONDS, partnerChainIDs: [], } From da32655ad473683c3c851fd63adc2c2656e9e26d Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Tue, 16 Jan 2024 10:46:37 -0500 Subject: [PATCH 136/192] adds more jsdoc comments to testsetup --- scripts/testSetup.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/scripts/testSetup.ts b/scripts/testSetup.ts index 1f2b3cf224..a4d6c272ae 100644 --- a/scripts/testSetup.ts +++ b/scripts/testSetup.ts @@ -43,6 +43,11 @@ dotenv.config() const isTestingOrbitChains = process.env.ORBIT_TEST === '1' +/** + * The RPC urls and private keys using during testing + * + * @note When the `ORBIT_TEST` env variable is `true`, we treat `ethUrl` as the L2 and `arbUrl` as the L3 + */ export const config = isTestingOrbitChains ? { arbUrl: process.env['ORBIT_URL'] as string, @@ -179,6 +184,9 @@ export const getCustomNetworks = async ( } } +/** + * Adds the L1 and L2 networks (as defined in the environment) to the global network registry + */ const setupL1NetworkForOrbit = async (): Promise<{ l2Network: L2Network l2Provider: providers.Provider @@ -216,6 +224,9 @@ const setupL1NetworkForOrbit = async (): Promise<{ return { l2Network, l2Provider } } +/** + * Adds the L3 network (as defined in the environment) to the global network registry + */ const setupOrbitNetworks = async (): Promise<{ customL1Network: L2Network customL2Network: L2Network @@ -237,6 +248,12 @@ const setupOrbitNetworks = async (): Promise<{ } } +/** + * Builds a child network configuration object from deployment data + * + * @note `l1Provider` and `l2Provider` can be a `l2Provider` and `l3Provider` in a parent-child + * relationship. They will be renamed in the next major version. + */ async function getCustomOrbitNetwork( deploymentData: DeploymentData, l1Provider: providers.Provider, From a1c70aa984d3a8ebdc5890ef0fe0138e09252d8c Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Tue, 16 Jan 2024 11:28:37 -0500 Subject: [PATCH 137/192] adds another comment --- scripts/testSetup.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/testSetup.ts b/scripts/testSetup.ts index a4d6c272ae..1644dce6d5 100644 --- a/scripts/testSetup.ts +++ b/scripts/testSetup.ts @@ -313,6 +313,12 @@ async function getCustomOrbitNetwork( return l2Network } +/** + * Builds network configuration and deploys the token bridge contracts + * + * @note `l1Deployer`/`l2Deployer` and `l1Url`/`l2url` can refer to an `L2` or `L3` in a + * parent-child relationship. They will be renamed in the next major version. + */ export const setupNetworks = async ( l1Deployer: Signer, l2Deployer: Signer, From c987678a75ff7a08d71bee44f74a057da180c157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 24 Jan 2024 11:44:37 +0100 Subject: [PATCH 138/192] clean up --- src/lib/dataEntities/networks.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 2689c068b6..ee04b64f11 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -404,7 +404,6 @@ const getChainsByType = ( const getL1Chains = () => getChainsByType(isL1Chain) const getArbitrumChains = () => getChainsByType(isArbitrumChain) -const getParentChains = () => getChainsByType(isParentChain) /** * Returns the parent chain for the given chain. @@ -470,7 +469,7 @@ export const getNetwork = async ( let network: L1Network | L2Network | undefined = undefined if (layer === 1) { - network = getParentChains()[chainID] + network = getL1Chains()[chainID] } else { network = getArbitrumChains()[chainID] } @@ -483,14 +482,13 @@ export const getNetwork = async ( } /** - * Returns the parent chain **(could be an L1 chain or an Arbitrum chain)** associated with the - * given Signer, Provider or chain id. + * Returns the L1 chain associated with the given signer, provider or chain id. * - * @note Throws if the chain is not a parent chain. + * @note Throws if the chain is not an L1 chain. */ export const getL1Network = ( signerOrProviderOrChainID: SignerOrProvider | number -): Promise => { +): Promise => { return getNetwork(signerOrProviderOrChainID, 1) as Promise } @@ -590,10 +588,7 @@ export const addCustomNetwork = ({ }): void => { if (customL1Network) { // check the if the parent chain is in any of the lists - if ( - l1Networks[customL1Network.chainID] || - l2Networks[customL1Network.chainID] - ) { + if (l1Networks[customL1Network.chainID]) { throw new ArbSdkError( `Network ${customL1Network.chainID} already included` ) From a118de2724fd6ab7f8191a5233a5a9c1d4173688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 24 Jan 2024 11:48:27 +0100 Subject: [PATCH 139/192] clean up --- src/lib/dataEntities/networks.ts | 16 ++++++++++------ src/lib/utils/multicall.ts | 4 ++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index ee04b64f11..6c3ffd9094 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -372,14 +372,18 @@ const isParentChain = (chain: L1Network | L2Network): boolean => { /** * Determines if a chain is an Arbitrum chain. Could be an L2 or an L3 chain. */ -const isArbitrumChain = (chain: L1Network | L2Network): chain is L2Network => { +const isArbitrumNetwork = ( + chain: L1Network | L2Network +): chain is L2Network => { return chain.isArbitrum } /** * Determines if a chain is specifically an L1 chain (not L2 or L3). */ -export const isL1Chain = (chain: L1Network | L2Network): chain is L1Network => { +export const isL1Network = ( + chain: L1Network | L2Network +): chain is L1Network => { return !chain.isArbitrum } @@ -402,14 +406,14 @@ const getChainsByType = ( ) as T } -const getL1Chains = () => getChainsByType(isL1Chain) -const getArbitrumChains = () => getChainsByType(isArbitrumChain) +const getL1Chains = () => getChainsByType(isL1Network) +const getArbitrumChains = () => getChainsByType(isArbitrumNetwork) /** * Returns the parent chain for the given chain. */ export const getParentForNetwork = (chain: L1Network | L2Network) => { - if (!isArbitrumChain(chain)) { + if (!isArbitrumNetwork(chain)) { throw new ArbSdkError(`Chain ${chain.chainID} is not an Arbitrum chain.`) } @@ -551,7 +555,7 @@ const addNetwork = (network: L1Network | L2Network) => { } // if it's an arbitrum chain, add it to the parent's list of children - if (isArbitrumChain(network)) { + if (isArbitrumNetwork(network)) { const parent: L1Network | L2Network | undefined = networks[network.partnerChainID] diff --git a/src/lib/utils/multicall.ts b/src/lib/utils/multicall.ts index 42b33aef06..187b170c1f 100644 --- a/src/lib/utils/multicall.ts +++ b/src/lib/utils/multicall.ts @@ -24,7 +24,7 @@ import { Multicall2 } from '../abi/Multicall2' import { Multicall2__factory } from '../abi/factories/Multicall2__factory' import { ArbSdkError } from '../dataEntities/errors' import { - isL1Chain, + isL1Network, L1Network, l1Networks, L2Network, @@ -142,7 +142,7 @@ export class MultiCaller { } let multiCallAddr: string - if (isL1Chain(network)) { + if (isL1Network(network)) { const firstL2 = l2Networks[network.partnerChainIDs[0]] if (!firstL2) throw new ArbSdkError( From 44d00361fc11f9b1206ebbea70aa024ef2f177df Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Fri, 26 Jan 2024 10:48:14 -0500 Subject: [PATCH 140/192] update custom fee token support to prepare for merge to main (#398) * adds l1/l2 bridge deploy for orbit * deploys both bridges, but gen:network is broken * gen:network working * comment * fmt * comment * raise weth withdraw gas limit * removes custom fee token code from main test paths * should pass tests in ci * restores commented out code and fixes failures * adds conditional for `approves the thing` * removes `.only` to run all tests * fix missing commented code * reverts some other changes * more reverts * deploy both token bridges * custom fee test works * fmt * ... * adds custom fee token tests to CI (#403) * unify integration tests to single script * wrap weth tests to not run with custom fee token * approves custom token * custom erc20 test works * eth test works * Revert "eth test works" This reverts commit 6d665648460ea0a14f4835a14ddc8107070ff15b. * eth tests work * fixes fetching balance * retryable tests work * standarderc20 tests work --------- Co-authored-by: Henry <11198460+godzillaba@users.noreply.github.com> --- .github/workflows/build-test.yml | 18 +- package.json | 3 +- scripts/deployBridge.ts | 199 ++++++-- scripts/fundCustomFeeToken.ts | 14 +- scripts/genNetwork.ts | 24 +- scripts/testSetup.ts | 212 ++++++--- src/lib/assetBridger/erc20Bridger.ts | 30 +- src/lib/dataEntities/networks.ts | 22 +- .../customFeeTokenEthBridger.test.ts | 430 +++++++++--------- .../customFeeTokenTestHelpers.ts | 60 +-- tests/integration/customerc20.test.ts | 27 +- tests/integration/eth.test.ts | 35 +- tests/integration/retryableData.test.ts | 62 ++- tests/integration/sanity.test.ts | 124 ++--- tests/integration/standarderc20.test.ts | 92 ++-- tests/integration/testHelpers.ts | 21 + tests/integration/weth.test.ts | 170 +++---- 17 files changed, 919 insertions(+), 624 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 7218a8959e..a9ae122511 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -124,12 +124,25 @@ jobs: run: CI=true yarn test:unit test-integration: - name: Test (Integration) - ${{ matrix.orbit-test == '1' && 'Orbit' || 'No Orbit' }} - Node ${{ matrix.node-version }} + name: Test (Int.) Node ${{ matrix.node-version }}/${{ matrix.orbit-test == '1' && 'Orbit' || 'No Orbit' }}/${{ matrix.custom-fee == '1' && 'Custom Fee' || 'No Custom Fee' }} runs-on: ubuntu-latest strategy: + fail-fast: false # runs all tests to completion even if one fails matrix: node-version: [16, 18, 20] - orbit-test: ['1', '0'] + orbit-test: ['0', '1'] + custom-fee: ['0'] + include: + - orbit-test: '1' + custom-fee: '1' + node-version: 16 + - orbit-test: '1' + custom-fee: '1' + node-version: 18 + - orbit-test: '1' + custom-fee: '1' + node-version: 20 + needs: install env: ORBIT_TEST: ${{ matrix.orbit-test }} @@ -151,6 +164,7 @@ jobs: nitro-testnode-ref: use-tokenbridge-creator no-token-bridge: true l3-node: ${{ matrix.orbit-test == '1' }} + args: ${{ matrix.custom-fee == '1' && '--l3-fee-token' || '' }} - name: Copy .env run: cp ./.env-sample ./.env diff --git a/package.json b/package.json index e93cbbf5e7..88c2a07428 100644 --- a/package.json +++ b/package.json @@ -31,8 +31,7 @@ "test": "mocha", "test:coverage": "nyc mocha", "test:fork": "SHOULD_FORK=1 hardhat test tests/fork/*.test.ts", - "test:integration": "mocha tests/integration/ --ignore tests/integration/custom-fee-token/ --timeout 30000000 --bail", - "test:integration:custom-fee-token": "mocha tests/integration/custom-fee-token/ --timeout 30000000 --bail", + "test:integration": "mocha tests/integration/ --timeout 30000000 --bail", "test:unit": "mocha --parallel tests/unit/ --timeout 30000 --bail", "test:ci": "nyc --reporter=lcovonly mocha --reporter xunit", "lint": "eslint .", diff --git a/scripts/deployBridge.ts b/scripts/deployBridge.ts index 1226542ea7..5a860b7b86 100644 --- a/scripts/deployBridge.ts +++ b/scripts/deployBridge.ts @@ -1,5 +1,9 @@ import { Signer, ContractFactory, constants } from 'ethers' +import { L1OrbitGatewayRouter__factory } from '../src/lib/abi/factories/L1OrbitGatewayRouter__factory' +import { L1OrbitERC20Gateway__factory } from '../src/lib/abi/factories/L1OrbitERC20Gateway__factory' +import { L1OrbitCustomGateway__factory } from '../src/lib/abi/factories/L1OrbitCustomGateway__factory' + import { L1GatewayRouter__factory } from '../src/lib/abi/factories/L1GatewayRouter__factory' import { L1ERC20Gateway__factory } from '../src/lib/abi/factories/L1ERC20Gateway__factory' import { L1CustomGateway__factory } from '../src/lib/abi/factories/L1CustomGateway__factory' @@ -18,6 +22,7 @@ import { AeWETH__factory } from '../src/lib/abi/factories/AeWETH__factory' import { TestWETH9__factory } from '../src/lib/abi/factories/TestWETH9__factory' import { Multicall2__factory } from '../src/lib/abi/factories/Multicall2__factory' import { ArbMulticall2__factory } from '../src/lib/abi/factories/ArbMulticall2__factory' +import { TokenBridge } from '../src/lib/dataEntities/networks' const deployBehindProxy = async < T extends ContractFactory & { contractName: string } @@ -39,7 +44,48 @@ const deployBehindProxy = async < return instance.attach(proxy.address) as ReturnType } -export const deployErc20L1 = async (deployer: Signer) => { +const deployErc20L1CustomFee = async (deployer: Signer) => { + const proxyAdmin = await new ProxyAdmin__factory().connect(deployer).deploy() + await proxyAdmin.deployed() + console.log('proxyAdmin', proxyAdmin.address) + + const router = await deployBehindProxy( + deployer, + new L1OrbitGatewayRouter__factory(), + proxyAdmin + ) + await router.deployed() + + const standardGateway = await deployBehindProxy( + deployer, + new L1OrbitERC20Gateway__factory(), + proxyAdmin + ) + await standardGateway.deployed() + + const customGateway = await deployBehindProxy( + deployer, + new L1OrbitCustomGateway__factory(), + proxyAdmin + ) + await customGateway.deployed() + + const multicall = await new Multicall2__factory().connect(deployer).deploy() + await multicall.deployed() + console.log('multicall', multicall.address) + + return { + proxyAdmin, + router, + standardGateway, + customGateway, + multicall, + weth: undefined, + wethGateway: undefined, + } +} + +const deployErc20L1 = async (deployer: Signer) => { const proxyAdmin = await new ProxyAdmin__factory().connect(deployer).deploy() await proxyAdmin.deployed() console.log('proxyAdmin', proxyAdmin.address) @@ -93,7 +139,29 @@ export const deployErc20L1 = async (deployer: Signer) => { } } -export const deployErc20L2 = async (deployer: Signer) => { +const deployL2Weth = async (deployer: Signer, proxyAdmin: ProxyAdmin) => { + const wethGateway = await deployBehindProxy( + deployer, + new L2WethGateway__factory(), + proxyAdmin + ) + await wethGateway.deployed() + + const weth = await deployBehindProxy( + deployer, + new AeWETH__factory(), + proxyAdmin + ) + await weth.deployed() + console.log('weth', weth.address) + + return { + wethGateway, + weth, + } +} + +const deployErc20L2NoWeth = async (deployer: Signer) => { const proxyAdmin = await new ProxyAdmin__factory().connect(deployer).deploy() await proxyAdmin.deployed() console.log('proxyAdmin', proxyAdmin.address) @@ -119,13 +187,6 @@ export const deployErc20L2 = async (deployer: Signer) => { ) await customGateway.deployed() - const wethGateway = await deployBehindProxy( - deployer, - new L2WethGateway__factory(), - proxyAdmin - ) - await wethGateway.deployed() - const standardArbERC20 = await new StandardArbERC20__factory() .connect(deployer) .deploy() @@ -141,13 +202,6 @@ export const deployErc20L2 = async (deployer: Signer) => { .deploy() await beaconProxyFactory.deployed() - const weth = await deployBehindProxy( - deployer, - new AeWETH__factory(), - proxyAdmin - ) - console.log('weth', weth.address) - const multicall = await new ArbMulticall2__factory() .connect(deployer) .deploy() @@ -159,24 +213,44 @@ export const deployErc20L2 = async (deployer: Signer) => { router, standardGateway, customGateway, - wethGateway, beacon, beaconProxyFactory, - weth, multicall, } } +const deployErc20L2CustomFee = async (deployer: Signer) => { + return { + ...(await deployErc20L2NoWeth(deployer)), + weth: undefined, + wethGateway: undefined, + } +} + +const deployErc20L2 = async (deployer: Signer) => { + const noWeth = await deployErc20L2NoWeth(deployer) + return { + ...noWeth, + ...(await deployL2Weth(deployer, noWeth.proxyAdmin)), + } +} + export const deployErc20AndInit = async ( l1Signer: Signer, l2Signer: Signer, - inboxAddress: string -) => { + inboxAddress: string, + usingCustomFee: boolean, + l1WethOverride?: string +): Promise => { console.log('deploying l1') - const l1 = await deployErc20L1(l1Signer) + const l1 = usingCustomFee + ? await deployErc20L1CustomFee(l1Signer) + : await deployErc20L1(l1Signer) console.log('deploying l2') - const l2 = await deployErc20L2(l2Signer) + const l2 = usingCustomFee + ? await deployErc20L2CustomFee(l2Signer) + : await deployErc20L2(l2Signer) console.log('initialising L2') await ( @@ -196,23 +270,25 @@ export const deployErc20AndInit = async ( l2.router.address ) ).wait() - await ( - await l2.weth.initialize( - 'WETH', - 'WETH', - 18, - l2.wethGateway.address, - l1.weth.address - ) - ).wait() - await ( - await l2.wethGateway.initialize( - l1.wethGateway.address, - l2.router.address, - l1.weth.address, - l2.weth.address - ) - ).wait() + if (l1.weth && l2.weth) { + await ( + await l2.weth.initialize( + 'WETH', + 'WETH', + 18, + l2.wethGateway.address, + l1WethOverride || l1.weth.address + ) + ).wait() + await ( + await l2.wethGateway.initialize( + l1.wethGateway.address, + l2.router.address, + l1WethOverride || l1.weth.address, + l2.weth.address + ) + ).wait() + } console.log('initialising L1') await ( @@ -242,15 +318,40 @@ export const deployErc20AndInit = async ( await l1Signer.getAddress() ) ).wait() - await ( - await l1.wethGateway.initialize( - l2.wethGateway.address, - l1.router.address, - inboxAddress, - l1.weth.address, - l2.weth.address - ) - ).wait() + if (l1.weth && l2.weth) { + await ( + await l1.wethGateway.initialize( + l2.wethGateway.address, + l1.router.address, + inboxAddress, + l1WethOverride || l1.weth.address, + l2.weth.address + ) + ).wait() + } + + const ret = { + l1CustomGateway: l1.customGateway.address, + l1ERC20Gateway: l1.standardGateway.address, + l1GatewayRouter: l1.router.address, + l1MultiCall: l1.multicall.address, + l1ProxyAdmin: l1.proxyAdmin.address, - return { l1, l2 } + l2CustomGateway: l2.customGateway.address, + l2ERC20Gateway: l2.standardGateway.address, + l2GatewayRouter: l2.router.address, + l2Multicall: l2.multicall.address, + l2ProxyAdmin: l2.proxyAdmin.address, + } + + if (l1.weth && l2.weth) { + return { + ...ret, + l1Weth: l1WethOverride || l1.weth.address, + l1WethGateway: l1.wethGateway.address, + l2Weth: l2.weth.address, + l2WethGateway: l2.wethGateway.address, + } + } + return ret } diff --git a/scripts/fundCustomFeeToken.ts b/scripts/fundCustomFeeToken.ts index 8d1a164dc6..7cf6b7e391 100644 --- a/scripts/fundCustomFeeToken.ts +++ b/scripts/fundCustomFeeToken.ts @@ -1,7 +1,19 @@ import { fundL1CustomFeeToken } from '../tests/integration/custom-fee-token/customFeeTokenTestHelpers' +import { getLocalNetworksFromFile } from './testSetup' async function main() { - await fundL1CustomFeeToken('0x3f1Eae7D46d88F08fc2F8ed27FCb2AB183EB2d0E') + const localNetworks = getLocalNetworksFromFile() + if (!localNetworks) { + console.error('No local networks found') + //TODO: get token address from deployment somehow + } + const nativeTokenAddress = localNetworks?.l2Network.nativeToken + if (!nativeTokenAddress || nativeTokenAddress === undefined) { + console.error('No native token found') + return + } + + await fundL1CustomFeeToken(nativeTokenAddress) } main() diff --git a/scripts/genNetwork.ts b/scripts/genNetwork.ts index cbaca90849..7551689eee 100644 --- a/scripts/genNetwork.ts +++ b/scripts/genNetwork.ts @@ -1,24 +1,16 @@ -import { ethers } from 'ethers' -import { setupNetworks, config, getSigner } from './testSetup' +import { testSetup } from './testSetup' import * as fs from 'fs' async function main() { - const ethProvider = new ethers.providers.JsonRpcProvider(config.ethUrl) - const arbProvider = new ethers.providers.JsonRpcProvider(config.arbUrl) - - const ethDeployer = getSigner(ethProvider, config.ethKey) - const arbDeployer = getSigner(arbProvider, config.arbKey) - - const { l1Network, l2Network } = await setupNetworks( - ethDeployer, - arbDeployer, - config.ethUrl, - config.arbUrl - ) - + fs.rmSync('localNetwork.json', { force: true }) + const setup = await testSetup() fs.writeFileSync( 'localNetwork.json', - JSON.stringify({ l1Network, l2Network }, null, 2) + JSON.stringify( + { l1Network: setup.l1Network, l2Network: setup.l2Network }, + null, + 2 + ) ) console.log('localnetwork.json updated') } diff --git a/scripts/testSetup.ts b/scripts/testSetup.ts index b1e2b854e7..863758fff8 100644 --- a/scripts/testSetup.ts +++ b/scripts/testSetup.ts @@ -29,7 +29,7 @@ import { getL2Network, addCustomNetwork, } from '../src/lib/dataEntities/networks' -import { Signer, providers } from 'ethers' +import { Signer, ethers, providers } from 'ethers' import { AdminErc20Bridger } from '../src/lib/assetBridger/erc20Bridger' import { execSync } from 'child_process' import { Bridge__factory } from '../src/lib/abi/factories/Bridge__factory' @@ -39,6 +39,9 @@ import * as path from 'path' import * as fs from 'fs' import { ArbSdkError } from '../src/lib/dataEntities/errors' import { ARB_MINIMUM_BLOCK_TIME_IN_SECONDS } from '../src/lib/dataEntities/constants' +import { IERC20Bridge__factory } from '../src/lib/abi/factories/IERC20Bridge__factory' +import { approveL1CustomFeeToken, fundL1CustomFeeToken, isL2NetworkWithCustomFeeToken } from '../tests/integration/custom-fee-token/customFeeTokenTestHelpers' +import { fundL1 } from '../tests/integration/testHelpers' dotenv.config() @@ -186,9 +189,9 @@ export const getCustomNetworks = async ( } /** - * Adds the L1 and L2 networks (as defined in the environment) to the global network registry + * Gets the parent network for an Orbit chain */ -const setupL1NetworkForOrbit = async (): Promise<{ +const getL1NetworkForOrbit = async (): Promise<{ l2Network: L2Network l2Provider: providers.Provider }> => { @@ -198,41 +201,23 @@ const setupL1NetworkForOrbit = async (): Promise<{ const deploymentData = getDeploymentData() const parsedDeploymentData = JSON.parse(deploymentData) as DeploymentData - const l1NetworkInfo = await l1Provider.getNetwork() - const l2NetworkInfo = await l2Provider.getNetwork() - - const l1Network: L1Network = { - blockTime: 10, - chainID: l1NetworkInfo.chainId, - explorerUrl: '', - isCustom: true, - name: 'EthLocal', - partnerChainIDs: [l2NetworkInfo.chainId], - isArbitrum: false, - } - const l2Network = await getCustomOrbitNetwork( parsedDeploymentData, l1Provider, l2Provider ) - addCustomNetwork({ - customL1Network: l1Network, - customL2Network: l2Network, - }) - return { l2Network, l2Provider } } /** - * Adds the L3 network (as defined in the environment) to the global network registry + * Gets the L3 Orbit network and its parent network */ -const setupOrbitNetworks = async (): Promise<{ +const getOrbitNetwork = async (): Promise<{ customL1Network: L2Network customL2Network: L2Network }> => { - const { l2Network, l2Provider } = await setupL1NetworkForOrbit() + const { l2Network, l2Provider } = await getL1NetworkForOrbit() const l3Provider = new JsonRpcProvider(process.env['ORBIT_URL']) const l3DeploymentData = getL3DeploymentData() @@ -314,6 +299,21 @@ async function getCustomOrbitNetwork( return l2Network } +export const getNetworkFeeToken = async ( + l1Provider: providers.Provider, + l2Network: L2Network +): Promise => { + const bridge = IERC20Bridge__factory.connect( + l2Network.ethBridge.bridge, + l1Provider + ) + try { + return await bridge.nativeToken() + } catch { + return undefined + } +} + /** * Builds network configuration and deploys the token bridge contracts * @@ -324,42 +324,36 @@ export const setupNetworks = async ( l1Deployer: Signer, l2Deployer: Signer, l1Url: string, - l2Url: string + l2Url: string, + shouldSetupOrbit: boolean, + l1WethOverride?: string ) => { - const customNetworks = isTestingOrbitChains - ? await setupOrbitNetworks() + const customNetworks = shouldSetupOrbit + ? await getOrbitNetwork() : await getCustomNetworks(l1Url, l2Url) - const { l1: l1Contracts, l2: l2Contracts } = await deployErc20AndInit( + const nativeToken = await getNetworkFeeToken( + l1Deployer.provider!, + customNetworks.customL2Network + ) + + const tokenBridge = await deployErc20AndInit( l1Deployer, l2Deployer, - customNetworks.customL2Network.ethBridge.inbox + customNetworks.customL2Network.ethBridge.inbox, + nativeToken !== undefined, + l1WethOverride ) const l2Network: L2Network = { ...customNetworks.customL2Network, - tokenBridge: { - l1CustomGateway: l1Contracts.customGateway.address, - l1ERC20Gateway: l1Contracts.standardGateway.address, - l1GatewayRouter: l1Contracts.router.address, - l1MultiCall: l1Contracts.multicall.address, - l1ProxyAdmin: l1Contracts.proxyAdmin.address, - l1Weth: l1Contracts.weth.address, - l1WethGateway: l1Contracts.wethGateway.address, - - l2CustomGateway: l2Contracts.customGateway.address, - l2ERC20Gateway: l2Contracts.standardGateway.address, - l2GatewayRouter: l2Contracts.router.address, - l2Multicall: l2Contracts.multicall.address, - l2ProxyAdmin: l2Contracts.proxyAdmin.address, - l2Weth: l2Contracts.weth.address, - l2WethGateway: l2Contracts.wethGateway.address, - }, + tokenBridge, + nativeToken, } - // in case of L3, we only need to add the L3, as L1 and L2 were registered inside "setupL1NetworkForOrbit" + // in case of L3, we only need to add the L3, as L1 and L2 were registered in a previous call to setupNetworks // register the network with the newly deployed token bridge contracts - if (isTestingOrbitChains) { + if (shouldSetupOrbit) { addCustomNetwork({ customL2Network: l2Network }) } else { addCustomNetwork({ ...customNetworks, customL2Network: l2Network }) @@ -368,17 +362,19 @@ export const setupNetworks = async ( // also register the weth gateway // we add it here rather than in deployBridge because // we have access to an adminerc20bridger - const adminErc20Bridger = new AdminErc20Bridger(l2Network) - await ( + if (tokenBridge.l1Weth) { + const adminErc20Bridger = new AdminErc20Bridger(l2Network) await ( - await adminErc20Bridger.setGateways(l1Deployer, l2Deployer.provider!, [ - { - gatewayAddr: l2Network.tokenBridge.l1WethGateway, - tokenAddr: l2Network.tokenBridge.l1Weth, - }, - ]) - ).wait() - ).waitForL2(l2Deployer) + await ( + await adminErc20Bridger.setGateways(l1Deployer, l2Deployer.provider!, [ + { + gatewayAddr: tokenBridge.l1WethGateway, + tokenAddr: tokenBridge.l1Weth, + }, + ]) + ).wait() + ).waitForL2(l2Deployer) + } return { l1Network: customNetworks.customL1Network, @@ -427,36 +423,75 @@ export const testSetup = async (): Promise<{ // the networks havent been added yet // check if theres an existing network available - const localNetworkFile = path.join(__dirname, '..', 'localNetwork.json') - if (fs.existsSync(localNetworkFile)) { - const { l1Network, l2Network } = JSON.parse( - fs.readFileSync(localNetworkFile).toString() - ) as { - l1Network: L1Network - l2Network: L2Network - } + const localNetworkFile = getLocalNetworksFromFile() + if (localNetworkFile) { + const { l1Network, l2Network } = localNetworkFile if (isTestingOrbitChains) { - await setupL1NetworkForOrbit() - addCustomNetwork({ customL2Network: l2Network }) + const _l1Network = l1Network as L2Network + const ethLocal: L1Network = { + blockTime: 10, + chainID: _l1Network.partnerChainID, + explorerUrl: '', + isCustom: true, + name: 'EthLocal', + partnerChainIDs: [_l1Network.chainID], + isArbitrum: false, + } + + addCustomNetwork({ + customL1Network: ethLocal, + customL2Network: _l1Network, + }) + + addCustomNetwork({ + customL2Network: l2Network, + }) + + setL1Network = l1Network + setL2Network = l2Network } else { addCustomNetwork({ - customL1Network: l1Network, + customL1Network: l1Network as L1Network, customL2Network: l2Network, }) - } - setL1Network = l1Network - setL2Network = l2Network + setL1Network = l1Network + setL2Network = l2Network + } } else { + let l1NetworkOverride: L2Network | undefined + + if (isTestingOrbitChains) { + //deploy l1/l2 bridge + const l1Url = process.env['ETH_URL']! + const l2Url = process.env['ARB_URL']! + const ethProvider = new JsonRpcProvider(l1Url) + const arbProvider = new JsonRpcProvider(l2Url) + const l1Deployer = getSigner(ethProvider, process.env['ETH_KEY']) + const l2Deployer = getSigner(arbProvider, process.env['ARB_KEY']) + const { l2Network: arbNetwork } = await setupNetworks( + l1Deployer, + l2Deployer, + l1Url, + l2Url, + false + ) + + l1NetworkOverride = arbNetwork + } + // deploy a new network const { l1Network, l2Network } = await setupNetworks( l1Deployer, l2Deployer, config.ethUrl, - config.arbUrl + config.arbUrl, + isTestingOrbitChains, + l1NetworkOverride?.tokenBridge.l2Weth ) - setL1Network = l1Network + + setL1Network = l1NetworkOverride || l1Network setL2Network = l2Network } } @@ -466,6 +501,12 @@ export const testSetup = async (): Promise<{ const ethBridger = new EthBridger(setL2Network) const inboxTools = new InboxTools(l1Signer, setL2Network) + if (isL2NetworkWithCustomFeeToken()) { + await fundL1(l1Signer) + await fundL1CustomFeeToken(l1Signer) + await approveL1CustomFeeToken(l1Signer) + } + return { l1Signer, l2Signer, @@ -481,3 +522,26 @@ export const testSetup = async (): Promise<{ l2Deployer, } } + +export function getLocalNetworksFromFile(): + | { + l1Network: L1Network | L2Network + l2Network: L2Network + } + | undefined { + try { + const pathToLocalNetworkFile = path.join( + __dirname, + '..', + 'localNetwork.json' + ) + if (fs.existsSync(pathToLocalNetworkFile)) { + const localNetworksFile = fs.readFileSync(pathToLocalNetworkFile, 'utf8') + return JSON.parse(localNetworksFile) + } + return undefined + } catch (err) { + console.log(err) + return undefined + } +} diff --git a/src/lib/assetBridger/erc20Bridger.ts b/src/lib/assetBridger/erc20Bridger.ts index 6cb0b169f6..916ada43a3 100644 --- a/src/lib/assetBridger/erc20Bridger.ts +++ b/src/lib/assetBridger/erc20Bridger.ts @@ -501,8 +501,9 @@ export class Erc20Bridger extends AssetBridger< // L2 WETH contract doesn't have the l1Address method on it if ( + this.l2Network.tokenBridge.l2Weth && erc20L2Address.toLowerCase() === - this.l2Network.tokenBridge.l2Weth.toLowerCase() + this.l2Network.tokenBridge.l2Weth.toLowerCase() ) { return this.l2Network.tokenBridge.l1Weth } @@ -769,17 +770,22 @@ export class Erc20Bridger extends AssetBridger< // in the future we want to do proper estimation here /* eslint-disable @typescript-eslint/no-unused-vars */ estimateL1GasLimit: async (l1Provider: Provider) => { - const l1GatewayAddress = await this.getL1GatewayAddress( - params.erc20l1Address, - l1Provider - ) - - // The WETH gateway is the only deposit that requires callvalue in the L2 user-tx (i.e., the recently un-wrapped ETH) - // Here we check if this is a WETH deposit, and include the callvalue for the gas estimate query if so - const isWeth = await this.isWethGateway(l1GatewayAddress, l1Provider) - - // measured 157421 - add some padding - return isWeth ? BigNumber.from(180000) : BigNumber.from(160000) + if (this.isNativeTokenEth) { + const l1GatewayAddress = await this.getL1GatewayAddress( + params.erc20l1Address, + l1Provider + ) + + // The WETH gateway is the only deposit that requires callvalue in the L2 user-tx (i.e., the recently un-wrapped ETH) + // Here we check if this is a WETH deposit, and include the callvalue for the gas estimate query if so + const isWeth = await this.isWethGateway(l1GatewayAddress, l1Provider) + + // measured 157421 - add some padding + return isWeth ? BigNumber.from(190000) : BigNumber.from(160000) + } else { + // measured 172867 - add some padding + return BigNumber.from(200000) + } }, } } diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 8db29f002a..e13e437a58 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -73,23 +73,35 @@ export interface L2Network extends Network { nativeToken?: string } -export interface TokenBridge { +type BaseTokenBridge = { l1GatewayRouter: string l2GatewayRouter: string l1ERC20Gateway: string l2ERC20Gateway: string l1CustomGateway: string l2CustomGateway: string - l1WethGateway: string - l2WethGateway: string - l2Weth: string - l1Weth: string l1ProxyAdmin: string l2ProxyAdmin: string l1MultiCall: string l2Multicall: string } +export type TokenBridge = BaseTokenBridge & + ( + | { + l1WethGateway: string + l2WethGateway: string + l2Weth: string + l1Weth: string + } + | { + l1WethGateway?: never + l2WethGateway?: never + l2Weth?: never + l1Weth?: never + } + ) + export interface EthBridge { bridge: string inbox: string diff --git a/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts b/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts index 2a058fa77b..a5f9262637 100644 --- a/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts +++ b/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts @@ -22,12 +22,7 @@ import dotenv from 'dotenv' import { parseEther } from '@ethersproject/units' -import { - testSetup, - fundL1CustomFeeToken, - fundL2CustomFeeToken, - approveL1CustomFeeToken, -} from './customFeeTokenTestHelpers' +import { isL2NetworkWithCustomFeeToken } from './customFeeTokenTestHelpers' import { fundL1 as fundL1Ether, mineUntilStop, @@ -38,224 +33,241 @@ import { L2ToL1Message, L2ToL1MessageStatus } from '../../../src' dotenv.config() -describe('EthBridger (with custom fee token)', async () => { - beforeEach('skipIfMainnet', async function () { - await skipIfMainnet(this) - }) - - it('approves the custom fee token to be spent by the Inbox on the parent chain (arbitrary amount, using params)', async function () { - const { ethBridger, nativeTokenContract, l1Signer } = await testSetup() - const amount = ethers.utils.parseEther('1') - - await fundL1Ether(l1Signer) - await fundL1CustomFeeToken(l1Signer) - - const approvalTx = await ethBridger.approveFeeToken({ - amount, - l1Signer, +if (isL2NetworkWithCustomFeeToken()) { + describe('EthBridger (with custom fee token)', async () => { + const { + testSetup, + fundL1CustomFeeToken, + fundL2CustomFeeToken, + approveL1CustomFeeToken, + } = await import('./customFeeTokenTestHelpers') + + beforeEach('skipIfMainnet', async function () { + await skipIfMainnet(this) }) - await approvalTx.wait() - const allowance = await nativeTokenContract.allowance( - await l1Signer.getAddress(), - ethBridger.l2Network.ethBridge.inbox - ) + it('approves the custom fee token to be spent by the Inbox on the parent chain (arbitrary amount, using params)', async function () { + const { ethBridger, nativeTokenContract, l1Signer } = await testSetup() + const amount = ethers.utils.parseEther('1') - expect(allowance.toString()).to.equal( - amount.toString(), - 'allowance incorrect' - ) - }) + await fundL1Ether(l1Signer) + await fundL1CustomFeeToken(l1Signer) - it('approves the custom fee token to be spent by the Inbox on the parent chain (max amount, using tx request)', async function () { - const { ethBridger, nativeTokenContract, l1Signer } = await testSetup() + const approvalTx = await ethBridger.approveFeeToken({ + amount, + l1Signer, + }) + await approvalTx.wait() - await fundL1Ether(l1Signer) - await fundL1CustomFeeToken(l1Signer) + const allowance = await nativeTokenContract.allowance( + await l1Signer.getAddress(), + ethBridger.l2Network.ethBridge.inbox + ) - const approvalTx = await ethBridger.approveFeeToken({ - txRequest: await ethBridger.getApproveFeeTokenRequest(), - l1Signer, + expect(allowance.toString()).to.equal( + amount.toString(), + 'allowance incorrect' + ) }) - await approvalTx.wait() - - const allowance = await nativeTokenContract.allowance( - await l1Signer.getAddress(), - ethBridger.l2Network.ethBridge.inbox - ) - expect(allowance.toString()).to.equal( - constants.MaxUint256.toString(), - 'allowance incorrect' - ) - }) + it('approves the custom fee token to be spent by the Inbox on the parent chain (max amount, using tx request)', async function () { + const { ethBridger, nativeTokenContract, l1Signer } = await testSetup() - it('deposits custom fee token (using params)', async function () { - const { ethBridger, nativeTokenContract, l1Signer, l2Signer, l2Provider } = - await testSetup() - const bridge = ethBridger.l2Network.ethBridge.bridge - const amount = parseEther('2') + await fundL1Ether(l1Signer) + await fundL1CustomFeeToken(l1Signer) - await fundL1Ether(l1Signer) - await fundL1CustomFeeToken(l1Signer) - await approveL1CustomFeeToken(l1Signer) + const approvalTx = await ethBridger.approveFeeToken({ + txRequest: await ethBridger.getApproveFeeTokenRequest(), + l1Signer, + }) + await approvalTx.wait() - const initialBalanceBridge = await nativeTokenContract.balanceOf(bridge) - const initialBalanceDepositor = await l2Signer.getBalance() + const allowance = await nativeTokenContract.allowance( + await l1Signer.getAddress(), + ethBridger.l2Network.ethBridge.inbox + ) - // perform the deposit - const depositTx = await ethBridger.deposit({ - amount, - l1Signer, + expect(allowance.toString()).to.equal( + constants.MaxUint256.toString(), + 'allowance incorrect' + ) }) - const depositTxReceipt = await depositTx.wait() - expect(depositTxReceipt.status).to.equal(1, 'deposit tx failed') - - expect( - // balance in the bridge after the deposit - (await nativeTokenContract.balanceOf(bridge)).toString() - ).to.equal( - // balance in the bridge after the deposit should equal to the initial balance in the bridge + the amount deposited - initialBalanceBridge.add(amount).toString(), - 'incorrect balance in bridge after deposit' - ) - - // wait for minting on L2 - await wait(30 * 1000) - - // check for cross-chain messages - const depositMessages = await depositTxReceipt.getEthDeposits(l2Provider) - expect(depositMessages.length).to.equal(1, 'failed to find deposit message') - const [depositMessage] = depositMessages - expect(depositMessage.value.toString()).to.equal(amount.toString()) - expect(depositMessage.to).to.equal(await l2Signer.getAddress()) - - expect( - // balance in the depositor account after the deposit - (await l2Signer.getBalance()).toString() - ).to.equal( - // balance in the depositor account after the deposit should equal to the initial balance in th depositor account + the amount deposited - initialBalanceDepositor.add(amount).toString(), - 'incorrect balance in depositor account after deposit' - ) - }) - it('withdraws custom fee token', async () => { - const { - l1Signer, - l1Provider, - l2Signer, - l2Provider, - ethBridger, - nativeTokenContract, - } = await testSetup() - const bridge = ethBridger.l2Network.ethBridge.bridge - const amount = parseEther('0.2') - - await fundL1Ether(l1Signer) - await fundL2CustomFeeToken(l2Signer) - - const from = await l2Signer.getAddress() - const destinationAddress = await l1Signer.getAddress() - - const initialBalanceBridge = await nativeTokenContract.balanceOf(bridge) - const initialBalanceDestination = await nativeTokenContract.balanceOf( - destinationAddress - ) - - const withdrawalTxRequest = await ethBridger.getWithdrawalRequest({ - amount, - destinationAddress, - from, + it('deposits custom fee token (using params)', async function () { + const { + ethBridger, + nativeTokenContract, + l1Signer, + l2Signer, + l2Provider, + } = await testSetup() + const bridge = ethBridger.l2Network.ethBridge.bridge + const amount = parseEther('2') + + await fundL1Ether(l1Signer) + await fundL1CustomFeeToken(l1Signer) + await approveL1CustomFeeToken(l1Signer) + + const initialBalanceBridge = await nativeTokenContract.balanceOf(bridge) + const initialBalanceDepositor = await l2Signer.getBalance() + + // perform the deposit + const depositTx = await ethBridger.deposit({ + amount, + l1Signer, + }) + const depositTxReceipt = await depositTx.wait() + expect(depositTxReceipt.status).to.equal(1, 'deposit tx failed') + + expect( + // balance in the bridge after the deposit + (await nativeTokenContract.balanceOf(bridge)).toString() + ).to.equal( + // balance in the bridge after the deposit should equal to the initial balance in the bridge + the amount deposited + initialBalanceBridge.add(amount).toString(), + 'incorrect balance in bridge after deposit' + ) + + // wait for minting on L2 + await wait(30 * 1000) + + // check for cross-chain messages + const depositMessages = await depositTxReceipt.getEthDeposits(l2Provider) + expect(depositMessages.length).to.equal( + 1, + 'failed to find deposit message' + ) + const [depositMessage] = depositMessages + expect(depositMessage.value.toString()).to.equal(amount.toString()) + expect(depositMessage.to).to.equal(await l2Signer.getAddress()) + + expect( + // balance in the depositor account after the deposit + (await l2Signer.getBalance()).toString() + ).to.equal( + // balance in the depositor account after the deposit should equal to the initial balance in th depositor account + the amount deposited + initialBalanceDepositor.add(amount).toString(), + 'incorrect balance in depositor account after deposit' + ) }) - const l1GasEstimate = await withdrawalTxRequest.estimateL1GasLimit( - l1Provider - ) - - const withdrawalTx = await ethBridger.withdraw({ - amount, - l2Signer: l2Signer, - destinationAddress, - from, + it('withdraws custom fee token', async () => { + const { + l1Signer, + l1Provider, + l2Signer, + l2Provider, + ethBridger, + nativeTokenContract, + } = await testSetup() + const bridge = ethBridger.l2Network.ethBridge.bridge + const amount = parseEther('0.2') + + await fundL1Ether(l1Signer) + await fundL2CustomFeeToken(l2Signer) + + const from = await l2Signer.getAddress() + const destinationAddress = await l1Signer.getAddress() + + const initialBalanceBridge = await nativeTokenContract.balanceOf(bridge) + const initialBalanceDestination = await nativeTokenContract.balanceOf( + destinationAddress + ) + + const withdrawalTxRequest = await ethBridger.getWithdrawalRequest({ + amount, + destinationAddress, + from, + }) + + const l1GasEstimate = await withdrawalTxRequest.estimateL1GasLimit( + l1Provider + ) + + const withdrawalTx = await ethBridger.withdraw({ + amount, + l2Signer: l2Signer, + destinationAddress, + from, + }) + const withdrawalTxReceipt = await withdrawalTx.wait() + + expect(withdrawalTxReceipt.status).to.equal( + 1, + 'initiate withdrawal tx failed' + ) + + const messages = await withdrawalTxReceipt.getL2ToL1Messages(l1Signer) + expect(messages.length).to.equal( + 1, + 'custom fee token withdraw getWithdrawalsInL2Transaction query came back empty' + ) + + const withdrawalEvents = await L2ToL1Message.getL2ToL1Events( + l2Provider, + { fromBlock: withdrawalTxReceipt.blockNumber, toBlock: 'latest' }, + undefined, + destinationAddress + ) + + expect(withdrawalEvents.length).to.equal( + 1, + 'custom fee token withdraw getL2ToL1EventData failed' + ) + + const [message] = messages + const messageStatus = await message.status(l2Provider) + expect( + messageStatus, + `custom fee token withdraw status returned ${messageStatus}` + ).to.be.eq(L2ToL1MessageStatus.UNCONFIRMED) + + // run a miner whilst withdrawing + const miner1 = Wallet.createRandom().connect(l1Provider) + const miner2 = Wallet.createRandom().connect(l2Provider) + await fundL1Ether(miner1, parseEther('1')) + await fundL2CustomFeeToken(miner2) + const state = { mining: true } + await Promise.race([ + mineUntilStop(miner1, state), + mineUntilStop(miner2, state), + message.waitUntilReadyToExecute(l2Provider), + ]) + state.mining = false + + expect(await message.status(l2Provider), 'confirmed status') + // + .to.eq(L2ToL1MessageStatus.CONFIRMED) + + const execTx = await message.execute(l2Provider) + const execTxReceipt = await execTx.wait() + + expect( + execTxReceipt.gasUsed.toNumber(), + 'gas used greater than estimate' + ).to.be.lessThan(l1GasEstimate.toNumber()) + + expect(await message.status(l2Provider), 'executed status') + // + .to.eq(L2ToL1MessageStatus.EXECUTED) + + expect( + // balance in the bridge after the withdrawal + (await nativeTokenContract.balanceOf(bridge)).toString() + ).to.equal( + // balance in the bridge after the withdrawal should equal to the initial balance in the bridge - the amount withdrawn + initialBalanceBridge.sub(amount).toString(), + 'incorrect balance in bridge after withdrawal' + ) + + expect( + // balance in the destination after the withdrawal + (await nativeTokenContract.balanceOf(destinationAddress)).toString() + ).to.equal( + // balance in the destination after the withdrawal should equal to the initial balance in the destination + the amount withdrawn + initialBalanceDestination.add(amount).toString(), + 'incorrect balance in destination after withdrawal' + ) }) - const withdrawalTxReceipt = await withdrawalTx.wait() - - expect(withdrawalTxReceipt.status).to.equal( - 1, - 'initiate withdrawal tx failed' - ) - - const messages = await withdrawalTxReceipt.getL2ToL1Messages(l1Signer) - expect(messages.length).to.equal( - 1, - 'custom fee token withdraw getWithdrawalsInL2Transaction query came back empty' - ) - - const withdrawalEvents = await L2ToL1Message.getL2ToL1Events( - l2Provider, - { fromBlock: withdrawalTxReceipt.blockNumber, toBlock: 'latest' }, - undefined, - destinationAddress - ) - - expect(withdrawalEvents.length).to.equal( - 1, - 'custom fee token withdraw getL2ToL1EventData failed' - ) - - const [message] = messages - const messageStatus = await message.status(l2Provider) - expect( - messageStatus, - `custom fee token withdraw status returned ${messageStatus}` - ).to.be.eq(L2ToL1MessageStatus.UNCONFIRMED) - - // run a miner whilst withdrawing - const miner1 = Wallet.createRandom().connect(l1Provider) - const miner2 = Wallet.createRandom().connect(l2Provider) - await fundL1Ether(miner1, parseEther('1')) - await fundL2CustomFeeToken(miner2) - const state = { mining: true } - await Promise.race([ - mineUntilStop(miner1, state), - mineUntilStop(miner2, state), - message.waitUntilReadyToExecute(l2Provider), - ]) - state.mining = false - - expect(await message.status(l2Provider), 'confirmed status') - // - .to.eq(L2ToL1MessageStatus.CONFIRMED) - - const execTx = await message.execute(l2Provider) - const execTxReceipt = await execTx.wait() - - expect( - execTxReceipt.gasUsed.toNumber(), - 'gas used greater than estimate' - ).to.be.lessThan(l1GasEstimate.toNumber()) - - expect(await message.status(l2Provider), 'executed status') - // - .to.eq(L2ToL1MessageStatus.EXECUTED) - - expect( - // balance in the bridge after the withdrawal - (await nativeTokenContract.balanceOf(bridge)).toString() - ).to.equal( - // balance in the bridge after the withdrawal should equal to the initial balance in the bridge - the amount withdrawn - initialBalanceBridge.sub(amount).toString(), - 'incorrect balance in bridge after withdrawal' - ) - - expect( - // balance in the destination after the withdrawal - (await nativeTokenContract.balanceOf(destinationAddress)).toString() - ).to.equal( - // balance in the destination after the withdrawal should equal to the initial balance in the destination + the amount withdrawn - initialBalanceDestination.add(amount).toString(), - 'incorrect balance in destination after withdrawal' - ) }) -}) +} diff --git a/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts b/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts index 7f87b51fc8..cae9ce1b86 100644 --- a/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts +++ b/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts @@ -1,34 +1,21 @@ -import { utils, Signer, Wallet } from 'ethers' import { StaticJsonRpcProvider } from '@ethersproject/providers' -import * as path from 'path' -import * as fs from 'fs' - -import { testSetup as _testSetup, config } from '../../../scripts/testSetup' +import { Signer, Wallet, utils } from 'ethers' + +import { + testSetup as _testSetup, + config, + getLocalNetworksFromFile, +} from '../../../scripts/testSetup' +import { Erc20Bridger, EthBridger } from '../../../src' import { ERC20__factory } from '../../../src/lib/abi/factories/ERC20__factory' -import { Erc20Bridger, EthBridger, L1Network, L2Network } from '../../../src' -const ethProvider = new StaticJsonRpcProvider(config.ethUrl) -const arbProvider = new StaticJsonRpcProvider(config.arbUrl) +// `config` isn't initialized yet, so we have to wrap these in functions +const ethProvider = ()=>new StaticJsonRpcProvider(config.ethUrl) +const arbProvider = ()=>new StaticJsonRpcProvider(config.arbUrl) +const localNetworks = ()=>getLocalNetworksFromFile() export function isL2NetworkWithCustomFeeToken(): boolean { - return typeof getLocalNetworks().l2Network.nativeToken !== 'undefined' -} - -function getLocalNetworks(): { - l1Network: L1Network - l2Network: L2Network -} { - const pathToLocalNetworkFile = path.join( - __dirname, - '..', - path.sep, - '..', - path.sep, - '..', - 'localNetwork.json' - ) - - return JSON.parse(fs.readFileSync(pathToLocalNetworkFile, 'utf8')) + return typeof localNetworks()?.l2Network.nativeToken !== 'undefined' } export async function testSetup() { @@ -42,7 +29,7 @@ export async function testSetup() { } export async function fundL1CustomFeeToken(l1SignerOrAddress: Signer | string) { - const nativeToken = getLocalNetworks().l2Network.nativeToken + const nativeToken = localNetworks()?.l2Network.nativeToken const address = typeof l1SignerOrAddress === 'string' ? l1SignerOrAddress @@ -55,26 +42,26 @@ export async function fundL1CustomFeeToken(l1SignerOrAddress: Signer | string) { } const deployerWallet = new Wallet( - utils.sha256(utils.toUtf8Bytes('user_l1user')), - ethProvider + utils.sha256(utils.toUtf8Bytes('user_token_bridge_deployer')), + ethProvider() ) const tokenContract = ERC20__factory.connect(nativeToken, deployerWallet) const tx = await tokenContract.transfer(address, utils.parseEther('10')) - await tx.wait() + await tx.wait() } export async function approveL1CustomFeeToken(l1Signer: Signer) { - const ethBridger = await EthBridger.fromProvider(arbProvider) + const ethBridger = await EthBridger.fromProvider(arbProvider()) const tx = await ethBridger.approveFeeToken({ l1Signer }) await tx.wait() } export async function getNativeTokenAllowance(owner: string, spender: string) { - const nativeToken = getLocalNetworks().l2Network.nativeToken - const nativeTokenContract = ERC20__factory.connect(nativeToken!, ethProvider) + const nativeToken = localNetworks()?.l2Network.nativeToken + const nativeTokenContract = ERC20__factory.connect(nativeToken!, ethProvider()) return nativeTokenContract.allowance(owner, spender) } @@ -82,17 +69,14 @@ export async function approveL1CustomFeeTokenForErc20Deposit( l1Signer: Signer, erc20L1Address: string ) { - const erc20Bridger = await Erc20Bridger.fromProvider(arbProvider) + const erc20Bridger = await Erc20Bridger.fromProvider(arbProvider()) const tx = await erc20Bridger.approveFeeToken({ erc20L1Address, l1Signer }) await tx.wait() } export async function fundL2CustomFeeToken(l2Signer: Signer) { - const deployerWallet = new Wallet( - utils.sha256(utils.toUtf8Bytes('user_l1user')), - arbProvider - ) + const deployerWallet = new Wallet(config.arbKey, arbProvider()) const tx = await deployerWallet.sendTransaction({ to: await l2Signer.getAddress(), diff --git a/tests/integration/customerc20.test.ts b/tests/integration/customerc20.test.ts index cc32cb6e87..684873e07b 100644 --- a/tests/integration/customerc20.test.ts +++ b/tests/integration/customerc20.test.ts @@ -24,6 +24,8 @@ import { L1CustomGateway__factory } from '../../src/lib/abi/factories/L1CustomGa import { L1GatewayRouter__factory } from '../../src/lib/abi/factories/L1GatewayRouter__factory' import { L2GatewayRouter__factory } from '../../src/lib/abi/factories/L2GatewayRouter__factory' import { TestArbCustomToken__factory } from '../../src/lib/abi/factories/TestArbCustomToken__factory' +import { TestOrbitCustomTokenL1 } from '../../src/lib/abi/TestOrbitCustomTokenL1' +import { TestOrbitCustomTokenL1__factory } from '../../src/lib/abi/factories/TestOrbitCustomTokenL1__factory' import { TestCustomTokenL1 } from '../../src/lib/abi/TestCustomTokenL1' import { TestCustomTokenL1__factory } from '../../src/lib/abi/factories/TestCustomTokenL1__factory' @@ -36,10 +38,14 @@ import { withdrawToken, } from './testHelpers' import { L1ToL2MessageStatus, L2Network } from '../../src' -import { Signer, constants } from 'ethers' +import { Signer, constants, ethers } from 'ethers' import { AdminErc20Bridger } from '../../src/lib/assetBridger/erc20Bridger' import { testSetup } from '../../scripts/testSetup' import { ERC20__factory } from '../../src/lib/abi/factories/ERC20__factory' +import { + fundL1CustomFeeToken, + isL2NetworkWithCustomFeeToken, +} from './custom-fee-token/customFeeTokenTestHelpers' const depositAmount = BigNumber.from(100) const withdrawalAmount = BigNumber.from(10) @@ -55,7 +61,7 @@ describe('Custom ERC20', () => { l2Signer: Signer adminErc20Bridger: AdminErc20Bridger l2Network: L2Network - l1CustomToken: TestCustomTokenL1 + l1CustomToken: TestCustomTokenL1 | TestOrbitCustomTokenL1 } before('init', async () => { @@ -65,6 +71,10 @@ describe('Custom ERC20', () => { } await fundL1(testState.l1Signer) await fundL2(testState.l2Signer) + + if (isL2NetworkWithCustomFeeToken()) { + await fundL1CustomFeeToken(testState.l1Signer) + } }) it('register custom token', async () => { @@ -114,12 +124,23 @@ const registerCustomToken = async ( adminErc20Bridger: AdminErc20Bridger ) => { // create a custom token on L1 and L2 - const l1CustomTokenFac = new TestCustomTokenL1__factory(l1Signer) + const l1CustomTokenFac = isL2NetworkWithCustomFeeToken() + ? new TestOrbitCustomTokenL1__factory(l1Signer) + : new TestCustomTokenL1__factory(l1Signer) const l1CustomToken = await l1CustomTokenFac.deploy( l2Network.tokenBridge.l1CustomGateway, l2Network.tokenBridge.l1GatewayRouter ) await l1CustomToken.deployed() + const amount = ethers.utils.parseEther('1') + + if (isL2NetworkWithCustomFeeToken()) { + const approvalTx = await ERC20__factory.connect( + l2Network.nativeToken!, + l1Signer + ).approve(l1CustomToken.address, amount) + await approvalTx.wait() + } const l2CustomTokenFac = new TestArbCustomToken__factory(l2Signer) const l2CustomToken = await l2CustomTokenFac.deploy( diff --git a/tests/integration/eth.test.ts b/tests/integration/eth.test.ts index ffde105451..4640fb9463 100644 --- a/tests/integration/eth.test.ts +++ b/tests/integration/eth.test.ts @@ -34,6 +34,9 @@ import { L2ToL1MessageStatus } from '../../src/lib/dataEntities/message' import { L2TransactionReceipt } from '../../src/lib/message/L2Transaction' import { L1ToL2MessageStatus } from '../../src/lib/message/L1ToL2Message' import { testSetup } from '../../scripts/testSetup' +import { isL2NetworkWithCustomFeeToken } from './custom-fee-token/customFeeTokenTestHelpers' +import { ERC20__factory } from '../../src/lib/abi/factories/ERC20__factory' + dotenv.config() describe('Ether', async () => { @@ -78,16 +81,19 @@ describe('Ether', async () => { ) }) - it('"EthBridger.approveFeeToken" throws when eth is used as native/fee token', async () => { - const { ethBridger, l1Signer } = await testSetup() - - try { - await ethBridger.approveFeeToken({ l1Signer }) - expect.fail(`"EthBridger.approveFeeToken" should have thrown`) - } catch (error: any) { - expect(error.message).to.equal('chain uses ETH as its native/fee token') - } - }) + // Test should only run if we're using eth as native fee + if (!isL2NetworkWithCustomFeeToken()) { + it('"EthBridger.approveFeeToken" throws when eth is used as native/fee token', async () => { + const { ethBridger, l1Signer } = await testSetup() + + try { + await ethBridger.approveFeeToken({ l1Signer }) + expect.fail(`"EthBridger.approveFeeToken" should have thrown`) + } catch (error: any) { + expect(error.message).to.equal('chain uses ETH as its native/fee token') + } + }) + } it('deposits ether', async () => { const { ethBridger, l1Signer, l2Signer } = await testSetup() @@ -300,9 +306,12 @@ describe('Ether', async () => { 'executed status' ).to.eq(L2ToL1MessageStatus.EXECUTED) - const finalRandomBalance = await l1Signer.provider!.getBalance( - randomAddress - ) + const finalRandomBalance = isL2NetworkWithCustomFeeToken() + ? await ERC20__factory.connect( + ethBridger.nativeToken!, + l1Signer.provider! + ).balanceOf(randomAddress) + : await l1Signer.provider!.getBalance(randomAddress) expect(finalRandomBalance.toString(), 'L1 final balance').to.eq( ethToWithdraw.toString() ) diff --git a/tests/integration/retryableData.test.ts b/tests/integration/retryableData.test.ts index c8f08966a1..2056f8e758 100644 --- a/tests/integration/retryableData.test.ts +++ b/tests/integration/retryableData.test.ts @@ -28,6 +28,8 @@ import { parseEther, randomBytes } from 'ethers/lib/utils' import { Inbox__factory } from '../../src/lib/abi/factories/Inbox__factory' import { GasOverrides } from '../../src/lib/message/L1ToL2MessageGasEstimator' const depositAmount = BigNumber.from(100) +import { ERC20Inbox__factory } from '../../src/lib/abi/factories/ERC20Inbox__factory' +import { isL2NetworkWithCustomFeeToken } from './custom-fee-token/customFeeTokenTestHelpers' describe('RevertData', () => { beforeEach('skipIfMainnet', async function () { @@ -59,7 +61,6 @@ describe('RevertData', () => { const { l1Signer, l2Network } = await testSetup() await fundL1(l1Signer) - const inbox = Inbox__factory.connect(l2Network.ethBridge.inbox, l1Signer) const { to, l2CallValue, @@ -73,17 +74,40 @@ describe('RevertData', () => { } = createRevertParams() try { - await inbox[func].createRetryableTicket( - to, - l2CallValue, - maxSubmissionCost, - excessFeeRefundAddress, - callValueRefundAddress, - gasLimit, - maxFeePerGas, - data, - { value } - ) + if (isL2NetworkWithCustomFeeToken()) { + const inbox = ERC20Inbox__factory.connect( + l2Network.ethBridge.inbox, + l1Signer + ) + await inbox[func].createRetryableTicket( + to, + l2CallValue, + maxSubmissionCost, + excessFeeRefundAddress, + callValueRefundAddress, + gasLimit, + maxFeePerGas, + value, + data + ) + } else { + const inbox = Inbox__factory.connect( + l2Network.ethBridge.inbox, + l1Signer + ) + await inbox[func].createRetryableTicket( + to, + l2CallValue, + maxSubmissionCost, + excessFeeRefundAddress, + callValueRefundAddress, + gasLimit, + maxFeePerGas, + data, + { value } + ) + } + assert.fail(`Expected ${func} to fail`) } catch (err) { const typedErr = err as Error @@ -138,6 +162,16 @@ describe('RevertData', () => { }) ).wait() + if (isL2NetworkWithCustomFeeToken()) { + // approve the custom fee token + await ( + await erc20Bridger.approveFeeToken({ + erc20L1Address: l1TokenAddress, + l1Signer: l1Signer, + }) + ).wait() + } + const retryableOverrides: GasOverrides = { maxFeePerGas: { base: RetryableDataTools.ErrorTriggeringParams.maxFeePerGas, @@ -182,7 +216,9 @@ describe('RevertData', () => { ) expect(parsed.data, 'data').to.eq(depositParams.retryableData.data) expect(parsed.deposit.toString(), 'deposit').to.eq( - depositParams.txRequest.value.toString() + isL2NetworkWithCustomFeeToken() + ? depositParams.retryableData.deposit.toString() + : depositParams.txRequest.value.toString() ) expect(parsed.excessFeeRefundAddress, 'excessFeeRefundAddress').to.eq( depositParams.retryableData.excessFeeRefundAddress diff --git a/tests/integration/sanity.test.ts b/tests/integration/sanity.test.ts index fd6ce6869e..9780aa0567 100644 --- a/tests/integration/sanity.test.ts +++ b/tests/integration/sanity.test.ts @@ -29,6 +29,7 @@ import { L1ERC20Gateway__factory } from '../../src/lib/abi/factories/L1ERC20Gate import { testSetup } from '../../scripts/testSetup' import { randomBytes, hexlify } from 'ethers/lib/utils' +import { isL2NetworkWithCustomFeeToken } from './custom-fee-token/customFeeTokenTestHelpers' const expectIgnoreCase = (expected: string, actual: string) => { expect(expected.toLocaleLowerCase()).to.equal(actual.toLocaleLowerCase()) @@ -94,63 +95,6 @@ describe('sanity checks (read-only)', async () => { expect(l2Router).to.equal(l2Network.tokenBridge.l2GatewayRouter) }) - it('weth gateways gateways public storage vars properly set', async () => { - const { l1Signer, l2Signer, l2Network } = await testSetup() - - const l1Gateway = await L1WethGateway__factory.connect( - l2Network.tokenBridge.l1WethGateway, - l1Signer - ) - const l2Gateway = await L2WethGateway__factory.connect( - l2Network.tokenBridge.l2WethGateway, - l2Signer - ) - - const l1Weth = await l1Gateway.l1Weth() - expectIgnoreCase(l1Weth, l2Network.tokenBridge.l1Weth) - - const l2Weth = await l2Gateway.l2Weth() - expectIgnoreCase(l2Weth, l2Network.tokenBridge.l2Weth) - - const l1GatewayCounterParty = await l1Gateway.counterpartGateway() - expectIgnoreCase(l1GatewayCounterParty, l2Network.tokenBridge.l2WethGateway) - - const l2GatewayCounterParty = await l2Gateway.counterpartGateway() - expectIgnoreCase(l2GatewayCounterParty, l2Network.tokenBridge.l1WethGateway) - - const l1Router = await l1Gateway.router() - expectIgnoreCase(l1Router, l2Network.tokenBridge.l1GatewayRouter) - - const l2Router = await l2Gateway.router() - expectIgnoreCase(l2Router, l2Network.tokenBridge.l2GatewayRouter) - }) - - it('aeWETh public vars properly set', async () => { - const { l2Signer, l2Network } = await testSetup() - - const aeWeth = AeWETH__factory.connect( - l2Network.tokenBridge.l2Weth, - l2Signer - ) - - const l2GatewayOnAeWeth = await aeWeth.l2Gateway() - expectIgnoreCase(l2GatewayOnAeWeth, l2Network.tokenBridge.l2WethGateway) - - const l1AddressOnAeWeth = await aeWeth.l1Address() - expectIgnoreCase(l1AddressOnAeWeth, l2Network.tokenBridge.l1Weth) - }) - - it('l1 gateway router points to right weth gateways', async () => { - const { adminErc20Bridger, l1Signer, l2Network } = await testSetup() - - const gateway = await adminErc20Bridger.getL1GatewayAddress( - l2Network.tokenBridge.l1Weth, - l1Signer.provider! - ) - - expect(gateway).to.equal(l2Network.tokenBridge.l1WethGateway) - }) - it('L1 and L2 implementations of calculateL2ERC20Address match', async () => { const { l1Signer, l2Signer, l2Network, erc20Bridger } = await testSetup() @@ -168,4 +112,70 @@ describe('sanity checks (read-only)', async () => { expect(erc20L2AddressAsPerL2).to.equal(erc20L2AddressAsPerL1) }) + + // WETH tests are only relevant for networks that use WETH as the native token + if (!isL2NetworkWithCustomFeeToken()) { + it('weth gateways gateways public storage vars properly set', async () => { + const { l1Signer, l2Signer, l2Network } = await testSetup() + + const l1Gateway = await L1WethGateway__factory.connect( + l2Network.tokenBridge.l1WethGateway!, + l1Signer + ) + const l2Gateway = await L2WethGateway__factory.connect( + l2Network.tokenBridge.l2WethGateway!, + l2Signer + ) + + const l1Weth = await l1Gateway.l1Weth() + expectIgnoreCase(l1Weth, l2Network.tokenBridge.l1Weth!) + + const l2Weth = await l2Gateway.l2Weth() + expectIgnoreCase(l2Weth, l2Network.tokenBridge.l2Weth!) + + const l1GatewayCounterParty = await l1Gateway.counterpartGateway() + expectIgnoreCase( + l1GatewayCounterParty, + l2Network.tokenBridge.l2WethGateway! + ) + + const l2GatewayCounterParty = await l2Gateway.counterpartGateway() + expectIgnoreCase( + l2GatewayCounterParty, + l2Network.tokenBridge.l1WethGateway! + ) + + const l1Router = await l1Gateway.router() + expectIgnoreCase(l1Router, l2Network.tokenBridge.l1GatewayRouter) + + const l2Router = await l2Gateway.router() + expectIgnoreCase(l2Router, l2Network.tokenBridge.l2GatewayRouter) + }) + + it('aeWETh public vars properly set', async () => { + const { l2Signer, l2Network } = await testSetup() + + const aeWeth = AeWETH__factory.connect( + l2Network.tokenBridge.l2Weth!, + l2Signer + ) + + const l2GatewayOnAeWeth = await aeWeth.l2Gateway() + expectIgnoreCase(l2GatewayOnAeWeth, l2Network.tokenBridge.l2WethGateway!) + + const l1AddressOnAeWeth = await aeWeth.l1Address() + expectIgnoreCase(l1AddressOnAeWeth, l2Network.tokenBridge.l1Weth!) + }) + + it('l1 gateway router points to right weth gateways', async () => { + const { adminErc20Bridger, l1Signer, l2Network } = await testSetup() + + const gateway = await adminErc20Bridger.getL1GatewayAddress( + l2Network.tokenBridge.l1Weth!, + l1Signer.provider! + ) + + expect(gateway).to.equal(l2Network.tokenBridge.l1WethGateway) + }) + } }) diff --git a/tests/integration/standarderc20.test.ts b/tests/integration/standarderc20.test.ts index 7590dd8628..59c284724a 100644 --- a/tests/integration/standarderc20.test.ts +++ b/tests/integration/standarderc20.test.ts @@ -25,6 +25,7 @@ import { depositToken, GatewayType, withdrawToken, + fundL2, } from './testHelpers' import { Erc20Bridger, @@ -45,15 +46,15 @@ import { ArbRetryableTx__factory } from '../../src/lib/abi/factories/ArbRetryabl import { NodeInterface__factory } from '../../src/lib/abi/factories/NodeInterface__factory' import { isDefined } from '../../src/lib/utils/lib' import { - fundL1CustomFeeToken, - approveL1CustomFeeToken, - fundL2CustomFeeToken, approveL1CustomFeeTokenForErc20Deposit, getNativeTokenAllowance, + isL2NetworkWithCustomFeeToken, } from './custom-fee-token/customFeeTokenTestHelpers' const depositAmount = BigNumber.from(100) const withdrawalAmount = BigNumber.from(10) +const isCustomFeeToken = isL2NetworkWithCustomFeeToken() + describe('standard ERC20', () => { beforeEach('skipIfMainnet', async function () { await skipIfMainnet(this) @@ -71,10 +72,7 @@ describe('standard ERC20', () => { before('init', async () => { const setup = await testSetup() await fundL1(setup.l1Signer) - await fundL1CustomFeeToken(setup.l1Signer) - await approveL1CustomFeeToken(setup.l1Signer) - await fundL2CustomFeeToken(setup.l2Signer) - + await fundL2(setup.l2Signer) const deployErc20 = new TestERC20__factory().connect(setup.l1Signer) const testToken = await deployErc20.deploy() await testToken.deployed() @@ -84,54 +82,54 @@ describe('standard ERC20', () => { testState = { ...setup, l1Token: testToken } }) - it('approves the thing', async () => { - const { l1Signer, l2Signer, erc20Bridger } = await testSetup() - - await fundL1(l1Signer) - await fundL1CustomFeeToken(l1Signer) - await fundL2CustomFeeToken(l2Signer) + if (isL2NetworkWithCustomFeeToken()) { + it('approves the thing', async () => { + const { l1Signer, erc20Bridger } = await testSetup() - const gatewayAddress = await erc20Bridger.getL1GatewayAddress( - testState.l1Token.address, - l1Signer.provider! - ) + const gatewayAddress = await erc20Bridger.getL1GatewayAddress( + testState.l1Token.address, + l1Signer.provider! + ) - const initialAllowance = await getNativeTokenAllowance( - await l1Signer.getAddress(), - gatewayAddress - ) + const initialAllowance = await getNativeTokenAllowance( + await l1Signer.getAddress(), + gatewayAddress + ) - console.log({ initialAllowance }) + console.log({ initialAllowance }) - expect(initialAllowance.toString()).to.eq( - constants.Zero.toString(), - 'initial allowance is not empty' - ) + expect(initialAllowance.toString()).to.eq( + constants.Zero.toString(), + 'initial allowance is not empty' + ) - const tx = await erc20Bridger.approveFeeToken({ - l1Signer: l1Signer, - erc20L1Address: testState.l1Token.address, - }) - await tx.wait() + const tx = await erc20Bridger.approveFeeToken({ + l1Signer: l1Signer, + erc20L1Address: testState.l1Token.address, + }) + await tx.wait() - const finalAllowance = await getNativeTokenAllowance( - await l1Signer.getAddress(), - gatewayAddress - ) + const finalAllowance = await getNativeTokenAllowance( + await l1Signer.getAddress(), + gatewayAddress + ) - console.log({ finalAllowance }) + console.log({ finalAllowance }) - expect(finalAllowance.toString()).to.eq( - constants.MaxUint256.toString(), - 'initial allowance is not empty' - ) - }) + expect(finalAllowance.toString()).to.eq( + constants.MaxUint256.toString(), + 'initial allowance is not empty' + ) + }) + } - it.only('deposits erc20', async () => { - await approveL1CustomFeeTokenForErc20Deposit( - testState.l1Signer, - testState.l1Token.address - ) + it('deposits erc20', async () => { + if (isCustomFeeToken) { + await approveL1CustomFeeTokenForErc20Deposit( + testState.l1Signer, + testState.l1Token.address + ) + } await depositToken( depositAmount, @@ -268,7 +266,7 @@ describe('standard ERC20', () => { // force the redeem to fail by submitted just a bit under the required gas // so it is enough to pay for L1 + L2 intrinsic gas costs - await redeemAndTest(waitRes.message, 0, gasComponents.gasEstimate.sub(1000)) + await redeemAndTest(waitRes.message, 0, gasComponents.gasEstimate.sub(3000)) await redeemAndTest(waitRes.message, 1) }) diff --git a/tests/integration/testHelpers.ts b/tests/integration/testHelpers.ts index a258c96daf..0c3bd39c90 100644 --- a/tests/integration/testHelpers.ts +++ b/tests/integration/testHelpers.ts @@ -35,6 +35,8 @@ import { L2Network } from '../../src/lib/dataEntities/networks' import { GasOverrides } from '../../src/lib/message/L1ToL2MessageGasEstimator' import { ArbSdkError } from '../../src/lib/dataEntities/errors' import { ERC20 } from '../../src/lib/abi/ERC20' +import { isL2NetworkWithCustomFeeToken } from './custom-fee-token/customFeeTokenTestHelpers' +import { ERC20__factory } from '../../src/lib/abi/factories/ERC20__factory' export const preFundAmount = parseEther('0.1') @@ -255,6 +257,25 @@ export const depositToken = async ( expect(allowance.eq(Erc20Bridger.MAX_APPROVAL), 'set token allowance failed') .to.be.true + if (isL2NetworkWithCustomFeeToken()) { + await ( + await erc20Bridger.approveFeeToken({ + l1Signer, + erc20L1Address: l1TokenAddress, + }) + ).wait() + + const feeTokenAllowance = await ERC20__factory.connect( + erc20Bridger.nativeToken!, + l1Signer + ).allowance(await l1Signer.getAddress(), expectedL1GatewayAddress) + + expect( + feeTokenAllowance.eq(Erc20Bridger.MAX_APPROVAL), + 'set fee token allowance failed' + ).to.be.true + } + const initialBridgeTokenBalance = await l1Token.balanceOf( expectedL1GatewayAddress ) diff --git a/tests/integration/weth.test.ts b/tests/integration/weth.test.ts index 5f9da71bb7..339822cac1 100644 --- a/tests/integration/weth.test.ts +++ b/tests/integration/weth.test.ts @@ -31,102 +31,106 @@ import { L1ToL2MessageStatus } from '../../src' import { Wallet } from 'ethers' import { testSetup } from '../../scripts/testSetup' import { ERC20__factory } from '../../src/lib/abi/factories/ERC20__factory' +import { isL2NetworkWithCustomFeeToken } from './custom-fee-token/customFeeTokenTestHelpers' -describe('WETH', async () => { - beforeEach('skipIfMainnet', async function () { - await skipIfMainnet(this) - }) +// WETH tests are only relevant for networks that use WETH as the native token +if (!isL2NetworkWithCustomFeeToken()) { + describe('WETH', async () => { + beforeEach('skipIfMainnet', async function () { + await skipIfMainnet(this) + }) - it('deposit WETH', async () => { - const { l2Network, l1Signer, l2Signer, erc20Bridger } = await testSetup() + it('deposit WETH', async () => { + const { l2Network, l1Signer, l2Signer, erc20Bridger } = await testSetup() - const l1WethAddress = l2Network.tokenBridge.l1Weth + const l1WethAddress = l2Network.tokenBridge.l1Weth! - const wethToWrap = parseEther('0.00001') - const wethToDeposit = parseEther('0.0000001') + const wethToWrap = parseEther('0.00001') + const wethToDeposit = parseEther('0.0000001') - await fundL1(l1Signer, parseEther('1')) + await fundL1(l1Signer, parseEther('1')) - const l2WETH = AeWETH__factory.connect( - l2Network.tokenBridge.l2Weth, - l2Signer.provider! - ) - expect( - (await l2WETH.balanceOf(await l2Signer.getAddress())).toString(), - 'start balance weth' - ).to.eq('0') + const l2WETH = AeWETH__factory.connect( + l2Network.tokenBridge.l2Weth!, + l2Signer.provider! + ) + expect( + (await l2WETH.balanceOf(await l2Signer.getAddress())).toString(), + 'start balance weth' + ).to.eq('0') - const l1WETH = AeWETH__factory.connect(l1WethAddress, l1Signer) - const res = await l1WETH.deposit({ - value: wethToWrap, - }) - await res.wait() - await depositToken( - wethToDeposit, - l1WethAddress, - erc20Bridger, - l1Signer, - l2Signer, - L1ToL2MessageStatus.REDEEMED, - GatewayType.WETH - ) + const l1WETH = AeWETH__factory.connect(l1WethAddress, l1Signer) + const res = await l1WETH.deposit({ + value: wethToWrap, + }) + await res.wait() + await depositToken( + wethToDeposit, + l1WethAddress, + erc20Bridger, + l1Signer, + l2Signer, + L1ToL2MessageStatus.REDEEMED, + GatewayType.WETH + ) - const l2WethGateway = await erc20Bridger.getL2GatewayAddress( - l1WethAddress, - l2Signer.provider! - ) - expect(l2WethGateway, 'l2 weth gateway').to.eq( - l2Network.tokenBridge.l2WethGateway - ) - const l2Token = erc20Bridger.getL2TokenContract( - l2Signer.provider!, - l2Network.tokenBridge.l2Weth - ) - expect(l2Token.address, 'l2 weth').to.eq(l2Network.tokenBridge.l2Weth) + const l2WethGateway = await erc20Bridger.getL2GatewayAddress( + l1WethAddress, + l2Signer.provider! + ) + expect(l2WethGateway, 'l2 weth gateway').to.eq( + l2Network.tokenBridge.l2WethGateway + ) + const l2Token = erc20Bridger.getL2TokenContract( + l2Signer.provider!, + l2Network.tokenBridge.l2Weth! + ) + expect(l2Token.address, 'l2 weth').to.eq(l2Network.tokenBridge.l2Weth) - // now try to withdraw the funds - await fundL2(l2Signer) - const l2Weth = AeWETH__factory.connect(l2Token.address, l2Signer) - const randomAddr = Wallet.createRandom().address - await ( - await l2Weth.connect(l2Signer).withdrawTo(randomAddr, wethToDeposit) - ).wait() - const afterBalance = await l2Signer.provider!.getBalance(randomAddr) + // now try to withdraw the funds + await fundL2(l2Signer) + const l2Weth = AeWETH__factory.connect(l2Token.address, l2Signer) + const randomAddr = Wallet.createRandom().address + await ( + await l2Weth.connect(l2Signer).withdrawTo(randomAddr, wethToDeposit) + ).wait() + const afterBalance = await l2Signer.provider!.getBalance(randomAddr) - expect(afterBalance.toString(), 'balance after').to.eq( - wethToDeposit.toString() - ) - }) + expect(afterBalance.toString(), 'balance after').to.eq( + wethToDeposit.toString() + ) + }) - it('withdraw WETH', async () => { - const wethToWrap = parseEther('0.00001') - const wethToWithdraw = parseEther('0.00000001') + it('withdraw WETH', async () => { + const wethToWrap = parseEther('0.00001') + const wethToWithdraw = parseEther('0.00000001') - const { l2Network, l1Signer, l2Signer, erc20Bridger } = await testSetup() - await fundL1(l1Signer) - await fundL2(l2Signer) + const { l2Network, l1Signer, l2Signer, erc20Bridger } = await testSetup() + await fundL1(l1Signer) + await fundL2(l2Signer) - const l2Weth = AeWETH__factory.connect( - l2Network.tokenBridge.l2Weth, - l2Signer - ) - const res = await l2Weth.deposit({ - value: wethToWrap, - }) - const rec = await res.wait() - expect(rec.status).to.equal(1, 'deposit txn failed') + const l2Weth = AeWETH__factory.connect( + l2Network.tokenBridge.l2Weth!, + l2Signer + ) + const res = await l2Weth.deposit({ + value: wethToWrap, + }) + const rec = await res.wait() + expect(rec.status).to.equal(1, 'deposit txn failed') - await withdrawToken({ - amount: wethToWithdraw, - erc20Bridger: erc20Bridger, - gatewayType: GatewayType.WETH, - l1Signer: l1Signer, - l1Token: ERC20__factory.connect( - l2Network.tokenBridge.l1Weth, - l1Signer.provider! - ), - l2Signer: l2Signer, - startBalance: wethToWrap, + await withdrawToken({ + amount: wethToWithdraw, + erc20Bridger: erc20Bridger, + gatewayType: GatewayType.WETH, + l1Signer: l1Signer, + l1Token: ERC20__factory.connect( + l2Network.tokenBridge.l1Weth!, + l1Signer.provider! + ), + l2Signer: l2Signer, + startBalance: wethToWrap, + }) }) }) -}) +} From 5200584bdb2915465ad31bd3eaaf0736723e6730 Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Fri, 26 Jan 2024 15:54:47 -0500 Subject: [PATCH 141/192] restores getParentChains to fix tests --- src/lib/dataEntities/networks.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 9876f200ad..3ee38768a1 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -425,6 +425,7 @@ const getChainsByType = ( const getL1Chains = () => getChainsByType(isL1Network) const getArbitrumChains = () => getChainsByType(isArbitrumNetwork) +const getParentChains = () => getChainsByType(isParentChain) /** * Returns the parent chain for the given chain. @@ -490,7 +491,7 @@ export const getNetwork = async ( let network: L1Network | L2Network | undefined = undefined if (layer === 1) { - network = getL1Chains()[chainID] + network = getParentChains()[chainID] } else { network = getArbitrumChains()[chainID] } From 6395ee2af107a3a979d1596698d3566d24a996f8 Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Fri, 26 Jan 2024 18:08:43 -0500 Subject: [PATCH 142/192] linting --- scripts/testSetup.ts | 8 ++++++-- .../custom-fee-token/customFeeTokenTestHelpers.ts | 13 ++++++++----- tests/integration/eth.test.ts | 10 +++++----- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/scripts/testSetup.ts b/scripts/testSetup.ts index 863758fff8..25102536e1 100644 --- a/scripts/testSetup.ts +++ b/scripts/testSetup.ts @@ -29,7 +29,7 @@ import { getL2Network, addCustomNetwork, } from '../src/lib/dataEntities/networks' -import { Signer, ethers, providers } from 'ethers' +import { Signer, providers } from 'ethers' import { AdminErc20Bridger } from '../src/lib/assetBridger/erc20Bridger' import { execSync } from 'child_process' import { Bridge__factory } from '../src/lib/abi/factories/Bridge__factory' @@ -40,7 +40,11 @@ import * as fs from 'fs' import { ArbSdkError } from '../src/lib/dataEntities/errors' import { ARB_MINIMUM_BLOCK_TIME_IN_SECONDS } from '../src/lib/dataEntities/constants' import { IERC20Bridge__factory } from '../src/lib/abi/factories/IERC20Bridge__factory' -import { approveL1CustomFeeToken, fundL1CustomFeeToken, isL2NetworkWithCustomFeeToken } from '../tests/integration/custom-fee-token/customFeeTokenTestHelpers' +import { + approveL1CustomFeeToken, + fundL1CustomFeeToken, + isL2NetworkWithCustomFeeToken, +} from '../tests/integration/custom-fee-token/customFeeTokenTestHelpers' import { fundL1 } from '../tests/integration/testHelpers' dotenv.config() diff --git a/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts b/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts index cae9ce1b86..0fa270093e 100644 --- a/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts +++ b/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts @@ -10,9 +10,9 @@ import { Erc20Bridger, EthBridger } from '../../../src' import { ERC20__factory } from '../../../src/lib/abi/factories/ERC20__factory' // `config` isn't initialized yet, so we have to wrap these in functions -const ethProvider = ()=>new StaticJsonRpcProvider(config.ethUrl) -const arbProvider = ()=>new StaticJsonRpcProvider(config.arbUrl) -const localNetworks = ()=>getLocalNetworksFromFile() +const ethProvider = () => new StaticJsonRpcProvider(config.ethUrl) +const arbProvider = () => new StaticJsonRpcProvider(config.arbUrl) +const localNetworks = () => getLocalNetworksFromFile() export function isL2NetworkWithCustomFeeToken(): boolean { return typeof localNetworks()?.l2Network.nativeToken !== 'undefined' @@ -49,7 +49,7 @@ export async function fundL1CustomFeeToken(l1SignerOrAddress: Signer | string) { const tokenContract = ERC20__factory.connect(nativeToken, deployerWallet) const tx = await tokenContract.transfer(address, utils.parseEther('10')) - await tx.wait() + await tx.wait() } export async function approveL1CustomFeeToken(l1Signer: Signer) { @@ -61,7 +61,10 @@ export async function approveL1CustomFeeToken(l1Signer: Signer) { export async function getNativeTokenAllowance(owner: string, spender: string) { const nativeToken = localNetworks()?.l2Network.nativeToken - const nativeTokenContract = ERC20__factory.connect(nativeToken!, ethProvider()) + const nativeTokenContract = ERC20__factory.connect( + nativeToken!, + ethProvider() + ) return nativeTokenContract.allowance(owner, spender) } diff --git a/tests/integration/eth.test.ts b/tests/integration/eth.test.ts index 4640fb9463..7a966b2201 100644 --- a/tests/integration/eth.test.ts +++ b/tests/integration/eth.test.ts @@ -307,11 +307,11 @@ describe('Ether', async () => { ).to.eq(L2ToL1MessageStatus.EXECUTED) const finalRandomBalance = isL2NetworkWithCustomFeeToken() - ? await ERC20__factory.connect( - ethBridger.nativeToken!, - l1Signer.provider! - ).balanceOf(randomAddress) - : await l1Signer.provider!.getBalance(randomAddress) + ? await ERC20__factory.connect( + ethBridger.nativeToken!, + l1Signer.provider! + ).balanceOf(randomAddress) + : await l1Signer.provider!.getBalance(randomAddress) expect(finalRandomBalance.toString(), 'L1 final balance').to.eq( ethToWithdraw.toString() ) From eda81be955d5aae35d715c623a5d702e39330f15 Mon Sep 17 00:00:00 2001 From: Doug <4741454+douglance@users.noreply.github.com> Date: Mon, 29 Jan 2024 09:51:06 -0500 Subject: [PATCH 143/192] update lockfile --- yarn.lock | 587 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 530 insertions(+), 57 deletions(-) diff --git a/yarn.lock b/yarn.lock index bbf4027247..bdc3fb5055 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,18 +15,7 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@arbitrum/nitro-contracts@^1.0.0-beta.8": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@arbitrum/nitro-contracts/-/nitro-contracts-1.0.2.tgz#c73dce72b8f969a5909d5aaef6da80681c667475" - integrity sha512-Y+cXIQNsy9UNANnFrDGKhHzNmOWttxpXP0/uJFTRmS+rgS4ozlr81/UmBo3tX6uWjziDtquhYRuG3wx17talOQ== - dependencies: - "@openzeppelin/contracts" "4.5.0" - "@openzeppelin/contracts-upgradeable" "4.5.2" - patch-package "^6.4.7" - optionalDependencies: - sol2uml "2.2.0" - -"@arbitrum/nitro-contracts@^1.1.0": +"@arbitrum/nitro-contracts@^1.0.0-beta.8", "@arbitrum/nitro-contracts@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@arbitrum/nitro-contracts/-/nitro-contracts-1.1.0.tgz#37ca26e026306ca9cc9fbeb183841fd8666ac7bb" integrity sha512-/tLlU++IFdaD9Bn+RYzQ6+6k+0iDPuqi/cNf9kv5N1I9NAApNx1qfsIHoHMEQAvLuY+gj+raH7TAESBbzTAuuw== @@ -1350,9 +1339,9 @@ integrity sha512-bQHV8R9Me8IaJoJ2vPG4rXcL7seB7YVuskr4f+f5RyOStSZetwzkWtoqDMl5erkBJy0lDRUnIR2WIkPiC0GJlg== "@openzeppelin/upgrades-core@^1.24.1": - version "1.28.0" - resolved "https://registry.yarnpkg.com/@openzeppelin/upgrades-core/-/upgrades-core-1.28.0.tgz#19405f272dc09e766c756d9d149cbd680168aef7" - integrity sha512-8RKlyg98Adv+46GxDaR0awL3R8bVCcQ27DcSEwrgWOp6siHh8sZg4a2l+2dhPl1510S6uBfhHSydMH5VX2BV5g== + version "1.32.3" + resolved "https://registry.yarnpkg.com/@openzeppelin/upgrades-core/-/upgrades-core-1.32.3.tgz#7f92aeab6f6c7300c8fa4c1cde14253b2bd62341" + integrity sha512-v04RbrBOTRiIhfkTRfY4M34I2wIcuz+K1cUk/6duulsMXvRpM6/IPWeXh+1Xr1K+xedJi7gcS/pNSXfYhYNXIg== dependencies: cbor "^9.0.0" chalk "^4.1.0" @@ -1361,7 +1350,7 @@ ethereumjs-util "^7.0.3" minimist "^1.2.7" proper-lockfile "^4.1.1" - solidity-ast "^0.4.26" + solidity-ast "^0.4.51" "@scure/base@~1.1.0": version "1.1.3" @@ -1912,11 +1901,43 @@ array-back@^4.0.1, array-back@^4.0.2: resolved "https://registry.yarnpkg.com/array-back/-/array-back-4.0.2.tgz#8004e999a6274586beeb27342168652fdb89fa1e" integrity sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg== +array-buffer-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" + integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== + dependencies: + call-bind "^1.0.2" + is-array-buffer "^3.0.1" + array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== +array.prototype.findlast@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.findlast/-/array.prototype.findlast-1.2.3.tgz#4e4b375de5adf4897fed155e2d2771564865cc3b" + integrity sha512-kcBubumjciBg4JKp5KTKtI7ec7tRefPk88yjkWJwaVKYd9QfTaxcsOxoMNKd7iBr447zCfDV0z1kOF47umv42g== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.2.1" + +arraybuffer.prototype.slice@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz#98bd561953e3e74bb34938e77647179dfe6e9f12" + integrity sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + is-array-buffer "^3.0.2" + is-shared-array-buffer "^1.0.2" + assertion-error@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" @@ -1958,6 +1979,11 @@ audit-ci@^6.3.0: semver "^7.0.0" yargs "^17.0.0" +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + axios@^0.27.2: version "0.27.2" resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" @@ -1967,9 +1993,9 @@ axios@^0.27.2: form-data "^4.0.0" axios@^1.6.5: - version "1.6.7" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7" - integrity sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA== + version "1.6.5" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.5.tgz#2c090da14aeeab3770ad30c3a1461bc970fb0cd8" + integrity sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg== dependencies: follow-redirects "^1.15.4" form-data "^4.0.0" @@ -2167,6 +2193,15 @@ caching-transform@^4.0.0: package-hash "^4.0.0" write-file-atomic "^3.0.0" +call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" + integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== + dependencies: + function-bind "^1.1.2" + get-intrinsic "^1.2.1" + set-function-length "^1.1.1" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -2582,6 +2617,24 @@ default-require-extensions@^3.0.0: dependencies: strip-bom "^4.0.0" +define-data-property@^1.0.1, define-data-property@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -2721,6 +2774,76 @@ env-paths@^2.2.0: resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== +es-abstract@^1.22.1: + version "1.22.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32" + integrity sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA== + dependencies: + array-buffer-byte-length "^1.0.0" + arraybuffer.prototype.slice "^1.0.2" + available-typed-arrays "^1.0.5" + call-bind "^1.0.5" + es-set-tostringtag "^2.0.1" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.2" + get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-typed-array "^1.1.12" + is-weakref "^1.0.2" + object-inspect "^1.13.1" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.1" + safe-array-concat "^1.0.1" + safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.8" + string.prototype.trimend "^1.0.7" + string.prototype.trimstart "^1.0.7" + typed-array-buffer "^1.0.0" + typed-array-byte-length "^1.0.0" + typed-array-byte-offset "^1.0.0" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.13" + +es-set-tostringtag@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz#11f7cc9f63376930a5f20be4915834f4bc74f9c9" + integrity sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q== + dependencies: + get-intrinsic "^1.2.2" + has-tostringtag "^1.0.0" + hasown "^2.0.0" + +es-shim-unscopables@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== + dependencies: + hasown "^2.0.0" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + es6-error@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" @@ -3190,21 +3313,18 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== -follow-redirects@^1.12.1: - version "1.15.3" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" - integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== - -follow-redirects@^1.14.9: - version "1.15.2" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" - integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== - -follow-redirects@^1.15.4: +follow-redirects@^1.12.1, follow-redirects@^1.14.9, follow-redirects@^1.15.4: version "1.15.5" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + foreground-child@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-2.0.0.tgz#71b32800c9f15aa8f2f83f4a6bd9bff35d861a53" @@ -3287,11 +3407,31 @@ fsevents@~2.3.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -3307,6 +3447,16 @@ get-func-name@^2.0.0, get-func-name@^2.0.2: resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" + integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== + dependencies: + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -3324,6 +3474,14 @@ get-stream@^5.1.0: dependencies: pump "^3.0.0" +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -3378,6 +3536,13 @@ globals@^13.6.0, globals@^13.9.0: dependencies: type-fest "^0.20.2" +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + globby@^11.0.4, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" @@ -3390,6 +3555,13 @@ globby@^11.0.4, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" @@ -3471,6 +3643,11 @@ hardhat@^2.18.3: uuid "^8.3.2" ws "^7.4.6" +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -3481,6 +3658,30 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" + integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== + dependencies: + get-intrinsic "^1.2.2" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + has@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/has/-/has-1.0.4.tgz#2eb2860e000011dae4f1406a86fe80e530fb2ec6" @@ -3511,6 +3712,13 @@ hasha@^5.0.0: is-stream "^2.0.0" type-fest "^0.8.0" +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + he@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -3617,6 +3825,15 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +internal-slot@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.6.tgz#37e756098c4911c5e912b8edbf71ed3aa116f930" + integrity sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg== + dependencies: + get-intrinsic "^1.2.2" + hasown "^2.0.0" + side-channel "^1.0.4" + io-ts@1.10.4: version "1.10.4" resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-1.10.4.tgz#cd5401b138de88e4f920adbcb7026e2d1967e6e2" @@ -3624,6 +3841,22 @@ io-ts@1.10.4: dependencies: fp-ts "^1.0.0" +is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" + integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + is-typed-array "^1.1.10" + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -3631,11 +3864,24 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + is-buffer@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + is-ci@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" @@ -3650,6 +3896,13 @@ is-core-module@^2.8.1: dependencies: has "^1.0.3" +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + is-docker@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" @@ -3677,6 +3930,18 @@ is-hex-prefixed@1.0.0: resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" integrity sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA== +is-negative-zero@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -3687,11 +3952,47 @@ is-plain-obj@^2.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" + is-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.9: + version "1.1.12" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" + integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== + dependencies: + which-typed-array "^1.1.11" + is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -3702,6 +4003,13 @@ is-unicode-supported@^0.1.0: resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -3714,6 +4022,11 @@ is-wsl@^2.1.1: dependencies: is-docker "^2.0.0" +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -4154,12 +4467,7 @@ minimatch@^9.0.0: dependencies: brace-expansion "^2.0.1" -minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== - -minimist@^1.2.6: +minimist@^1.2.5, minimist@^1.2.6: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== @@ -4374,6 +4682,26 @@ nyc@^15.1.0: test-exclude "^6.0.0" yargs "^15.0.2" +object-inspect@^1.13.1, object-inspect@^1.9.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.4: + version "4.1.5" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + has-symbols "^1.0.3" + object-keys "^1.1.1" + obliterator@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.4.tgz#fa650e019b2d075d745e44f1effeb13a2adbe816" @@ -4761,6 +5089,15 @@ reduce-flatten@^2.0.0: resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" integrity sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w== +regexp.prototype.flags@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" + integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + set-function-name "^2.0.0" + regexpp@^3.1.0, regexpp@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" @@ -4872,6 +5209,16 @@ rustbn.js@~0.2.0: resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA== +safe-array-concat@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.0.tgz#8d0cae9cb806d6d1c06e08ab13d847293ebe0692" + integrity sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg== + dependencies: + call-bind "^1.0.5" + get-intrinsic "^1.2.2" + has-symbols "^1.0.3" + isarray "^2.0.5" + safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -4882,6 +5229,15 @@ safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-regex-test@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.2.tgz#3ba32bdb3ea35f940ee87e5087c60ee786c3f6c5" + integrity sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ== + dependencies: + call-bind "^1.0.5" + get-intrinsic "^1.2.2" + is-regex "^1.1.4" + "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -4901,12 +5257,7 @@ secp256k1@^4.0.1: node-addon-api "^2.0.0" node-gyp-build "^4.2.0" -semver@^5.3.0, semver@^5.6.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@^5.5.0: +semver@^5.3.0, semver@^5.5.0, semver@^5.6.0: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== @@ -4916,14 +5267,7 @@ semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.0.0, semver@^7.2.1, semver@^7.3.5, semver@^7.3.7: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== - dependencies: - lru-cache "^6.0.0" - -semver@^7.5.3: +semver@^7.0.0, semver@^7.2.1, semver@^7.3.5, semver@^7.3.7, semver@^7.5.3: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== @@ -4942,6 +5286,26 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +set-function-length@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.0.tgz#2f81dc6c16c7059bda5ab7c82c11f03a515ed8e1" + integrity sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w== + dependencies: + define-data-property "^1.1.1" + function-bind "^1.1.2" + get-intrinsic "^1.2.2" + gopd "^1.0.1" + has-property-descriptors "^1.0.1" + +set-function-name@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" + integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA== + dependencies: + define-data-property "^1.0.1" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.0" + setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" @@ -4994,6 +5358,15 @@ shiki@^0.14.1: vscode-oniguruma "^1.7.0" vscode-textmate "^8.0.0" +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + signal-exit@^3.0.2: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -5053,10 +5426,12 @@ solc@0.7.3: semver "^5.5.0" tmp "0.0.33" -solidity-ast@^0.4.26: - version "0.4.49" - resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.49.tgz#ecba89d10c0067845b7848c3a3e8cc61a4fc5b82" - integrity sha512-Pr5sCAj1SFqzwFZw1HPKSq0PehlQNdM8GwKyAVYh2DOn7/cCK8LUKD1HeHnKtTgBW7hi9h4nnnan7hpAg5RhWQ== +solidity-ast@^0.4.51: + version "0.4.55" + resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.55.tgz#00b685e6eefb2e8dfb67df1fe0afbe3b3bfb4b28" + integrity sha512-qeEU/r/K+V5lrAw8iswf2/yfWAnSGs3WKPHI+zAFKFjX0dIBVXEU/swQ8eJQYHf6PJWUZFO2uWV4V1wEOkeQbA== + dependencies: + array.prototype.findlast "^1.2.2" solidity-comments-extractor@^0.0.7: version "0.0.7" @@ -5134,6 +5509,33 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string.prototype.trim@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" + integrity sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +string.prototype.trimend@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" + integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +string.prototype.trimstart@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" + integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -5435,6 +5837,45 @@ typechain@7.0.0: ts-command-line-args "^2.2.0" ts-essentials "^7.0.1" +typed-array-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" + integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-typed-array "^1.1.10" + +typed-array-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" + integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" + integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -5479,6 +5920,16 @@ uglify-js@^3.1.4: resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.15.4.tgz#fa95c257e88f85614915b906204b9623d4fa340d" integrity sha512-vMOPGDuvXecPs34V74qDKk4iJ/SN4vL3Ow/23ixafENYvtrNvtbcgUeugTcUGRGsOF/5fU8/NYSL5Hyb3l1OJA== +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + unbzip2-stream@1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" @@ -5572,11 +6023,33 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + which-module@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== +which-typed-array@^1.1.11, which-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36" + integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.4" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + which@2.0.2, which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -5592,9 +6065,9 @@ which@^1.2.9: isexe "^2.0.0" word-wrap@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== wordwrap@^1.0.0: version "1.0.0" From be3708fe021dabfb144eb03d37b9cc44f082d4e7 Mon Sep 17 00:00:00 2001 From: spsjvc Date: Tue, 30 Jan 2024 11:22:55 +0100 Subject: [PATCH 144/192] remove obsolete tests --- tests/unit/network.test.ts | 52 -------------------------------------- 1 file changed, 52 deletions(-) diff --git a/tests/unit/network.test.ts b/tests/unit/network.test.ts index ad578e9f3f..1cd8d937db 100644 --- a/tests/unit/network.test.ts +++ b/tests/unit/network.test.ts @@ -225,24 +225,6 @@ describe('Networks', () => { } }) - it('successfully fetches a parent chain with `getNetwork`', async function () { - const arbOneNetwork = await getL2Network(arbOneId) - - const mockCustomNetwork = { - customL2Network: { - ...arbOneNetwork, - chainID: mockOrbitChainId, - partnerChainID: arbOneId, - isArbitrum: true, - isCustom: true, - }, - } as const - - addCustomNetwork(mockCustomNetwork) - const parentChain = await getL1Network(arbOneId) - expect(parentChain.chainID, fetchErrorMessage).to.be.eq(arbOneId) - }) - it('successfully fetches an L2 network with `getL2Network`', async function () { const network = await getL2Network(arbOneId) expect(network.chainID, fetchErrorMessage).to.be.eq(arbOneId) @@ -325,40 +307,6 @@ describe('Networks', () => { ) } }) - - it('fails to fetch a network in L1 until a child is added', async function () { - let parentChain - try { - parentChain = await getL1Network(arbOneId) - } catch (err) { - // should fail - expect(err).to.be.an('error') - expect((err as Error).message).to.be.eq( - `Unrecognized network ${arbOneId}.` - ) - } finally { - expect( - parentChain, - '`getNetwork` returned a result for an Orbit chain.' - ).to.be.undefined - } - - const arbOneNetwork = await getL2Network(arbOneId) - - const mockCustomNetwork = { - customL2Network: { - ...arbOneNetwork, - chainID: mockOrbitChainId, - partnerChainID: arbOneId, - isArbitrum: true, - isCustom: true, - }, - } as const - - addCustomNetwork(mockCustomNetwork) - parentChain = await getL1Network(arbOneId) - expect(parentChain.chainID, fetchErrorMessage).to.be.eq(arbOneId) - }) }) describe('returns correct networks', () => { From 5824ef4667e0a4018937f1a9ad97590a8baf6512 Mon Sep 17 00:00:00 2001 From: spsjvc Date: Tue, 30 Jan 2024 12:01:52 +0100 Subject: [PATCH 145/192] update ref --- .github/workflows/build-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 7218a8959e..121d98c86e 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -148,7 +148,7 @@ jobs: - name: Set up the local node uses: OffchainLabs/actions/run-nitro-test-node@main with: - nitro-testnode-ref: use-tokenbridge-creator + nitro-testnode-ref: master no-token-bridge: true l3-node: ${{ matrix.orbit-test == '1' }} From 48056bcf8a117f1224af400c371920ef8b8078e8 Mon Sep 17 00:00:00 2001 From: spsjvc Date: Fri, 2 Feb 2024 12:54:58 +0100 Subject: [PATCH 146/192] fix --- scripts/testSetup.ts | 9 +++++++-- src/lib/dataEntities/networks.ts | 13 ++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/scripts/testSetup.ts b/scripts/testSetup.ts index 1644dce6d5..1e7c8238d0 100644 --- a/scripts/testSetup.ts +++ b/scripts/testSetup.ts @@ -361,7 +361,10 @@ export const setupNetworks = async ( if (isTestingOrbitChains) { addCustomNetwork({ customL2Network: l2Network }) } else { - addCustomNetwork({ ...customNetworks, customL2Network: l2Network }) + addCustomNetwork({ + customL1Network: customNetworks.customL1Network as L1Network, + customL2Network: l2Network, + }) } // also register the weth gateway @@ -416,7 +419,9 @@ export const testSetup = async (): Promise<{ let setL1Network: L1Network | L2Network, setL2Network: L2Network try { - const l1Network = await getL1Network(l1Deployer) + const l1Network = isTestingOrbitChains + ? await getL2Network(l1Deployer) + : await getL1Network(l1Deployer) const l2Network = await getL2Network(l2Deployer) setL1Network = l1Network setL2Network = l2Network diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 6c3ffd9094..2de197f5b1 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -581,15 +581,10 @@ const addNetwork = (network: L1Network | L2Network) => { export const addCustomNetwork = ({ customL1Network, customL2Network, -}: - | { - customL1Network?: L1Network - customL2Network: L2Network - } - | { - customL1Network?: L2Network - customL2Network: L2Network - }): void => { +}: { + customL1Network?: L1Network + customL2Network: L2Network +}): void => { if (customL1Network) { // check the if the parent chain is in any of the lists if (l1Networks[customL1Network.chainID]) { From 05b854083c8c9cf3ab2783d6316280047c486dbd Mon Sep 17 00:00:00 2001 From: spsjvc Date: Fri, 2 Feb 2024 12:58:34 +0100 Subject: [PATCH 147/192] revert comments --- src/lib/assetBridger/assetBridger.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/lib/assetBridger/assetBridger.ts b/src/lib/assetBridger/assetBridger.ts index 9e1079b613..db535eff45 100644 --- a/src/lib/assetBridger/assetBridger.ts +++ b/src/lib/assetBridger/assetBridger.ts @@ -30,12 +30,9 @@ import { } from '../dataEntities/signerOrProvider' /** - * Base for bridging assets from a parent chain to a child chain and back. + * Base for bridging assets from l1 to l2 and back */ export abstract class AssetBridger { - /** - * The parent chain. Could be an L1 or an L2. - */ public readonly l1Network: L1Network | L2Network public constructor(public readonly l2Network: L2Network) { @@ -43,7 +40,7 @@ export abstract class AssetBridger { } /** - * Check the signer/provider matches the parent chain, throws if not. + * Check the signer/provider matches the l1Network, throws if not * @param sop */ protected async checkL1Network(sop: SignerOrProvider): Promise { @@ -51,7 +48,7 @@ export abstract class AssetBridger { } /** - * Check the signer/provider matches the child chain, throws if not. + * Check the signer/provider matches the l2Network, throws if not * @param sop */ protected async checkL2Network(sop: SignerOrProvider): Promise { @@ -59,13 +56,13 @@ export abstract class AssetBridger { } /** - * Transfer assets from parent chain to child chain. + * Transfer assets from L1 to L2 * @param params */ public abstract deposit(params: DepositParams): Promise /** - * Transfer assets from child chain to parent chain. + * Transfer assets from L2 to L1 * @param params */ public abstract withdraw( From 555de6964eb373faefc9ebb040b5a3031e270ed0 Mon Sep 17 00:00:00 2001 From: spsjvc Date: Fri, 2 Feb 2024 13:04:35 +0100 Subject: [PATCH 148/192] remove obsolete test --- tests/unit/network.test.ts | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/tests/unit/network.test.ts b/tests/unit/network.test.ts index 1cd8d937db..298445605f 100644 --- a/tests/unit/network.test.ts +++ b/tests/unit/network.test.ts @@ -83,32 +83,6 @@ describe('Networks', () => { expect(await getNetwork(mockOrbitChainId, 2)).to.be.ok }) - it('adds a custom L2 and Orbit chain', async function () { - const arbOneNetwork = await getL2Network(arbOneId) - - const mockCustomNetwork = { - customL1Network: { - ...arbOneNetwork, - partnerChainID: mainnetId, - chainID: mockL2ChainId, - isArbitrum: true, - isCustom: true, - }, - customL2Network: { - ...arbOneNetwork, - chainID: mockOrbitChainId, - partnerChainID: mockL2ChainId, - isArbitrum: true, - isCustom: true, - }, - } as const - - addCustomNetwork(mockCustomNetwork) - - expect(await getL2Network(mockL2ChainId)).to.be.ok - expect(await getL2Network(mockOrbitChainId)).to.be.ok - }) - it('adds a custom L1, L2, and L3', async function () { const ethNetwork = await getL1Network(mainnetId) const arbOneNetwork = await getL2Network(arbOneId) From 7fd3af88d353e7dfd80efababb27bced7fb58ec1 Mon Sep 17 00:00:00 2001 From: spsjvc Date: Fri, 2 Feb 2024 14:26:00 +0100 Subject: [PATCH 149/192] start cleaning up tests --- src/lib/dataEntities/networks.ts | 6 + tests/unit/network.test.ts | 258 ++++++++++++++++--------------- 2 files changed, 136 insertions(+), 128 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 2de197f5b1..fb949583b5 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -586,6 +586,12 @@ export const addCustomNetwork = ({ customL2Network: L2Network }): void => { if (customL1Network) { + if (customL1Network.chainID !== customL2Network.partnerChainID) { + throw new ArbSdkError( + `Partner chain id for L2 network ${customL2Network.chainID} doesn't match the provided L1 network. Expected ${customL1Network.chainID} but got ${customL2Network.partnerChainID}.` + ) + } + // check the if the parent chain is in any of the lists if (l1Networks[customL1Network.chainID]) { throw new ArbSdkError( diff --git a/tests/unit/network.test.ts b/tests/unit/network.test.ts index 298445605f..6f635b2352 100644 --- a/tests/unit/network.test.ts +++ b/tests/unit/network.test.ts @@ -1,6 +1,5 @@ import { expect } from 'chai' import { - getNetwork, resetNetworksToDefault, addCustomNetwork, getL1Network, @@ -9,169 +8,174 @@ import { l2Networks, } from '../../src/lib/dataEntities/networks' -const mainnetId = 1 -const arbOneId = 42161 -const arbNovaId = 42170 +const ethereumMainnetChainId = 1 +const arbitrumOneChainId = 42161 +const arbitrumNovaChainId = 42170 + const mockL1ChainId = 111111 const mockL2ChainId = 222222 -const mockOrbitChainId = 99999999 +const mockL3ChainId = 99999999 -describe('Networks', () => { +describe('Networks', async () => { const fetchErrorMessage = 'Network fetched successfully but the chain ID is invalid.' + const ethereumMainnet = await getL1Network(ethereumMainnetChainId) + const arbitrumOne = await getL2Network(arbitrumOneChainId) + beforeEach(async () => { resetNetworksToDefault() }) describe('adding networks', () => { it('adds a custom L2 network', async function () { - const arbOneNetwork = await getL2Network(arbOneId) - const mockCustomNetwork = { - customL2Network: { - ...arbOneNetwork, - chainID: mockL2ChainId, - isArbitrum: true, - isCustom: true, - }, + const customL2Network = { + ...arbitrumOne, + chainID: mockL2ChainId, + isArbitrum: true, + isCustom: true, } as const - addCustomNetwork(mockCustomNetwork) + addCustomNetwork({ customL2Network }) expect(await getL2Network(mockL2ChainId)).to.be.ok + + // assert chain was added as child + const l1Network = await getL1Network(customL2Network.partnerChainID) + expect(l1Network.partnerChainIDs).to.include(mockL2ChainId) }) it('adds a custom L1 and L2 network', async function () { - const ethNetwork = await getL1Network(mainnetId) - const arbOneNetwork = await getL2Network(arbOneId) - const mockCustomNetwork = { - customL1Network: { - ...ethNetwork, - chainID: mockL1ChainId, - isArbitrum: false, - isCustom: true, - }, - customL2Network: { - ...arbOneNetwork, - chainID: mockL2ChainId, - isArbitrum: true, - isCustom: true, - }, + const customL1Network = { + ...ethereumMainnet, + chainID: mockL1ChainId, + isArbitrum: false, + isCustom: true, } as const - addCustomNetwork(mockCustomNetwork) + const customL2Network = { + ...arbitrumOne, + partnerChainID: mockL1ChainId, + chainID: mockL2ChainId, + isArbitrum: true, + isCustom: true, + } as const + + addCustomNetwork({ customL1Network, customL2Network }) expect(await getL1Network(mockL1ChainId)).to.be.ok expect(await getL2Network(mockL2ChainId)).to.be.ok - }) - it('adds a custom Orbit chain', async function () { - const arbOneNetwork = await getL2Network(arbOneId) + // assert chain was added as child + const l1Network = await getL1Network(mockL1ChainId) + expect(l1Network.partnerChainIDs).to.include(mockL2ChainId) + }) - const mockCustomNetwork = { - customL2Network: { - ...arbOneNetwork, - chainID: mockOrbitChainId, - partnerChainID: arbOneId, - isArbitrum: true, - isCustom: true, - }, + it('adds a custom L3 network', async function () { + const customL2Network = { + ...arbitrumOne, + chainID: mockL3ChainId, + partnerChainID: arbitrumOneChainId, + isArbitrum: true, + isCustom: true, } as const - addCustomNetwork(mockCustomNetwork) + addCustomNetwork({ customL2Network }) - expect(await getNetwork(mockOrbitChainId, 2)).to.be.ok - }) + expect(await getL2Network(mockL3ChainId)).to.be.ok - it('adds a custom L1, L2, and L3', async function () { - const ethNetwork = await getL1Network(mainnetId) - const arbOneNetwork = await getL2Network(arbOneId) + // assert chain was added as child + const l2Network = await getL2Network(customL2Network.partnerChainID) + expect(l2Network.partnerChainIDs).to.include(mockL3ChainId) + }) - const mockCustomNetwork = { - customL1Network: { - ...ethNetwork, - chainID: mockL1ChainId, - isArbitrum: false, - isCustom: true, - }, - customL2Network: { - ...arbOneNetwork, - chainID: mockL2ChainId, - isArbitrum: true, - isCustom: true, - }, + it('adds a custom L1, L2, and L3 network', async function () { + const customL1Network = { + ...ethereumMainnet, + chainID: mockL1ChainId, + isArbitrum: false, + isCustom: true, } as const - addCustomNetwork(mockCustomNetwork) - - const mockOrbitNetwork = { - customL2Network: { - ...arbOneNetwork, - chainID: mockOrbitChainId, - partnerChainID: mockL2ChainId, - isArbitrum: true, - isCustom: true, - }, + const customL2Network = { + ...arbitrumOne, + chainID: mockL2ChainId, + partnerChainID: mockL1ChainId, + isArbitrum: true, + isCustom: true, } as const - addCustomNetwork(mockOrbitNetwork) + addCustomNetwork({ customL1Network, customL2Network }) expect(await getL1Network(mockL1ChainId)).to.be.ok expect(await getL2Network(mockL2ChainId)).to.be.ok - expect(await getL2Network(mockOrbitChainId)).to.be.ok + + // assert chain was added as child + const l1Network = await getL1Network(mockL1ChainId) + expect(l1Network.partnerChainIDs).to.include(mockL2ChainId) + + const customL3Network = { + ...arbitrumOne, + chainID: mockL3ChainId, + partnerChainID: mockL2ChainId, + isArbitrum: true, + isCustom: true, + } as const + + addCustomNetwork({ customL2Network: customL3Network }) + + expect(await getL2Network(mockL3ChainId)).to.be.ok + + // assert chain was added as child + const l2Network = await getL2Network(mockL2ChainId) + expect(l2Network.partnerChainIDs).to.include(mockL3ChainId) }) - it('fails to add an Orbit chain paired with default L1', async function () { - const arbOneNetwork = await getL2Network(arbOneId) + it('fails to add a custom L1 and L2 network if they do not match', async function () { + const wrongPartnerChainId = 1241244 - const mockCustomNetwork = { - customL2Network: { - ...arbOneNetwork, - chainID: mockOrbitChainId, - partnerChainID: mainnetId, - isArbitrum: true, - isCustom: true, - }, + const customL1Network = { + ...ethereumMainnet, + chainID: mockL1ChainId, + isArbitrum: false, + isCustom: true, + } as const + + const customL2Network = { + ...arbitrumOne, + partnerChainID: wrongPartnerChainId, + chainID: mockL2ChainId, + isArbitrum: true, + isCustom: true, } as const try { - addCustomNetwork(mockCustomNetwork) + addCustomNetwork({ customL1Network, customL2Network }) } catch (err) { // should fail expect(err).to.be.an('error') expect((err as Error).message).to.be.eq( - 'Orbit chains must be paired with an L2 chain' + `Partner chain id for L2 network ${customL2Network.chainID} doesn't match the provided L1 network. Expected ${customL1Network.chainID} but got ${wrongPartnerChainId}.` ) } }) - it('fails to add a custom L1 paired with an L3', async function () { - const ethNetwork = await getL1Network(mainnetId) - const arbOneNetwork = await getL2Network(arbOneId) - - const mockCustomNetwork = { - customL1Network: { - ...ethNetwork, - chainID: mockL1ChainId, - isArbitrum: false, - isCustom: true, - }, - customL2Network: { - ...arbOneNetwork, - chainID: mockOrbitChainId, - partnerChainID: mockL1ChainId, - isArbitrum: true, - isCustom: true, - }, - } as const - + it('fails to add a custom L3 without previously registering L2', async function () { try { - addCustomNetwork(mockCustomNetwork) + addCustomNetwork({ + customL2Network: { + ...arbitrumOne, + chainID: mockL3ChainId, + partnerChainID: mockL2ChainId, + isArbitrum: true, + isCustom: true, + }, + }) } catch (err) { // should fail expect(err).to.be.an('error') expect((err as Error).message).to.be.eq( - 'Orbit chains must be paired with an L2 chain' + `Network ${mockL3ChainId}'s parent network ${mockL2ChainId} is not recognized` ) } }) @@ -179,19 +183,24 @@ describe('Networks', () => { describe('fetching networks', () => { it('successfully fetches an L1 network with `getL1Network`', async function () { - const network = await getL1Network(mainnetId) - expect(network.chainID, fetchErrorMessage).to.be.eq(mainnetId) + const network = await getL1Network(ethereumMainnetChainId) + expect(network.chainID).to.be.eq(ethereumMainnetChainId) + }) + + it('successfully fetches an L2 network with `getL2Network`', async function () { + const network = await getL2Network(arbitrumOneChainId) + expect(network.chainID, fetchErrorMessage).to.be.eq(arbitrumOneChainId) }) it('fails to fetch an L2 network that is not a parent with `getL1Network`', async function () { let network try { - network = await getL1Network(arbNovaId) + network = await getL1Network(arbitrumNovaChainId) } catch (err) { // should fail expect(err).to.be.an('error') expect((err as Error).message).to.be.eq( - `Unrecognized network ${arbNovaId}.` + `Unrecognized network ${arbitrumNovaChainId}.` ) } finally { expect(network, '`getL1Network` returned a result for an L2 network.') @@ -199,20 +208,15 @@ describe('Networks', () => { } }) - it('successfully fetches an L2 network with `getL2Network`', async function () { - const network = await getL2Network(arbOneId) - expect(network.chainID, fetchErrorMessage).to.be.eq(arbOneId) - }) - it('fails to fetch an L1 network with `getL2Network`', async function () { let network try { - network = await getL2Network(mainnetId) + network = await getL2Network(ethereumMainnetChainId) } catch (err) { // should fail expect(err).to.be.an('error') expect((err as Error).message).to.be.eq( - `Unrecognized network ${mainnetId}.` + `Unrecognized network ${ethereumMainnetChainId}.` ) } finally { expect(network, '`getL2Network` returned a result for an L1 network.') @@ -223,12 +227,12 @@ describe('Networks', () => { it('fails to fetch an Orbit chain with `getL1Network`', async function () { let parentChain try { - parentChain = await getL1Network(mockOrbitChainId) + parentChain = await getL1Network(mockL3ChainId) } catch (err) { // should fail expect(err).to.be.an('error') expect((err as Error).message).to.be.eq( - `Unrecognized network ${mockOrbitChainId}.` + `Unrecognized network ${mockL3ChainId}.` ) } finally { expect( @@ -238,22 +242,20 @@ describe('Networks', () => { } }) - it('successfully fetches an Orbit chain with `getL2Network`', async function () { - const arbOneNetwork = await getL2Network(arbOneId) - + it('successfully fetches an L3 chain with `getL2Network`', async function () { const mockCustomNetwork = { customL2Network: { - ...arbOneNetwork, - chainID: mockOrbitChainId, - partnerChainID: arbOneId, + ...arbitrumOne, + chainID: mockL3ChainId, + partnerChainID: arbitrumOneChainId, isArbitrum: true, isCustom: true, }, } as const addCustomNetwork(mockCustomNetwork) - const chain = await getNetwork(mockOrbitChainId, 2) - expect(chain.chainID, fetchErrorMessage).to.be.eq(mockOrbitChainId) + const chain = await getL2Network(mockL3ChainId) + expect(chain.chainID, fetchErrorMessage).to.be.eq(mockL3ChainId) }) it('fails to fetch an unrecognized L1 network', async () => { From 5d95a0d309bf5855b96731b63bc3d06fd7de2632 Mon Sep 17 00:00:00 2001 From: spsjvc Date: Fri, 2 Feb 2024 14:31:11 +0100 Subject: [PATCH 150/192] more --- tests/unit/network.test.ts | 67 +++++++++++++------------------------- 1 file changed, 22 insertions(+), 45 deletions(-) diff --git a/tests/unit/network.test.ts b/tests/unit/network.test.ts index 6f635b2352..9506250174 100644 --- a/tests/unit/network.test.ts +++ b/tests/unit/network.test.ts @@ -192,77 +192,54 @@ describe('Networks', async () => { expect(network.chainID, fetchErrorMessage).to.be.eq(arbitrumOneChainId) }) - it('fails to fetch an L2 network that is not a parent with `getL1Network`', async function () { - let network + it('fails to fetch a registered L2 network with `getL1Network`', async function () { try { - network = await getL1Network(arbitrumNovaChainId) + await getL1Network(arbitrumOneChainId) } catch (err) { // should fail expect(err).to.be.an('error') expect((err as Error).message).to.be.eq( - `Unrecognized network ${arbitrumNovaChainId}.` + `Unrecognized network ${arbitrumOneChainId}.` ) - } finally { - expect(network, '`getL1Network` returned a result for an L2 network.') - .to.be.undefined } }) - it('fails to fetch an L1 network with `getL2Network`', async function () { - let network + it('fails to fetch a registered L1 network with `getL2Network`', async function () { try { - network = await getL2Network(ethereumMainnetChainId) + await getL2Network(ethereumMainnetChainId) } catch (err) { // should fail expect(err).to.be.an('error') expect((err as Error).message).to.be.eq( `Unrecognized network ${ethereumMainnetChainId}.` ) - } finally { - expect(network, '`getL2Network` returned a result for an L1 network.') - .to.be.undefined - } - }) - - it('fails to fetch an Orbit chain with `getL1Network`', async function () { - let parentChain - try { - parentChain = await getL1Network(mockL3ChainId) - } catch (err) { - // should fail - expect(err).to.be.an('error') - expect((err as Error).message).to.be.eq( - `Unrecognized network ${mockL3ChainId}.` - ) - } finally { - expect( - parentChain, - '`getNetwork` returned a result for an Orbit chain.' - ).to.be.undefined } }) it('successfully fetches an L3 chain with `getL2Network`', async function () { - const mockCustomNetwork = { - customL2Network: { - ...arbitrumOne, - chainID: mockL3ChainId, - partnerChainID: arbitrumOneChainId, - isArbitrum: true, - isCustom: true, - }, + const customL3Network = { + ...arbitrumOne, + chainID: mockL3ChainId, + partnerChainID: arbitrumOneChainId, + isArbitrum: true, + isCustom: true, } as const - addCustomNetwork(mockCustomNetwork) - const chain = await getL2Network(mockL3ChainId) - expect(chain.chainID, fetchErrorMessage).to.be.eq(mockL3ChainId) + addCustomNetwork({ customL2Network: customL3Network }) + + const l3Network = await getL2Network(mockL3ChainId) + expect(l3Network.chainID, fetchErrorMessage).to.be.eq(mockL3ChainId) + + // assert chain was added as child + const l2Network = await getL2Network(customL3Network.partnerChainID) + expect(l2Network.partnerChainIDs).to.include(mockL3ChainId) }) it('fails to fetch an unrecognized L1 network', async () => { const chainId = 9999 + try { await getL1Network(chainId) - expect.fail('Expected error was not thrown') } catch (err) { expect(err).to.be.instanceOf(Error) expect((err as Error).message).to.be.eq( @@ -271,11 +248,11 @@ describe('Networks', async () => { } }) - it('fails to fetch an unrecognized L2 network', async () => { + it('fails to fetch an unrecognized L2/L3 network', async () => { const chainId = 9999 + try { await getL2Network(chainId) - expect.fail('Expected error was not thrown') } catch (err) { expect(err).to.be.instanceOf(Error) expect((err as Error).message).to.be.eq( From 0861bd1e0d1e4f832991a8a5f151bb054a56afe3 Mon Sep 17 00:00:00 2001 From: spsjvc Date: Fri, 2 Feb 2024 14:35:17 +0100 Subject: [PATCH 151/192] add more assertions --- tests/unit/network.test.ts | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/tests/unit/network.test.ts b/tests/unit/network.test.ts index 9506250174..45878fd938 100644 --- a/tests/unit/network.test.ts +++ b/tests/unit/network.test.ts @@ -10,7 +10,6 @@ import { const ethereumMainnetChainId = 1 const arbitrumOneChainId = 42161 -const arbitrumNovaChainId = 42170 const mockL1ChainId = 111111 const mockL2ChainId = 222222 @@ -32,6 +31,7 @@ describe('Networks', async () => { const customL2Network = { ...arbitrumOne, chainID: mockL2ChainId, + partnerChainID: ethereumMainnetChainId, isArbitrum: true, isCustom: true, } as const @@ -40,9 +40,12 @@ describe('Networks', async () => { expect(await getL2Network(mockL2ChainId)).to.be.ok - // assert chain was added as child + // assert network was added as child const l1Network = await getL1Network(customL2Network.partnerChainID) expect(l1Network.partnerChainIDs).to.include(mockL2ChainId) + // assert network has correct parent + const l2Network = await getL2Network(customL2Network.chainID) + expect(l2Network.partnerChainID).to.equal(ethereumMainnetChainId) }) it('adds a custom L1 and L2 network', async function () { @@ -66,9 +69,12 @@ describe('Networks', async () => { expect(await getL1Network(mockL1ChainId)).to.be.ok expect(await getL2Network(mockL2ChainId)).to.be.ok - // assert chain was added as child + // assert network was added as child const l1Network = await getL1Network(mockL1ChainId) expect(l1Network.partnerChainIDs).to.include(mockL2ChainId) + // assert network has correct parent + const l2Network = await getL2Network(customL2Network.chainID) + expect(l2Network.partnerChainID).to.equal(mockL1ChainId) }) it('adds a custom L3 network', async function () { @@ -84,9 +90,12 @@ describe('Networks', async () => { expect(await getL2Network(mockL3ChainId)).to.be.ok - // assert chain was added as child + // assert network was added as child const l2Network = await getL2Network(customL2Network.partnerChainID) expect(l2Network.partnerChainIDs).to.include(mockL3ChainId) + // assert network has correct parent + const l3Network = await getL2Network(mockL3ChainId) + expect(l3Network.partnerChainID).to.equal(arbitrumOneChainId) }) it('adds a custom L1, L2, and L3 network', async function () { @@ -110,9 +119,12 @@ describe('Networks', async () => { expect(await getL1Network(mockL1ChainId)).to.be.ok expect(await getL2Network(mockL2ChainId)).to.be.ok - // assert chain was added as child + // assert network was added as child const l1Network = await getL1Network(mockL1ChainId) expect(l1Network.partnerChainIDs).to.include(mockL2ChainId) + // assert network has correct parent + const l2Network = await getL2Network(mockL2ChainId) + expect(l2Network.partnerChainID).to.equal(mockL1ChainId) const customL3Network = { ...arbitrumOne, @@ -126,9 +138,12 @@ describe('Networks', async () => { expect(await getL2Network(mockL3ChainId)).to.be.ok - // assert chain was added as child - const l2Network = await getL2Network(mockL2ChainId) - expect(l2Network.partnerChainIDs).to.include(mockL3ChainId) + // assert network was added as child + const l2NetworkAgain = await getL2Network(mockL2ChainId) + expect(l2NetworkAgain.partnerChainIDs).to.include(mockL3ChainId) + // assert network has correct parent + const l3Network = await getL2Network(mockL3ChainId) + expect(l3Network.partnerChainID).to.equal(mockL2ChainId) }) it('fails to add a custom L1 and L2 network if they do not match', async function () { @@ -230,7 +245,7 @@ describe('Networks', async () => { const l3Network = await getL2Network(mockL3ChainId) expect(l3Network.chainID, fetchErrorMessage).to.be.eq(mockL3ChainId) - // assert chain was added as child + // assert network was added as child const l2Network = await getL2Network(customL3Network.partnerChainID) expect(l2Network.partnerChainIDs).to.include(mockL3ChainId) }) From d478039ccb5964be94b3e6fd8c618c770a1616be Mon Sep 17 00:00:00 2001 From: spsjvc Date: Fri, 2 Feb 2024 14:38:55 +0100 Subject: [PATCH 152/192] finish cleanup --- tests/unit/network.test.ts | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/tests/unit/network.test.ts b/tests/unit/network.test.ts index 45878fd938..8c428a4b3a 100644 --- a/tests/unit/network.test.ts +++ b/tests/unit/network.test.ts @@ -16,18 +16,14 @@ const mockL2ChainId = 222222 const mockL3ChainId = 99999999 describe('Networks', async () => { - const fetchErrorMessage = - 'Network fetched successfully but the chain ID is invalid.' - - const ethereumMainnet = await getL1Network(ethereumMainnetChainId) - const arbitrumOne = await getL2Network(arbitrumOneChainId) - beforeEach(async () => { resetNetworksToDefault() }) describe('adding networks', () => { it('adds a custom L2 network', async function () { + const arbitrumOne = await getL2Network(arbitrumOneChainId) + const customL2Network = { ...arbitrumOne, chainID: mockL2ChainId, @@ -49,6 +45,9 @@ describe('Networks', async () => { }) it('adds a custom L1 and L2 network', async function () { + const ethereumMainnet = await getL1Network(ethereumMainnetChainId) + const arbitrumOne = await getL2Network(arbitrumOneChainId) + const customL1Network = { ...ethereumMainnet, chainID: mockL1ChainId, @@ -78,6 +77,8 @@ describe('Networks', async () => { }) it('adds a custom L3 network', async function () { + const arbitrumOne = await getL2Network(arbitrumOneChainId) + const customL2Network = { ...arbitrumOne, chainID: mockL3ChainId, @@ -99,6 +100,9 @@ describe('Networks', async () => { }) it('adds a custom L1, L2, and L3 network', async function () { + const ethereumMainnet = await getL1Network(ethereumMainnetChainId) + const arbitrumOne = await getL2Network(arbitrumOneChainId) + const customL1Network = { ...ethereumMainnet, chainID: mockL1ChainId, @@ -147,6 +151,9 @@ describe('Networks', async () => { }) it('fails to add a custom L1 and L2 network if they do not match', async function () { + const ethereumMainnet = await getL1Network(ethereumMainnetChainId) + const arbitrumOne = await getL2Network(arbitrumOneChainId) + const wrongPartnerChainId = 1241244 const customL1Network = { @@ -176,6 +183,8 @@ describe('Networks', async () => { }) it('fails to add a custom L3 without previously registering L2', async function () { + const arbitrumOne = await getL2Network(arbitrumOneChainId) + try { addCustomNetwork({ customL2Network: { @@ -204,7 +213,7 @@ describe('Networks', async () => { it('successfully fetches an L2 network with `getL2Network`', async function () { const network = await getL2Network(arbitrumOneChainId) - expect(network.chainID, fetchErrorMessage).to.be.eq(arbitrumOneChainId) + expect(network.chainID).to.be.eq(arbitrumOneChainId) }) it('fails to fetch a registered L2 network with `getL1Network`', async function () { @@ -232,6 +241,8 @@ describe('Networks', async () => { }) it('successfully fetches an L3 chain with `getL2Network`', async function () { + const arbitrumOne = await getL2Network(arbitrumOneChainId) + const customL3Network = { ...arbitrumOne, chainID: mockL3ChainId, @@ -243,7 +254,9 @@ describe('Networks', async () => { addCustomNetwork({ customL2Network: customL3Network }) const l3Network = await getL2Network(mockL3ChainId) - expect(l3Network.chainID, fetchErrorMessage).to.be.eq(mockL3ChainId) + expect(l3Network.chainID).to.be.eq(mockL3ChainId) + // assert network has correct parent + expect(l3Network.partnerChainID).to.equal(arbitrumOneChainId) // assert network was added as child const l2Network = await getL2Network(customL3Network.partnerChainID) From 0d91ae4fd4604b9b4ebae0223e83a3cd4851c623 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Fri, 2 Feb 2024 14:42:29 +0100 Subject: [PATCH 153/192] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a036a7dffc..42958687f3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@arbitrum/sdk", - "version": "3.2.0-beta.0", + "version": "3.2.0-beta.1", "description": "Typescript library client-side interactions with Arbitrum", "author": "Offchain Labs, Inc.", "license": "Apache-2.0", From c13397476f8dc2171c2074b06525c16d20e59932 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Fri, 2 Feb 2024 15:43:08 +0100 Subject: [PATCH 154/192] revert changes --- src/lib/dataEntities/networks.ts | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index d8d47860ad..755235cf56 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -73,35 +73,23 @@ export interface L2Network extends Network { nativeToken?: string } -type BaseTokenBridge = { +export interface TokenBridge { l1GatewayRouter: string l2GatewayRouter: string l1ERC20Gateway: string l2ERC20Gateway: string l1CustomGateway: string l2CustomGateway: string + l1WethGateway: string + l2WethGateway: string + l2Weth: string + l1Weth: string l1ProxyAdmin: string l2ProxyAdmin: string l1MultiCall: string l2Multicall: string } -export type TokenBridge = BaseTokenBridge & - ( - | { - l1WethGateway: string - l2WethGateway: string - l2Weth: string - l1Weth: string - } - | { - l1WethGateway?: never - l2WethGateway?: never - l2Weth?: never - l1Weth?: never - } - ) - export interface EthBridge { bridge: string inbox: string @@ -425,7 +413,6 @@ const getChainsByType = ( const getL1Chains = () => getChainsByType(isL1Network) const getArbitrumChains = () => getChainsByType(isArbitrumNetwork) -const getParentChains = () => getChainsByType(isParentChain) /** * Returns the parent chain for the given chain. @@ -491,7 +478,7 @@ export const getNetwork = async ( let network: L1Network | L2Network | undefined = undefined if (layer === 1) { - network = getParentChains()[chainID] + network = getL1Chains()[chainID] } else { network = getArbitrumChains()[chainID] } From abdc024decda1cad14dd54b35631fef1bca9d53d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Fri, 2 Feb 2024 15:45:04 +0100 Subject: [PATCH 155/192] remove bangs --- tests/integration/sanity.test.ts | 20 ++++++++++---------- tests/integration/weth.test.ts | 10 +++++----- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/integration/sanity.test.ts b/tests/integration/sanity.test.ts index 9780aa0567..288983034c 100644 --- a/tests/integration/sanity.test.ts +++ b/tests/integration/sanity.test.ts @@ -119,30 +119,30 @@ describe('sanity checks (read-only)', async () => { const { l1Signer, l2Signer, l2Network } = await testSetup() const l1Gateway = await L1WethGateway__factory.connect( - l2Network.tokenBridge.l1WethGateway!, + l2Network.tokenBridge.l1WethGateway, l1Signer ) const l2Gateway = await L2WethGateway__factory.connect( - l2Network.tokenBridge.l2WethGateway!, + l2Network.tokenBridge.l2WethGateway, l2Signer ) const l1Weth = await l1Gateway.l1Weth() - expectIgnoreCase(l1Weth, l2Network.tokenBridge.l1Weth!) + expectIgnoreCase(l1Weth, l2Network.tokenBridge.l1Weth) const l2Weth = await l2Gateway.l2Weth() - expectIgnoreCase(l2Weth, l2Network.tokenBridge.l2Weth!) + expectIgnoreCase(l2Weth, l2Network.tokenBridge.l2Weth) const l1GatewayCounterParty = await l1Gateway.counterpartGateway() expectIgnoreCase( l1GatewayCounterParty, - l2Network.tokenBridge.l2WethGateway! + l2Network.tokenBridge.l2WethGateway ) const l2GatewayCounterParty = await l2Gateway.counterpartGateway() expectIgnoreCase( l2GatewayCounterParty, - l2Network.tokenBridge.l1WethGateway! + l2Network.tokenBridge.l1WethGateway ) const l1Router = await l1Gateway.router() @@ -156,22 +156,22 @@ describe('sanity checks (read-only)', async () => { const { l2Signer, l2Network } = await testSetup() const aeWeth = AeWETH__factory.connect( - l2Network.tokenBridge.l2Weth!, + l2Network.tokenBridge.l2Weth, l2Signer ) const l2GatewayOnAeWeth = await aeWeth.l2Gateway() - expectIgnoreCase(l2GatewayOnAeWeth, l2Network.tokenBridge.l2WethGateway!) + expectIgnoreCase(l2GatewayOnAeWeth, l2Network.tokenBridge.l2WethGateway) const l1AddressOnAeWeth = await aeWeth.l1Address() - expectIgnoreCase(l1AddressOnAeWeth, l2Network.tokenBridge.l1Weth!) + expectIgnoreCase(l1AddressOnAeWeth, l2Network.tokenBridge.l1Weth) }) it('l1 gateway router points to right weth gateways', async () => { const { adminErc20Bridger, l1Signer, l2Network } = await testSetup() const gateway = await adminErc20Bridger.getL1GatewayAddress( - l2Network.tokenBridge.l1Weth!, + l2Network.tokenBridge.l1Weth, l1Signer.provider! ) diff --git a/tests/integration/weth.test.ts b/tests/integration/weth.test.ts index 339822cac1..efcbd08e3a 100644 --- a/tests/integration/weth.test.ts +++ b/tests/integration/weth.test.ts @@ -43,7 +43,7 @@ if (!isL2NetworkWithCustomFeeToken()) { it('deposit WETH', async () => { const { l2Network, l1Signer, l2Signer, erc20Bridger } = await testSetup() - const l1WethAddress = l2Network.tokenBridge.l1Weth! + const l1WethAddress = l2Network.tokenBridge.l1Weth const wethToWrap = parseEther('0.00001') const wethToDeposit = parseEther('0.0000001') @@ -51,7 +51,7 @@ if (!isL2NetworkWithCustomFeeToken()) { await fundL1(l1Signer, parseEther('1')) const l2WETH = AeWETH__factory.connect( - l2Network.tokenBridge.l2Weth!, + l2Network.tokenBridge.l2Weth, l2Signer.provider! ) expect( @@ -83,7 +83,7 @@ if (!isL2NetworkWithCustomFeeToken()) { ) const l2Token = erc20Bridger.getL2TokenContract( l2Signer.provider!, - l2Network.tokenBridge.l2Weth! + l2Network.tokenBridge.l2Weth ) expect(l2Token.address, 'l2 weth').to.eq(l2Network.tokenBridge.l2Weth) @@ -110,7 +110,7 @@ if (!isL2NetworkWithCustomFeeToken()) { await fundL2(l2Signer) const l2Weth = AeWETH__factory.connect( - l2Network.tokenBridge.l2Weth!, + l2Network.tokenBridge.l2Weth, l2Signer ) const res = await l2Weth.deposit({ @@ -125,7 +125,7 @@ if (!isL2NetworkWithCustomFeeToken()) { gatewayType: GatewayType.WETH, l1Signer: l1Signer, l1Token: ERC20__factory.connect( - l2Network.tokenBridge.l1Weth!, + l2Network.tokenBridge.l1Weth, l1Signer.provider! ), l2Signer: l2Signer, From 6fe62da66bf6c958b89496cf82f45ff32464a945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Fri, 2 Feb 2024 15:46:55 +0100 Subject: [PATCH 156/192] bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fdec70234f..cc61381af0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@arbitrum/sdk", - "version": "3.2.0-beta.1-custom-fee-token.0", + "version": "3.2.0-beta.1-custom-fee-token.1", "description": "Typescript library client-side interactions with Arbitrum", "author": "Offchain Labs, Inc.", "license": "Apache-2.0", From 61794ae2c48c2e70ff34a7d47449865d6576d83c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Fri, 2 Feb 2024 18:22:22 +0100 Subject: [PATCH 157/192] switch to early return --- src/lib/assetBridger/erc20Bridger.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/lib/assetBridger/erc20Bridger.ts b/src/lib/assetBridger/erc20Bridger.ts index 916ada43a3..c49787f3aa 100644 --- a/src/lib/assetBridger/erc20Bridger.ts +++ b/src/lib/assetBridger/erc20Bridger.ts @@ -770,22 +770,22 @@ export class Erc20Bridger extends AssetBridger< // in the future we want to do proper estimation here /* eslint-disable @typescript-eslint/no-unused-vars */ estimateL1GasLimit: async (l1Provider: Provider) => { - if (this.isNativeTokenEth) { - const l1GatewayAddress = await this.getL1GatewayAddress( - params.erc20l1Address, - l1Provider - ) - - // The WETH gateway is the only deposit that requires callvalue in the L2 user-tx (i.e., the recently un-wrapped ETH) - // Here we check if this is a WETH deposit, and include the callvalue for the gas estimate query if so - const isWeth = await this.isWethGateway(l1GatewayAddress, l1Provider) - - // measured 157421 - add some padding - return isWeth ? BigNumber.from(190000) : BigNumber.from(160000) - } else { + if (!this.isNativeTokenEth) { // measured 172867 - add some padding return BigNumber.from(200000) } + + const l1GatewayAddress = await this.getL1GatewayAddress( + params.erc20l1Address, + l1Provider + ) + + // The WETH gateway is the only deposit that requires callvalue in the L2 user-tx (i.e., the recently un-wrapped ETH) + // Here we check if this is a WETH deposit, and include the callvalue for the gas estimate query if so + const isWeth = await this.isWethGateway(l1GatewayAddress, l1Provider) + + // measured 157421 - add some padding + return isWeth ? BigNumber.from(190000) : BigNumber.from(160000) }, } } From 4e9b55765cae88db278bf7459bacedfa2c889517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Sun, 4 Feb 2024 20:12:10 +0100 Subject: [PATCH 158/192] clean up --- src/lib/assetBridger/assetBridger.ts | 3 +++ src/lib/dataEntities/networks.ts | 20 ++++++++++---------- src/lib/inbox/inbox.ts | 7 ++++++- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/lib/assetBridger/assetBridger.ts b/src/lib/assetBridger/assetBridger.ts index db535eff45..ac0725da70 100644 --- a/src/lib/assetBridger/assetBridger.ts +++ b/src/lib/assetBridger/assetBridger.ts @@ -33,6 +33,9 @@ import { * Base for bridging assets from l1 to l2 and back */ export abstract class AssetBridger { + /** + * Parent chain for the given Arbitrum chain, can be an L1 or an L2 + */ public readonly l1Network: L1Network | L2Network public constructor(public readonly l2Network: L2Network) { diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index fb949583b5..e81c73394c 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -35,7 +35,7 @@ export interface Network { */ blockTime: number /** - * Chain ids of children chains. + * Chain ids of children chains, i.e. chains that settle to this chain. */ partnerChainIDs: number[] } @@ -54,7 +54,7 @@ export interface L2Network extends Network { tokenBridge: TokenBridge ethBridge: EthBridge /** - * Chain id of the parent chain. + * Chain id of the parent chain, i.e. the chain on which this chain settles to. */ partnerChainID: number isArbitrum: true @@ -182,6 +182,7 @@ export const networks: Networks = { name: 'Arbitrum One', explorerUrl: 'https://arbiscan.io', partnerChainID: 1, + partnerChainIDs: [], isArbitrum: true, tokenBridge: mainnetTokenBridge, ethBridge: mainnetETHBridge, @@ -197,7 +198,6 @@ export const networks: Networks = { */ depositTimeout: 1800000, blockTime: ARB_MINIMUM_BLOCK_TIME_IN_SECONDS, - partnerChainIDs: [], }, 421613: { chainID: 421613, @@ -215,6 +215,7 @@ export const networks: Networks = { isCustom: false, name: 'Arbitrum Rollup Goerli Testnet', partnerChainID: 5, + partnerChainIDs: [], tokenBridge: { l1CustomGateway: '0x9fDD1C4E4AA24EEc1d913FABea925594a20d43C7', l1ERC20Gateway: '0x715D99480b77A8d9D603638e593a539E21345FdF', @@ -239,7 +240,6 @@ export const networks: Networks = { */ depositTimeout: 3960000, blockTime: ARB_MINIMUM_BLOCK_TIME_IN_SECONDS, - partnerChainIDs: [], }, 42170: { chainID: 42170, @@ -256,6 +256,7 @@ export const networks: Networks = { isCustom: false, name: 'Arbitrum Nova', partnerChainID: 1, + partnerChainIDs: [], retryableLifetimeSeconds: SEVEN_DAYS_IN_SECONDS, tokenBridge: { l1CustomGateway: '0x23122da8C581AA7E0d07A36Ff1f16F799650232f', @@ -282,7 +283,6 @@ export const networks: Networks = { */ depositTimeout: 1800000, blockTime: ARB_MINIMUM_BLOCK_TIME_IN_SECONDS, - partnerChainIDs: [], }, 421614: { chainID: 421614, @@ -337,6 +337,7 @@ export const networks: Networks = { isCustom: false, name: 'Stylus Testnet', partnerChainID: 421614, + partnerChainIDs: [], retryableLifetimeSeconds: SEVEN_DAYS_IN_SECONDS, tokenBridge: { l1CustomGateway: '0xd624D491A5Bc32de52a2e1481846752213bF7415', @@ -358,7 +359,6 @@ export const networks: Networks = { nitroGenesisL1Block: 0, depositTimeout: 900000, blockTime: ARB_MINIMUM_BLOCK_TIME_IN_SECONDS, - partnerChainIDs: [], }, } @@ -510,7 +510,7 @@ export const getL2Network = ( /** * Returns the addresses of all contracts that make up the ETH bridge * @param rollupContractAddress Address of the Rollup contract - * @param l1SignerOrProvider An L1 signer or provider + * @param l1SignerOrProvider A parent chain signer or provider * @returns EthBridge object with all information about the ETH bridge */ export const getEthBridgeInformation = async ( @@ -573,10 +573,10 @@ const addNetwork = (network: L1Network | L2Network) => { } /** - * Registers a pair of custom chains (parent and child). These networks will be returned in `getL1Network` and `getL2Network`, respectively. + * Registers a pair of custom L1 and L2 chains, or a single custom Arbitrum chain (L2 or L3). * - * @param customL1Network the parent chain **(could be an L1 or L2 chain)** - * @param customL2Network the child chain **(must be an Arbitrum chain)** + * @param customL1Network the custom L1 chain (optional) + * @param customL2Network the custom L2 or L3 chain */ export const addCustomNetwork = ({ customL1Network, diff --git a/src/lib/inbox/inbox.ts b/src/lib/inbox/inbox.ts index 77af736d26..ceb98b0be4 100644 --- a/src/lib/inbox/inbox.ts +++ b/src/lib/inbox/inbox.ts @@ -61,8 +61,13 @@ type RequiredTransactionRequestType = RequiredPick< * Tools for interacting with the inbox and bridge contracts */ export class InboxTools { - // by L1 we really mean "parent chain", the misnomer will be fixed in the next major version + /** + * Parent chain provider + */ private readonly l1Provider: Provider + /** + * Parent chain for the given Arbitrum chain, can be an L1 or an L2 + */ private readonly l1Network: L1Network | L2Network constructor( From 49a8c5bf45ac1d7f1b7ca8781c6b339e2bba2e5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Mon, 5 Feb 2024 09:43:14 +0100 Subject: [PATCH 159/192] move property up --- src/lib/dataEntities/networks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index e81c73394c..ef1d33700e 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -651,6 +651,7 @@ export const addDefaultLocalNetwork = (): { isCustom: true, name: 'ArbLocal', partnerChainID: 1337, + partnerChainIDs: [], retryableLifetimeSeconds: 604800, nitroGenesisBlock: 0, nitroGenesisL1Block: 0, @@ -672,7 +673,6 @@ export const addDefaultLocalNetwork = (): { l2WethGateway: '0x4A2bA922052bA54e29c5417bC979Daaf7D5Fe4f4', }, blockTime: ARB_MINIMUM_BLOCK_TIME_IN_SECONDS, - partnerChainIDs: [], } addCustomNetwork({ From d1e2fc5301a7a2c25d9b59f52b767721cc2a30c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Mon, 5 Feb 2024 09:44:07 +0100 Subject: [PATCH 160/192] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 42958687f3..0c4e95fb44 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@arbitrum/sdk", - "version": "3.2.0-beta.1", + "version": "3.2.0-beta.2", "description": "Typescript library client-side interactions with Arbitrum", "author": "Offchain Labs, Inc.", "license": "Apache-2.0", From d2ea33af27a7524ca7b1425d713b0f1616ad8ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Mon, 5 Feb 2024 12:11:55 +0100 Subject: [PATCH 161/192] fix script --- scripts/deployBridge.ts | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/scripts/deployBridge.ts b/scripts/deployBridge.ts index 5a860b7b86..3e7570ddab 100644 --- a/scripts/deployBridge.ts +++ b/scripts/deployBridge.ts @@ -330,28 +330,21 @@ export const deployErc20AndInit = async ( ).wait() } - const ret = { + return { l1CustomGateway: l1.customGateway.address, l1ERC20Gateway: l1.standardGateway.address, l1GatewayRouter: l1.router.address, l1MultiCall: l1.multicall.address, l1ProxyAdmin: l1.proxyAdmin.address, + l1Weth: l1WethOverride || l1.weth?.address || constants.AddressZero, + l1WethGateway: l1.wethGateway?.address || constants.AddressZero, l2CustomGateway: l2.customGateway.address, l2ERC20Gateway: l2.standardGateway.address, l2GatewayRouter: l2.router.address, l2Multicall: l2.multicall.address, l2ProxyAdmin: l2.proxyAdmin.address, + l2Weth: l2.weth?.address || constants.AddressZero, + l2WethGateway: l2.wethGateway?.address || constants.AddressZero, } - - if (l1.weth && l2.weth) { - return { - ...ret, - l1Weth: l1WethOverride || l1.weth.address, - l1WethGateway: l1.wethGateway.address, - l2Weth: l2.weth.address, - l2WethGateway: l2.wethGateway.address, - } - } - return ret } From 4fbb2cac181fe4a4b6e52441b54b95200c3f74b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Mon, 5 Feb 2024 14:15:43 +0100 Subject: [PATCH 162/192] fix --- scripts/testSetup.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/testSetup.ts b/scripts/testSetup.ts index 1401eaa0ce..e5730f222f 100644 --- a/scripts/testSetup.ts +++ b/scripts/testSetup.ts @@ -29,7 +29,7 @@ import { getL2Network, addCustomNetwork, } from '../src/lib/dataEntities/networks' -import { Signer, providers } from 'ethers' +import { Signer, constants, providers } from 'ethers' import { AdminErc20Bridger } from '../src/lib/assetBridger/erc20Bridger' import { execSync } from 'child_process' import { Bridge__factory } from '../src/lib/abi/factories/Bridge__factory' @@ -369,7 +369,7 @@ export const setupNetworks = async ( // also register the weth gateway // we add it here rather than in deployBridge because // we have access to an adminerc20bridger - if (tokenBridge.l1Weth) { + if (tokenBridge.l1Weth !== constants.AddressZero) { const adminErc20Bridger = new AdminErc20Bridger(l2Network) await ( await ( From f2b43da2aeed16bfeb88b0a23a4d9e966344a314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Mon, 5 Feb 2024 14:25:54 +0100 Subject: [PATCH 163/192] clean up job names --- .github/workflows/build-test.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 9331ded505..aa2ed2c1b6 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -9,7 +9,7 @@ env: jobs: install: - name: 'Install' + name: Install on Node.js v${{ matrix.node-version }} runs-on: ubuntu-latest strategy: matrix: @@ -27,7 +27,7 @@ jobs: uses: OffchainLabs/actions/node-modules/install@main lint: - name: Lint + name: Lint on Node.js v${{ matrix.node-version }} runs-on: ubuntu-latest strategy: matrix: @@ -76,7 +76,7 @@ jobs: verbose: false audit: - name: Audit + name: Audit on Node.js v${{ matrix.node-version }} runs-on: ubuntu-latest strategy: matrix: @@ -97,7 +97,7 @@ jobs: - run: yarn audit:ci test-unit: - name: Test (Unit) + name: Test (Unit) on Node.js v${{ matrix.node-version }} runs-on: ubuntu-latest strategy: matrix: @@ -124,7 +124,7 @@ jobs: run: CI=true yarn test:unit test-integration: - name: Test (Int.) Node ${{ matrix.node-version }}/${{ matrix.orbit-test == '1' && 'Orbit' || 'No Orbit' }}/${{ matrix.custom-fee == '1' && 'Custom Fee' || 'No Custom Fee' }} + name: Test (Integration) on Node.js v${{ matrix.node-version }}${{ matrix.orbit-test == '1' && ' with L3' || '' }}${{ matrix.custom-fee == '1' && ' with custom gas token' || '' }} runs-on: ubuntu-latest strategy: fail-fast: false # runs all tests to completion even if one fails From eb31f6925e5da14f0824cdb38dc8451a9acade01 Mon Sep 17 00:00:00 2001 From: Henry <11198460+godzillaba@users.noreply.github.com> Date: Mon, 5 Feb 2024 09:58:50 -0500 Subject: [PATCH 164/192] use testnode bridge (#408) * use testnode bridge * fmt * chore: new path * chore: remove unused * change actions branch --------- Co-authored-by: gzeon --- .github/workflows/build-test.yml | 1 - scripts/deployBridge.ts | 350 -------------- scripts/genNetwork.ts | 86 +++- scripts/testSetup.ts | 445 ++---------------- .../customFeeTokenTestHelpers.ts | 9 +- 5 files changed, 119 insertions(+), 772 deletions(-) delete mode 100644 scripts/deployBridge.ts diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index aa2ed2c1b6..ee6e16b19c 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -162,7 +162,6 @@ jobs: uses: OffchainLabs/actions/run-nitro-test-node@main with: nitro-testnode-ref: master - no-token-bridge: true l3-node: ${{ matrix.orbit-test == '1' }} args: ${{ matrix.custom-fee == '1' && '--l3-fee-token' || '' }} diff --git a/scripts/deployBridge.ts b/scripts/deployBridge.ts deleted file mode 100644 index 3e7570ddab..0000000000 --- a/scripts/deployBridge.ts +++ /dev/null @@ -1,350 +0,0 @@ -import { Signer, ContractFactory, constants } from 'ethers' - -import { L1OrbitGatewayRouter__factory } from '../src/lib/abi/factories/L1OrbitGatewayRouter__factory' -import { L1OrbitERC20Gateway__factory } from '../src/lib/abi/factories/L1OrbitERC20Gateway__factory' -import { L1OrbitCustomGateway__factory } from '../src/lib/abi/factories/L1OrbitCustomGateway__factory' - -import { L1GatewayRouter__factory } from '../src/lib/abi/factories/L1GatewayRouter__factory' -import { L1ERC20Gateway__factory } from '../src/lib/abi/factories/L1ERC20Gateway__factory' -import { L1CustomGateway__factory } from '../src/lib/abi/factories/L1CustomGateway__factory' -import { L1WethGateway__factory } from '../src/lib/abi/factories/L1WethGateway__factory' -import { L2GatewayRouter__factory } from '../src/lib/abi/factories/L2GatewayRouter__factory' -import { L2ERC20Gateway__factory } from '../src/lib/abi/factories/L2ERC20Gateway__factory' -import { L2CustomGateway__factory } from '../src/lib/abi/factories/L2CustomGateway__factory' -import { L2WethGateway__factory } from '../src/lib/abi/factories/L2WethGateway__factory' -import { StandardArbERC20__factory } from '../src/lib/abi/factories/StandardArbERC20__factory' -import { UpgradeableBeacon__factory } from '../src/lib/abi/factories/UpgradeableBeacon__factory' -import { BeaconProxyFactory__factory } from '../src/lib/abi/factories/BeaconProxyFactory__factory' -import { TransparentUpgradeableProxy__factory } from '../src/lib/abi/factories/TransparentUpgradeableProxy__factory' -import { ProxyAdmin } from '../src/lib/abi/ProxyAdmin' -import { ProxyAdmin__factory } from '../src/lib/abi/factories/ProxyAdmin__factory' -import { AeWETH__factory } from '../src/lib/abi/factories/AeWETH__factory' -import { TestWETH9__factory } from '../src/lib/abi/factories/TestWETH9__factory' -import { Multicall2__factory } from '../src/lib/abi/factories/Multicall2__factory' -import { ArbMulticall2__factory } from '../src/lib/abi/factories/ArbMulticall2__factory' -import { TokenBridge } from '../src/lib/dataEntities/networks' - -const deployBehindProxy = async < - T extends ContractFactory & { contractName: string } ->( - deployer: Signer, - factory: T, - admin: ProxyAdmin, - dataToCallProxy = '0x' -): Promise> => { - const instance = await factory.connect(deployer).deploy() - await instance.deployed() - - const proxy = await new TransparentUpgradeableProxy__factory() - .connect(deployer) - .deploy(instance.address, admin.address, dataToCallProxy) - await proxy.deployed() - console.log(factory['contractName'], proxy.address) - - return instance.attach(proxy.address) as ReturnType -} - -const deployErc20L1CustomFee = async (deployer: Signer) => { - const proxyAdmin = await new ProxyAdmin__factory().connect(deployer).deploy() - await proxyAdmin.deployed() - console.log('proxyAdmin', proxyAdmin.address) - - const router = await deployBehindProxy( - deployer, - new L1OrbitGatewayRouter__factory(), - proxyAdmin - ) - await router.deployed() - - const standardGateway = await deployBehindProxy( - deployer, - new L1OrbitERC20Gateway__factory(), - proxyAdmin - ) - await standardGateway.deployed() - - const customGateway = await deployBehindProxy( - deployer, - new L1OrbitCustomGateway__factory(), - proxyAdmin - ) - await customGateway.deployed() - - const multicall = await new Multicall2__factory().connect(deployer).deploy() - await multicall.deployed() - console.log('multicall', multicall.address) - - return { - proxyAdmin, - router, - standardGateway, - customGateway, - multicall, - weth: undefined, - wethGateway: undefined, - } -} - -const deployErc20L1 = async (deployer: Signer) => { - const proxyAdmin = await new ProxyAdmin__factory().connect(deployer).deploy() - await proxyAdmin.deployed() - console.log('proxyAdmin', proxyAdmin.address) - - const router = await deployBehindProxy( - deployer, - new L1GatewayRouter__factory(), - proxyAdmin - ) - await router.deployed() - - const standardGateway = await deployBehindProxy( - deployer, - new L1ERC20Gateway__factory(), - proxyAdmin - ) - await standardGateway.deployed() - - const customGateway = await deployBehindProxy( - deployer, - new L1CustomGateway__factory(), - proxyAdmin - ) - await customGateway.deployed() - - const wethGateway = await deployBehindProxy( - deployer, - new L1WethGateway__factory(), - proxyAdmin - ) - await wethGateway.deployed() - - const weth = await new TestWETH9__factory() - .connect(deployer) - .deploy('WETH', 'WETH') - await weth.deployed() - console.log('weth', weth.address) - - const multicall = await new Multicall2__factory().connect(deployer).deploy() - await multicall.deployed() - console.log('multicall', multicall.address) - - return { - proxyAdmin, - router, - standardGateway, - customGateway, - wethGateway, - weth, - multicall, - } -} - -const deployL2Weth = async (deployer: Signer, proxyAdmin: ProxyAdmin) => { - const wethGateway = await deployBehindProxy( - deployer, - new L2WethGateway__factory(), - proxyAdmin - ) - await wethGateway.deployed() - - const weth = await deployBehindProxy( - deployer, - new AeWETH__factory(), - proxyAdmin - ) - await weth.deployed() - console.log('weth', weth.address) - - return { - wethGateway, - weth, - } -} - -const deployErc20L2NoWeth = async (deployer: Signer) => { - const proxyAdmin = await new ProxyAdmin__factory().connect(deployer).deploy() - await proxyAdmin.deployed() - console.log('proxyAdmin', proxyAdmin.address) - - const router = await deployBehindProxy( - deployer, - new L2GatewayRouter__factory(), - proxyAdmin - ) - await router.deployed() - - const standardGateway = await deployBehindProxy( - deployer, - new L2ERC20Gateway__factory(), - proxyAdmin - ) - await standardGateway.deployed() - - const customGateway = await deployBehindProxy( - deployer, - new L2CustomGateway__factory(), - proxyAdmin - ) - await customGateway.deployed() - - const standardArbERC20 = await new StandardArbERC20__factory() - .connect(deployer) - .deploy() - await standardArbERC20.deployed() - - const beacon = await new UpgradeableBeacon__factory() - .connect(deployer) - .deploy(standardArbERC20.address) - await beacon.deployed() - - const beaconProxyFactory = await new BeaconProxyFactory__factory() - .connect(deployer) - .deploy() - await beaconProxyFactory.deployed() - - const multicall = await new ArbMulticall2__factory() - .connect(deployer) - .deploy() - await multicall.deployed() - console.log('multicall', multicall.address) - - return { - proxyAdmin, - router, - standardGateway, - customGateway, - beacon, - beaconProxyFactory, - multicall, - } -} - -const deployErc20L2CustomFee = async (deployer: Signer) => { - return { - ...(await deployErc20L2NoWeth(deployer)), - weth: undefined, - wethGateway: undefined, - } -} - -const deployErc20L2 = async (deployer: Signer) => { - const noWeth = await deployErc20L2NoWeth(deployer) - return { - ...noWeth, - ...(await deployL2Weth(deployer, noWeth.proxyAdmin)), - } -} - -export const deployErc20AndInit = async ( - l1Signer: Signer, - l2Signer: Signer, - inboxAddress: string, - usingCustomFee: boolean, - l1WethOverride?: string -): Promise => { - console.log('deploying l1') - const l1 = usingCustomFee - ? await deployErc20L1CustomFee(l1Signer) - : await deployErc20L1(l1Signer) - - console.log('deploying l2') - const l2 = usingCustomFee - ? await deployErc20L2CustomFee(l2Signer) - : await deployErc20L2(l2Signer) - - console.log('initialising L2') - await ( - await l2.router.initialize(l1.router.address, l2.standardGateway.address) - ).wait() - await (await l2.beaconProxyFactory.initialize(l2.beacon.address)).wait() - await ( - await l2.standardGateway.initialize( - l1.standardGateway.address, - l2.router.address, - l2.beaconProxyFactory.address - ) - ).wait() - await ( - await l2.customGateway.initialize( - l1.customGateway.address, - l2.router.address - ) - ).wait() - if (l1.weth && l2.weth) { - await ( - await l2.weth.initialize( - 'WETH', - 'WETH', - 18, - l2.wethGateway.address, - l1WethOverride || l1.weth.address - ) - ).wait() - await ( - await l2.wethGateway.initialize( - l1.wethGateway.address, - l2.router.address, - l1WethOverride || l1.weth.address, - l2.weth.address - ) - ).wait() - } - - console.log('initialising L1') - await ( - await l1.router.initialize( - await l1Signer.getAddress(), - l1.standardGateway.address, - constants.AddressZero, - l2.router.address, - inboxAddress - ) - ).wait() - - await ( - await l1.standardGateway.initialize( - l2.standardGateway.address, - l1.router.address, - inboxAddress, - await l2.beaconProxyFactory.cloneableProxyHash(), - l2.beaconProxyFactory.address - ) - ).wait() - await ( - await l1.customGateway.initialize( - l2.customGateway.address, - l1.router.address, - inboxAddress, - await l1Signer.getAddress() - ) - ).wait() - if (l1.weth && l2.weth) { - await ( - await l1.wethGateway.initialize( - l2.wethGateway.address, - l1.router.address, - inboxAddress, - l1WethOverride || l1.weth.address, - l2.weth.address - ) - ).wait() - } - - return { - l1CustomGateway: l1.customGateway.address, - l1ERC20Gateway: l1.standardGateway.address, - l1GatewayRouter: l1.router.address, - l1MultiCall: l1.multicall.address, - l1ProxyAdmin: l1.proxyAdmin.address, - l1Weth: l1WethOverride || l1.weth?.address || constants.AddressZero, - l1WethGateway: l1.wethGateway?.address || constants.AddressZero, - - l2CustomGateway: l2.customGateway.address, - l2ERC20Gateway: l2.standardGateway.address, - l2GatewayRouter: l2.router.address, - l2Multicall: l2.multicall.address, - l2ProxyAdmin: l2.proxyAdmin.address, - l2Weth: l2.weth?.address || constants.AddressZero, - l2WethGateway: l2.wethGateway?.address || constants.AddressZero, - } -} diff --git a/scripts/genNetwork.ts b/scripts/genNetwork.ts index 7551689eee..c704ee622c 100644 --- a/scripts/genNetwork.ts +++ b/scripts/genNetwork.ts @@ -1,17 +1,85 @@ -import { testSetup } from './testSetup' +import * as dotenv from 'dotenv' +dotenv.config() +import { execSync } from 'child_process' import * as fs from 'fs' +import { L2Network } from '../src' +import { ARB_MINIMUM_BLOCK_TIME_IN_SECONDS } from '../src/lib/dataEntities/constants' +import { IERC20Bridge__factory } from '../src/lib/abi/factories/IERC20Bridge__factory' +import { ethers } from 'ethers' + +const isTestingOrbitChains = process.env.ORBIT_TEST === '1' + +function getLocalNetworksFromContainer(which: 'l1l2' | 'l2l3'): any { + const dockerNames = [ + 'nitro_sequencer_1', + 'nitro-sequencer-1', + 'nitro-testnode-sequencer-1', + 'nitro-testnode_sequencer_1', + ] + for (const dockerName of dockerNames) { + try { + return JSON.parse( + execSync( + `docker exec ${dockerName} cat /tokenbridge-data/${which}_network.json` + ).toString() + ) + } catch { + // empty on purpose + } + } + throw new Error('nitro-testnode sequencer not found') +} + +/** + * the container's files are written by the token bridge deployment step of the test node, which runs a script in token-bridge-contracts. + * once the script in token-bridge-contracts repo uses an sdk version with the same types and is updated to populate those fields, + * we can remove this patchwork + */ +async function patchNetworks( + l2Network: L2Network, + l3Network: L2Network | undefined, + l2Provider: ethers.providers.Provider | undefined +) { + // we need to add partnerChainIDs to the L2 network + l2Network.partnerChainIDs = l3Network ? [l3Network.chainID] : [] + l2Network.blockTime = ARB_MINIMUM_BLOCK_TIME_IN_SECONDS + + // native token for l3 + if (l3Network && l2Provider) { + l3Network.partnerChainIDs = [] + l3Network.blockTime = ARB_MINIMUM_BLOCK_TIME_IN_SECONDS + try { + l3Network.nativeToken = await IERC20Bridge__factory.connect( + l3Network.ethBridge.bridge, + l2Provider + ).nativeToken() + } catch (e) { + // l3 network doesn't have a native token + } + } +} async function main() { fs.rmSync('localNetwork.json', { force: true }) - const setup = await testSetup() - fs.writeFileSync( - 'localNetwork.json', - JSON.stringify( - { l1Network: setup.l1Network, l2Network: setup.l2Network }, - null, - 2 + + let output = getLocalNetworksFromContainer('l1l2') + + if (isTestingOrbitChains) { + const { l2Network: l3Network } = getLocalNetworksFromContainer('l2l3') + await patchNetworks( + output.l2Network, + l3Network, + new ethers.providers.JsonRpcProvider(process.env['ARB_URL']) ) - ) + output = { + l1Network: output.l2Network, + l2Network: l3Network, + } + } else { + await patchNetworks(output.l2Network, undefined, undefined) + } + + fs.writeFileSync('localNetwork.json', JSON.stringify(output, null, 2)) console.log('localnetwork.json updated') } diff --git a/scripts/testSetup.ts b/scripts/testSetup.ts index e5730f222f..b16cbc8b6c 100644 --- a/scripts/testSetup.ts +++ b/scripts/testSetup.ts @@ -29,17 +29,11 @@ import { getL2Network, addCustomNetwork, } from '../src/lib/dataEntities/networks' -import { Signer, constants, providers } from 'ethers' +import { Signer } from 'ethers' import { AdminErc20Bridger } from '../src/lib/assetBridger/erc20Bridger' -import { execSync } from 'child_process' -import { Bridge__factory } from '../src/lib/abi/factories/Bridge__factory' -import { RollupAdminLogic__factory } from '../src/lib/abi/factories/RollupAdminLogic__factory' -import { deployErc20AndInit } from './deployBridge' import * as path from 'path' import * as fs from 'fs' import { ArbSdkError } from '../src/lib/dataEntities/errors' -import { ARB_MINIMUM_BLOCK_TIME_IN_SECONDS } from '../src/lib/dataEntities/constants' -import { IERC20Bridge__factory } from '../src/lib/abi/factories/IERC20Bridge__factory' import { approveL1CustomFeeToken, fundL1CustomFeeToken, @@ -70,325 +64,6 @@ export const config = isTestingOrbitChains ethKey: process.env['ETH_KEY'] as string, } -type DeploymentData = { - bridge: string - inbox: string - ['sequencer-inbox']: string - rollup: string -} - -function getDeploymentData(): string { - const dockerNames = [ - 'nitro_sequencer_1', - 'nitro-sequencer-1', - 'nitro-testnode-sequencer-1', - 'nitro-testnode_sequencer_1', - ] - for (const dockerName of dockerNames) { - try { - return execSync( - 'docker exec ' + dockerName + ' cat /config/deployment.json' - ).toString() - } catch { - // empty on purpose - } - } - throw new Error('nitro-testnode sequencer not found') -} - -function getL3DeploymentData() { - const dockerNames = ['nitro-testnode-l3node-1'] - for (const dockerName of dockerNames) { - try { - return execSync( - 'docker exec ' + dockerName + ' cat /config/l3deployment.json' - ).toString() - } catch { - // empty on purpose - } - } - throw new Error('nitro-testnode-l3node sequencer not found') -} - -export const getCustomNetworks = async ( - l1Url: string, - l2Url: string -): Promise<{ - customL1Network: L1Network - customL2Network: L2Network -}> => { - const l1Provider = new JsonRpcProvider(l1Url) - const l2Provider = new JsonRpcProvider(l2Url) - const deploymentData = getDeploymentData() - const parsedDeploymentData = JSON.parse(deploymentData) as DeploymentData - - const rollup = RollupAdminLogic__factory.connect( - parsedDeploymentData.rollup, - l1Provider - ) - const confirmPeriodBlocks = await rollup.confirmPeriodBlocks() - - const bridge = Bridge__factory.connect( - parsedDeploymentData.bridge, - l1Provider - ) - const outboxAddr = await bridge.allowedOutboxList(0) - - const l1NetworkInfo = await l1Provider.getNetwork() - const l2NetworkInfo = await l2Provider.getNetwork() - - const l1Network: L1Network = { - blockTime: 10, - chainID: l1NetworkInfo.chainId, - explorerUrl: '', - isCustom: true, - name: 'EthLocal', - partnerChainIDs: [l2NetworkInfo.chainId], - isArbitrum: false, - } - - const l2Network: L2Network = { - chainID: l2NetworkInfo.chainId, - confirmPeriodBlocks: confirmPeriodBlocks.toNumber(), - ethBridge: { - bridge: parsedDeploymentData.bridge, - inbox: parsedDeploymentData.inbox, - outbox: outboxAddr, - rollup: parsedDeploymentData.rollup, - sequencerInbox: parsedDeploymentData['sequencer-inbox'], - }, - explorerUrl: '', - isArbitrum: true, - isCustom: true, - name: 'ArbLocal', - partnerChainID: l1NetworkInfo.chainId, - retryableLifetimeSeconds: 7 * 24 * 60 * 60, - nitroGenesisBlock: 0, - nitroGenesisL1Block: 0, - depositTimeout: 900000, - tokenBridge: { - l1CustomGateway: '', - l1ERC20Gateway: '', - l1GatewayRouter: '', - l1MultiCall: '', - l1ProxyAdmin: '', - l1Weth: '', - l1WethGateway: '', - l2CustomGateway: '', - l2ERC20Gateway: '', - l2GatewayRouter: '', - l2Multicall: '', - l2ProxyAdmin: '', - l2Weth: '', - l2WethGateway: '', - }, - blockTime: ARB_MINIMUM_BLOCK_TIME_IN_SECONDS, - partnerChainIDs: [], - } - - return { - customL1Network: l1Network, - customL2Network: l2Network, - } -} - -/** - * Gets the parent network for an Orbit chain - */ -const getL1NetworkForOrbit = async (): Promise<{ - l2Network: L2Network - l2Provider: providers.Provider -}> => { - const l1Provider = new JsonRpcProvider(process.env['ETH_URL']) - const l2Provider = new JsonRpcProvider(process.env['ARB_URL']) - - const deploymentData = getDeploymentData() - const parsedDeploymentData = JSON.parse(deploymentData) as DeploymentData - - const l2Network = await getCustomOrbitNetwork( - parsedDeploymentData, - l1Provider, - l2Provider - ) - - return { l2Network, l2Provider } -} - -/** - * Gets the L3 Orbit network and its parent network - */ -const getOrbitNetwork = async (): Promise<{ - customL1Network: L2Network - customL2Network: L2Network -}> => { - const { l2Network, l2Provider } = await getL1NetworkForOrbit() - const l3Provider = new JsonRpcProvider(process.env['ORBIT_URL']) - - const l3DeploymentData = getL3DeploymentData() - const parsedL3DeploymentData = JSON.parse(l3DeploymentData) as DeploymentData - const l3Network = await getCustomOrbitNetwork( - parsedL3DeploymentData, - l2Provider, - l3Provider - ) - - return { - customL1Network: l2Network, - customL2Network: l3Network, - } -} - -/** - * Builds a child network configuration object from deployment data - * - * @note `l1Provider` and `l2Provider` can be a `l2Provider` and `l3Provider` in a parent-child - * relationship. They will be renamed in the next major version. - */ -async function getCustomOrbitNetwork( - deploymentData: DeploymentData, - l1Provider: providers.Provider, - l2Provider: providers.Provider -) { - const rollup = RollupAdminLogic__factory.connect( - deploymentData.rollup, - l1Provider - ) - const confirmPeriodBlocks = await rollup.confirmPeriodBlocks() - - const bridge = Bridge__factory.connect(deploymentData.bridge, l1Provider) - const outboxAddr = await bridge.allowedOutboxList(0) - - const l1NetworkInfo = await l1Provider.getNetwork() - const l2NetworkInfo = await l2Provider.getNetwork() - - const l2Network: L2Network = { - chainID: l2NetworkInfo.chainId, - confirmPeriodBlocks: confirmPeriodBlocks.toNumber(), - ethBridge: { - bridge: deploymentData.bridge, - inbox: deploymentData.inbox, - outbox: outboxAddr, - rollup: deploymentData.rollup, - sequencerInbox: deploymentData['sequencer-inbox'], - }, - explorerUrl: '', - isArbitrum: true, - isCustom: true, - name: 'ArbLocal', - partnerChainID: l1NetworkInfo.chainId, - retryableLifetimeSeconds: 7 * 24 * 60 * 60, - nitroGenesisBlock: 0, - nitroGenesisL1Block: 0, - depositTimeout: 900000, - tokenBridge: { - l1CustomGateway: '', - l1ERC20Gateway: '', - l1GatewayRouter: '', - l1MultiCall: '', - l1ProxyAdmin: '', - l1Weth: '', - l1WethGateway: '', - l2CustomGateway: '', - l2ERC20Gateway: '', - l2GatewayRouter: '', - l2Multicall: '', - l2ProxyAdmin: '', - l2Weth: '', - l2WethGateway: '', - }, - blockTime: ARB_MINIMUM_BLOCK_TIME_IN_SECONDS, - partnerChainIDs: [], - } - - return l2Network -} - -export const getNetworkFeeToken = async ( - l1Provider: providers.Provider, - l2Network: L2Network -): Promise => { - const bridge = IERC20Bridge__factory.connect( - l2Network.ethBridge.bridge, - l1Provider - ) - try { - return await bridge.nativeToken() - } catch { - return undefined - } -} - -/** - * Builds network configuration and deploys the token bridge contracts - * - * @note `l1Deployer`/`l2Deployer` and `l1Url`/`l2url` can refer to an `L2` or `L3` in a - * parent-child relationship. They will be renamed in the next major version. - */ -export const setupNetworks = async ( - l1Deployer: Signer, - l2Deployer: Signer, - l1Url: string, - l2Url: string, - shouldSetupOrbit: boolean, - l1WethOverride?: string -) => { - const customNetworks = shouldSetupOrbit - ? await getOrbitNetwork() - : await getCustomNetworks(l1Url, l2Url) - - const nativeToken = await getNetworkFeeToken( - l1Deployer.provider!, - customNetworks.customL2Network - ) - - const tokenBridge = await deployErc20AndInit( - l1Deployer, - l2Deployer, - customNetworks.customL2Network.ethBridge.inbox, - nativeToken !== undefined, - l1WethOverride - ) - - const l2Network: L2Network = { - ...customNetworks.customL2Network, - tokenBridge, - nativeToken, - } - - // in case of L3, we only need to add the L3, as L1 and L2 were registered in a previous call to setupNetworks - // register the network with the newly deployed token bridge contracts - if (shouldSetupOrbit) { - addCustomNetwork({ customL2Network: l2Network }) - } else { - addCustomNetwork({ - customL1Network: customNetworks.customL1Network as L1Network, - customL2Network: l2Network, - }) - } - - // also register the weth gateway - // we add it here rather than in deployBridge because - // we have access to an adminerc20bridger - if (tokenBridge.l1Weth !== constants.AddressZero) { - const adminErc20Bridger = new AdminErc20Bridger(l2Network) - await ( - await ( - await adminErc20Bridger.setGateways(l1Deployer, l2Deployer.provider!, [ - { - gatewayAddr: tokenBridge.l1WethGateway, - tokenAddr: tokenBridge.l1Weth, - }, - ]) - ).wait() - ).waitForL2(l2Deployer) - } - - return { - l1Network: customNetworks.customL1Network, - l2Network, - } -} - export const getSigner = (provider: JsonRpcProvider, key?: string) => { if (!key && !provider) throw new ArbSdkError('Provide at least one of key or provider.') @@ -433,74 +108,39 @@ export const testSetup = async (): Promise<{ // check if theres an existing network available const localNetworkFile = getLocalNetworksFromFile() - if (localNetworkFile) { - const { l1Network, l2Network } = localNetworkFile - - if (isTestingOrbitChains) { - const _l1Network = l1Network as L2Network - const ethLocal: L1Network = { - blockTime: 10, - chainID: _l1Network.partnerChainID, - explorerUrl: '', - isCustom: true, - name: 'EthLocal', - partnerChainIDs: [_l1Network.chainID], - isArbitrum: false, - } - - addCustomNetwork({ - customL1Network: ethLocal, - customL2Network: _l1Network, - }) - - addCustomNetwork({ - customL2Network: l2Network, - }) - setL1Network = l1Network - setL2Network = l2Network - } else { - addCustomNetwork({ - customL1Network: l1Network as L1Network, - customL2Network: l2Network, - }) - - setL1Network = l1Network - setL2Network = l2Network + const { l1Network, l2Network } = localNetworkFile + + if (isTestingOrbitChains) { + const _l1Network = l1Network as L2Network + const ethLocal: L1Network = { + blockTime: 10, + chainID: _l1Network.partnerChainID, + explorerUrl: '', + isCustom: true, + name: 'EthLocal', + partnerChainIDs: [_l1Network.chainID], + isArbitrum: false, } - } else { - let l1NetworkOverride: L2Network | undefined - if (isTestingOrbitChains) { - //deploy l1/l2 bridge - const l1Url = process.env['ETH_URL']! - const l2Url = process.env['ARB_URL']! - const ethProvider = new JsonRpcProvider(l1Url) - const arbProvider = new JsonRpcProvider(l2Url) - const l1Deployer = getSigner(ethProvider, process.env['ETH_KEY']) - const l2Deployer = getSigner(arbProvider, process.env['ARB_KEY']) - const { l2Network: arbNetwork } = await setupNetworks( - l1Deployer, - l2Deployer, - l1Url, - l2Url, - false - ) + addCustomNetwork({ + customL1Network: ethLocal, + customL2Network: _l1Network, + }) - l1NetworkOverride = arbNetwork - } + addCustomNetwork({ + customL2Network: l2Network, + }) - // deploy a new network - const { l1Network, l2Network } = await setupNetworks( - l1Deployer, - l2Deployer, - config.ethUrl, - config.arbUrl, - isTestingOrbitChains, - l1NetworkOverride?.tokenBridge.l2Weth - ) + setL1Network = l1Network + setL2Network = l2Network + } else { + addCustomNetwork({ + customL1Network: l1Network as L1Network, + customL2Network: l2Network, + }) - setL1Network = l1NetworkOverride || l1Network + setL1Network = l1Network setL2Network = l2Network } } @@ -532,25 +172,14 @@ export const testSetup = async (): Promise<{ } } -export function getLocalNetworksFromFile(): - | { - l1Network: L1Network | L2Network - l2Network: L2Network - } - | undefined { - try { - const pathToLocalNetworkFile = path.join( - __dirname, - '..', - 'localNetwork.json' - ) - if (fs.existsSync(pathToLocalNetworkFile)) { - const localNetworksFile = fs.readFileSync(pathToLocalNetworkFile, 'utf8') - return JSON.parse(localNetworksFile) - } - return undefined - } catch (err) { - console.log(err) - return undefined +export function getLocalNetworksFromFile(): { + l1Network: L1Network | L2Network + l2Network: L2Network +} { + const pathToLocalNetworkFile = path.join(__dirname, '..', 'localNetwork.json') + if (!fs.existsSync(pathToLocalNetworkFile)) { + throw new ArbSdkError('localNetwork.json not found, must gen:network first') } + const localNetworksFile = fs.readFileSync(pathToLocalNetworkFile, 'utf8') + return JSON.parse(localNetworksFile) } diff --git a/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts b/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts index 0fa270093e..0c3a41e53c 100644 --- a/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts +++ b/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts @@ -1,5 +1,5 @@ import { StaticJsonRpcProvider } from '@ethersproject/providers' -import { Signer, Wallet, utils } from 'ethers' +import { Signer, Wallet, ethers, utils } from 'ethers' import { testSetup as _testSetup, @@ -15,7 +15,8 @@ const arbProvider = () => new StaticJsonRpcProvider(config.arbUrl) const localNetworks = () => getLocalNetworksFromFile() export function isL2NetworkWithCustomFeeToken(): boolean { - return typeof localNetworks()?.l2Network.nativeToken !== 'undefined' + const nt = localNetworks().l2Network.nativeToken + return typeof nt !== 'undefined' && nt !== ethers.constants.AddressZero } export async function testSetup() { @@ -29,7 +30,7 @@ export async function testSetup() { } export async function fundL1CustomFeeToken(l1SignerOrAddress: Signer | string) { - const nativeToken = localNetworks()?.l2Network.nativeToken + const nativeToken = localNetworks().l2Network.nativeToken const address = typeof l1SignerOrAddress === 'string' ? l1SignerOrAddress @@ -60,7 +61,7 @@ export async function approveL1CustomFeeToken(l1Signer: Signer) { } export async function getNativeTokenAllowance(owner: string, spender: string) { - const nativeToken = localNetworks()?.l2Network.nativeToken + const nativeToken = localNetworks().l2Network.nativeToken const nativeTokenContract = ERC20__factory.connect( nativeToken!, ethProvider() From 27127b4ae36121e6d4854ea8468303530ec0f146 Mon Sep 17 00:00:00 2001 From: spsjvc Date: Mon, 5 Feb 2024 16:36:18 +0100 Subject: [PATCH 165/192] move nativeToken to parent class --- src/lib/assetBridger/assetBridger.ts | 8 ++++++++ src/lib/assetBridger/erc20Bridger.ts | 8 -------- src/lib/assetBridger/ethBridger.ts | 8 -------- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/lib/assetBridger/assetBridger.ts b/src/lib/assetBridger/assetBridger.ts index ac0725da70..f57b58ceb6 100644 --- a/src/lib/assetBridger/assetBridger.ts +++ b/src/lib/assetBridger/assetBridger.ts @@ -38,8 +38,16 @@ export abstract class AssetBridger { */ public readonly l1Network: L1Network | L2Network + /** + * In case of a chain that uses ETH as its native/gas token, this is either `undefined` or the zero address. + * + * In case of a chain that uses an ERC-20 token from the parent chain as its native/gas token, this is the address of said token on the parent chain. + */ + public readonly nativeToken?: string + public constructor(public readonly l2Network: L2Network) { this.l1Network = getParentForNetwork(l2Network) + this.nativeToken = l2Network.nativeToken } /** diff --git a/src/lib/assetBridger/erc20Bridger.ts b/src/lib/assetBridger/erc20Bridger.ts index c49787f3aa..192d9d612e 100644 --- a/src/lib/assetBridger/erc20Bridger.ts +++ b/src/lib/assetBridger/erc20Bridger.ts @@ -175,19 +175,11 @@ export class Erc20Bridger extends AssetBridger< public static MAX_APPROVAL: BigNumber = MaxUint256 public static MIN_CUSTOM_DEPOSIT_GAS_LIMIT = BigNumber.from(275000) - /** - * In case of a chain that uses ETH as its native/fee token, this is either undefined or the zero address. - * In case of a chain that uses an ERC-20 token from the parent chain as its native/fee token, this is the address of said token on the parent chain. - */ - public readonly nativeToken?: string - /** * Bridger for moving ERC20 tokens back and forth between L1 to L2 */ public constructor(l2Network: L2Network) { super(l2Network) - - this.nativeToken = l2Network.nativeToken } /** diff --git a/src/lib/assetBridger/ethBridger.ts b/src/lib/assetBridger/ethBridger.ts index a0540ef580..8c48be5307 100644 --- a/src/lib/assetBridger/ethBridger.ts +++ b/src/lib/assetBridger/ethBridger.ts @@ -164,16 +164,8 @@ export class EthBridger extends AssetBridger< EthDepositParams | EthDepositToParams | L1ToL2TxReqAndSigner, EthWithdrawParams | L2ToL1TxReqAndSigner > { - /** - * In case of a chain that uses ETH as its native/fee token, this is either undefined or the zero address. - * In case of a chain that uses an ERC-20 token from the parent chain as its native/fee token, this is the address of said token on the parent chain. - */ - public readonly nativeToken?: string - public constructor(public readonly l2Network: L2Network) { super(l2Network) - - this.nativeToken = l2Network.nativeToken } /** From fdf1526ab531c86934c2c2d73207c8659b86d856 Mon Sep 17 00:00:00 2001 From: spsjvc Date: Mon, 5 Feb 2024 16:39:31 +0100 Subject: [PATCH 166/192] move nativeTokenIsEth to parent class --- src/lib/assetBridger/assetBridger.ts | 10 ++++++++++ src/lib/assetBridger/erc20Bridger.ts | 18 +++++------------- src/lib/assetBridger/ethBridger.ts | 16 ++++------------ 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/lib/assetBridger/assetBridger.ts b/src/lib/assetBridger/assetBridger.ts index f57b58ceb6..579237c22e 100644 --- a/src/lib/assetBridger/assetBridger.ts +++ b/src/lib/assetBridger/assetBridger.ts @@ -16,6 +16,8 @@ /* eslint-env node */ 'use strict' +import { constants } from 'ethers' + import { L1ContractTransaction } from '../message/L1Transaction' import { L2ContractTransaction } from '../message/L2Transaction' @@ -66,6 +68,14 @@ export abstract class AssetBridger { await SignerProviderUtils.checkNetworkMatches(sop, this.l2Network.chainID) } + /** + * Whether the chain uses ETH as its native/gas token + * @returns {boolean} + */ + protected get nativeTokenIsEth() { + return !this.nativeToken || this.nativeToken === constants.AddressZero + } + /** * Transfer assets from L1 to L2 * @param params diff --git a/src/lib/assetBridger/erc20Bridger.ts b/src/lib/assetBridger/erc20Bridger.ts index 192d9d612e..d8b15f0ce1 100644 --- a/src/lib/assetBridger/erc20Bridger.ts +++ b/src/lib/assetBridger/erc20Bridger.ts @@ -182,14 +182,6 @@ export class Erc20Bridger extends AssetBridger< super(l2Network) } - /** - * Whether the chain uses ETH as its native/fee token. - * @returns - */ - private get isNativeTokenEth() { - return !this.nativeToken || this.nativeToken === constants.AddressZero - } - /** * Instantiates a new Erc20Bridger from an L2 Provider * @param l2Provider @@ -242,7 +234,7 @@ export class Erc20Bridger extends AssetBridger< public async getApproveFeeTokenRequest( params: ProviderTokenApproveParams ): Promise>> { - if (this.isNativeTokenEth) { + if (this.nativeTokenIsEth) { throw new Error('chain uses ETH as its native/fee token') } @@ -258,7 +250,7 @@ export class Erc20Bridger extends AssetBridger< public async approveFeeToken( params: ApproveParamsOrTxRequest ): Promise { - if (this.isNativeTokenEth) { + if (this.nativeTokenIsEth) { throw new Error('chain uses ETH as its native/fee token') } @@ -563,7 +555,7 @@ export class Erc20Bridger extends AssetBridger< ) { // the call value should be zero when paying with a custom fee token, // as the fee amount is packed inside the last parameter (`data`) of the call to `outboundTransfer` - if (!this.isNativeTokenEth) { + if (!this.nativeTokenIsEth) { return constants.Zero } @@ -580,7 +572,7 @@ export class Erc20Bridger extends AssetBridger< private getDepositRequestOutboundTransferDataParam( depositParams: OmitTyped ) { - if (!this.isNativeTokenEth) { + if (!this.nativeTokenIsEth) { return defaultAbiCoder.encode( ['uint256', 'bytes', 'uint256'], [ @@ -762,7 +754,7 @@ export class Erc20Bridger extends AssetBridger< // in the future we want to do proper estimation here /* eslint-disable @typescript-eslint/no-unused-vars */ estimateL1GasLimit: async (l1Provider: Provider) => { - if (!this.isNativeTokenEth) { + if (!this.nativeTokenIsEth) { // measured 172867 - add some padding return BigNumber.from(200000) } diff --git a/src/lib/assetBridger/ethBridger.ts b/src/lib/assetBridger/ethBridger.ts index 8c48be5307..0448407f4d 100644 --- a/src/lib/assetBridger/ethBridger.ts +++ b/src/lib/assetBridger/ethBridger.ts @@ -168,14 +168,6 @@ export class EthBridger extends AssetBridger< super(l2Network) } - /** - * Whether the chain uses ETH as its native/fee token. - * @returns - */ - private get isNativeTokenEth() { - return !this.nativeToken || this.nativeToken === constants.AddressZero - } - /** * Instantiates a new EthBridger from an L2 Provider * @param l2Provider @@ -202,7 +194,7 @@ export class EthBridger extends AssetBridger< public getApproveFeeTokenRequest( params?: ApproveFeeTokenParams ): Required> { - if (this.isNativeTokenEth) { + if (this.nativeTokenIsEth) { throw new Error('chain uses ETH as its native/fee token') } @@ -226,7 +218,7 @@ export class EthBridger extends AssetBridger< public async approveFeeToken( params: WithL1Signer ) { - if (this.isNativeTokenEth) { + if (this.nativeTokenIsEth) { throw new Error('chain uses ETH as its native/fee token') } @@ -246,7 +238,7 @@ export class EthBridger extends AssetBridger< * @returns */ private getDepositRequestData(params: EthDepositRequestParams) { - if (!this.isNativeTokenEth) { + if (!this.nativeTokenIsEth) { return ( ERC20Inbox__factory.createInterface() as unknown as { encodeFunctionData( @@ -278,7 +270,7 @@ export class EthBridger extends AssetBridger< return { txRequest: { to: this.l2Network.ethBridge.inbox, - value: this.isNativeTokenEth ? params.amount : 0, + value: this.nativeTokenIsEth ? params.amount : 0, data: this.getDepositRequestData(params), from: params.from, }, From 45e63a81fbb145ebcc17d85cbe67bdbaa62a4e30 Mon Sep 17 00:00:00 2001 From: spsjvc Date: Mon, 5 Feb 2024 16:40:14 +0100 Subject: [PATCH 167/192] clean up jsdoc --- src/lib/assetBridger/assetBridger.ts | 4 ++-- src/lib/dataEntities/networks.ts | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/lib/assetBridger/assetBridger.ts b/src/lib/assetBridger/assetBridger.ts index 579237c22e..f6eeb77389 100644 --- a/src/lib/assetBridger/assetBridger.ts +++ b/src/lib/assetBridger/assetBridger.ts @@ -41,9 +41,9 @@ export abstract class AssetBridger { public readonly l1Network: L1Network | L2Network /** - * In case of a chain that uses ETH as its native/gas token, this is either `undefined` or the zero address. + * In case of a chain that uses ETH as its native/gas token, this is either `undefined` or the zero address * - * In case of a chain that uses an ERC-20 token from the parent chain as its native/gas token, this is the address of said token on the parent chain. + * In case of a chain that uses an ERC-20 token from the parent chain as its native/gas token, this is the address of said token on the parent chain */ public readonly nativeToken?: string diff --git a/src/lib/dataEntities/networks.ts b/src/lib/dataEntities/networks.ts index 6df1528015..3983cdb971 100644 --- a/src/lib/dataEntities/networks.ts +++ b/src/lib/dataEntities/networks.ts @@ -67,8 +67,9 @@ export interface L2Network extends Network { */ depositTimeout: number /** - * In case of a chain that uses ETH as its native/fee token, this is either undefined or the zero address. - * In case of a chain that uses an ERC-20 token from the parent chain as its native/fee token, this is the address of said token on the parent chain. + * In case of a chain that uses ETH as its native/gas token, this is either `undefined` or the zero address + * + * In case of a chain that uses an ERC-20 token from the parent chain as its native/gas token, this is the address of said token on the parent chain */ nativeToken?: string } From 243f020d79d60f60575ce14883476e629d7cf69f Mon Sep 17 00:00:00 2001 From: spsjvc Date: Mon, 5 Feb 2024 16:44:46 +0100 Subject: [PATCH 168/192] clean up gen abi --- hardhat.nitro-contracts-abigen.ts | 18 ------------------ hardhat.token-bridge-contracts-abigen.ts | 18 ------------------ scripts/genAbi.js | 17 ++--------------- 3 files changed, 2 insertions(+), 51 deletions(-) delete mode 100644 hardhat.nitro-contracts-abigen.ts delete mode 100644 hardhat.token-bridge-contracts-abigen.ts diff --git a/hardhat.nitro-contracts-abigen.ts b/hardhat.nitro-contracts-abigen.ts deleted file mode 100644 index 247cefbbce..0000000000 --- a/hardhat.nitro-contracts-abigen.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @type import('hardhat/config').HardhatUserConfig - */ -module.exports = { - solidity: { - version: '0.8.9', - settings: { - optimizer: { - enabled: true, - runs: 100, - }, - }, - }, - paths: { - sources: './src', - artifacts: './build/contracts', - }, -} diff --git a/hardhat.token-bridge-contracts-abigen.ts b/hardhat.token-bridge-contracts-abigen.ts deleted file mode 100644 index 140d1da329..0000000000 --- a/hardhat.token-bridge-contracts-abigen.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @type import('hardhat/config').HardhatUserConfig - */ -module.exports = { - solidity: { - version: '0.8.16', - settings: { - optimizer: { - enabled: true, - runs: 100, - }, - }, - }, - paths: { - sources: './contracts', - artifacts: './build/contracts', - }, -} diff --git a/scripts/genAbi.js b/scripts/genAbi.js index 87d285eb5e..dda628e2f4 100644 --- a/scripts/genAbi.js +++ b/scripts/genAbi.js @@ -27,24 +27,11 @@ async function main() { // https://yarnpkg.com/advanced/rulebook#packages-should-never-write-inside-their-own-folder-outside-of-postinstall // instead of writing in postinstall in each of those packages, we should target a local folder in sdk's postinstall - // copy the hardhat config to nitro-contracts - execSync( - `cp ${cwd}/hardhat.nitro-contracts-abigen.ts ${nitroPath}/hardhat-abigen.ts` - ) - // copy the hardhat config to token-bridge-contracts - execSync( - `cp ${cwd}/hardhat.token-bridge-contracts-abigen.ts ${tokenBridgePath}/hardhat-abigen.ts` - ) - console.log('building @arbitrum/nitro-contracts') - execSync(`${npmExec} run build --config hardhat-abigen.ts`, { - cwd: nitroPath, - }) + execSync(`${npmExec} run build`, { cwd: nitroPath }) console.log('building @arbitrum/token-bridge-contracts') - execSync(`${npmExec} run build --config hardhat-abigen.ts`, { - cwd: tokenBridgePath, - }) + execSync(`${npmExec} run build`, { cwd: tokenBridgePath }) console.log('Done compiling') From 716fb1a4cb21330de612157b1d78441156fad673 Mon Sep 17 00:00:00 2001 From: spsjvc Date: Mon, 5 Feb 2024 17:56:04 +0100 Subject: [PATCH 169/192] rename fee token to gas token --- src/lib/assetBridger/erc20Bridger.ts | 23 ++++---- src/lib/assetBridger/ethBridger.ts | 52 +++++++++---------- .../customFeeTokenEthBridger.test.ts | 6 +-- .../customFeeTokenTestHelpers.ts | 4 +- tests/integration/eth.test.ts | 6 +-- tests/integration/retryableData.test.ts | 2 +- tests/integration/standarderc20.test.ts | 2 +- tests/integration/testHelpers.ts | 2 +- 8 files changed, 47 insertions(+), 50 deletions(-) diff --git a/src/lib/assetBridger/erc20Bridger.ts b/src/lib/assetBridger/erc20Bridger.ts index d8b15f0ce1..d88aee4edd 100644 --- a/src/lib/assetBridger/erc20Bridger.ts +++ b/src/lib/assetBridger/erc20Bridger.ts @@ -228,14 +228,14 @@ export class Erc20Bridger extends AssetBridger< } /** - * Creates a transaction request for approving the custom fee token to be spent by the relevant Gateway on the parent chain. + * Creates a transaction request for approving the custom gas token to be spent by the relevant gateway on the parent chain * @param params */ - public async getApproveFeeTokenRequest( + public async getApproveGasTokenRequest( params: ProviderTokenApproveParams ): Promise>> { if (this.nativeTokenIsEth) { - throw new Error('chain uses ETH as its native/fee token') + throw new Error('chain uses ETH as its native/gas token') } const txRequest = await this.getApproveTokenRequest(params) @@ -244,26 +244,27 @@ export class Erc20Bridger extends AssetBridger< } /** - * Approves the custom fee token to be spent by the relevant Gateway on the parent chain. + * Approves the custom gas token to be spent by the relevant gateway on the parent chain * @param params */ - public async approveFeeToken( + public async approveGasToken( params: ApproveParamsOrTxRequest ): Promise { if (this.nativeTokenIsEth) { - throw new Error('chain uses ETH as its native/fee token') + throw new Error('chain uses ETH as its native/gas token') } await this.checkL1Network(params.l1Signer) - const approveRequest = this.isApproveParams(params) - ? await this.getApproveFeeTokenRequest({ + const approveGasTokenRequest = this.isApproveParams(params) + ? await this.getApproveGasTokenRequest({ ...params, l1Provider: SignerProviderUtils.getProviderOrThrow(params.l1Signer), }) : params.txRequest - return await params.l1Signer.sendTransaction({ - ...approveRequest, + + return params.l1Signer.sendTransaction({ + ...approveGasTokenRequest, ...params.overrides, }) } @@ -553,7 +554,7 @@ export class Erc20Bridger extends AssetBridger< private getDepositRequestCallValue( depositParams: OmitTyped ) { - // the call value should be zero when paying with a custom fee token, + // the call value should be zero when paying with a custom gas token, // as the fee amount is packed inside the last parameter (`data`) of the call to `outboundTransfer` if (!this.nativeTokenIsEth) { return constants.Zero diff --git a/src/lib/assetBridger/ethBridger.ts b/src/lib/assetBridger/ethBridger.ts index 0448407f4d..f3cf36af35 100644 --- a/src/lib/assetBridger/ethBridger.ts +++ b/src/lib/assetBridger/ethBridger.ts @@ -49,7 +49,7 @@ import { MissingProviderArbSdkError } from '../dataEntities/errors' import { L2Network, getL2Network } from '../dataEntities/networks' import { ERC20__factory } from '../abi/factories/ERC20__factory' -export type ApproveFeeTokenParams = { +export type ApproveGasTokenParams = { /** * Amount to approve. Defaults to max int. */ @@ -60,7 +60,7 @@ export type ApproveFeeTokenParams = { overrides?: PayableOverrides } -export type ApproveFeeTokenTxRequest = { +export type ApproveGasTokenTxRequest = { /** * Transaction request */ @@ -71,11 +71,11 @@ export type ApproveFeeTokenTxRequest = { overrides?: Overrides } -export type ApproveFeeTokenParamsOrTxRequest = - | ApproveFeeTokenParams - | ApproveFeeTokenTxRequest +export type ApproveGasTokenParamsOrTxRequest = + | ApproveGasTokenParams + | ApproveGasTokenTxRequest -type WithL1Signer = T & { +type WithL1Signer = T & { l1Signer: Signer } @@ -164,10 +164,6 @@ export class EthBridger extends AssetBridger< EthDepositParams | EthDepositToParams | L1ToL2TxReqAndSigner, EthWithdrawParams | L2ToL1TxReqAndSigner > { - public constructor(public readonly l2Network: L2Network) { - super(l2Network) - } - /** * Instantiates a new EthBridger from an L2 Provider * @param l2Provider @@ -178,24 +174,24 @@ export class EthBridger extends AssetBridger< } /** - * Asserts that the provided argument is of type `ApproveFeeTokenParams` and not `ApproveFeeTokenTxRequest`. + * Asserts that the provided argument is of type `ApproveGasTokenParams` and not `ApproveGasTokenTxRequest`. * @param params */ - private isApproveFeeTokenParams( - params: ApproveFeeTokenParamsOrTxRequest - ): params is WithL1Signer { - return typeof (params as ApproveFeeTokenTxRequest).txRequest === 'undefined' + private isApproveGasTokenParams( + params: ApproveGasTokenParamsOrTxRequest + ): params is WithL1Signer { + return typeof (params as ApproveGasTokenTxRequest).txRequest === 'undefined' } /** - * Creates a transaction request for approving the custom fee token to be spent by the Inbox on the parent chain. + * Creates a transaction request for approving the custom gas token to be spent by the inbox on the parent chain * @param params */ - public getApproveFeeTokenRequest( - params?: ApproveFeeTokenParams + public getApproveGasTokenRequest( + params?: ApproveGasTokenParams ): Required> { if (this.nativeTokenIsEth) { - throw new Error('chain uses ETH as its native/fee token') + throw new Error('chain uses ETH as its native/gas token') } const erc20Interface = ERC20__factory.createInterface() @@ -212,28 +208,28 @@ export class EthBridger extends AssetBridger< } /** - * Approves the custom fee token to be spent by the Inbox on the parent chain. + * Approves the custom gas token to be spent by the Inbox on the parent chain. * @param params */ - public async approveFeeToken( - params: WithL1Signer + public async approveGasToken( + params: WithL1Signer ) { if (this.nativeTokenIsEth) { - throw new Error('chain uses ETH as its native/fee token') + throw new Error('chain uses ETH as its native/gas token') } - const approveFeeTokenRequest = this.isApproveFeeTokenParams(params) - ? this.getApproveFeeTokenRequest(params) + const approveGasTokenRequest = this.isApproveGasTokenParams(params) + ? this.getApproveGasTokenRequest(params) : params.txRequest - return await params.l1Signer.sendTransaction({ - ...approveFeeTokenRequest, + return params.l1Signer.sendTransaction({ + ...approveGasTokenRequest, ...params.overrides, }) } /** - * Gets the transaction data for a tx request necessary for depositing ETH or other native/fee token. + * Gets the transaction calldata for a tx request necessary for depositing ETH or custom gas token * @param params * @returns */ diff --git a/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts b/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts index a5f9262637..16b2ae270b 100644 --- a/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts +++ b/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts @@ -53,7 +53,7 @@ if (isL2NetworkWithCustomFeeToken()) { await fundL1Ether(l1Signer) await fundL1CustomFeeToken(l1Signer) - const approvalTx = await ethBridger.approveFeeToken({ + const approvalTx = await ethBridger.approveGasToken({ amount, l1Signer, }) @@ -76,8 +76,8 @@ if (isL2NetworkWithCustomFeeToken()) { await fundL1Ether(l1Signer) await fundL1CustomFeeToken(l1Signer) - const approvalTx = await ethBridger.approveFeeToken({ - txRequest: await ethBridger.getApproveFeeTokenRequest(), + const approvalTx = await ethBridger.approveGasToken({ + txRequest: await ethBridger.getApproveGasTokenRequest(), l1Signer, }) await approvalTx.wait() diff --git a/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts b/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts index 0c3a41e53c..62329a9d72 100644 --- a/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts +++ b/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts @@ -56,7 +56,7 @@ export async function fundL1CustomFeeToken(l1SignerOrAddress: Signer | string) { export async function approveL1CustomFeeToken(l1Signer: Signer) { const ethBridger = await EthBridger.fromProvider(arbProvider()) - const tx = await ethBridger.approveFeeToken({ l1Signer }) + const tx = await ethBridger.approveGasToken({ l1Signer }) await tx.wait() } @@ -75,7 +75,7 @@ export async function approveL1CustomFeeTokenForErc20Deposit( ) { const erc20Bridger = await Erc20Bridger.fromProvider(arbProvider()) - const tx = await erc20Bridger.approveFeeToken({ erc20L1Address, l1Signer }) + const tx = await erc20Bridger.approveGasToken({ erc20L1Address, l1Signer }) await tx.wait() } diff --git a/tests/integration/eth.test.ts b/tests/integration/eth.test.ts index 7a966b2201..23220c6b30 100644 --- a/tests/integration/eth.test.ts +++ b/tests/integration/eth.test.ts @@ -83,12 +83,12 @@ describe('Ether', async () => { // Test should only run if we're using eth as native fee if (!isL2NetworkWithCustomFeeToken()) { - it('"EthBridger.approveFeeToken" throws when eth is used as native/fee token', async () => { + it('"EthBridger.approveGasToken" throws when eth is used as native/fee token', async () => { const { ethBridger, l1Signer } = await testSetup() try { - await ethBridger.approveFeeToken({ l1Signer }) - expect.fail(`"EthBridger.approveFeeToken" should have thrown`) + await ethBridger.approveGasToken({ l1Signer }) + expect.fail(`"EthBridger.approveGasToken" should have thrown`) } catch (error: any) { expect(error.message).to.equal('chain uses ETH as its native/fee token') } diff --git a/tests/integration/retryableData.test.ts b/tests/integration/retryableData.test.ts index 2056f8e758..a8d6e95999 100644 --- a/tests/integration/retryableData.test.ts +++ b/tests/integration/retryableData.test.ts @@ -165,7 +165,7 @@ describe('RevertData', () => { if (isL2NetworkWithCustomFeeToken()) { // approve the custom fee token await ( - await erc20Bridger.approveFeeToken({ + await erc20Bridger.approveGasToken({ erc20L1Address: l1TokenAddress, l1Signer: l1Signer, }) diff --git a/tests/integration/standarderc20.test.ts b/tests/integration/standarderc20.test.ts index 59c284724a..cfe29816ea 100644 --- a/tests/integration/standarderc20.test.ts +++ b/tests/integration/standarderc20.test.ts @@ -103,7 +103,7 @@ describe('standard ERC20', () => { 'initial allowance is not empty' ) - const tx = await erc20Bridger.approveFeeToken({ + const tx = await erc20Bridger.approveGasToken({ l1Signer: l1Signer, erc20L1Address: testState.l1Token.address, }) diff --git a/tests/integration/testHelpers.ts b/tests/integration/testHelpers.ts index 0c3bd39c90..e88d7f48cf 100644 --- a/tests/integration/testHelpers.ts +++ b/tests/integration/testHelpers.ts @@ -259,7 +259,7 @@ export const depositToken = async ( if (isL2NetworkWithCustomFeeToken()) { await ( - await erc20Bridger.approveFeeToken({ + await erc20Bridger.approveGasToken({ l1Signer, erc20L1Address: l1TokenAddress, }) From b4ca425c156a2a64a19f4565be82def816022b58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Mon, 12 Feb 2024 10:54:46 +0100 Subject: [PATCH 170/192] remove check --- src/lib/assetBridger/erc20Bridger.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/assetBridger/erc20Bridger.ts b/src/lib/assetBridger/erc20Bridger.ts index d88aee4edd..f441d9bcf0 100644 --- a/src/lib/assetBridger/erc20Bridger.ts +++ b/src/lib/assetBridger/erc20Bridger.ts @@ -486,9 +486,8 @@ export class Erc20Bridger extends AssetBridger< // L2 WETH contract doesn't have the l1Address method on it if ( - this.l2Network.tokenBridge.l2Weth && erc20L2Address.toLowerCase() === - this.l2Network.tokenBridge.l2Weth.toLowerCase() + this.l2Network.tokenBridge.l2Weth.toLowerCase() ) { return this.l2Network.tokenBridge.l1Weth } From 86380d2dbe4e53907bf28d028a6366df6ebb21c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Mon, 12 Feb 2024 11:00:15 +0100 Subject: [PATCH 171/192] add docs --- src/lib/assetBridger/erc20Bridger.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/lib/assetBridger/erc20Bridger.ts b/src/lib/assetBridger/erc20Bridger.ts index f441d9bcf0..3be1c77e0b 100644 --- a/src/lib/assetBridger/erc20Bridger.ts +++ b/src/lib/assetBridger/erc20Bridger.ts @@ -554,7 +554,7 @@ export class Erc20Bridger extends AssetBridger< depositParams: OmitTyped ) { // the call value should be zero when paying with a custom gas token, - // as the fee amount is packed inside the last parameter (`data`) of the call to `outboundTransfer` + // as the fee amount is packed inside the last parameter (`data`) of the call to `outboundTransfer`, see `getDepositRequestOutboundTransferDataParam` if (!this.nativeTokenIsEth) { return constants.Zero } @@ -568,7 +568,11 @@ export class Erc20Bridger extends AssetBridger< .add(depositParams.maxSubmissionCost) } - // todo(spsjvc): jsdoc + /** + * Get the `data` param for call to `outboundTransfer` + * @param depositParams + * @returns + */ private getDepositRequestOutboundTransferDataParam( depositParams: OmitTyped ) { @@ -576,8 +580,11 @@ export class Erc20Bridger extends AssetBridger< return defaultAbiCoder.encode( ['uint256', 'bytes', 'uint256'], [ + // maxSubmissionCost constants.Zero, + // callHookData '0x', + // nativeTokenTotalFee depositParams.gasLimit .mul(depositParams.maxFeePerGas) .add(depositParams.maxSubmissionCost), @@ -587,7 +594,12 @@ export class Erc20Bridger extends AssetBridger< return defaultAbiCoder.encode( ['uint256', 'bytes'], - [depositParams.maxSubmissionCost, '0x'] + [ + // maxSubmissionCost + depositParams.maxSubmissionCost, + // callHookData + '0x', + ] ) } From 34dcdc561710e8369fc434f7f958049efd613984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Mon, 12 Feb 2024 11:05:38 +0100 Subject: [PATCH 172/192] clean up --- src/lib/assetBridger/ethBridger.ts | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/lib/assetBridger/ethBridger.ts b/src/lib/assetBridger/ethBridger.ts index f3cf36af35..cde2cae407 100644 --- a/src/lib/assetBridger/ethBridger.ts +++ b/src/lib/assetBridger/ethBridger.ts @@ -46,7 +46,7 @@ import { import { OmitTyped } from '../utils/types' import { SignerProviderUtils } from '../dataEntities/signerOrProvider' import { MissingProviderArbSdkError } from '../dataEntities/errors' -import { L2Network, getL2Network } from '../dataEntities/networks' +import { getL2Network } from '../dataEntities/networks' import { ERC20__factory } from '../abi/factories/ERC20__factory' export type ApproveGasTokenParams = { @@ -194,11 +194,15 @@ export class EthBridger extends AssetBridger< throw new Error('chain uses ETH as its native/gas token') } - const erc20Interface = ERC20__factory.createInterface() - const data = erc20Interface.encodeFunctionData('approve', [ - this.l2Network.ethBridge.inbox, - params?.amount ?? constants.MaxUint256, - ]) + const data = ERC20__factory.createInterface().encodeFunctionData( + 'approve', + [ + // spender + this.l2Network.ethBridge.inbox, + // value + params?.amount ?? constants.MaxUint256, + ] + ) return { to: this.nativeToken!, @@ -229,7 +233,7 @@ export class EthBridger extends AssetBridger< } /** - * Gets the transaction calldata for a tx request necessary for depositing ETH or custom gas token + * Gets transaction calldata for a tx request for depositing ETH or custom gas token * @param params * @returns */ @@ -256,7 +260,7 @@ export class EthBridger extends AssetBridger< } /** - * Get a transaction request for an eth deposit + * Gets tx request for depositing ETH or custom gas token * @param params * @returns */ From 9e5ce1d28907a59f197ca13af477864b0b56f670 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Mon, 12 Feb 2024 11:08:26 +0100 Subject: [PATCH 173/192] rename --- src/lib/message/L1ToL2MessageCreator.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/message/L1ToL2MessageCreator.ts b/src/lib/message/L1ToL2MessageCreator.ts index afb55760c4..8824a39b75 100644 --- a/src/lib/message/L1ToL2MessageCreator.ts +++ b/src/lib/message/L1ToL2MessageCreator.ts @@ -74,9 +74,9 @@ export class L1ToL2MessageCreator { estimates: Pick, excessFeeRefundAddress: string, callValueRefundAddress: string, - isNativeTokenEth: boolean + nativeTokenIsEth: boolean ) { - if (!isNativeTokenEth) { + if (!nativeTokenIsEth) { return ERC20Inbox__factory.createInterface().encodeFunctionData( 'createRetryableTicket', [ @@ -139,21 +139,21 @@ export class L1ToL2MessageCreator { ) const l2Network = await getL2Network(l2Provider) - const isNativeTokenEth = typeof l2Network.nativeToken === 'undefined' + const nativeTokenIsEth = typeof l2Network.nativeToken === 'undefined' const data = L1ToL2MessageCreator.getTicketCreationRequestData( params, estimates, excessFeeRefundAddress, callValueRefundAddress, - isNativeTokenEth + nativeTokenIsEth ) return { txRequest: { to: l2Network.ethBridge.inbox, data, - value: isNativeTokenEth ? estimates.deposit : constants.Zero, + value: nativeTokenIsEth ? estimates.deposit : constants.Zero, from: params.from, }, retryableData: { From 9c8a66e85587bb6cde225e0db0ca19e6b190aa10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Mon, 12 Feb 2024 11:39:02 +0100 Subject: [PATCH 174/192] add comment --- src/lib/assetBridger/erc20Bridger.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/assetBridger/erc20Bridger.ts b/src/lib/assetBridger/erc20Bridger.ts index 3be1c77e0b..bac2073be1 100644 --- a/src/lib/assetBridger/erc20Bridger.ts +++ b/src/lib/assetBridger/erc20Bridger.ts @@ -587,7 +587,7 @@ export class Erc20Bridger extends AssetBridger< // nativeTokenTotalFee depositParams.gasLimit .mul(depositParams.maxFeePerGas) - .add(depositParams.maxSubmissionCost), + .add(depositParams.maxSubmissionCost), // will be zero ] ) } From f79541c27bcfe95d2cc1d3772a998499f5e1092b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Mon, 12 Feb 2024 11:52:12 +0100 Subject: [PATCH 175/192] clean up --- src/lib/assetBridger/erc20Bridger.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/assetBridger/erc20Bridger.ts b/src/lib/assetBridger/erc20Bridger.ts index 797311f2d7..ac422b56e8 100644 --- a/src/lib/assetBridger/erc20Bridger.ts +++ b/src/lib/assetBridger/erc20Bridger.ts @@ -650,6 +650,8 @@ export class Erc20Bridger extends AssetBridger< params.maxSubmissionCost || depositParams.maxSubmissionCost const iGatewayRouter = L1GatewayRouter__factory.createInterface() + const innerData = + this.getDepositRequestOutboundTransferDataParam(depositParams) const functionData = defaultedParams.excessFeeRefundAddress !== defaultedParams.from @@ -660,7 +662,7 @@ export class Erc20Bridger extends AssetBridger< amount, depositParams.gasLimit, depositParams.maxFeePerGas, - this.getDepositRequestOutboundTransferDataParam(depositParams), + innerData, ]) : iGatewayRouter.encodeFunctionData('outboundTransfer', [ erc20L1Address, @@ -668,7 +670,7 @@ export class Erc20Bridger extends AssetBridger< amount, depositParams.gasLimit, depositParams.maxFeePerGas, - this.getDepositRequestOutboundTransferDataParam(depositParams), + innerData, ]) return { From b4a565b69d1e6c0a080d00629a1a892257eda269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Mon, 12 Feb 2024 12:13:18 +0100 Subject: [PATCH 176/192] add test --- .../L1ToL2MessageGasEstimator.test.ts | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 tests/integration/L1ToL2MessageGasEstimator.test.ts diff --git a/tests/integration/L1ToL2MessageGasEstimator.test.ts b/tests/integration/L1ToL2MessageGasEstimator.test.ts new file mode 100644 index 0000000000..55c3c93051 --- /dev/null +++ b/tests/integration/L1ToL2MessageGasEstimator.test.ts @@ -0,0 +1,65 @@ +/* + * Copyright 2021, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* eslint-env node */ +'use strict' + +import { expect } from 'chai' +import { BigNumber } from 'ethers' + +import { skipIfMainnet } from './testHelpers' +import { testSetup } from '../../scripts/testSetup' +import { L1ToL2MessageGasEstimator } from '../../src' +import { isL2NetworkWithCustomFeeToken } from './custom-fee-token/customFeeTokenTestHelpers' + +const isCustomFeeToken = isL2NetworkWithCustomFeeToken() + +describe('L1ToL2MessageGasEstimator', () => { + beforeEach('skipIfMainnet', async function () { + await skipIfMainnet(this) + }) + + if (!isCustomFeeToken) { + it(`"estimateSubmissionFee" returns non-0 for eth chain`, async () => { + const { l1Provider, l2Provider } = await testSetup() + + const submissionFee = await new L1ToL2MessageGasEstimator( + l2Provider + ).estimateSubmissionFee( + l1Provider, + await l1Provider.getGasPrice(), + 123456 + ) + + expect(submissionFee.toString()).to.not.eq(BigNumber.from(0).toString()) + }) + } + + if (isCustomFeeToken) { + it(`"estimateSubmissionFee" returns 0 for custom gas token chain`, async () => { + const { l1Provider, l2Provider } = await testSetup() + + const submissionFee = await new L1ToL2MessageGasEstimator( + l2Provider + ).estimateSubmissionFee( + l1Provider, + await l1Provider.getGasPrice(), + 123456 + ) + + expect(submissionFee.toString()).to.eq(BigNumber.from(0).toString()) + }) + } +}) From ce42be3e43f5f5df86ac9c28c3e6dc05408402b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Mon, 12 Feb 2024 14:29:49 +0100 Subject: [PATCH 177/192] fix --- tests/integration/eth.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/eth.test.ts b/tests/integration/eth.test.ts index 23220c6b30..3f499af551 100644 --- a/tests/integration/eth.test.ts +++ b/tests/integration/eth.test.ts @@ -83,14 +83,14 @@ describe('Ether', async () => { // Test should only run if we're using eth as native fee if (!isL2NetworkWithCustomFeeToken()) { - it('"EthBridger.approveGasToken" throws when eth is used as native/fee token', async () => { + it('"EthBridger.approveGasToken" throws when eth is used as native/gas token', async () => { const { ethBridger, l1Signer } = await testSetup() try { await ethBridger.approveGasToken({ l1Signer }) expect.fail(`"EthBridger.approveGasToken" should have thrown`) } catch (error: any) { - expect(error.message).to.equal('chain uses ETH as its native/fee token') + expect(error.message).to.equal('chain uses ETH as its native/gas token') } }) } From 2f1b5dfabb8eb1b0ad417294467deafc34023eaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Mon, 12 Feb 2024 14:38:22 +0100 Subject: [PATCH 178/192] rename and jsdoc --- src/lib/assetBridger/erc20Bridger.ts | 6 +++--- src/lib/message/L1ToL2MessageCreator.ts | 14 +++++++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/lib/assetBridger/erc20Bridger.ts b/src/lib/assetBridger/erc20Bridger.ts index ac422b56e8..ac69743ae4 100644 --- a/src/lib/assetBridger/erc20Bridger.ts +++ b/src/lib/assetBridger/erc20Bridger.ts @@ -558,7 +558,7 @@ export class Erc20Bridger extends AssetBridger< depositParams: OmitTyped ) { // the call value should be zero when paying with a custom gas token, - // as the fee amount is packed inside the last parameter (`data`) of the call to `outboundTransfer`, see `getDepositRequestOutboundTransferDataParam` + // as the fee amount is packed inside the last parameter (`data`) of the call to `outboundTransfer`, see `getDepositRequestOutboundTransferInnerData` if (!this.nativeTokenIsEth) { return constants.Zero } @@ -577,7 +577,7 @@ export class Erc20Bridger extends AssetBridger< * @param depositParams * @returns */ - private getDepositRequestOutboundTransferDataParam( + private getDepositRequestOutboundTransferInnerData( depositParams: OmitTyped ) { if (!this.nativeTokenIsEth) { @@ -651,7 +651,7 @@ export class Erc20Bridger extends AssetBridger< const iGatewayRouter = L1GatewayRouter__factory.createInterface() const innerData = - this.getDepositRequestOutboundTransferDataParam(depositParams) + this.getDepositRequestOutboundTransferInnerData(depositParams) const functionData = defaultedParams.excessFeeRefundAddress !== defaultedParams.from diff --git a/src/lib/message/L1ToL2MessageCreator.ts b/src/lib/message/L1ToL2MessageCreator.ts index 8824a39b75..2c3223d2db 100644 --- a/src/lib/message/L1ToL2MessageCreator.ts +++ b/src/lib/message/L1ToL2MessageCreator.ts @@ -68,8 +68,16 @@ export class L1ToL2MessageCreator { ) } - // todo(spsjvc): jsdoc - protected static getTicketCreationRequestData( + /** + * Prepare calldata for a call to create a retryable ticket + * @param params + * @param estimates + * @param excessFeeRefundAddress + * @param callValueRefundAddress + * @param nativeTokenIsEth + * @returns + */ + protected static getTicketCreationRequestCallData( params: L1ToL2MessageParams, estimates: Pick, excessFeeRefundAddress: string, @@ -141,7 +149,7 @@ export class L1ToL2MessageCreator { const l2Network = await getL2Network(l2Provider) const nativeTokenIsEth = typeof l2Network.nativeToken === 'undefined' - const data = L1ToL2MessageCreator.getTicketCreationRequestData( + const data = L1ToL2MessageCreator.getTicketCreationRequestCallData( params, estimates, excessFeeRefundAddress, From 7caa38815e20d3039cd4469f395068ea165a6c48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Mon, 12 Feb 2024 15:02:14 +0100 Subject: [PATCH 179/192] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6f3813a592..8dea3b1179 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@arbitrum/sdk", - "version": "3.2.0-custom-fee-token.0", + "version": "3.3.0-beta.0", "description": "Typescript library client-side interactions with Arbitrum", "author": "Offchain Labs, Inc.", "license": "Apache-2.0", From 746cf4e1eb60189fb5f61217d5afa27b230c6371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Tue, 13 Feb 2024 12:51:06 +0100 Subject: [PATCH 180/192] fix --- src/lib/assetBridger/erc20Bridger.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/assetBridger/erc20Bridger.ts b/src/lib/assetBridger/erc20Bridger.ts index ac69743ae4..283884a5a2 100644 --- a/src/lib/assetBridger/erc20Bridger.ts +++ b/src/lib/assetBridger/erc20Bridger.ts @@ -585,7 +585,7 @@ export class Erc20Bridger extends AssetBridger< ['uint256', 'bytes', 'uint256'], [ // maxSubmissionCost - constants.Zero, + depositParams.maxSubmissionCost, // will be zero // callHookData '0x', // nativeTokenTotalFee From 0f43ffa9e7a2ca4c3474a3fd857ad74af63303c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Tue, 13 Feb 2024 13:01:42 +0100 Subject: [PATCH 181/192] remove unused script --- package.json | 3 +-- scripts/fundCustomFeeToken.ts | 19 ------------------- 2 files changed, 1 insertion(+), 21 deletions(-) delete mode 100644 scripts/fundCustomFeeToken.ts diff --git a/package.json b/package.json index 8dea3b1179..f7737f2c14 100644 --- a/package.json +++ b/package.json @@ -44,8 +44,7 @@ "setStandard": "ts-node scripts/setStandardGateways.ts", "setCustom": "ts-node scripts/setArbCustomGateways.ts", "cancelRetryable": "ts-node scripts/cancelRetryable.ts", - "bridgeStandardToken": "ts-node scripts/deployStandard.ts", - "fund": "ts-node scripts/fundCustomFeeToken.ts" + "bridgeStandardToken": "ts-node scripts/deployStandard.ts" }, "dependencies": { "@ethersproject/address": "^5.0.8", diff --git a/scripts/fundCustomFeeToken.ts b/scripts/fundCustomFeeToken.ts deleted file mode 100644 index 7cf6b7e391..0000000000 --- a/scripts/fundCustomFeeToken.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { fundL1CustomFeeToken } from '../tests/integration/custom-fee-token/customFeeTokenTestHelpers' -import { getLocalNetworksFromFile } from './testSetup' - -async function main() { - const localNetworks = getLocalNetworksFromFile() - if (!localNetworks) { - console.error('No local networks found') - //TODO: get token address from deployment somehow - } - const nativeTokenAddress = localNetworks?.l2Network.nativeToken - if (!nativeTokenAddress || nativeTokenAddress === undefined) { - console.error('No native token found') - return - } - - await fundL1CustomFeeToken(nativeTokenAddress) -} - -main() From 69aa6ef8d1f091ec85e5daea630c32a66592d912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Tue, 13 Feb 2024 17:18:35 +0100 Subject: [PATCH 182/192] rename for consistency --- .../custom-fee-token/customFeeTokenTestHelpers.ts | 5 ++++- tests/integration/standarderc20.test.ts | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts b/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts index 62329a9d72..2a9f127e08 100644 --- a/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts +++ b/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts @@ -60,7 +60,10 @@ export async function approveL1CustomFeeToken(l1Signer: Signer) { await tx.wait() } -export async function getNativeTokenAllowance(owner: string, spender: string) { +export async function getL1CustomFeeTokenAllowance( + owner: string, + spender: string +) { const nativeToken = localNetworks().l2Network.nativeToken const nativeTokenContract = ERC20__factory.connect( nativeToken!, diff --git a/tests/integration/standarderc20.test.ts b/tests/integration/standarderc20.test.ts index 78045e44ac..3e868d6182 100644 --- a/tests/integration/standarderc20.test.ts +++ b/tests/integration/standarderc20.test.ts @@ -46,8 +46,8 @@ import { ArbRetryableTx__factory } from '../../src/lib/abi/factories/ArbRetryabl import { NodeInterface__factory } from '../../src/lib/abi/factories/NodeInterface__factory' import { isDefined } from '../../src/lib/utils/lib' import { + getL1CustomFeeTokenAllowance, approveL1CustomFeeTokenForErc20Deposit, - getNativeTokenAllowance, isL2NetworkWithCustomFeeToken, } from './custom-fee-token/customFeeTokenTestHelpers' const depositAmount = BigNumber.from(100) @@ -91,7 +91,7 @@ describe('standard ERC20', () => { l1Signer.provider! ) - const initialAllowance = await getNativeTokenAllowance( + const initialAllowance = await getL1CustomFeeTokenAllowance( await l1Signer.getAddress(), gatewayAddress ) @@ -109,7 +109,7 @@ describe('standard ERC20', () => { }) await tx.wait() - const finalAllowance = await getNativeTokenAllowance( + const finalAllowance = await getL1CustomFeeTokenAllowance( await l1Signer.getAddress(), gatewayAddress ) From ed9a199c585e58e09b486d87ce23d1843ea94e3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Tue, 13 Feb 2024 18:06:02 +0100 Subject: [PATCH 183/192] clean up --- tests/integration/weth.test.ts | 192 +++++++++++++++++---------------- 1 file changed, 97 insertions(+), 95 deletions(-) diff --git a/tests/integration/weth.test.ts b/tests/integration/weth.test.ts index 6654c3829f..49b0056f75 100644 --- a/tests/integration/weth.test.ts +++ b/tests/integration/weth.test.ts @@ -33,104 +33,106 @@ import { testSetup } from '../../scripts/testSetup' import { ERC20__factory } from '../../src/lib/abi/factories/ERC20__factory' import { isL2NetworkWithCustomFeeToken } from './custom-fee-token/customFeeTokenTestHelpers' -// WETH tests are only relevant for networks that use WETH as the native token -if (!isL2NetworkWithCustomFeeToken()) { - describe('WETH', async () => { - beforeEach('skipIfMainnet', async function () { - await skipIfMainnet(this) +// only run when not using a custom gas token chain +const describeWithCustomGasTokenPatch = !isL2NetworkWithCustomFeeToken() + ? describe + : describe.skip + +describeWithCustomGasTokenPatch('WETH', async () => { + beforeEach('skipIfMainnet', async function () { + await skipIfMainnet(this) + }) + + it('deposit WETH', async () => { + const { l2Network, l1Signer, l2Signer, erc20Bridger } = await testSetup() + + const l1WethAddress = l2Network.tokenBridge.l1Weth + + const wethToWrap = parseEther('0.00001') + const wethToDeposit = parseEther('0.0000001') + + await fundL1(l1Signer, parseEther('1')) + + const l2WETH = AeWETH__factory.connect( + l2Network.tokenBridge.l2Weth, + l2Signer.provider! + ) + expect( + (await l2WETH.balanceOf(await l2Signer.getAddress())).toString(), + 'start balance weth' + ).to.eq('0') + + const l1WETH = AeWETH__factory.connect(l1WethAddress, l1Signer) + const res = await l1WETH.deposit({ + value: wethToWrap, }) + await res.wait() + await depositToken({ + depositAmount: wethToDeposit, + l1TokenAddress: l1WethAddress, + erc20Bridger, + l1Signer, + l2Signer, + expectedStatus: L1ToL2MessageStatus.REDEEMED, + expectedGatewayType: GatewayType.WETH, + }) + + const l2WethGateway = await erc20Bridger.getL2GatewayAddress( + l1WethAddress, + l2Signer.provider! + ) + expect(l2WethGateway, 'l2 weth gateway').to.eq( + l2Network.tokenBridge.l2WethGateway + ) + const l2Token = erc20Bridger.getL2TokenContract( + l2Signer.provider!, + l2Network.tokenBridge.l2Weth + ) + expect(l2Token.address, 'l2 weth').to.eq(l2Network.tokenBridge.l2Weth) + + // now try to withdraw the funds + await fundL2(l2Signer) + const l2Weth = AeWETH__factory.connect(l2Token.address, l2Signer) + const randomAddr = Wallet.createRandom().address + await ( + await l2Weth.connect(l2Signer).withdrawTo(randomAddr, wethToDeposit) + ).wait() + const afterBalance = await l2Signer.provider!.getBalance(randomAddr) + + expect(afterBalance.toString(), 'balance after').to.eq( + wethToDeposit.toString() + ) + }) + + it('withdraw WETH', async () => { + const wethToWrap = parseEther('0.00001') + const wethToWithdraw = parseEther('0.00000001') + + const { l2Network, l1Signer, l2Signer, erc20Bridger } = await testSetup() + await fundL1(l1Signer) + await fundL2(l2Signer) - it('deposit WETH', async () => { - const { l2Network, l1Signer, l2Signer, erc20Bridger } = await testSetup() - - const l1WethAddress = l2Network.tokenBridge.l1Weth - - const wethToWrap = parseEther('0.00001') - const wethToDeposit = parseEther('0.0000001') - - await fundL1(l1Signer, parseEther('1')) - - const l2WETH = AeWETH__factory.connect( - l2Network.tokenBridge.l2Weth, - l2Signer.provider! - ) - expect( - (await l2WETH.balanceOf(await l2Signer.getAddress())).toString(), - 'start balance weth' - ).to.eq('0') - - const l1WETH = AeWETH__factory.connect(l1WethAddress, l1Signer) - const res = await l1WETH.deposit({ - value: wethToWrap, - }) - await res.wait() - await depositToken({ - depositAmount: wethToDeposit, - l1TokenAddress: l1WethAddress, - erc20Bridger, - l1Signer, - l2Signer, - expectedStatus: L1ToL2MessageStatus.REDEEMED, - expectedGatewayType: GatewayType.WETH, - }) - - const l2WethGateway = await erc20Bridger.getL2GatewayAddress( - l1WethAddress, - l2Signer.provider! - ) - expect(l2WethGateway, 'l2 weth gateway').to.eq( - l2Network.tokenBridge.l2WethGateway - ) - const l2Token = erc20Bridger.getL2TokenContract( - l2Signer.provider!, - l2Network.tokenBridge.l2Weth - ) - expect(l2Token.address, 'l2 weth').to.eq(l2Network.tokenBridge.l2Weth) - - // now try to withdraw the funds - await fundL2(l2Signer) - const l2Weth = AeWETH__factory.connect(l2Token.address, l2Signer) - const randomAddr = Wallet.createRandom().address - await ( - await l2Weth.connect(l2Signer).withdrawTo(randomAddr, wethToDeposit) - ).wait() - const afterBalance = await l2Signer.provider!.getBalance(randomAddr) - - expect(afterBalance.toString(), 'balance after').to.eq( - wethToDeposit.toString() - ) + const l2Weth = AeWETH__factory.connect( + l2Network.tokenBridge.l2Weth, + l2Signer + ) + const res = await l2Weth.deposit({ + value: wethToWrap, }) + const rec = await res.wait() + expect(rec.status).to.equal(1, 'deposit txn failed') - it('withdraw WETH', async () => { - const wethToWrap = parseEther('0.00001') - const wethToWithdraw = parseEther('0.00000001') - - const { l2Network, l1Signer, l2Signer, erc20Bridger } = await testSetup() - await fundL1(l1Signer) - await fundL2(l2Signer) - - const l2Weth = AeWETH__factory.connect( - l2Network.tokenBridge.l2Weth, - l2Signer - ) - const res = await l2Weth.deposit({ - value: wethToWrap, - }) - const rec = await res.wait() - expect(rec.status).to.equal(1, 'deposit txn failed') - - await withdrawToken({ - amount: wethToWithdraw, - erc20Bridger: erc20Bridger, - gatewayType: GatewayType.WETH, - l1Signer: l1Signer, - l1Token: ERC20__factory.connect( - l2Network.tokenBridge.l1Weth, - l1Signer.provider! - ), - l2Signer: l2Signer, - startBalance: wethToWrap, - }) + await withdrawToken({ + amount: wethToWithdraw, + erc20Bridger: erc20Bridger, + gatewayType: GatewayType.WETH, + l1Signer: l1Signer, + l1Token: ERC20__factory.connect( + l2Network.tokenBridge.l1Weth, + l1Signer.provider! + ), + l2Signer: l2Signer, + startBalance: wethToWrap, }) }) -} +}) From b541d27ba1805e13757bbaf705ae04b44ea6dfeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Tue, 13 Feb 2024 18:07:54 +0100 Subject: [PATCH 184/192] patch --- .../customFeeTokenEthBridger.test.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts b/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts index 16b2ae270b..964cde669e 100644 --- a/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts +++ b/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts @@ -33,8 +33,14 @@ import { L2ToL1Message, L2ToL1MessageStatus } from '../../../src' dotenv.config() -if (isL2NetworkWithCustomFeeToken()) { - describe('EthBridger (with custom fee token)', async () => { +// only run when using a custom gas token chain +const describeWithCustomGasTokenPatch = isL2NetworkWithCustomFeeToken() + ? describe + : describe.skip + +describeWithCustomGasTokenPatch( + 'EthBridger (with custom fee token)', + async () => { const { testSetup, fundL1CustomFeeToken, @@ -269,5 +275,5 @@ if (isL2NetworkWithCustomFeeToken()) { 'incorrect balance in destination after withdrawal' ) }) - }) -} + } +) From 8ed3593a4f4916b48389b2bb48d7dc522ec1d8e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Tue, 13 Feb 2024 19:01:03 +0100 Subject: [PATCH 185/192] clean up --- .../customFeeTokenEthBridger.test.ts | 11 +++----- .../custom-fee-token/mochaExtensions.ts | 27 +++++++++++++++++++ tests/integration/describeCustom.ts | 15 +++++++++++ tests/integration/weth.test.ts | 9 ++----- 4 files changed, 47 insertions(+), 15 deletions(-) create mode 100644 tests/integration/custom-fee-token/mochaExtensions.ts create mode 100644 tests/integration/describeCustom.ts diff --git a/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts b/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts index 964cde669e..0e5593bee8 100644 --- a/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts +++ b/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts @@ -22,7 +22,6 @@ import dotenv from 'dotenv' import { parseEther } from '@ethersproject/units' -import { isL2NetworkWithCustomFeeToken } from './customFeeTokenTestHelpers' import { fundL1 as fundL1Ether, mineUntilStop, @@ -30,15 +29,11 @@ import { wait, } from '../testHelpers' import { L2ToL1Message, L2ToL1MessageStatus } from '../../../src' +import { describeOnlyWhenCustomGasToken } from './mochaExtensions' dotenv.config() -// only run when using a custom gas token chain -const describeWithCustomGasTokenPatch = isL2NetworkWithCustomFeeToken() - ? describe - : describe.skip - -describeWithCustomGasTokenPatch( +describeOnlyWhenCustomGasToken( 'EthBridger (with custom fee token)', async () => { const { @@ -83,7 +78,7 @@ describeWithCustomGasTokenPatch( await fundL1CustomFeeToken(l1Signer) const approvalTx = await ethBridger.approveGasToken({ - txRequest: await ethBridger.getApproveGasTokenRequest(), + txRequest: ethBridger.getApproveGasTokenRequest(), l1Signer, }) await approvalTx.wait() diff --git a/tests/integration/custom-fee-token/mochaExtensions.ts b/tests/integration/custom-fee-token/mochaExtensions.ts new file mode 100644 index 0000000000..c882700a6b --- /dev/null +++ b/tests/integration/custom-fee-token/mochaExtensions.ts @@ -0,0 +1,27 @@ +import { isL2NetworkWithCustomFeeToken } from './customFeeTokenTestHelpers' + +const customGasTokenEnvironment = isL2NetworkWithCustomFeeToken() + +/** + * Only run when in an eth chain environment + */ +export const describeOnlyWhenEth = customGasTokenEnvironment + ? describe.skip + : describe + +/** + * Only run when in a custom gas token chain environment + */ +export const describeOnlyWhenCustomGasToken = customGasTokenEnvironment + ? describe + : describe.skip + +/** + * Only run when in an eth chain environment + */ +export const itOnlyWhenEth = customGasTokenEnvironment ? it.skip : it + +/** + * Only run when in a custom gas token chain environment + */ +export const itOnlyWhenCustomGasToken = customGasTokenEnvironment ? it : it.skip diff --git a/tests/integration/describeCustom.ts b/tests/integration/describeCustom.ts new file mode 100644 index 0000000000..7cc0af41c7 --- /dev/null +++ b/tests/integration/describeCustom.ts @@ -0,0 +1,15 @@ +import { isL2NetworkWithCustomFeeToken } from './custom-fee-token/customFeeTokenTestHelpers' +import { + fundL1 as fundL1Ether, + mineUntilStop, + skipIfMainnet, + wait, +} from '../testHelpers' +import { L2ToL1Message, L2ToL1MessageStatus } from '../../../src' + +dotenv.config() + +// only run when using a custom gas token chain +const describeWithCustomGasTokenPatch = isL2NetworkWithCustomFeeToken() + ? describe + : describe.skip diff --git a/tests/integration/weth.test.ts b/tests/integration/weth.test.ts index 49b0056f75..2a68c7eba3 100644 --- a/tests/integration/weth.test.ts +++ b/tests/integration/weth.test.ts @@ -31,14 +31,9 @@ import { L1ToL2MessageStatus } from '../../src' import { Wallet } from 'ethers' import { testSetup } from '../../scripts/testSetup' import { ERC20__factory } from '../../src/lib/abi/factories/ERC20__factory' -import { isL2NetworkWithCustomFeeToken } from './custom-fee-token/customFeeTokenTestHelpers' +import { describeOnlyWhenEth } from './custom-fee-token/mochaExtensions' -// only run when not using a custom gas token chain -const describeWithCustomGasTokenPatch = !isL2NetworkWithCustomFeeToken() - ? describe - : describe.skip - -describeWithCustomGasTokenPatch('WETH', async () => { +describeOnlyWhenEth('WETH', async () => { beforeEach('skipIfMainnet', async function () { await skipIfMainnet(this) }) From 27fe056e2fa8e6cfde3eaecbfb1ca877d46f238f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Tue, 13 Feb 2024 19:11:03 +0100 Subject: [PATCH 186/192] oops --- tests/integration/describeCustom.ts | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 tests/integration/describeCustom.ts diff --git a/tests/integration/describeCustom.ts b/tests/integration/describeCustom.ts deleted file mode 100644 index 7cc0af41c7..0000000000 --- a/tests/integration/describeCustom.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { isL2NetworkWithCustomFeeToken } from './custom-fee-token/customFeeTokenTestHelpers' -import { - fundL1 as fundL1Ether, - mineUntilStop, - skipIfMainnet, - wait, -} from '../testHelpers' -import { L2ToL1Message, L2ToL1MessageStatus } from '../../../src' - -dotenv.config() - -// only run when using a custom gas token chain -const describeWithCustomGasTokenPatch = isL2NetworkWithCustomFeeToken() - ? describe - : describe.skip From 4d5f96869c4016bd103f615a572a1f96286650fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Tue, 13 Feb 2024 19:21:56 +0100 Subject: [PATCH 187/192] clean up more --- .../L1ToL2MessageGasEstimator.test.ts | 25 ++-- tests/integration/eth.test.ts | 11 +- tests/integration/sanity.test.ts | 134 +++++++++--------- 3 files changed, 87 insertions(+), 83 deletions(-) diff --git a/tests/integration/L1ToL2MessageGasEstimator.test.ts b/tests/integration/L1ToL2MessageGasEstimator.test.ts index 55c3c93051..2ea66d7d46 100644 --- a/tests/integration/L1ToL2MessageGasEstimator.test.ts +++ b/tests/integration/L1ToL2MessageGasEstimator.test.ts @@ -22,17 +22,19 @@ import { BigNumber } from 'ethers' import { skipIfMainnet } from './testHelpers' import { testSetup } from '../../scripts/testSetup' import { L1ToL2MessageGasEstimator } from '../../src' -import { isL2NetworkWithCustomFeeToken } from './custom-fee-token/customFeeTokenTestHelpers' - -const isCustomFeeToken = isL2NetworkWithCustomFeeToken() +import { + itOnlyWhenEth, + itOnlyWhenCustomGasToken, +} from './custom-fee-token/mochaExtensions' describe('L1ToL2MessageGasEstimator', () => { beforeEach('skipIfMainnet', async function () { await skipIfMainnet(this) }) - if (!isCustomFeeToken) { - it(`"estimateSubmissionFee" returns non-0 for eth chain`, async () => { + itOnlyWhenEth( + `"estimateSubmissionFee" returns non-0 for eth chain`, + async () => { const { l1Provider, l2Provider } = await testSetup() const submissionFee = await new L1ToL2MessageGasEstimator( @@ -44,11 +46,12 @@ describe('L1ToL2MessageGasEstimator', () => { ) expect(submissionFee.toString()).to.not.eq(BigNumber.from(0).toString()) - }) - } + } + ) - if (isCustomFeeToken) { - it(`"estimateSubmissionFee" returns 0 for custom gas token chain`, async () => { + itOnlyWhenCustomGasToken( + `"estimateSubmissionFee" returns 0 for custom gas token chain`, + async () => { const { l1Provider, l2Provider } = await testSetup() const submissionFee = await new L1ToL2MessageGasEstimator( @@ -60,6 +63,6 @@ describe('L1ToL2MessageGasEstimator', () => { ) expect(submissionFee.toString()).to.eq(BigNumber.from(0).toString()) - }) - } + } + ) }) diff --git a/tests/integration/eth.test.ts b/tests/integration/eth.test.ts index 3f499af551..b502c82bea 100644 --- a/tests/integration/eth.test.ts +++ b/tests/integration/eth.test.ts @@ -36,6 +36,7 @@ import { L1ToL2MessageStatus } from '../../src/lib/message/L1ToL2Message' import { testSetup } from '../../scripts/testSetup' import { isL2NetworkWithCustomFeeToken } from './custom-fee-token/customFeeTokenTestHelpers' import { ERC20__factory } from '../../src/lib/abi/factories/ERC20__factory' +import { itOnlyWhenEth } from './custom-fee-token/mochaExtensions' dotenv.config() @@ -81,9 +82,9 @@ describe('Ether', async () => { ) }) - // Test should only run if we're using eth as native fee - if (!isL2NetworkWithCustomFeeToken()) { - it('"EthBridger.approveGasToken" throws when eth is used as native/gas token', async () => { + itOnlyWhenEth( + '"EthBridger.approveGasToken" throws when eth is used as native/gas token', + async () => { const { ethBridger, l1Signer } = await testSetup() try { @@ -92,8 +93,8 @@ describe('Ether', async () => { } catch (error: any) { expect(error.message).to.equal('chain uses ETH as its native/gas token') } - }) - } + } + ) it('deposits ether', async () => { const { ethBridger, l1Signer, l2Signer } = await testSetup() diff --git a/tests/integration/sanity.test.ts b/tests/integration/sanity.test.ts index 288983034c..6b4bce5010 100644 --- a/tests/integration/sanity.test.ts +++ b/tests/integration/sanity.test.ts @@ -29,7 +29,7 @@ import { L1ERC20Gateway__factory } from '../../src/lib/abi/factories/L1ERC20Gate import { testSetup } from '../../scripts/testSetup' import { randomBytes, hexlify } from 'ethers/lib/utils' -import { isL2NetworkWithCustomFeeToken } from './custom-fee-token/customFeeTokenTestHelpers' +import { itOnlyWhenEth } from './custom-fee-token/mochaExtensions' const expectIgnoreCase = (expected: string, actual: string) => { expect(expected.toLocaleLowerCase()).to.equal(actual.toLocaleLowerCase()) @@ -68,6 +68,72 @@ describe('sanity checks (read-only)', async () => { expect(l2Router).to.equal(l2Network.tokenBridge.l2GatewayRouter) }) + itOnlyWhenEth( + 'weth gateways gateways public storage vars properly set', + async () => { + const { l1Signer, l2Signer, l2Network } = await testSetup() + + const l1Gateway = await L1WethGateway__factory.connect( + l2Network.tokenBridge.l1WethGateway, + l1Signer + ) + const l2Gateway = await L2WethGateway__factory.connect( + l2Network.tokenBridge.l2WethGateway, + l2Signer + ) + + const l1Weth = await l1Gateway.l1Weth() + expectIgnoreCase(l1Weth, l2Network.tokenBridge.l1Weth) + + const l2Weth = await l2Gateway.l2Weth() + expectIgnoreCase(l2Weth, l2Network.tokenBridge.l2Weth) + + const l1GatewayCounterParty = await l1Gateway.counterpartGateway() + expectIgnoreCase( + l1GatewayCounterParty, + l2Network.tokenBridge.l2WethGateway + ) + + const l2GatewayCounterParty = await l2Gateway.counterpartGateway() + expectIgnoreCase( + l2GatewayCounterParty, + l2Network.tokenBridge.l1WethGateway + ) + + const l1Router = await l1Gateway.router() + expectIgnoreCase(l1Router, l2Network.tokenBridge.l1GatewayRouter) + + const l2Router = await l2Gateway.router() + expectIgnoreCase(l2Router, l2Network.tokenBridge.l2GatewayRouter) + } + ) + + itOnlyWhenEth('aeWETh public vars properly set', async () => { + const { l2Signer, l2Network } = await testSetup() + + const aeWeth = AeWETH__factory.connect( + l2Network.tokenBridge.l2Weth, + l2Signer + ) + + const l2GatewayOnAeWeth = await aeWeth.l2Gateway() + expectIgnoreCase(l2GatewayOnAeWeth, l2Network.tokenBridge.l2WethGateway) + + const l1AddressOnAeWeth = await aeWeth.l1Address() + expectIgnoreCase(l1AddressOnAeWeth, l2Network.tokenBridge.l1Weth) + }) + + itOnlyWhenEth('l1 gateway router points to right weth gateways', async () => { + const { adminErc20Bridger, l1Signer, l2Network } = await testSetup() + + const gateway = await adminErc20Bridger.getL1GatewayAddress( + l2Network.tokenBridge.l1Weth, + l1Signer.provider! + ) + + expect(gateway).to.equal(l2Network.tokenBridge.l1WethGateway) + }) + it('custom gateways public storage vars properly set', async () => { const { l1Signer, l2Signer, l2Network } = await testSetup() const l1Gateway = await L1CustomGateway__factory.connect( @@ -112,70 +178,4 @@ describe('sanity checks (read-only)', async () => { expect(erc20L2AddressAsPerL2).to.equal(erc20L2AddressAsPerL1) }) - - // WETH tests are only relevant for networks that use WETH as the native token - if (!isL2NetworkWithCustomFeeToken()) { - it('weth gateways gateways public storage vars properly set', async () => { - const { l1Signer, l2Signer, l2Network } = await testSetup() - - const l1Gateway = await L1WethGateway__factory.connect( - l2Network.tokenBridge.l1WethGateway, - l1Signer - ) - const l2Gateway = await L2WethGateway__factory.connect( - l2Network.tokenBridge.l2WethGateway, - l2Signer - ) - - const l1Weth = await l1Gateway.l1Weth() - expectIgnoreCase(l1Weth, l2Network.tokenBridge.l1Weth) - - const l2Weth = await l2Gateway.l2Weth() - expectIgnoreCase(l2Weth, l2Network.tokenBridge.l2Weth) - - const l1GatewayCounterParty = await l1Gateway.counterpartGateway() - expectIgnoreCase( - l1GatewayCounterParty, - l2Network.tokenBridge.l2WethGateway - ) - - const l2GatewayCounterParty = await l2Gateway.counterpartGateway() - expectIgnoreCase( - l2GatewayCounterParty, - l2Network.tokenBridge.l1WethGateway - ) - - const l1Router = await l1Gateway.router() - expectIgnoreCase(l1Router, l2Network.tokenBridge.l1GatewayRouter) - - const l2Router = await l2Gateway.router() - expectIgnoreCase(l2Router, l2Network.tokenBridge.l2GatewayRouter) - }) - - it('aeWETh public vars properly set', async () => { - const { l2Signer, l2Network } = await testSetup() - - const aeWeth = AeWETH__factory.connect( - l2Network.tokenBridge.l2Weth, - l2Signer - ) - - const l2GatewayOnAeWeth = await aeWeth.l2Gateway() - expectIgnoreCase(l2GatewayOnAeWeth, l2Network.tokenBridge.l2WethGateway) - - const l1AddressOnAeWeth = await aeWeth.l1Address() - expectIgnoreCase(l1AddressOnAeWeth, l2Network.tokenBridge.l1Weth) - }) - - it('l1 gateway router points to right weth gateways', async () => { - const { adminErc20Bridger, l1Signer, l2Network } = await testSetup() - - const gateway = await adminErc20Bridger.getL1GatewayAddress( - l2Network.tokenBridge.l1Weth, - l1Signer.provider! - ) - - expect(gateway).to.equal(l2Network.tokenBridge.l1WethGateway) - }) - } }) From abdf6686d64ac6c657f4f5a949e3c486978c8f73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Tue, 13 Feb 2024 19:26:52 +0100 Subject: [PATCH 188/192] clean up more --- .../integration/l1ToL2MessageCreator.test.ts | 6 +- tests/integration/sanity.test.ts | 54 +++++++-------- tests/integration/standarderc20.test.ts | 67 +++++++++---------- 3 files changed, 61 insertions(+), 66 deletions(-) diff --git a/tests/integration/l1ToL2MessageCreator.test.ts b/tests/integration/l1ToL2MessageCreator.test.ts index 88f3b2157c..e171096e01 100644 --- a/tests/integration/l1ToL2MessageCreator.test.ts +++ b/tests/integration/l1ToL2MessageCreator.test.ts @@ -29,8 +29,6 @@ import { isL2NetworkWithCustomFeeToken, } from './custom-fee-token/customFeeTokenTestHelpers' -const isCustomFeeToken = isL2NetworkWithCustomFeeToken() - describe('L1ToL2MessageCreator', () => { beforeEach('skipIfMainnet', async function () { await skipIfMainnet(this) @@ -47,7 +45,7 @@ describe('L1ToL2MessageCreator', () => { // Funding L1 wallet await fundL1(l1Signer) - if (isCustomFeeToken) { + if (isL2NetworkWithCustomFeeToken()) { await fundL1CustomFeeToken(l1Signer) await approveL1CustomFeeToken(l1Signer) } @@ -105,7 +103,7 @@ describe('L1ToL2MessageCreator', () => { // Funding L1 wallet await fundL1(l1Signer) - if (isCustomFeeToken) { + if (isL2NetworkWithCustomFeeToken()) { await fundL1CustomFeeToken(l1Signer) await approveL1CustomFeeToken(l1Signer) } diff --git a/tests/integration/sanity.test.ts b/tests/integration/sanity.test.ts index 6b4bce5010..b33ba34be1 100644 --- a/tests/integration/sanity.test.ts +++ b/tests/integration/sanity.test.ts @@ -68,6 +68,33 @@ describe('sanity checks (read-only)', async () => { expect(l2Router).to.equal(l2Network.tokenBridge.l2GatewayRouter) }) + it('custom gateways public storage vars properly set', async () => { + const { l1Signer, l2Signer, l2Network } = await testSetup() + const l1Gateway = await L1CustomGateway__factory.connect( + l2Network.tokenBridge.l1CustomGateway, + l1Signer + ) + const l2Gateway = await L2CustomGateway__factory.connect( + l2Network.tokenBridge.l2CustomGateway, + l2Signer + ) + const l1GatewayCounterParty = await l1Gateway.counterpartGateway() + expect(l1GatewayCounterParty).to.equal( + l2Network.tokenBridge.l2CustomGateway + ) + + const l2GatewayCounterParty = await l2Gateway.counterpartGateway() + expect(l2GatewayCounterParty).to.equal( + l2Network.tokenBridge.l1CustomGateway + ) + + const l1Router = await l1Gateway.router() + expect(l1Router).to.equal(l2Network.tokenBridge.l1GatewayRouter) + + const l2Router = await l2Gateway.router() + expect(l2Router).to.equal(l2Network.tokenBridge.l2GatewayRouter) + }) + itOnlyWhenEth( 'weth gateways gateways public storage vars properly set', async () => { @@ -134,33 +161,6 @@ describe('sanity checks (read-only)', async () => { expect(gateway).to.equal(l2Network.tokenBridge.l1WethGateway) }) - it('custom gateways public storage vars properly set', async () => { - const { l1Signer, l2Signer, l2Network } = await testSetup() - const l1Gateway = await L1CustomGateway__factory.connect( - l2Network.tokenBridge.l1CustomGateway, - l1Signer - ) - const l2Gateway = await L2CustomGateway__factory.connect( - l2Network.tokenBridge.l2CustomGateway, - l2Signer - ) - const l1GatewayCounterParty = await l1Gateway.counterpartGateway() - expect(l1GatewayCounterParty).to.equal( - l2Network.tokenBridge.l2CustomGateway - ) - - const l2GatewayCounterParty = await l2Gateway.counterpartGateway() - expect(l2GatewayCounterParty).to.equal( - l2Network.tokenBridge.l1CustomGateway - ) - - const l1Router = await l1Gateway.router() - expect(l1Router).to.equal(l2Network.tokenBridge.l1GatewayRouter) - - const l2Router = await l2Gateway.router() - expect(l2Router).to.equal(l2Network.tokenBridge.l2GatewayRouter) - }) - it('L1 and L2 implementations of calculateL2ERC20Address match', async () => { const { l1Signer, l2Signer, l2Network, erc20Bridger } = await testSetup() diff --git a/tests/integration/standarderc20.test.ts b/tests/integration/standarderc20.test.ts index 3e868d6182..0da87c54d5 100644 --- a/tests/integration/standarderc20.test.ts +++ b/tests/integration/standarderc20.test.ts @@ -50,11 +50,10 @@ import { approveL1CustomFeeTokenForErc20Deposit, isL2NetworkWithCustomFeeToken, } from './custom-fee-token/customFeeTokenTestHelpers' +import { itOnlyWhenCustomGasToken } from './custom-fee-token/mochaExtensions' const depositAmount = BigNumber.from(100) const withdrawalAmount = BigNumber.from(10) -const isCustomFeeToken = isL2NetworkWithCustomFeeToken() - describe('standard ERC20', () => { beforeEach('skipIfMainnet', async function () { await skipIfMainnet(this) @@ -82,49 +81,47 @@ describe('standard ERC20', () => { testState = { ...setup, l1Token: testToken } }) - if (isL2NetworkWithCustomFeeToken()) { - it('approves the thing', async () => { - const { l1Signer, erc20Bridger } = await testSetup() + itOnlyWhenCustomGasToken('approves the thing', async () => { + const { l1Signer, erc20Bridger } = await testSetup() - const gatewayAddress = await erc20Bridger.getL1GatewayAddress( - testState.l1Token.address, - l1Signer.provider! - ) + const gatewayAddress = await erc20Bridger.getL1GatewayAddress( + testState.l1Token.address, + l1Signer.provider! + ) - const initialAllowance = await getL1CustomFeeTokenAllowance( - await l1Signer.getAddress(), - gatewayAddress - ) + const initialAllowance = await getL1CustomFeeTokenAllowance( + await l1Signer.getAddress(), + gatewayAddress + ) - console.log({ initialAllowance }) + console.log({ initialAllowance }) - expect(initialAllowance.toString()).to.eq( - constants.Zero.toString(), - 'initial allowance is not empty' - ) + expect(initialAllowance.toString()).to.eq( + constants.Zero.toString(), + 'initial allowance is not empty' + ) - const tx = await erc20Bridger.approveGasToken({ - l1Signer: l1Signer, - erc20L1Address: testState.l1Token.address, - }) - await tx.wait() + const tx = await erc20Bridger.approveGasToken({ + l1Signer: l1Signer, + erc20L1Address: testState.l1Token.address, + }) + await tx.wait() - const finalAllowance = await getL1CustomFeeTokenAllowance( - await l1Signer.getAddress(), - gatewayAddress - ) + const finalAllowance = await getL1CustomFeeTokenAllowance( + await l1Signer.getAddress(), + gatewayAddress + ) - console.log({ finalAllowance }) + console.log({ finalAllowance }) - expect(finalAllowance.toString()).to.eq( - constants.MaxUint256.toString(), - 'initial allowance is not empty' - ) - }) - } + expect(finalAllowance.toString()).to.eq( + constants.MaxUint256.toString(), + 'initial allowance is not empty' + ) + }) it('deposits erc20', async () => { - if (isCustomFeeToken) { + if (isL2NetworkWithCustomFeeToken()) { await approveL1CustomFeeTokenForErc20Deposit( testState.l1Signer, testState.l1Token.address From 702834855057382e75ab6c0a60518b52897e2782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Tue, 13 Feb 2024 19:28:31 +0100 Subject: [PATCH 189/192] rename --- tests/integration/customerc20.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/customerc20.test.ts b/tests/integration/customerc20.test.ts index 40ac2bd3fc..5ae55acb42 100644 --- a/tests/integration/customerc20.test.ts +++ b/tests/integration/customerc20.test.ts @@ -152,10 +152,10 @@ const registerCustomToken = async ( adminErc20Bridger: AdminErc20Bridger ) => { // create a custom token on L1 and L2 - const l1CustomTokenFac = isL2NetworkWithCustomFeeToken() + const l1CustomTokenFactory = isL2NetworkWithCustomFeeToken() ? new TestOrbitCustomTokenL1__factory(l1Signer) : new TestCustomTokenL1__factory(l1Signer) - const l1CustomToken = await l1CustomTokenFac.deploy( + const l1CustomToken = await l1CustomTokenFactory.deploy( l2Network.tokenBridge.l1CustomGateway, l2Network.tokenBridge.l1GatewayRouter ) From 08f9af619abac49f0e8c9699dd6a451ca0168538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Tue, 13 Feb 2024 19:31:29 +0100 Subject: [PATCH 190/192] lol --- tests/integration/standarderc20.test.ts | 63 ++++++++++++------------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/tests/integration/standarderc20.test.ts b/tests/integration/standarderc20.test.ts index 0da87c54d5..6a4399983c 100644 --- a/tests/integration/standarderc20.test.ts +++ b/tests/integration/standarderc20.test.ts @@ -81,44 +81,43 @@ describe('standard ERC20', () => { testState = { ...setup, l1Token: testToken } }) - itOnlyWhenCustomGasToken('approves the thing', async () => { - const { l1Signer, erc20Bridger } = await testSetup() + itOnlyWhenCustomGasToken( + 'approves custom gas token to be spent by the relevant gateway', + async () => { + const { l1Signer, erc20Bridger } = await testSetup() - const gatewayAddress = await erc20Bridger.getL1GatewayAddress( - testState.l1Token.address, - l1Signer.provider! - ) - - const initialAllowance = await getL1CustomFeeTokenAllowance( - await l1Signer.getAddress(), - gatewayAddress - ) - - console.log({ initialAllowance }) + const gatewayAddress = await erc20Bridger.getL1GatewayAddress( + testState.l1Token.address, + l1Signer.provider! + ) - expect(initialAllowance.toString()).to.eq( - constants.Zero.toString(), - 'initial allowance is not empty' - ) + const initialAllowance = await getL1CustomFeeTokenAllowance( + await l1Signer.getAddress(), + gatewayAddress + ) - const tx = await erc20Bridger.approveGasToken({ - l1Signer: l1Signer, - erc20L1Address: testState.l1Token.address, - }) - await tx.wait() + expect(initialAllowance.toString()).to.eq( + constants.Zero.toString(), + 'initial allowance is not empty' + ) - const finalAllowance = await getL1CustomFeeTokenAllowance( - await l1Signer.getAddress(), - gatewayAddress - ) + const tx = await erc20Bridger.approveGasToken({ + l1Signer: l1Signer, + erc20L1Address: testState.l1Token.address, + }) + await tx.wait() - console.log({ finalAllowance }) + const finalAllowance = await getL1CustomFeeTokenAllowance( + await l1Signer.getAddress(), + gatewayAddress + ) - expect(finalAllowance.toString()).to.eq( - constants.MaxUint256.toString(), - 'initial allowance is not empty' - ) - }) + expect(finalAllowance.toString()).to.eq( + constants.MaxUint256.toString(), + 'initial allowance is not empty' + ) + } + ) it('deposits erc20', async () => { if (isL2NetworkWithCustomFeeToken()) { From af5586fc54fefc204d86f0eceef671f42ebd8fcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Tue, 13 Feb 2024 20:02:35 +0100 Subject: [PATCH 191/192] bump contracts --- package.json | 4 +- yarn.lock | 471 +++------------------------------------------------ 2 files changed, 26 insertions(+), 449 deletions(-) diff --git a/package.json b/package.json index f7737f2c14..63f2713d17 100644 --- a/package.json +++ b/package.json @@ -54,8 +54,8 @@ "ethers": "^5.1.0" }, "devDependencies": { - "@arbitrum/nitro-contracts": "^1.1.0", - "@arbitrum/token-bridge-contracts": "^1.2.0", + "@arbitrum/nitro-contracts": "^1.1.1", + "@arbitrum/token-bridge-contracts": "^1.2.1", "@nomiclabs/hardhat-ethers": "^2.0.4", "@typechain/ethers-v5": "9.0.0", "@types/chai": "^4.2.11", diff --git a/yarn.lock b/yarn.lock index b204661858..8635a3fb00 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,11 +2,6 @@ # yarn lockfile v1 -"@aduh95/viz.js@^3.7.0": - version "3.7.0" - resolved "https://registry.yarnpkg.com/@aduh95/viz.js/-/viz.js-3.7.0.tgz#a20d86c5fc8f6abebdc39b96a4326e10375d77c0" - integrity sha512-20Pk2Z98fbPLkECcrZSJszKos/OgtvJJR3NcbVfgCJ6EQjDNzW2P1BKqImOz3tJ952dvO2DWEhcLhQ1Wz1e9ng== - "@ampproject/remapping@^2.2.0": version "2.2.1" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" @@ -15,7 +10,7 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@arbitrum/nitro-contracts@^1.0.0-beta.8": +"@arbitrum/nitro-contracts@1.1.1", "@arbitrum/nitro-contracts@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@arbitrum/nitro-contracts/-/nitro-contracts-1.1.1.tgz#2d8a2f9ab757bb7654562aebe435bff833c4b98d" integrity sha512-4Tyk3XVHz+bm8UujUC78LYSw3xAxyYvBCxfEX4z3qE4/ww7Qck/rmce5gbHMzQjArEAzAP2YSfYIFuIFuRXtfg== @@ -25,24 +20,12 @@ "@openzeppelin/contracts-upgradeable" "4.5.2" patch-package "^6.4.7" -"@arbitrum/nitro-contracts@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@arbitrum/nitro-contracts/-/nitro-contracts-1.1.0.tgz#37ca26e026306ca9cc9fbeb183841fd8666ac7bb" - integrity sha512-/tLlU++IFdaD9Bn+RYzQ6+6k+0iDPuqi/cNf9kv5N1I9NAApNx1qfsIHoHMEQAvLuY+gj+raH7TAESBbzTAuuw== - dependencies: - "@offchainlabs/upgrade-executor" "1.1.0-beta.0" - "@openzeppelin/contracts" "4.5.0" - "@openzeppelin/contracts-upgradeable" "4.5.2" - patch-package "^6.4.7" - optionalDependencies: - sol2uml "2.2.0" - -"@arbitrum/token-bridge-contracts@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@arbitrum/token-bridge-contracts/-/token-bridge-contracts-1.2.0.tgz#b1dc02e123393848d0d8e5c167028bafa0ac8229" - integrity sha512-GdPn6nIlhkuacgvXZkmYLmNdmOcxVoHEfkEHtNlaBnMqS+HPXEA8keqZDesBHkQ96b9PCwwCegNEx/ZjM+Rq+g== +"@arbitrum/token-bridge-contracts@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@arbitrum/token-bridge-contracts/-/token-bridge-contracts-1.2.1.tgz#4838d70182bc0d6b36adfd733d7b4650e596c979" + integrity sha512-ngKeay/0O91QyVLCbcd2pyJIWtzKi2KsPm+O9q3xCfPkjuCZ0+MK5OdhYKCbvGc0jf39R7izRsvmUHE0qA603A== dependencies: - "@arbitrum/nitro-contracts" "^1.0.0-beta.8" + "@arbitrum/nitro-contracts" "1.1.1" "@offchainlabs/upgrade-executor" "1.1.0-beta.0" "@openzeppelin/contracts" "4.8.3" "@openzeppelin/contracts-upgradeable" "4.8.3" @@ -1459,13 +1442,6 @@ dependencies: antlr4ts "^0.5.0-alpha.4" -"@solidity-parser/parser@^0.14.3": - version "0.14.5" - resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.5.tgz#87bc3cc7b068e08195c219c91cd8ddff5ef1a804" - integrity sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg== - dependencies: - antlr4ts "^0.5.0-alpha.4" - "@tsconfig/node10@^1.0.7": version "1.0.8" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" @@ -1581,13 +1557,6 @@ dependencies: "@types/yargs-parser" "*" -"@types/yauzl@^2.9.1": - version "2.10.0" - resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.0.tgz#b3248295276cf8c6f153ebe6a9aba0c988cb2599" - integrity sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw== - dependencies: - "@types/node" "*" - "@typescript-eslint/eslint-plugin-tslint@^5.27.1": version "5.27.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin-tslint/-/eslint-plugin-tslint-5.27.1.tgz#9d89f72ee9c6f182af07da696f8ccb6803afd7d9" @@ -1995,14 +1964,6 @@ available-typed-arrays@^1.0.5, available-typed-arrays@^1.0.6: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.6.tgz#ac812d8ce5a6b976d738e1c45f08d0b00bc7d725" integrity sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg== -axios@^0.27.2: - version "0.27.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" - integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== - dependencies: - follow-redirects "^1.14.9" - form-data "^4.0.0" - axios@^1.6.5: version "1.6.5" resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.5.tgz#2c090da14aeeab3770ad30c3a1461bc970fb0cd8" @@ -2044,15 +2005,6 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bl@^4.0.3: - version "4.1.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" - integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== - dependencies: - buffer "^5.5.0" - inherits "^2.0.4" - readable-stream "^3.4.0" - blakejs@^1.1.0: version "1.2.1" resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814" @@ -2068,11 +2020,6 @@ bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== -boolbase@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" - integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -2153,11 +2100,6 @@ bs58check@^2.1.2: create-hash "^1.1.0" safe-buffer "^5.1.2" -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== - buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -2168,14 +2110,6 @@ buffer-xor@^1.0.3: resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== -buffer@^5.2.1, buffer@^5.5.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" @@ -2288,31 +2222,6 @@ check-error@^1.0.3: dependencies: get-func-name "^2.0.2" -cheerio-select@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-2.1.0.tgz#4d8673286b8126ca2a8e42740d5e3c4884ae21b4" - integrity sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g== - dependencies: - boolbase "^1.0.0" - css-select "^5.1.0" - css-what "^6.1.0" - domelementtype "^2.3.0" - domhandler "^5.0.3" - domutils "^3.0.1" - -cheerio@^1.0.0-rc.11: - version "1.0.0-rc.12" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.12.tgz#788bf7466506b1c6bf5fae51d24a2c4d62e47683" - integrity sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q== - dependencies: - cheerio-select "^2.1.0" - dom-serializer "^2.0.0" - domhandler "^5.0.3" - domutils "^3.0.1" - htmlparser2 "^8.0.1" - parse5 "^7.0.0" - parse5-htmlparser2-tree-adapter "^7.0.0" - chokidar@3.5.3, chokidar@^3.4.0: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" @@ -2328,11 +2237,6 @@ chokidar@3.5.3, chokidar@^3.4.0: optionalDependencies: fsevents "~2.3.2" -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - ci-info@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" @@ -2446,11 +2350,6 @@ commander@^2.12.1: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@^9.2.0, commander@^9.4.0: - version "9.5.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" - integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== - commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -2476,30 +2375,6 @@ convert-source-map@^2.0.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== -convert-svg-core@^0.6.4: - version "0.6.4" - resolved "https://registry.yarnpkg.com/convert-svg-core/-/convert-svg-core-0.6.4.tgz#a38ad47f32acbb229a4fa9eec9771308c2fe1443" - integrity sha512-8mS0n7otc1lljTte4z7nDhihEakKCRq4w5ivMnIGeOZuD/OV/eDZNNEgGLV1ET3p+rMbnrZnX4lAcsf14WzD5w== - dependencies: - chalk "^4.1.2" - cheerio "^1.0.0-rc.11" - commander "^9.2.0" - file-url "^3.0.0" - get-stdin "^8.0.0" - glob "^8.0.1" - lodash.omit "^4.5.0" - lodash.pick "^4.4.0" - pollock "^0.2.0" - puppeteer "^13.7.0" - tmp "^0.2.1" - -convert-svg-to-png@^0.6.4: - version "0.6.4" - resolved "https://registry.yarnpkg.com/convert-svg-to-png/-/convert-svg-to-png-0.6.4.tgz#de0f5d46042639cdfe4020b492b8b0a3c0743b4e" - integrity sha512-zHNTuVedkyuhMl+f+HMm2L7+TKDYCKFAqAmDqUr0dN7/xtgYe76PPAydjlFzeLbzEpGtEfhaA15q+ejpLaVo3g== - dependencies: - convert-svg-core "^0.6.4" - cookie@^0.4.1: version "0.4.2" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" @@ -2538,13 +2413,6 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-fetch@3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" - integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== - dependencies: - node-fetch "2.6.7" - cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -2565,22 +2433,6 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -css-select@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" - integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg== - dependencies: - boolbase "^1.0.0" - css-what "^6.1.0" - domhandler "^5.0.2" - domutils "^3.0.1" - nth-check "^2.0.1" - -css-what@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" - integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== - debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" @@ -2658,11 +2510,6 @@ depd@2.0.0: resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== -devtools-protocol@0.0.981744: - version "0.0.981744" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.981744.tgz#9960da0370284577d46c28979a0b32651022bacf" - integrity sha512-0cuGS8+jhR67Fy7qG3i3Pc7Aw494sb9yG9QgpG97SFVWwolgYjlhJg7n+UaHxOQT30d1TYu/EYe9k01ivLErIg== - diff@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" @@ -2687,36 +2534,6 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dom-serializer@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" - integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== - dependencies: - domelementtype "^2.3.0" - domhandler "^5.0.2" - entities "^4.2.0" - -domelementtype@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" - integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== - -domhandler@^5.0.1, domhandler@^5.0.2, domhandler@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" - integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== - dependencies: - domelementtype "^2.3.0" - -domutils@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.0.1.tgz#696b3875238338cb186b6c0612bd4901c89a4f1c" - integrity sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q== - dependencies: - dom-serializer "^2.0.0" - domelementtype "^2.3.0" - domhandler "^5.0.1" - dotenv@^10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" @@ -2755,13 +2572,6 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -end-of-stream@^1.1.0, end-of-stream@^1.4.1: - version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - enquirer@^2.3.0: version "2.4.1" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.4.1.tgz#93334b3fbd74fc7097b224ab4a8fb7e40bf4ae56" @@ -2777,11 +2587,6 @@ enquirer@^2.3.5: dependencies: ansi-colors "^4.1.1" -entities@^4.2.0, entities@^4.4.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" - integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== - env-paths@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" @@ -3127,7 +2932,7 @@ ethers@^5.1.0: "@ethersproject/web" "5.6.0" "@ethersproject/wordlists" "5.6.0" -ethers@^5.6.9, ethers@^5.7.1: +ethers@^5.7.1: version "5.7.2" resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== @@ -3192,17 +2997,6 @@ evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" -extract-zip@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" - integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== - dependencies: - debug "^4.1.1" - get-stream "^5.1.0" - yauzl "^2.10.0" - optionalDependencies: - "@types/yauzl" "^2.9.1" - fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -3241,13 +3035,6 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== - dependencies: - pend "~1.2.0" - file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" @@ -3255,11 +3042,6 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" -file-url@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/file-url/-/file-url-3.0.0.tgz#247a586a746ce9f7a8ed05560290968afc262a77" - integrity sha512-g872QGsHexznxkIAdK8UiZRe7SkE6kvylShU4Nsj8NvfvZag7S0QuQ4IgvPDkk75HxgjIVDwycFTDAgIiO4nDA== - fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -3331,7 +3113,7 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== -follow-redirects@^1.12.1, follow-redirects@^1.14.9, follow-redirects@^1.15.4: +follow-redirects@^1.12.1, follow-redirects@^1.15.4: version "1.15.5" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== @@ -3380,11 +3162,6 @@ fromentries@^1.2.0: resolved "https://registry.yarnpkg.com/fromentries/-/fromentries-1.3.2.tgz#e4bca6808816bf8f93b52750f1127f5a6fd86e3a" integrity sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg== -fs-constants@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" - integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== - fs-extra@^0.30.0: version "0.30.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" @@ -3481,18 +3258,6 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== -get-stdin@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53" - integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg== - -get-stream@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" - get-symbol-description@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" @@ -3533,17 +3298,6 @@ glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8.0.1: - version "8.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" - globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -3753,16 +3507,6 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== -htmlparser2@^8.0.1: - version "8.0.2" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.2.tgz#f002151705b383e62433b5cf466f5b716edaec21" - integrity sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA== - dependencies: - domelementtype "^2.3.0" - domhandler "^5.0.3" - domutils "^3.0.1" - entities "^4.4.0" - http-errors@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" @@ -3774,7 +3518,7 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" -https-proxy-agent@5.0.1, https-proxy-agent@^5.0.0: +https-proxy-agent@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== @@ -3789,7 +3533,7 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -ieee754@^1.1.13, ieee754@^1.2.1: +ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -4111,11 +3855,6 @@ jju@^1.4.0: resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" integrity sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA== -js-graph-algorithms@^1.0.18: - version "1.0.18" - resolved "https://registry.yarnpkg.com/js-graph-algorithms/-/js-graph-algorithms-1.0.18.tgz#f96ec87bf194f5c0a31365fa0e1d07b7b962d891" - integrity sha512-Gu1wtWzXBzGeye/j9BuyplGHscwqKRZodp/0M1vyBc19RJpblSwKGu099KwwaTx9cRIV+Qupk8xUMfEiGfFqSA== - js-sdsl@^4.1.4: version "4.4.2" resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.2.tgz#2e3c031b1f47d3aca8b775532e3ebb0818e7f847" @@ -4227,11 +3966,6 @@ klaw@^1.0.0: optionalDependencies: graceful-fs "^4.1.9" -klaw@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/klaw/-/klaw-4.1.0.tgz#5df608067d8cb62bbfb24374f8e5d956323338f3" - integrity sha512-1zGZ9MF9H22UnkpVeuaGKOjfA2t6WrfdrJmGjy16ykcjnKQDmHVX+KI477rpbGevz/5FD4MC3xf1oxylBgcaQw== - kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" @@ -4303,12 +4037,7 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.omit@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.omit/-/lodash.omit-4.5.0.tgz#6eb19ae5a1ee1dd9df0b969e66ce0b7fa30b5e60" - integrity sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg== - -lodash.pick@^4.4.0, "lodash.pick@https://github.com/lodash/lodash/archive/refs/tags/4.17.21.tar.gz": +"lodash.pick@https://github.com/lodash/lodash/archive/refs/tags/4.17.21.tar.gz": version "4.17.21" resolved "https://github.com/lodash/lodash/archive/refs/tags/4.17.21.tar.gz#af60acc8255a4eb9a7c698a4de55b6ec6993edc2" @@ -4460,7 +4189,7 @@ minimatch@4.2.1: dependencies: brace-expansion "^1.1.7" -minimatch@5.0.1, minimatch@^5.0.1: +minimatch@5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== @@ -4491,11 +4220,6 @@ minimist@^1.2.7: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -mkdirp-classic@^0.5.2: - version "0.5.3" - resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" - integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== - mkdirp@^0.5.3: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" @@ -4622,13 +4346,6 @@ node-addon-api@^2.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== -node-fetch@2.6.7: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: version "4.6.1" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.1.tgz#24b6d075e5e391b8d5539d98c7fc5c210cac8a3e" @@ -4656,13 +4373,6 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -nth-check@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" - integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== - dependencies: - boolbase "^1.0.0" - nyc@^15.1.0: version "15.1.0" resolved "https://registry.yarnpkg.com/nyc/-/nyc-15.1.0.tgz#1335dae12ddc87b6e249d5a1994ca4bdaea75f02" @@ -4721,7 +4431,7 @@ obliterator@^2.0.0: resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.4.tgz#fa650e019b2d075d745e44f1effeb13a2adbe816" integrity sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ== -once@^1.3.0, once@^1.3.1, once@^1.4.0: +once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= @@ -4836,21 +4546,6 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse5-htmlparser2-tree-adapter@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz#23c2cc233bcf09bb7beba8b8a69d46b08c62c2f1" - integrity sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g== - dependencies: - domhandler "^5.0.2" - parse5 "^7.0.0" - -parse5@^7.0.0: - version "7.1.2" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" - integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== - dependencies: - entities "^4.4.0" - patch-package@^6.4.7: version "6.5.1" resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-6.5.1.tgz#3e5d00c16997e6160291fee06a521c42ac99b621" @@ -4929,11 +4624,6 @@ pbkdf2@^3.0.17: safe-buffer "^5.0.1" sha.js "^2.4.8" -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== - picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -4944,18 +4634,13 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pkg-dir@4.2.0, pkg-dir@^4.1.0: +pkg-dir@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== dependencies: find-up "^4.0.0" -pollock@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/pollock/-/pollock-0.2.1.tgz#01273ae3542511492d07f1c10fa53f149b37c6ad" - integrity sha512-2Xy6LImSXm0ANKv9BKSVuCa6Z4ACbK7oUrl9gtUgqLkekL7n9C0mlWsOGYYuGbCG8xT0x3Q4F31C3ZMyVQjwsg== - prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -4992,7 +4677,7 @@ process-on-spawn@^1.0.0: dependencies: fromentries "^1.2.0" -progress@2.0.3, progress@^2.0.0: +progress@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== @@ -5014,42 +4699,16 @@ proper-lockfile@^4.1.1: retry "^0.12.0" signal-exit "^3.0.2" -proxy-from-env@1.1.0, proxy-from-env@^1.1.0: +proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - punycode@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -puppeteer@^13.7.0: - version "13.7.0" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-13.7.0.tgz#18e16f83e397cf02f7a0804c67c1603d381cfb0b" - integrity sha512-U1uufzBjz3+PkpCxFrWzh4OrMIdIb2ztzCu0YEPfRHjHswcSwHZswnK+WdsOQJsRV8WeTg3jLhJR4D867+fjsA== - dependencies: - cross-fetch "3.1.5" - debug "4.3.4" - devtools-protocol "0.0.981744" - extract-zip "2.0.1" - https-proxy-agent "5.0.1" - pkg-dir "4.2.0" - progress "2.0.3" - proxy-from-env "1.1.0" - rimraf "3.0.2" - tar-fs "2.1.1" - unbzip2-stream "1.4.3" - ws "8.5.0" - queue-microtask@^1.2.2, queue-microtask@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -5077,7 +4736,7 @@ raw-body@^2.4.1: iconv-lite "0.4.24" unpipe "1.0.0" -readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: +readable-stream@^3.6.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -5175,13 +4834,6 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - rimraf@^2.2.8, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" @@ -5189,6 +4841,13 @@ rimraf@^2.2.8, rimraf@^2.6.3: dependencies: glob "^7.1.3" +rimraf@^3.0.0, rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" @@ -5412,21 +5071,6 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" -sol2uml@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/sol2uml/-/sol2uml-2.2.0.tgz#145b1b85cc2c5d466d596f3426aae4dd4dc946f2" - integrity sha512-JMBvn3ZMT/1egoZjheM4Mh9gQudrlVjFZ1VS0gjQ/eluITT08U6V438Jyku28OuXz42aXNbGS80JuRZo0J7pLg== - dependencies: - "@aduh95/viz.js" "^3.7.0" - "@solidity-parser/parser" "^0.14.3" - axios "^0.27.2" - commander "^9.4.0" - convert-svg-to-png "^0.6.4" - debug "^4.3.4" - ethers "^5.6.9" - js-graph-algorithms "^1.0.18" - klaw "^4.0.1" - solc@0.7.3: version "0.7.3" resolved "https://registry.yarnpkg.com/solc/-/solc-0.7.3.tgz#04646961bd867a744f63d2b4e3c0701ffdc7d78a" @@ -5630,27 +5274,6 @@ table@^6.0.9: string-width "^4.2.3" strip-ansi "^6.0.1" -tar-fs@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" - integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== - dependencies: - chownr "^1.1.1" - mkdirp-classic "^0.5.2" - pump "^3.0.0" - tar-stream "^2.1.4" - -tar-stream@^2.1.4: - version "2.2.0" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" - integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== - dependencies: - bl "^4.0.3" - end-of-stream "^1.4.1" - fs-constants "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.1.1" - test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" @@ -5677,13 +5300,6 @@ tmp@0.0.33, tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" -tmp@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" - integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== - dependencies: - rimraf "^3.0.0" - to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" @@ -5701,11 +5317,6 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== - ts-command-line-args@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/ts-command-line-args/-/ts-command-line-args-2.2.1.tgz#fd6913e542099012c0ffb2496126a8f38305c7d6" @@ -5946,14 +5557,6 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" -unbzip2-stream@1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" - integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== - dependencies: - buffer "^5.2.1" - through "^2.3.8" - undici-types@~5.26.4: version "5.26.5" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" @@ -6026,19 +5629,6 @@ vscode-textmate@^8.0.0: resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-8.0.0.tgz#2c7a3b1163ef0441097e0b5d6389cd5504b59e5d" integrity sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg== -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" @@ -6146,11 +5736,6 @@ ws@7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== -ws@8.5.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" - integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== - ws@^7.4.6: version "7.5.9" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" @@ -6270,14 +5855,6 @@ yargs@^17.3.1: y18n "^5.0.5" yargs-parser "^21.0.0" -yauzl@^2.10.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" - yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" From 836bdb4223c953b52c6a63ec01d435083717a470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dragi=C5=A1a=20Spasojevi=C4=87?= Date: Wed, 14 Feb 2024 11:24:52 +0100 Subject: [PATCH 192/192] update values for L3 --- src/lib/assetBridger/erc20Bridger.ts | 14 ++++++++------ src/lib/assetBridger/ethBridger.ts | 15 +++++++++++---- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/lib/assetBridger/erc20Bridger.ts b/src/lib/assetBridger/erc20Bridger.ts index 283884a5a2..af036892fa 100644 --- a/src/lib/assetBridger/erc20Bridger.ts +++ b/src/lib/assetBridger/erc20Bridger.ts @@ -71,6 +71,7 @@ import { OmitTyped, RequiredPick } from '../utils/types' import { RetryableDataTools } from '../dataEntities/retryableData' import { EventArgs } from '../dataEntities/event' import { L1ToL2MessageGasParams } from '../message/L1ToL2MessageCreator' +import { isArbitrumChain } from '../utils/lib' export interface TokenApproveParams { /** @@ -784,13 +785,14 @@ export class Erc20Bridger extends AssetBridger< value: BigNumber.from(0), from: params.from, }, - // we make this async and expect a provider since we - // in the future we want to do proper estimation here - /* eslint-disable @typescript-eslint/no-unused-vars */ + // todo: do proper estimation estimateL1GasLimit: async (l1Provider: Provider) => { - if (!this.nativeTokenIsEth) { - // measured 172867 - add some padding - return BigNumber.from(200000) + if (await isArbitrumChain(l1Provider)) { + // values for L3 are dependent on the L1 base fee, so hardcoding can never be accurate + // however, this is only an estimate used for display, so should be good enough + // + // measured with token withdrawals from Rari then added some padding + return BigNumber.from(8_000_000) } const l1GatewayAddress = await this.getL1GatewayAddress( diff --git a/src/lib/assetBridger/ethBridger.ts b/src/lib/assetBridger/ethBridger.ts index cde2cae407..66943079c4 100644 --- a/src/lib/assetBridger/ethBridger.ts +++ b/src/lib/assetBridger/ethBridger.ts @@ -48,6 +48,7 @@ import { SignerProviderUtils } from '../dataEntities/signerOrProvider' import { MissingProviderArbSdkError } from '../dataEntities/errors' import { getL2Network } from '../dataEntities/networks' import { ERC20__factory } from '../abi/factories/ERC20__factory' +import { isArbitrumChain } from '../utils/lib' export type ApproveGasTokenParams = { /** @@ -379,11 +380,17 @@ export class EthBridger extends AssetBridger< value: params.amount, from: params.from, }, - // we make this async and expect a provider since we - // in the future we want to do proper estimation here - /* eslint-disable @typescript-eslint/no-unused-vars */ + // todo: do proper estimation estimateL1GasLimit: async (l1Provider: Provider) => { - // measured 126998 - add some padding + if (await isArbitrumChain(l1Provider)) { + // values for L3 are dependent on the L1 base fee, so hardcoding can never be accurate + // however, this is only an estimate used for display, so should be good enough + // + // measured with withdrawals from Xai and Rari then added some padding + return BigNumber.from(4_000_000) + } + + // measured 126998 - add some padding return BigNumber.from(130000) }, }