Skip to content

Commit 1848b14

Browse files
L2EthToken Tests (#152)
Co-authored-by: Uacias <[email protected]>
1 parent 97b86e2 commit 1848b14

File tree

1 file changed

+237
-0
lines changed

1 file changed

+237
-0
lines changed
+237
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
import { expect } from "chai";
2+
import { ethers, network } from "hardhat";
3+
import type { Wallet } from "zksync-web3";
4+
import type { L2EthToken } from "../typechain";
5+
import { L2EthTokenFactory } from "../typechain";
6+
import { deployContractOnAddress, getWallets, loadArtifact, provider } from "./shared/utils";
7+
import type { BigNumber } from "ethers";
8+
import { TEST_BOOTLOADER_FORMAL_ADDRESS, TEST_ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS } from "./shared/constants";
9+
import { prepareEnvironment, setResult } from "./shared/mocks";
10+
import { randomBytes } from "crypto";
11+
12+
describe("L2EthToken tests", () => {
13+
const richWallet = getWallets()[0];
14+
let wallets: Array<Wallet>;
15+
let l2EthToken: L2EthToken;
16+
let bootloaderAccount: ethers.Signer;
17+
let mailboxIface: ethers.utils.Interface;
18+
19+
before(async () => {
20+
await prepareEnvironment();
21+
await deployContractOnAddress(TEST_ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS, "L2EthToken");
22+
l2EthToken = L2EthTokenFactory.connect(TEST_ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS, richWallet);
23+
bootloaderAccount = await ethers.getImpersonatedSigner(TEST_BOOTLOADER_FORMAL_ADDRESS);
24+
mailboxIface = new ethers.utils.Interface((await loadArtifact("IMailbox")).abi);
25+
});
26+
27+
beforeEach(async () => {
28+
wallets = Array.from({ length: 2 }, () => ethers.Wallet.createRandom().connect(provider));
29+
});
30+
31+
after(async function () {
32+
await network.provider.request({
33+
method: "hardhat_stopImpersonatingAccount",
34+
params: [TEST_BOOTLOADER_FORMAL_ADDRESS],
35+
});
36+
});
37+
38+
describe("mint", () => {
39+
it("called by bootlader", async () => {
40+
const initialSupply: BigNumber = await l2EthToken.totalSupply();
41+
const initialBalanceOfWallet: BigNumber = await l2EthToken.balanceOf(wallets[0].address);
42+
const amountToMint: BigNumber = ethers.utils.parseEther("10.0");
43+
44+
await expect(l2EthToken.connect(bootloaderAccount).mint(wallets[0].address, amountToMint))
45+
.to.emit(l2EthToken, "Mint")
46+
.withArgs(wallets[0].address, amountToMint);
47+
48+
const finalSupply: BigNumber = await l2EthToken.totalSupply();
49+
const balanceOfWallet: BigNumber = await l2EthToken.balanceOf(wallets[0].address);
50+
expect(finalSupply).to.equal(initialSupply.add(amountToMint));
51+
expect(balanceOfWallet).to.equal(initialBalanceOfWallet.add(amountToMint));
52+
});
53+
54+
it("not called by bootloader", async () => {
55+
const amountToMint: BigNumber = ethers.utils.parseEther("10.0");
56+
await expect(l2EthToken.connect(wallets[0]).mint(wallets[0].address, amountToMint)).to.be.rejectedWith(
57+
"Callable only by the bootloader"
58+
);
59+
});
60+
});
61+
62+
describe("transfer", () => {
63+
it("transfer successfully", async () => {
64+
await (
65+
await l2EthToken.connect(bootloaderAccount).mint(wallets[0].address, ethers.utils.parseEther("100.0"))
66+
).wait();
67+
68+
const senderBalanceBeforeTransfer: BigNumber = await l2EthToken.balanceOf(wallets[0].address);
69+
const recipientBalanceBeforeTransfer: BigNumber = await l2EthToken.balanceOf(wallets[1].address);
70+
71+
const amountToTransfer = ethers.utils.parseEther("10.0");
72+
73+
await expect(
74+
l2EthToken.connect(bootloaderAccount).transferFromTo(wallets[0].address, wallets[1].address, amountToTransfer)
75+
)
76+
.to.emit(l2EthToken, "Transfer")
77+
.withArgs(wallets[0].address, wallets[1].address, amountToTransfer);
78+
79+
const senderBalanceAfterTransfer: BigNumber = await l2EthToken.balanceOf(wallets[0].address);
80+
const recipientBalanceAfterTransfer: BigNumber = await l2EthToken.balanceOf(wallets[1].address);
81+
expect(senderBalanceAfterTransfer).to.be.eq(senderBalanceBeforeTransfer.sub(amountToTransfer));
82+
expect(recipientBalanceAfterTransfer).to.be.eq(recipientBalanceBeforeTransfer.add(amountToTransfer));
83+
});
84+
85+
it("no tranfser due to insufficient balance", async () => {
86+
await (
87+
await l2EthToken.connect(bootloaderAccount).mint(wallets[0].address, ethers.utils.parseEther("5.0"))
88+
).wait();
89+
const amountToTransfer: BigNumber = ethers.utils.parseEther("6.0");
90+
91+
await expect(
92+
l2EthToken.connect(bootloaderAccount).transferFromTo(wallets[0].address, wallets[1].address, amountToTransfer)
93+
).to.be.rejectedWith("Transfer amount exceeds balance");
94+
});
95+
96+
it("no transfer - require special access", async () => {
97+
const maliciousWallet: Wallet = ethers.Wallet.createRandom().connect(provider);
98+
await (
99+
await l2EthToken.connect(bootloaderAccount).mint(maliciousWallet.address, ethers.utils.parseEther("20.0"))
100+
).wait();
101+
102+
const amountToTransfer: BigNumber = ethers.utils.parseEther("20.0");
103+
104+
await expect(
105+
l2EthToken
106+
.connect(maliciousWallet)
107+
.transferFromTo(maliciousWallet.address, wallets[1].address, amountToTransfer)
108+
).to.be.rejectedWith("Only system contracts with special access can call this method");
109+
});
110+
});
111+
112+
describe("balanceOf", () => {
113+
it("walletFrom address", async () => {
114+
const amountToMint: BigNumber = ethers.utils.parseEther("10.0");
115+
116+
await l2EthToken.connect(bootloaderAccount).mint(wallets[0].address, amountToMint);
117+
const balance = await l2EthToken.balanceOf(wallets[0].address);
118+
expect(balance).to.equal(amountToMint);
119+
});
120+
121+
it("address larger than 20 bytes", async () => {
122+
const amountToMint: BigNumber = ethers.utils.parseEther("123.0");
123+
124+
const res = await l2EthToken.connect(bootloaderAccount).mint(wallets[0].address, amountToMint);
125+
await res.wait();
126+
const largerAddress = ethers.BigNumber.from(
127+
"0x" + randomBytes(12).toString("hex") + wallets[0].address.slice(2)
128+
).toHexString();
129+
const balance = await l2EthToken.balanceOf(largerAddress);
130+
131+
expect(balance).to.equal(amountToMint);
132+
});
133+
});
134+
135+
describe("totalSupply", () => {
136+
it("correct total supply", async () => {
137+
const totalSupplyBefore = await l2EthToken.totalSupply();
138+
const amountToMint: BigNumber = ethers.utils.parseEther("10.0");
139+
140+
await l2EthToken.connect(bootloaderAccount).mint(wallets[0].address, amountToMint);
141+
const totalSupply = await l2EthToken.totalSupply();
142+
143+
expect(totalSupply).to.equal(totalSupplyBefore.add(amountToMint));
144+
});
145+
});
146+
147+
describe("name", () => {
148+
it("correct name", async () => {
149+
const name = await l2EthToken.name();
150+
expect(name).to.equal("Ether");
151+
});
152+
});
153+
154+
describe("symbol", () => {
155+
it("correct symbol", async () => {
156+
const symbol = await l2EthToken.symbol();
157+
expect(symbol).to.equal("ETH");
158+
});
159+
});
160+
161+
describe("decimals", () => {
162+
it("correct decimals", async () => {
163+
const decimals = await l2EthToken.decimals();
164+
expect(decimals).to.equal(18);
165+
});
166+
});
167+
168+
describe("withdraw", () => {
169+
it("event, balance, totalsupply", async () => {
170+
const amountToWithdraw: BigNumber = ethers.utils.parseEther("1.0");
171+
const message: string = ethers.utils.solidityPack(
172+
["bytes4", "address", "uint256"],
173+
[mailboxIface.getSighash("finalizeEthWithdrawal"), wallets[1].address, amountToWithdraw]
174+
);
175+
176+
await setResult("L1Messenger", "sendToL1", [message], {
177+
failure: false,
178+
returnData: ethers.utils.defaultAbiCoder.encode(["bytes32"], [ethers.utils.keccak256(message)]),
179+
});
180+
181+
// To prevent underflow since initial values are 0's and we are substracting from them
182+
const amountToMint: BigNumber = ethers.utils.parseEther("100.0");
183+
await (await l2EthToken.connect(bootloaderAccount).mint(l2EthToken.address, amountToMint)).wait();
184+
185+
const balanceBeforeWithdrawal: BigNumber = await l2EthToken.balanceOf(l2EthToken.address);
186+
const totalSupplyBefore = await l2EthToken.totalSupply();
187+
188+
await expect(l2EthToken.connect(richWallet).withdraw(wallets[1].address, { value: amountToWithdraw }))
189+
.to.emit(l2EthToken, "Withdrawal")
190+
.withArgs(richWallet.address, wallets[1].address, amountToWithdraw);
191+
192+
const balanceAfterWithdrawal: BigNumber = await l2EthToken.balanceOf(l2EthToken.address);
193+
const totalSupplyAfter = await l2EthToken.totalSupply();
194+
195+
expect(balanceAfterWithdrawal).to.equal(balanceBeforeWithdrawal.sub(amountToWithdraw));
196+
expect(totalSupplyAfter).to.equal(totalSupplyBefore.sub(amountToWithdraw));
197+
});
198+
199+
it("event, balance, totalsupply, withdrawWithMessage", async () => {
200+
const amountToWithdraw: BigNumber = ethers.utils.parseEther("1.0");
201+
const additionalData: string = ethers.utils.defaultAbiCoder.encode(["string"], ["additional data"]);
202+
const message: string = ethers.utils.solidityPack(
203+
["bytes4", "address", "uint256", "address", "bytes"],
204+
[
205+
mailboxIface.getSighash("finalizeEthWithdrawal"),
206+
wallets[1].address,
207+
amountToWithdraw,
208+
richWallet.address,
209+
additionalData,
210+
]
211+
);
212+
213+
await setResult("L1Messenger", "sendToL1", [message], {
214+
failure: false,
215+
returnData: ethers.utils.defaultAbiCoder.encode(["bytes32"], [ethers.utils.keccak256(message)]),
216+
});
217+
218+
// Consitency reasons - won't crash if test order reverse
219+
const amountToMint: BigNumber = ethers.utils.parseEther("100.0");
220+
await (await l2EthToken.connect(bootloaderAccount).mint(l2EthToken.address, amountToMint)).wait();
221+
222+
const totalSupplyBefore = await l2EthToken.totalSupply();
223+
const balanceBeforeWithdrawal: BigNumber = await l2EthToken.balanceOf(l2EthToken.address);
224+
await expect(
225+
l2EthToken.connect(richWallet).withdrawWithMessage(wallets[1].address, additionalData, {
226+
value: amountToWithdraw,
227+
})
228+
)
229+
.to.emit(l2EthToken, "WithdrawalWithMessage")
230+
.withArgs(richWallet.address, wallets[1].address, amountToWithdraw, additionalData);
231+
const totalSupplyAfter = await l2EthToken.totalSupply();
232+
const balanceAfterWithdrawal: BigNumber = await l2EthToken.balanceOf(l2EthToken.address);
233+
expect(balanceAfterWithdrawal).to.equal(balanceBeforeWithdrawal.sub(amountToWithdraw));
234+
expect(totalSupplyAfter).to.equal(totalSupplyBefore.sub(amountToWithdraw));
235+
});
236+
});
237+
});

0 commit comments

Comments
 (0)