From e46b8cdfd6f467c4d41ca47680279378045a4cfe Mon Sep 17 00:00:00 2001 From: Neo Date: Wed, 3 Jan 2024 13:45:38 +0100 Subject: [PATCH 01/11] L2EthToken tests --- system-contracts/test/L2EthToken.spec.ts | 250 +++++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 system-contracts/test/L2EthToken.spec.ts diff --git a/system-contracts/test/L2EthToken.spec.ts b/system-contracts/test/L2EthToken.spec.ts new file mode 100644 index 000000000..cef8dcd7e --- /dev/null +++ b/system-contracts/test/L2EthToken.spec.ts @@ -0,0 +1,250 @@ +import { expect } from "chai"; +import { ethers } from "hardhat"; +import type { Wallet } from "zksync-web3"; +import type { L2EthToken } from "../typechain"; +import { IMailboxFactory } from "../typechain/IMailboxFactory"; +import { L2EthTokenFactory } from "../typechain"; +import { deployContractOnAddress, getWallets, provider } from "./shared/utils"; +import type { BigNumber } from "ethers"; +import { TEST_BOOTLOADER_FORMAL_ADDRESS, TEST_ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS } from "./shared/constants"; +import { prepareEnvironment, setResult } from "./shared/mocks"; +import { randomBytes } from "crypto"; + +describe("L2EthToken tests", () => { + const richWallet = getWallets()[0]; + let wallets: Array; + let l2EthToken: L2EthToken; + let bootloaderAccount: ethers.Signer; + + before(async () => { + await prepareEnvironment(); + await deployContractOnAddress(TEST_ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS, "L2EthToken"); + l2EthToken = L2EthTokenFactory.connect(TEST_ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS, richWallet); + bootloaderAccount = await ethers.getImpersonatedSigner(TEST_BOOTLOADER_FORMAL_ADDRESS); + }); + + beforeEach(async () => { + wallets = Array.from({ length: 2 }, () => ethers.Wallet.createRandom().connect(provider)); + }); + + describe("mint", () => { + it("called by bootlader", async () => { + const initialSupply: BigNumber = await l2EthToken.totalSupply(); + const initialBalanceOfWallet: BigNumber = await l2EthToken.balanceOf(wallets[0].address); + const amountToMint: BigNumber = ethers.utils.parseEther("10.0"); + + await expect(l2EthToken.connect(bootloaderAccount).mint(wallets[0].address, amountToMint)) + .to.emit(l2EthToken, "Mint") + .withArgs(wallets[0].address, amountToMint); + + const finalSupply: BigNumber = await l2EthToken.totalSupply(); + const balanceOfWallet: BigNumber = await l2EthToken.balanceOf(wallets[0].address); + expect(finalSupply).to.equal(initialSupply.add(amountToMint)); + expect(balanceOfWallet).to.equal(initialBalanceOfWallet.add(amountToMint)); + }); + + it("not called by bootloader", async () => { + const amountToMint: BigNumber = ethers.utils.parseEther("10.0"); + await expect(l2EthToken.connect(wallets[0]).mint(wallets[0].address, amountToMint)).to.be.rejectedWith( + "Callable only by the bootloader" + ); + }); + }); + + describe("transfer", () => { + it("transfer successfully", async () => { + await l2EthToken.connect(bootloaderAccount).mint(wallets[0].address, ethers.utils.parseEther("100.0")); + + const senderBalandeBeforeTransfer: BigNumber = await l2EthToken.balanceOf(wallets[0].address); + const recipientBalanceBeforeTransfer: BigNumber = await l2EthToken.balanceOf(wallets[1].address); + + const amountToTransfer = ethers.utils.parseEther("10.0"); + + await expect( + l2EthToken.connect(bootloaderAccount).transferFromTo(wallets[0].address, wallets[1].address, amountToTransfer) + ) + .to.emit(l2EthToken, "Transfer") + .withArgs(wallets[0].address, wallets[1].address, amountToTransfer); + + const senderBalanceAfterTransfer: BigNumber = await l2EthToken.balanceOf(wallets[0].address); + const recipientBalanceAfterTransfer: BigNumber = await l2EthToken.balanceOf(wallets[1].address); + expect(senderBalanceAfterTransfer).to.be.eq(senderBalandeBeforeTransfer.sub(amountToTransfer)); + expect(recipientBalanceAfterTransfer).to.be.eq(recipientBalanceBeforeTransfer.add(amountToTransfer)); + }); + + it("no tranfser due to insufficient balance", async () => { + await l2EthToken.connect(bootloaderAccount).mint(wallets[0].address, ethers.utils.parseEther("5.0")); + const amountToTransfer: BigNumber = ethers.utils.parseEther("6.0"); + + await expect( + l2EthToken.connect(bootloaderAccount).transferFromTo(wallets[0].address, wallets[1].address, amountToTransfer) + ).to.be.rejectedWith("Transfer amount exceeds balance"); + }); + + it("no transfer - require special access", async () => { + const maliciousWallet: Wallet = ethers.Wallet.createRandom().connect(provider); + await l2EthToken.connect(bootloaderAccount).mint(maliciousWallet.address, ethers.utils.parseEther("20.0")); + + const amountToTransfer: BigNumber = ethers.utils.parseEther("20.0"); + + await expect( + l2EthToken + .connect(maliciousWallet) + .transferFromTo(maliciousWallet.address, wallets[1].address, amountToTransfer) + ).to.be.rejectedWith("Only system contracts with special access can call this method"); + }); + }); + + describe("balanceOf", () => { + it("walletFrom address", async () => { + const amountToMint: BigNumber = ethers.utils.parseEther("10.0"); + + await l2EthToken.connect(bootloaderAccount).mint(wallets[0].address, amountToMint); + const balance = await l2EthToken.balanceOf(wallets[0].address); + expect(balance).to.equal(amountToMint); + }); + + it("address larger than 20 bytes", async () => { + const amountToMint: BigNumber = ethers.utils.parseEther("123.0"); + + const res = await l2EthToken.connect(bootloaderAccount).mint(wallets[0].address, amountToMint); + await res.wait(); + const largerAddress = ethers.BigNumber.from( + "0x" + randomBytes(12).toString("hex") + wallets[0].address.slice(2) + ).toHexString(); + const balance = await l2EthToken.balanceOf(largerAddress); + + expect(balance).to.equal(amountToMint); + }); + }); + + describe("totalSupply", () => { + it("correct total supply", async () => { + const totalSupplyBefore = await l2EthToken.totalSupply(); + const amountToMint: BigNumber = ethers.utils.parseEther("10.0"); + + await l2EthToken.connect(bootloaderAccount).mint(wallets[0].address, amountToMint); + const totalSupply = await l2EthToken.totalSupply(); + + expect(totalSupply).to.equal(totalSupplyBefore.add(amountToMint)); + }); + }); + + describe("name", () => { + it("correct name", async () => { + const name = await l2EthToken.name(); + expect(name).to.equal("Ether"); + }); + }); + + describe("symbol", () => { + it("correct symbol", async () => { + const symbol = await l2EthToken.symbol(); + expect(symbol).to.equal("ETH"); + }); + }); + + describe("decimals", () => { + it("correct decimals", async () => { + const decimals = await l2EthToken.decimals(); + expect(decimals).to.equal(18); + }); + }); + + describe("withdraw", () => { + it("big amount to withdraw, underflow contract balance", async () => { + const TWO_TO_256: BigNumber = ethers.BigNumber.from(2).pow(256); + const selector = IMailboxFactory.connect("0x00", provider).interface.getSighash("finalizeEthWithdrawal"); + const amountToWithdraw: BigNumber = ethers.utils.parseEther("300.0"); + + const message: string = ethers.utils.solidityPack( + ["bytes4", "address", "uint256"], + [selector, wallets[1].address, amountToWithdraw] + ); + + await setResult("L1Messenger", "sendToL1", [message], { + failure: false, + returnData: ethers.utils.defaultAbiCoder.encode(["bytes32"], [ethers.utils.keccak256(message)]), + }); + + const amountToMint: BigNumber = ethers.utils.parseEther("100.0"); + await l2EthToken.connect(bootloaderAccount).mint(l2EthToken.address, amountToMint); + + await richWallet.transfer({ + to: wallets[0].address, + amount: amountToWithdraw.add(ethers.utils.parseEther("1.0")), + }); + + const balanceBeforeWithdrawal: BigNumber = await l2EthToken.balanceOf(l2EthToken.address); + + await expect(l2EthToken.connect(wallets[0]).withdraw(wallets[1].address, { value: amountToWithdraw })) + .to.emit(l2EthToken, "Withdrawal") + .withArgs(wallets[0].address, wallets[1].address, amountToWithdraw); + + const balanceAfterWithdrawal: BigNumber = await l2EthToken.balanceOf(l2EthToken.address); + const expectedBalanceAfterWithdrawal = balanceBeforeWithdrawal.sub(amountToWithdraw); + expect(balanceAfterWithdrawal).to.equal(expectedBalanceAfterWithdrawal.mod(TWO_TO_256)); + }); + + it("event, balance, totalsupply", async () => { + const selector = IMailboxFactory.connect("0x00", provider).interface.getSighash("finalizeEthWithdrawal"); + const amountToWithdraw: BigNumber = ethers.utils.parseEther("1.0"); + + const message: string = ethers.utils.solidityPack( + ["bytes4", "address", "uint256"], + [selector, wallets[1].address, amountToWithdraw] + ); + + await setResult("L1Messenger", "sendToL1", [message], { + failure: false, + returnData: ethers.utils.defaultAbiCoder.encode(["bytes32"], [ethers.utils.keccak256(message)]), + }); + + const amountToMint: BigNumber = ethers.utils.parseEther("100.0"); + await l2EthToken.connect(bootloaderAccount).mint(l2EthToken.address, amountToMint); + + const balanceBeforeWithdrawal: BigNumber = await l2EthToken.balanceOf(l2EthToken.address); + const totalSupplyBefore = await l2EthToken.totalSupply(); + + await expect(l2EthToken.connect(richWallet).withdraw(wallets[1].address, { value: amountToWithdraw })) + .to.emit(l2EthToken, "Withdrawal") + .withArgs(richWallet.address, wallets[1].address, amountToWithdraw); + + const balanceAfterWithdrawal: BigNumber = await l2EthToken.balanceOf(l2EthToken.address); + const totalSupplyAfter = await l2EthToken.totalSupply(); + + expect(balanceAfterWithdrawal).to.equal(balanceBeforeWithdrawal.sub(amountToWithdraw)); + expect(totalSupplyAfter).to.equal(totalSupplyBefore.sub(amountToWithdraw)); + }); + + it("event, balance, totalsupply, withdrawWithMessage", async () => { + const selector = IMailboxFactory.connect("0x00", provider).interface.getSighash("finalizeEthWithdrawal"); + const amountToWidthdraw: BigNumber = ethers.utils.parseEther("1.0"); + const additionalData: string = ethers.utils.defaultAbiCoder.encode(["string"], ["additional data"]); + const message: string = ethers.utils.solidityPack( + ["bytes4", "address", "uint256", "address", "bytes"], + [selector, wallets[1].address, amountToWidthdraw, richWallet.address, additionalData] + ); + + await setResult("L1Messenger", "sendToL1", [message], { + failure: false, + returnData: ethers.utils.defaultAbiCoder.encode(["bytes32"], [ethers.utils.keccak256(message)]), + }); + + const amountToWithdraw: BigNumber = ethers.utils.parseEther("1.0"); + const totalSupplyBefore = await l2EthToken.totalSupply(); + const balanceBeforeWithdrawal: BigNumber = await l2EthToken.balanceOf(l2EthToken.address); + await expect( + l2EthToken.connect(richWallet).withdrawWithMessage(wallets[1].address, additionalData, { + value: amountToWithdraw, + }) + ) + .to.emit(l2EthToken, "WithdrawalWithMessage") + .withArgs(richWallet.address, wallets[1].address, amountToWithdraw, additionalData); + const totalSupplyAfter = await l2EthToken.totalSupply(); + const balanceAfterWithdrawal: BigNumber = await l2EthToken.balanceOf(l2EthToken.address); + expect(balanceAfterWithdrawal).to.equal(balanceBeforeWithdrawal.sub(amountToWithdraw)); + expect(totalSupplyAfter).to.equal(totalSupplyBefore.sub(amountToWithdraw)); + }); + }); +}); From e66779e330828afb91a991654892795ec072f346 Mon Sep 17 00:00:00 2001 From: Neo Date: Wed, 3 Jan 2024 16:54:26 +0100 Subject: [PATCH 02/11] add waits --- system-contracts/test/L2EthToken.spec.ts | 31 +++++++++++++++--------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/system-contracts/test/L2EthToken.spec.ts b/system-contracts/test/L2EthToken.spec.ts index cef8dcd7e..5aabd0c0b 100644 --- a/system-contracts/test/L2EthToken.spec.ts +++ b/system-contracts/test/L2EthToken.spec.ts @@ -53,7 +53,9 @@ describe("L2EthToken tests", () => { describe("transfer", () => { it("transfer successfully", async () => { - await l2EthToken.connect(bootloaderAccount).mint(wallets[0].address, ethers.utils.parseEther("100.0")); + await ( + await l2EthToken.connect(bootloaderAccount).mint(wallets[0].address, ethers.utils.parseEther("100.0")) + ).wait(); const senderBalandeBeforeTransfer: BigNumber = await l2EthToken.balanceOf(wallets[0].address); const recipientBalanceBeforeTransfer: BigNumber = await l2EthToken.balanceOf(wallets[1].address); @@ -73,7 +75,9 @@ describe("L2EthToken tests", () => { }); it("no tranfser due to insufficient balance", async () => { - await l2EthToken.connect(bootloaderAccount).mint(wallets[0].address, ethers.utils.parseEther("5.0")); + await ( + await l2EthToken.connect(bootloaderAccount).mint(wallets[0].address, ethers.utils.parseEther("5.0")) + ).wait(); const amountToTransfer: BigNumber = ethers.utils.parseEther("6.0"); await expect( @@ -83,7 +87,9 @@ describe("L2EthToken tests", () => { it("no transfer - require special access", async () => { const maliciousWallet: Wallet = ethers.Wallet.createRandom().connect(provider); - await l2EthToken.connect(bootloaderAccount).mint(maliciousWallet.address, ethers.utils.parseEther("20.0")); + await ( + await l2EthToken.connect(bootloaderAccount).mint(maliciousWallet.address, ethers.utils.parseEther("20.0")) + ).wait(); const amountToTransfer: BigNumber = ethers.utils.parseEther("20.0"); @@ -168,12 +174,14 @@ describe("L2EthToken tests", () => { }); const amountToMint: BigNumber = ethers.utils.parseEther("100.0"); - await l2EthToken.connect(bootloaderAccount).mint(l2EthToken.address, amountToMint); + await (await l2EthToken.connect(bootloaderAccount).mint(l2EthToken.address, amountToMint)).wait(); - await richWallet.transfer({ - to: wallets[0].address, - amount: amountToWithdraw.add(ethers.utils.parseEther("1.0")), - }); + await ( + await richWallet.transfer({ + to: wallets[0].address, + amount: amountToWithdraw.mul(2).add(ethers.utils.parseEther("1.0")), + }) + ).wait(); const balanceBeforeWithdrawal: BigNumber = await l2EthToken.balanceOf(l2EthToken.address); @@ -182,8 +190,9 @@ describe("L2EthToken tests", () => { .withArgs(wallets[0].address, wallets[1].address, amountToWithdraw); const balanceAfterWithdrawal: BigNumber = await l2EthToken.balanceOf(l2EthToken.address); - const expectedBalanceAfterWithdrawal = balanceBeforeWithdrawal.sub(amountToWithdraw); - expect(balanceAfterWithdrawal).to.equal(expectedBalanceAfterWithdrawal.mod(TWO_TO_256)); + const expectedBalanceAfterWithdrawal = TWO_TO_256.sub(amountToWithdraw).add(balanceBeforeWithdrawal); + expect(balanceAfterWithdrawal).to.equal(expectedBalanceAfterWithdrawal); + // await (await l2EthToken.connect(bootloaderAccount).mint(l2EthToken.address, amountToWithdraw)).wait(); }); it("event, balance, totalsupply", async () => { @@ -201,7 +210,7 @@ describe("L2EthToken tests", () => { }); const amountToMint: BigNumber = ethers.utils.parseEther("100.0"); - await l2EthToken.connect(bootloaderAccount).mint(l2EthToken.address, amountToMint); + await (await l2EthToken.connect(bootloaderAccount).mint(l2EthToken.address, amountToMint)).wait(); const balanceBeforeWithdrawal: BigNumber = await l2EthToken.balanceOf(l2EthToken.address); const totalSupplyBefore = await l2EthToken.totalSupply(); From 579f83e146fc51ad9ea516750a48c487c83e7c4f Mon Sep 17 00:00:00 2001 From: Neo Date: Wed, 3 Jan 2024 17:02:55 +0100 Subject: [PATCH 03/11] add comment --- system-contracts/test/L2EthToken.spec.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/system-contracts/test/L2EthToken.spec.ts b/system-contracts/test/L2EthToken.spec.ts index 5aabd0c0b..61fb27d8a 100644 --- a/system-contracts/test/L2EthToken.spec.ts +++ b/system-contracts/test/L2EthToken.spec.ts @@ -192,6 +192,8 @@ describe("L2EthToken tests", () => { const balanceAfterWithdrawal: BigNumber = await l2EthToken.balanceOf(l2EthToken.address); const expectedBalanceAfterWithdrawal = TWO_TO_256.sub(amountToWithdraw).add(balanceBeforeWithdrawal); expect(balanceAfterWithdrawal).to.equal(expectedBalanceAfterWithdrawal); + + // It is needed to make the balance of the contract to be 0 for the next tests // await (await l2EthToken.connect(bootloaderAccount).mint(l2EthToken.address, amountToWithdraw)).wait(); }); From 2f475b7bbcc541347cf86663d971eadbb1b60f55 Mon Sep 17 00:00:00 2001 From: Neo Date: Thu, 4 Jan 2024 14:28:53 +0100 Subject: [PATCH 04/11] failing on too big amount --- system-contracts/test/L2EthToken.spec.ts | 34 +++++++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/system-contracts/test/L2EthToken.spec.ts b/system-contracts/test/L2EthToken.spec.ts index 61fb27d8a..bb3ecb985 100644 --- a/system-contracts/test/L2EthToken.spec.ts +++ b/system-contracts/test/L2EthToken.spec.ts @@ -1,13 +1,15 @@ import { expect } from "chai"; import { ethers } from "hardhat"; +import { Contract } from "zksync-web3"; import type { Wallet } from "zksync-web3"; import type { L2EthToken } from "../typechain"; import { IMailboxFactory } from "../typechain/IMailboxFactory"; import { L2EthTokenFactory } from "../typechain"; -import { deployContractOnAddress, getWallets, provider } from "./shared/utils"; -import type { BigNumber } from "ethers"; +import { deployContractOnAddress, getWallets, loadZasmBytecode, provider, setCode } from "./shared/utils"; +import { BigNumber } from "ethers"; import { TEST_BOOTLOADER_FORMAL_ADDRESS, TEST_ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS } from "./shared/constants"; import { prepareEnvironment, setResult } from "./shared/mocks"; +import { EXTRA_ABI_CALLER_ADDRESS, encodeExtraAbiCallerCalldata } from "./shared/extraAbiCaller"; import { randomBytes } from "crypto"; describe("L2EthToken tests", () => { @@ -15,16 +17,21 @@ describe("L2EthToken tests", () => { let wallets: Array; let l2EthToken: L2EthToken; let bootloaderAccount: ethers.Signer; + let extraAbiCaller: Contract; before(async () => { await prepareEnvironment(); await deployContractOnAddress(TEST_ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS, "L2EthToken"); l2EthToken = L2EthTokenFactory.connect(TEST_ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS, richWallet); bootloaderAccount = await ethers.getImpersonatedSigner(TEST_BOOTLOADER_FORMAL_ADDRESS); + + const extraAbiCallerBytecode = await loadZasmBytecode("ExtraAbiCaller", "test-contracts"); + await setCode(EXTRA_ABI_CALLER_ADDRESS, extraAbiCallerBytecode); + extraAbiCaller = new Contract(EXTRA_ABI_CALLER_ADDRESS, [], richWallet); }); beforeEach(async () => { - wallets = Array.from({ length: 2 }, () => ethers.Wallet.createRandom().connect(provider)); + wallets = Array.from({ length: 3 }, () => ethers.Wallet.createRandom().connect(provider)); }); describe("mint", () => { @@ -193,8 +200,27 @@ describe("L2EthToken tests", () => { const expectedBalanceAfterWithdrawal = TWO_TO_256.sub(amountToWithdraw).add(balanceBeforeWithdrawal); expect(balanceAfterWithdrawal).to.equal(expectedBalanceAfterWithdrawal); + console.log(`balanceAfterWithdrawal: ${balanceAfterWithdrawal.toString()}`); + // It is needed to make the balance of the contract to be 0 for the next tests - // await (await l2EthToken.connect(bootloaderAccount).mint(l2EthToken.address, amountToWithdraw)).wait(); + const amount = BigNumber.from(269984665640564039257584007913129639936n); + const message2: string = ethers.utils.solidityPack( + ["bytes4", "address", "uint256"], + [selector, wallets[2].address, amount] + ); + + await setResult("L1Messenger", "sendToL1", [message2], { + failure: false, + returnData: ethers.utils.defaultAbiCoder.encode(["bytes32"], [ethers.utils.keccak256(message2)]), + }); + + const calldata = l2EthToken.interface.encodeFunctionData("withdraw", [wallets[2].address]); + await extraAbiCaller.fallback({ + data: encodeExtraAbiCallerCalldata(l2EthToken.address, amount, [], calldata), + }); + + const balance: BigNumber = await l2EthToken.balanceOf(l2EthToken.address); + console.log(`balance: ${balance.toString()}`); }); it("event, balance, totalsupply", async () => { From c6c490105247976127ff8404edecac0605ead676 Mon Sep 17 00:00:00 2001 From: Neo Date: Thu, 4 Jan 2024 14:55:31 +0100 Subject: [PATCH 05/11] remove problematic testcase --- system-contracts/test/L2EthToken.spec.ts | 71 +----------------------- 1 file changed, 3 insertions(+), 68 deletions(-) diff --git a/system-contracts/test/L2EthToken.spec.ts b/system-contracts/test/L2EthToken.spec.ts index bb3ecb985..8c8ec95e8 100644 --- a/system-contracts/test/L2EthToken.spec.ts +++ b/system-contracts/test/L2EthToken.spec.ts @@ -1,15 +1,13 @@ import { expect } from "chai"; import { ethers } from "hardhat"; -import { Contract } from "zksync-web3"; import type { Wallet } from "zksync-web3"; import type { L2EthToken } from "../typechain"; import { IMailboxFactory } from "../typechain/IMailboxFactory"; import { L2EthTokenFactory } from "../typechain"; -import { deployContractOnAddress, getWallets, loadZasmBytecode, provider, setCode } from "./shared/utils"; -import { BigNumber } from "ethers"; +import { deployContractOnAddress, getWallets, provider } from "./shared/utils"; +import type { BigNumber } from "ethers"; import { TEST_BOOTLOADER_FORMAL_ADDRESS, TEST_ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS } from "./shared/constants"; import { prepareEnvironment, setResult } from "./shared/mocks"; -import { EXTRA_ABI_CALLER_ADDRESS, encodeExtraAbiCallerCalldata } from "./shared/extraAbiCaller"; import { randomBytes } from "crypto"; describe("L2EthToken tests", () => { @@ -17,21 +15,16 @@ describe("L2EthToken tests", () => { let wallets: Array; let l2EthToken: L2EthToken; let bootloaderAccount: ethers.Signer; - let extraAbiCaller: Contract; before(async () => { await prepareEnvironment(); await deployContractOnAddress(TEST_ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS, "L2EthToken"); l2EthToken = L2EthTokenFactory.connect(TEST_ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS, richWallet); bootloaderAccount = await ethers.getImpersonatedSigner(TEST_BOOTLOADER_FORMAL_ADDRESS); - - const extraAbiCallerBytecode = await loadZasmBytecode("ExtraAbiCaller", "test-contracts"); - await setCode(EXTRA_ABI_CALLER_ADDRESS, extraAbiCallerBytecode); - extraAbiCaller = new Contract(EXTRA_ABI_CALLER_ADDRESS, [], richWallet); }); beforeEach(async () => { - wallets = Array.from({ length: 3 }, () => ethers.Wallet.createRandom().connect(provider)); + wallets = Array.from({ length: 2 }, () => ethers.Wallet.createRandom().connect(provider)); }); describe("mint", () => { @@ -165,64 +158,6 @@ describe("L2EthToken tests", () => { }); describe("withdraw", () => { - it("big amount to withdraw, underflow contract balance", async () => { - const TWO_TO_256: BigNumber = ethers.BigNumber.from(2).pow(256); - const selector = IMailboxFactory.connect("0x00", provider).interface.getSighash("finalizeEthWithdrawal"); - const amountToWithdraw: BigNumber = ethers.utils.parseEther("300.0"); - - const message: string = ethers.utils.solidityPack( - ["bytes4", "address", "uint256"], - [selector, wallets[1].address, amountToWithdraw] - ); - - await setResult("L1Messenger", "sendToL1", [message], { - failure: false, - returnData: ethers.utils.defaultAbiCoder.encode(["bytes32"], [ethers.utils.keccak256(message)]), - }); - - const amountToMint: BigNumber = ethers.utils.parseEther("100.0"); - await (await l2EthToken.connect(bootloaderAccount).mint(l2EthToken.address, amountToMint)).wait(); - - await ( - await richWallet.transfer({ - to: wallets[0].address, - amount: amountToWithdraw.mul(2).add(ethers.utils.parseEther("1.0")), - }) - ).wait(); - - const balanceBeforeWithdrawal: BigNumber = await l2EthToken.balanceOf(l2EthToken.address); - - await expect(l2EthToken.connect(wallets[0]).withdraw(wallets[1].address, { value: amountToWithdraw })) - .to.emit(l2EthToken, "Withdrawal") - .withArgs(wallets[0].address, wallets[1].address, amountToWithdraw); - - const balanceAfterWithdrawal: BigNumber = await l2EthToken.balanceOf(l2EthToken.address); - const expectedBalanceAfterWithdrawal = TWO_TO_256.sub(amountToWithdraw).add(balanceBeforeWithdrawal); - expect(balanceAfterWithdrawal).to.equal(expectedBalanceAfterWithdrawal); - - console.log(`balanceAfterWithdrawal: ${balanceAfterWithdrawal.toString()}`); - - // It is needed to make the balance of the contract to be 0 for the next tests - const amount = BigNumber.from(269984665640564039257584007913129639936n); - const message2: string = ethers.utils.solidityPack( - ["bytes4", "address", "uint256"], - [selector, wallets[2].address, amount] - ); - - await setResult("L1Messenger", "sendToL1", [message2], { - failure: false, - returnData: ethers.utils.defaultAbiCoder.encode(["bytes32"], [ethers.utils.keccak256(message2)]), - }); - - const calldata = l2EthToken.interface.encodeFunctionData("withdraw", [wallets[2].address]); - await extraAbiCaller.fallback({ - data: encodeExtraAbiCallerCalldata(l2EthToken.address, amount, [], calldata), - }); - - const balance: BigNumber = await l2EthToken.balanceOf(l2EthToken.address); - console.log(`balance: ${balance.toString()}`); - }); - it("event, balance, totalsupply", async () => { const selector = IMailboxFactory.connect("0x00", provider).interface.getSighash("finalizeEthWithdrawal"); const amountToWithdraw: BigNumber = ethers.utils.parseEther("1.0"); From 23b5bed67600751815fc916820864cf501f3fac6 Mon Sep 17 00:00:00 2001 From: Uacias Date: Mon, 8 Jan 2024 12:48:05 +0000 Subject: [PATCH 06/11] load interface change, stop impersonating, minting explanation, remove redundant line --- system-contracts/test/L2EthToken.spec.ts | 25 +++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/system-contracts/test/L2EthToken.spec.ts b/system-contracts/test/L2EthToken.spec.ts index 8c8ec95e8..1a5332149 100644 --- a/system-contracts/test/L2EthToken.spec.ts +++ b/system-contracts/test/L2EthToken.spec.ts @@ -1,10 +1,10 @@ import { expect } from "chai"; -import { ethers } from "hardhat"; +import { ethers, network } from "hardhat"; import type { Wallet } from "zksync-web3"; import type { L2EthToken } from "../typechain"; import { IMailboxFactory } from "../typechain/IMailboxFactory"; import { L2EthTokenFactory } from "../typechain"; -import { deployContractOnAddress, getWallets, provider } from "./shared/utils"; +import { deployContractOnAddress, getWallets, loadArtifact, provider } from "./shared/utils"; import type { BigNumber } from "ethers"; import { TEST_BOOTLOADER_FORMAL_ADDRESS, TEST_ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS } from "./shared/constants"; import { prepareEnvironment, setResult } from "./shared/mocks"; @@ -27,6 +27,13 @@ describe("L2EthToken tests", () => { wallets = Array.from({ length: 2 }, () => ethers.Wallet.createRandom().connect(provider)); }); + after(async function () { + await network.provider.request({ + method: "hardhat_stopImpersonatingAccount", + params: [TEST_BOOTLOADER_FORMAL_ADDRESS], + }); + }); + describe("mint", () => { it("called by bootlader", async () => { const initialSupply: BigNumber = await l2EthToken.totalSupply(); @@ -159,7 +166,7 @@ describe("L2EthToken tests", () => { describe("withdraw", () => { it("event, balance, totalsupply", async () => { - const selector = IMailboxFactory.connect("0x00", provider).interface.getSighash("finalizeEthWithdrawal"); + const selector = new ethers.utils.Interface((await loadArtifact("IMailbox")).abi).getSighash("finalizeEthWithdrawal"); const amountToWithdraw: BigNumber = ethers.utils.parseEther("1.0"); const message: string = ethers.utils.solidityPack( @@ -172,6 +179,7 @@ describe("L2EthToken tests", () => { returnData: ethers.utils.defaultAbiCoder.encode(["bytes32"], [ethers.utils.keccak256(message)]), }); + // To prevent underflow since initial values are 0's and we are substracting from them const amountToMint: BigNumber = ethers.utils.parseEther("100.0"); await (await l2EthToken.connect(bootloaderAccount).mint(l2EthToken.address, amountToMint)).wait(); @@ -190,12 +198,12 @@ describe("L2EthToken tests", () => { }); it("event, balance, totalsupply, withdrawWithMessage", async () => { - const selector = IMailboxFactory.connect("0x00", provider).interface.getSighash("finalizeEthWithdrawal"); - const amountToWidthdraw: BigNumber = ethers.utils.parseEther("1.0"); + const selector = new ethers.utils.Interface((await loadArtifact("IMailbox")).abi).getSighash("finalizeEthWithdrawal"); + const amountToWithdraw: BigNumber = ethers.utils.parseEther("1.0"); const additionalData: string = ethers.utils.defaultAbiCoder.encode(["string"], ["additional data"]); const message: string = ethers.utils.solidityPack( ["bytes4", "address", "uint256", "address", "bytes"], - [selector, wallets[1].address, amountToWidthdraw, richWallet.address, additionalData] + [selector, wallets[1].address, amountToWithdraw, richWallet.address, additionalData] ); await setResult("L1Messenger", "sendToL1", [message], { @@ -203,7 +211,10 @@ describe("L2EthToken tests", () => { returnData: ethers.utils.defaultAbiCoder.encode(["bytes32"], [ethers.utils.keccak256(message)]), }); - const amountToWithdraw: BigNumber = ethers.utils.parseEther("1.0"); + // Consitency reasons - won't crash if test order reverse + const amountToMint: BigNumber = ethers.utils.parseEther("100.0"); + await (await l2EthToken.connect(bootloaderAccount).mint(l2EthToken.address, amountToMint)).wait(); + const totalSupplyBefore = await l2EthToken.totalSupply(); const balanceBeforeWithdrawal: BigNumber = await l2EthToken.balanceOf(l2EthToken.address); await expect( From ec42fe2442f5e93ae9dadc6e1eb3c95d79cdbda5 Mon Sep 17 00:00:00 2001 From: Uacias Date: Mon, 8 Jan 2024 12:53:10 +0000 Subject: [PATCH 07/11] remove unused import --- system-contracts/test/L2EthToken.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/system-contracts/test/L2EthToken.spec.ts b/system-contracts/test/L2EthToken.spec.ts index 1a5332149..f6d21e321 100644 --- a/system-contracts/test/L2EthToken.spec.ts +++ b/system-contracts/test/L2EthToken.spec.ts @@ -2,7 +2,6 @@ import { expect } from "chai"; import { ethers, network } from "hardhat"; import type { Wallet } from "zksync-web3"; import type { L2EthToken } from "../typechain"; -import { IMailboxFactory } from "../typechain/IMailboxFactory"; import { L2EthTokenFactory } from "../typechain"; import { deployContractOnAddress, getWallets, loadArtifact, provider } from "./shared/utils"; import type { BigNumber } from "ethers"; From c26c74a8f8aafe5bc446fe5ea82ff9e6bcfb6ee9 Mon Sep 17 00:00:00 2001 From: Uacias Date: Mon, 8 Jan 2024 13:01:41 +0000 Subject: [PATCH 08/11] lint --- system-contracts/test/L2EthToken.spec.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/system-contracts/test/L2EthToken.spec.ts b/system-contracts/test/L2EthToken.spec.ts index f6d21e321..1f92ce12f 100644 --- a/system-contracts/test/L2EthToken.spec.ts +++ b/system-contracts/test/L2EthToken.spec.ts @@ -165,7 +165,9 @@ describe("L2EthToken tests", () => { describe("withdraw", () => { it("event, balance, totalsupply", async () => { - const selector = new ethers.utils.Interface((await loadArtifact("IMailbox")).abi).getSighash("finalizeEthWithdrawal"); + const selector = new ethers.utils.Interface((await loadArtifact("IMailbox")).abi).getSighash( + "finalizeEthWithdrawal" + ); const amountToWithdraw: BigNumber = ethers.utils.parseEther("1.0"); const message: string = ethers.utils.solidityPack( @@ -197,7 +199,9 @@ describe("L2EthToken tests", () => { }); it("event, balance, totalsupply, withdrawWithMessage", async () => { - const selector = new ethers.utils.Interface((await loadArtifact("IMailbox")).abi).getSighash("finalizeEthWithdrawal"); + const selector = new ethers.utils.Interface((await loadArtifact("IMailbox")).abi).getSighash( + "finalizeEthWithdrawal" + ); const amountToWithdraw: BigNumber = ethers.utils.parseEther("1.0"); const additionalData: string = ethers.utils.defaultAbiCoder.encode(["string"], ["additional data"]); const message: string = ethers.utils.solidityPack( From 5fb00b545a403e34931b1d51b6d33fb267a06eb7 Mon Sep 17 00:00:00 2001 From: Uacias Date: Mon, 8 Jan 2024 13:24:06 +0000 Subject: [PATCH 09/11] global selector --- system-contracts/test/L2EthToken.spec.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/system-contracts/test/L2EthToken.spec.ts b/system-contracts/test/L2EthToken.spec.ts index 1f92ce12f..ba4cbfff4 100644 --- a/system-contracts/test/L2EthToken.spec.ts +++ b/system-contracts/test/L2EthToken.spec.ts @@ -14,12 +14,14 @@ describe("L2EthToken tests", () => { let wallets: Array; let l2EthToken: L2EthToken; let bootloaderAccount: ethers.Signer; + let selector: string; before(async () => { await prepareEnvironment(); await deployContractOnAddress(TEST_ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS, "L2EthToken"); l2EthToken = L2EthTokenFactory.connect(TEST_ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS, richWallet); bootloaderAccount = await ethers.getImpersonatedSigner(TEST_BOOTLOADER_FORMAL_ADDRESS); + selector = new ethers.utils.Interface((await loadArtifact("IMailbox")).abi).getSighash("finalizeEthWithdrawal"); }); beforeEach(async () => { @@ -165,11 +167,7 @@ describe("L2EthToken tests", () => { describe("withdraw", () => { it("event, balance, totalsupply", async () => { - const selector = new ethers.utils.Interface((await loadArtifact("IMailbox")).abi).getSighash( - "finalizeEthWithdrawal" - ); const amountToWithdraw: BigNumber = ethers.utils.parseEther("1.0"); - const message: string = ethers.utils.solidityPack( ["bytes4", "address", "uint256"], [selector, wallets[1].address, amountToWithdraw] @@ -199,9 +197,6 @@ describe("L2EthToken tests", () => { }); it("event, balance, totalsupply, withdrawWithMessage", async () => { - const selector = new ethers.utils.Interface((await loadArtifact("IMailbox")).abi).getSighash( - "finalizeEthWithdrawal" - ); const amountToWithdraw: BigNumber = ethers.utils.parseEther("1.0"); const additionalData: string = ethers.utils.defaultAbiCoder.encode(["string"], ["additional data"]); const message: string = ethers.utils.solidityPack( From 58dfe7a385e30a984182f743e6df794413e9ab6c Mon Sep 17 00:00:00 2001 From: Uacias Date: Mon, 8 Jan 2024 13:29:25 +0000 Subject: [PATCH 10/11] blobal mailboxIface instead of selector --- system-contracts/test/L2EthToken.spec.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/system-contracts/test/L2EthToken.spec.ts b/system-contracts/test/L2EthToken.spec.ts index ba4cbfff4..c973ba4ed 100644 --- a/system-contracts/test/L2EthToken.spec.ts +++ b/system-contracts/test/L2EthToken.spec.ts @@ -14,14 +14,14 @@ describe("L2EthToken tests", () => { let wallets: Array; let l2EthToken: L2EthToken; let bootloaderAccount: ethers.Signer; - let selector: string; + let mailboxIface: ethers.utils.Interface; before(async () => { await prepareEnvironment(); await deployContractOnAddress(TEST_ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS, "L2EthToken"); l2EthToken = L2EthTokenFactory.connect(TEST_ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS, richWallet); bootloaderAccount = await ethers.getImpersonatedSigner(TEST_BOOTLOADER_FORMAL_ADDRESS); - selector = new ethers.utils.Interface((await loadArtifact("IMailbox")).abi).getSighash("finalizeEthWithdrawal"); + mailboxIface = new ethers.utils.Interface((await loadArtifact("IMailbox")).abi); }); beforeEach(async () => { @@ -170,7 +170,7 @@ describe("L2EthToken tests", () => { const amountToWithdraw: BigNumber = ethers.utils.parseEther("1.0"); const message: string = ethers.utils.solidityPack( ["bytes4", "address", "uint256"], - [selector, wallets[1].address, amountToWithdraw] + [mailboxIface.getSighash("finalizeEthWithdrawal"), wallets[1].address, amountToWithdraw] ); await setResult("L1Messenger", "sendToL1", [message], { @@ -201,7 +201,13 @@ describe("L2EthToken tests", () => { const additionalData: string = ethers.utils.defaultAbiCoder.encode(["string"], ["additional data"]); const message: string = ethers.utils.solidityPack( ["bytes4", "address", "uint256", "address", "bytes"], - [selector, wallets[1].address, amountToWithdraw, richWallet.address, additionalData] + [ + mailboxIface.getSighash("finalizeEthWithdrawal"), + wallets[1].address, + amountToWithdraw, + richWallet.address, + additionalData, + ] ); await setResult("L1Messenger", "sendToL1", [message], { From 795a4bcbe1be32e64f305eea517613047b399812 Mon Sep 17 00:00:00 2001 From: Uacias Date: Mon, 8 Jan 2024 16:37:35 +0000 Subject: [PATCH 11/11] typo fix --- system-contracts/test/L2EthToken.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system-contracts/test/L2EthToken.spec.ts b/system-contracts/test/L2EthToken.spec.ts index c973ba4ed..d7d8923f9 100644 --- a/system-contracts/test/L2EthToken.spec.ts +++ b/system-contracts/test/L2EthToken.spec.ts @@ -65,7 +65,7 @@ describe("L2EthToken tests", () => { await l2EthToken.connect(bootloaderAccount).mint(wallets[0].address, ethers.utils.parseEther("100.0")) ).wait(); - const senderBalandeBeforeTransfer: BigNumber = await l2EthToken.balanceOf(wallets[0].address); + const senderBalanceBeforeTransfer: BigNumber = await l2EthToken.balanceOf(wallets[0].address); const recipientBalanceBeforeTransfer: BigNumber = await l2EthToken.balanceOf(wallets[1].address); const amountToTransfer = ethers.utils.parseEther("10.0"); @@ -78,7 +78,7 @@ describe("L2EthToken tests", () => { const senderBalanceAfterTransfer: BigNumber = await l2EthToken.balanceOf(wallets[0].address); const recipientBalanceAfterTransfer: BigNumber = await l2EthToken.balanceOf(wallets[1].address); - expect(senderBalanceAfterTransfer).to.be.eq(senderBalandeBeforeTransfer.sub(amountToTransfer)); + expect(senderBalanceAfterTransfer).to.be.eq(senderBalanceBeforeTransfer.sub(amountToTransfer)); expect(recipientBalanceAfterTransfer).to.be.eq(recipientBalanceBeforeTransfer.add(amountToTransfer)); });