diff --git a/package.json b/package.json index 1efb710caa..b933a2e06d 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,8 @@ "@ethersproject/bignumber": "^5.1.1", "@ethersproject/bytes": "^5.0.8", "async-mutex": "^0.4.0", - "ethers": "^5.1.0" + "ethers": "^5.1.0", + "ethers-v6": "npm:ethers@^6.11.1" }, "devDependencies": { "@arbitrum/nitro-contracts": "^1.1.1", diff --git a/scripts/deployStandard.ts b/scripts/deployStandard.ts index fd66b2b680..78133b305e 100644 --- a/scripts/deployStandard.ts +++ b/scripts/deployStandard.ts @@ -5,6 +5,7 @@ import { constants, BigNumber, utils } from 'ethers' import { MultiCaller } from '../src' import axios from 'axios' import prompt from 'prompts' +import { ZeroAddress } from 'ethers-v6' dotenv.config() const privKey = process.env.PRIVKEY as string @@ -106,7 +107,7 @@ const main = async () => { l1TokenAddress, l1Provider ) - if (l2TokenAddress === constants.AddressZero) { + if (l2TokenAddress === ZeroAddress) { throw new Error(`${l1TokenAddress} can't be bridged`) } diff --git a/src/lib/assetBridger/assetBridger.ts b/src/lib/assetBridger/assetBridger.ts index 75c2962611..b803e3dc31 100644 --- a/src/lib/assetBridger/assetBridger.ts +++ b/src/lib/assetBridger/assetBridger.ts @@ -16,8 +16,7 @@ /* eslint-env node */ 'use strict' -import { constants } from 'ethers' - +import { ZeroAddress } from 'ethers-v6' import { ParentContractTransaction } from '../message/ParentTransaction' import { ChildContractTransaction } from '../message/ChildTransaction' @@ -66,7 +65,7 @@ export abstract class AssetBridger { * @returns {boolean} */ protected get nativeTokenIsEth() { - return !this.nativeToken || this.nativeToken === constants.AddressZero + return !this.nativeToken || this.nativeToken === ZeroAddress } /** diff --git a/src/lib/inbox/inbox.ts b/src/lib/inbox/inbox.ts index ca2967f59b..dcf46148bb 100644 --- a/src/lib/inbox/inbox.ts +++ b/src/lib/inbox/inbox.ts @@ -18,7 +18,8 @@ import { Signer } from '@ethersproject/abstract-signer' import { Block, Provider } from '@ethersproject/abstract-provider' -import { BigNumber, ContractTransaction, ethers, Overrides } from 'ethers' +import { BigNumber, ContractTransaction, Overrides } from 'ethers' +import { ZeroAddress, hexlify, solidityPacked, toBeArray } from 'ethers-v6' import { TransactionRequest } from '@ethersproject/providers' import { Bridge } from '../abi/Bridge' @@ -107,7 +108,7 @@ export class InboxTools { if ( childChainTransactionRequest.to === '0x' || !isDefined(childChainTransactionRequest.to) || - childChainTransactionRequest.to === ethers.constants.AddressZero + childChainTransactionRequest.to === ZeroAddress ) { return true } @@ -132,7 +133,7 @@ export class InboxTools { childChainTransactionRequest ) const gasComponents = await nodeInterface.callStatic.gasEstimateComponents( - childChainTransactionRequest.to || ethers.constants.AddressZero, + childChainTransactionRequest.to || ZeroAddress, contractCreation, childChainTransactionRequest.data, { @@ -358,9 +359,9 @@ export class InboxTools { this.parentChainSigner ) - const sendData = ethers.utils.solidityPack( + const sendData = solidityPacked( ['uint8', 'bytes'], - [ethers.utils.hexlify(InboxMessageKind.L2MessageType_signedTx), signedTx] + [hexlify(toBeArray(InboxMessageKind.L2MessageType_signedTx)), signedTx] ) return await delayedInbox.functions.sendL2Message(sendData) @@ -410,7 +411,7 @@ export class InboxTools { // however, it is needed when we call to estimateArbitrumGas, so // we add a zero address here. if (!isDefined(tx.to)) { - tx.to = ethers.constants.AddressZero + tx.to = ZeroAddress } //estimate gas on child chain diff --git a/src/lib/message/ParentToChildMessage.ts b/src/lib/message/ParentToChildMessage.ts index dd9454b13f..1a238d3bc1 100644 --- a/src/lib/message/ParentToChildMessage.ts +++ b/src/lib/message/ParentToChildMessage.ts @@ -21,9 +21,10 @@ import { Provider } from '@ethersproject/abstract-provider' import { Signer } from '@ethersproject/abstract-signer' import { ContractTransaction } from '@ethersproject/contracts' import { BigNumber } from '@ethersproject/bignumber' -import { concat, zeroPad } from '@ethersproject/bytes' +import { concat } from '@ethersproject/bytes' import { getAddress } from '@ethersproject/address' import { keccak256 } from '@ethersproject/keccak256' +import { ZeroAddress, toBeArray, encodeRlp, zeroPadValue } from 'ethers-v6' import { ArbRetryableTx__factory } from '../abi/factories/ArbRetryableTx__factory' import { @@ -36,7 +37,7 @@ import { SignerOrProvider, } from '../dataEntities/signerOrProvider' import { ArbSdkError } from '../dataEntities/errors' -import { ethers, Overrides } from 'ethers' +import { Overrides } from 'ethers' import { ChildTransactionReceipt, RedeemTransaction } from './ChildTransaction' import { getArbitrumNetwork } from '../../lib/dataEntities/networks' import { RetryableMessageParams } from '../dataEntities/message' @@ -140,7 +141,7 @@ export abstract class ParentToChildMessage { data: string ): string { const formatNumber = (value: BigNumber): Uint8Array => { - return ethers.utils.stripZeros(value.toHexString()) + return toBeArray(value.toHexString()) } const chainId = BigNumber.from(chainChainId) @@ -148,7 +149,7 @@ export abstract class ParentToChildMessage { const fields: any[] = [ formatNumber(chainId), - zeroPad(formatNumber(msgNum), 32), + zeroPadValue(formatNumber(msgNum), 32), fromAddress, formatNumber(parentChainBaseFee), @@ -156,7 +157,7 @@ export abstract class ParentToChildMessage { formatNumber(maxFeePerGas), formatNumber(gasLimit), // when destAddress is 0x0, arbos treat that as nil - destAddress === ethers.constants.AddressZero ? '0x' : destAddress, + destAddress === ZeroAddress ? '0x' : destAddress, formatNumber(chainCallValue), callValueRefundAddress, formatNumber(maxSubmissionFee), @@ -165,12 +166,10 @@ export abstract class ParentToChildMessage { ] // arbitrum submit retry transactions have type 0x69 - const rlpEnc = ethers.utils.hexConcat([ - '0x69', - ethers.utils.RLP.encode(fields), - ]) + const rlpEnc = concat(['0x69', encodeRlp(fields)]) - return ethers.utils.keccak256(rlpEnc) + const hash = keccak256(rlpEnc) + return hash } public static fromEventComponents( @@ -571,22 +570,22 @@ export class ParentToChildMessageReaderClassic { this.retryableCreationId = keccak256( concat([ - zeroPad(BigNumber.from(chainId).toHexString(), 32), - zeroPad(bitFlip(this.messageNumber).toHexString(), 32), + zeroPadValue(BigNumber.from(chainId).toHexString(), 32), + zeroPadValue(bitFlip(this.messageNumber).toHexString(), 32), ]) ) this.autoRedeemId = keccak256( concat([ - zeroPad(this.retryableCreationId, 32), - zeroPad(BigNumber.from(1).toHexString(), 32), + zeroPadValue(this.retryableCreationId, 32), + zeroPadValue(BigNumber.from(1).toHexString(), 32), ]) ) this.chainTxHash = keccak256( concat([ - zeroPad(this.retryableCreationId, 32), - zeroPad(BigNumber.from(0).toHexString(), 32), + zeroPadValue(this.retryableCreationId, 32), + zeroPadValue(BigNumber.from(0).toHexString(), 32), ]) ) } @@ -594,9 +593,9 @@ export class ParentToChildMessageReaderClassic { private calculateChainDerivedHash(retryableCreationId: string): string { return keccak256( concat([ - zeroPad(retryableCreationId, 32), + zeroPadValue(retryableCreationId, 32), // BN 0 meaning Chain TX - zeroPad(BigNumber.from(0).toHexString(), 32), + zeroPadValue(BigNumber.from(0).toHexString(), 32), ]) ) } @@ -769,7 +768,7 @@ export class EthDepositMessage { value: BigNumber ): string { const formatNumber = (numberVal: BigNumber): Uint8Array => { - return ethers.utils.stripZeros(numberVal.toHexString()) + return toBeArray(numberVal.toHexString()) } const chainId = BigNumber.from(chainChainId) @@ -778,19 +777,16 @@ export class EthDepositMessage { // https://github.com/OffchainLabs/go-ethereum/blob/07e017aa73e32be92aadb52fa327c552e1b7b118/core/types/arb_types.go#L302-L308 const fields = [ formatNumber(chainId), - zeroPad(formatNumber(msgNum), 32), + zeroPadValue(formatNumber(msgNum), 32), getAddress(fromAddress), getAddress(toAddress), formatNumber(value), ] // arbitrum eth deposit transactions have type 0x64 - const rlpEnc = ethers.utils.hexConcat([ - '0x64', - ethers.utils.RLP.encode(fields), - ]) + const rlpEnc = concat(['0x64', encodeRlp(fields)]) - return ethers.utils.keccak256(rlpEnc) + return keccak256(rlpEnc) } /** diff --git a/src/lib/utils/eventFetcher.ts b/src/lib/utils/eventFetcher.ts index 67fda1bee2..b6cc14b4a4 100644 --- a/src/lib/utils/eventFetcher.ts +++ b/src/lib/utils/eventFetcher.ts @@ -18,7 +18,7 @@ import { Provider, BlockTag, Filter } from '@ethersproject/abstract-provider' import { Contract, Event } from '@ethersproject/contracts' -import { constants } from 'ethers' +import { ZeroAddress } from 'ethers-v6' import { TypedEvent, TypedEventFilter } from '../abi/common' import { EventArgs, TypeChainContractFactory } from '../dataEntities/event' @@ -67,7 +67,7 @@ export class EventFetcher { } ): Promise>[]> { const contract = contractFactory.connect( - filter.address || constants.AddressZero, + filter.address || ZeroAddress, this.provider ) const eventFilter = topicGenerator(contract) diff --git a/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts b/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts index 4ce32939ad..cc53fa7530 100644 --- a/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts +++ b/tests/integration/custom-fee-token/customFeeTokenEthBridger.test.ts @@ -17,10 +17,10 @@ 'use strict' import { expect } from 'chai' -import { ethers, constants, Wallet } from 'ethers' +import { constants, Wallet } from 'ethers' import dotenv from 'dotenv' -import { parseEther } from '@ethersproject/units' +import { parseEther } from 'ethers-v6' import { fundParentSigner as fundParentSignerEther, @@ -52,7 +52,7 @@ describeOnlyWhenCustomGasToken( 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, parentSigner } = await testSetup() - const amount = ethers.utils.parseEther('1') + const amount = parseEther('1') await fundParentSignerEther(parentSigner) await fundParentCustomFeeToken(parentSigner) diff --git a/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts b/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts index 89abe126fb..ae8e816120 100644 --- a/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts +++ b/tests/integration/custom-fee-token/customFeeTokenTestHelpers.ts @@ -1,5 +1,6 @@ import { StaticJsonRpcProvider } from '@ethersproject/providers' -import { Signer, Wallet, ethers, utils } from 'ethers' +import { Signer, Wallet, utils } from 'ethers' +import { ZeroAddress } from 'ethers-v6' import { testSetup as _testSetup, @@ -16,7 +17,7 @@ const localNetworks = () => getLocalNetworksFromFile() export function isArbitrumNetworkWithCustomFeeToken(): boolean { const nt = localNetworks().l2Network.nativeToken - return typeof nt !== 'undefined' && nt !== ethers.constants.AddressZero + return typeof nt !== 'undefined' && nt !== ZeroAddress } export async function testSetup() { diff --git a/tests/integration/customerc20.test.ts b/tests/integration/customerc20.test.ts index eb6b024b7e..a7a0fd0625 100644 --- a/tests/integration/customerc20.test.ts +++ b/tests/integration/customerc20.test.ts @@ -17,7 +17,8 @@ 'use strict' import { expect } from 'chai' -import { Signer, Wallet, constants, utils, ethers } from 'ethers' +import { Signer, Wallet, utils } from 'ethers' +import { ZeroAddress, parseEther } from 'ethers-v6' import { BigNumber } from '@ethersproject/bignumber' import { Logger, LogLevel } from '@ethersproject/logger' Logger.setLogLevel(LogLevel.ERROR) @@ -163,7 +164,7 @@ const registerCustomToken = async ( childChain.tokenBridge.l1GatewayRouter ) await l1CustomToken.deployed() - const amount = ethers.utils.parseEther('1') + const amount = parseEther('1') if (isArbitrumNetworkWithCustomFeeToken()) { const approvalTx = await ERC20__factory.connect( @@ -199,28 +200,28 @@ const registerCustomToken = async ( expect( startL1GatewayAddress, 'Start l1GatewayAddress not equal empty address' - ).to.eq(constants.AddressZero) + ).to.eq(ZeroAddress) const startL2GatewayAddress = await l2GatewayRouter.l1TokenToGateway( l2CustomToken.address ) expect( startL2GatewayAddress, 'Start l2GatewayAddress not equal empty address' - ).to.eq(constants.AddressZero) + ).to.eq(ZeroAddress) const startL1Erc20Address = await l1CustomGateway.l1ToL2Token( l1CustomToken.address ) expect( startL1Erc20Address, 'Start l1Erc20Address not equal empty address' - ).to.eq(constants.AddressZero) + ).to.eq(ZeroAddress) const startL2Erc20Address = await l2CustomGateway.l1ToL2Token( l1CustomToken.address ) expect( startL2Erc20Address, 'Start l2Erc20Address not equal empty address' - ).to.eq(constants.AddressZero) + ).to.eq(ZeroAddress) // send the messages const regTx = await adminErc20Bridger.registerCustomToken( diff --git a/tests/integration/sendChildmsg.test.ts b/tests/integration/sendChildmsg.test.ts index 1f1d87bbd0..bd6e60aef7 100644 --- a/tests/integration/sendChildmsg.test.ts +++ b/tests/integration/sendChildmsg.test.ts @@ -18,6 +18,7 @@ ;('use strict') import { BigNumber, ethers, Signer } from 'ethers' +import { Transaction } from 'ethers-v6' import { InboxTools } from '../../src/lib/inbox/inbox' import { getArbitrumNetwork, @@ -75,7 +76,7 @@ describe('Send signedTx to child chain using inbox', async () => { ) const parentStatus = parentTransactionReceipt?.status expect(parentStatus).to.equal(1, 'parent txn failed') - const childTx = ethers.utils.parseTransaction(signedMsg) + const childTx = Transaction.from(signedMsg) const childTxhash = childTx.hash! const childTxReceipt = await childDeployer.provider!.waitForTransaction( childTxhash @@ -101,7 +102,7 @@ describe('Send signedTx to child chain using inbox', async () => { await sendSignedTx(testState, info) const parentStatus = parentTransactionReceipt?.status expect(parentStatus).to.equal(1) - const childTxhash = ethers.utils.parseTransaction(signedMsg).hash! + const childTxhash = Transaction.from(signedMsg).hash! const childTxReceipt = await childDeployer.provider!.waitForTransaction( childTxhash ) @@ -131,11 +132,8 @@ describe('Send signedTx to child chain using inbox', async () => { const enoughFeeTx = await sendSignedTx(testState, info) const enoughFeeParentStatus = enoughFeeTx.parentTransactionReceipt?.status expect(enoughFeeParentStatus).to.equal(1) - const childLowFeeTxhash = ethers.utils.parseTransaction(lowFeeTx.signedMsg) - .hash! - const childEnoughFeeTxhash = ethers.utils.parseTransaction( - enoughFeeTx.signedMsg - ).hash! + const childLowFeeTxhash = Transaction.from(lowFeeTx.signedMsg).hash! + const childEnoughFeeTxhash = Transaction.from(enoughFeeTx.signedMsg).hash! const childTEnoughFeeReceipt = await childDeployer.provider!.waitForTransaction(childEnoughFeeTxhash) diff --git a/yarn.lock b/yarn.lock index a73034c372..4708191674 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@adraffy/ens-normalize@1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz#63430d04bd8c5e74f8d7d049338f1cd9d4f02069" + integrity sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw== + "@ampproject/remapping@^2.2.0": version "2.2.1" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" @@ -1057,11 +1062,23 @@ tweetnacl "^1.0.3" tweetnacl-util "^0.15.1" +"@noble/curves@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" + integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== + dependencies: + "@noble/hashes" "1.3.2" + "@noble/hashes@1.2.0", "@noble/hashes@~1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ== +"@noble/hashes@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== + "@noble/secp256k1@1.7.1", "@noble/secp256k1@~1.7.0": version "1.7.1" resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" @@ -1511,6 +1528,11 @@ dependencies: undici-types "~5.26.4" +"@types/node@18.15.13": + version "18.15.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" + integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== + "@types/pbkdf2@^3.0.0": version "3.1.1" resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.1.tgz#c290c1f0d3dc364af94c2c5ee92046a13b7f89fd" @@ -1752,6 +1774,11 @@ aes-js@3.0.0: resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" integrity sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0= +aes-js@4.0.0-beta.5: + version "4.0.0-beta.5" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-4.0.0-beta.5.tgz#8d2452c52adedebc3a3e28465d858c11ca315873" + integrity sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q== + agent-base@6: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -2896,6 +2923,19 @@ ethereumjs-util@^7.0.3: ethereum-cryptography "^0.1.3" rlp "^2.2.4" +"ethers-v6@npm:ethers@^6.11.1": + version "6.11.1" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.11.1.tgz#96aae00b627c2e35f9b0a4d65c7ab658259ee6af" + integrity sha512-mxTAE6wqJQAbp5QAe/+o+rXOID7Nw91OZXvgpjDa1r4fAbq2Nu314oEZSbjoRLacuCzs7kUC3clEvkCQowffGg== + dependencies: + "@adraffy/ens-normalize" "1.10.1" + "@noble/curves" "1.2.0" + "@noble/hashes" "1.3.2" + "@types/node" "18.15.13" + aes-js "4.0.0-beta.5" + tslib "2.4.0" + ws "8.5.0" + ethers@^5.1.0: version "5.6.4" resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.6.4.tgz#23629e9a7d4bc5802dfb53d4da420d738744b53c" @@ -5358,6 +5398,11 @@ ts-node@^10.2.1: v8-compile-cache-lib "^3.0.0" yn "3.1.1" +tslib@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + tslib@^1.13.0, tslib@^1.8.1, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" @@ -5736,6 +5781,11 @@ 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"