Skip to content

Commit c09df21

Browse files
zimphaicemelon
andauthored
feat(contracts): gateway to add enforced transaction (#400)
Co-authored-by: Haichen Shen <[email protected]>
1 parent 066c57c commit c09df21

15 files changed

+1497
-730
lines changed

Diff for: .prettierrc

+11-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@
1313
"singleQuote": false,
1414
"bracketSpacing": false
1515
}
16-
}
16+
},
17+
{
18+
"files": "scripts/**/*.sol",
19+
"options": {
20+
"printWidth": 120,
21+
"tabWidth": 4,
22+
"useTabs": false,
23+
"singleQuote": false,
24+
"bracketSpacing": false
25+
}
26+
}
1727
]
1828
}

Diff for: integration-test/EnforcedTxGateway.spec.ts

+324
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
/* eslint-disable node/no-unpublished-import */
2+
/* eslint-disable node/no-missing-import */
3+
import { expect } from "chai";
4+
import { BigNumberish, BytesLike, constants, utils } from "ethers";
5+
import { ethers } from "hardhat";
6+
import { EnforcedTxGateway, L1MessageQueue, L2GasPriceOracle, MockCaller } from "../typechain";
7+
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
8+
9+
describe("EnforcedTxGateway.spec", async () => {
10+
let deployer: SignerWithAddress;
11+
let feeVault: SignerWithAddress;
12+
let signer: SignerWithAddress;
13+
14+
let caller: MockCaller;
15+
let gateway: EnforcedTxGateway;
16+
let oracle: L2GasPriceOracle;
17+
let queue: L1MessageQueue;
18+
19+
beforeEach(async () => {
20+
[deployer, feeVault, signer] = await ethers.getSigners();
21+
22+
const L1MessageQueue = await ethers.getContractFactory("L1MessageQueue", deployer);
23+
queue = await L1MessageQueue.deploy();
24+
await queue.deployed();
25+
26+
const L2GasPriceOracle = await ethers.getContractFactory("L2GasPriceOracle", deployer);
27+
oracle = await L2GasPriceOracle.deploy();
28+
29+
const EnforcedTxGateway = await ethers.getContractFactory("EnforcedTxGateway", deployer);
30+
gateway = await EnforcedTxGateway.deploy();
31+
await gateway.deployed();
32+
33+
const MockCaller = await ethers.getContractFactory("MockCaller", deployer);
34+
caller = await MockCaller.deploy();
35+
await caller.deployed();
36+
37+
await queue.initialize(constants.AddressZero, gateway.address, oracle.address, 10000000);
38+
await gateway.initialize(queue.address, feeVault.address);
39+
await oracle.initialize(21000, 0, 8, 16);
40+
41+
const Whitelist = await ethers.getContractFactory("Whitelist", deployer);
42+
const whitelist = await Whitelist.deploy(deployer.address);
43+
await whitelist.deployed();
44+
45+
await whitelist.updateWhitelistStatus([deployer.address], true);
46+
await oracle.updateWhitelist(whitelist.address);
47+
await oracle.setL2BaseFee(1);
48+
});
49+
50+
context("auth", async () => {
51+
it("should initialize correctly", async () => {
52+
expect(await gateway.owner()).to.eq(deployer.address);
53+
expect(await gateway.messageQueue()).to.eq(queue.address);
54+
expect(await gateway.feeVault()).to.eq(feeVault.address);
55+
expect(await gateway.paused()).to.eq(false);
56+
});
57+
58+
it("should revert, when initlaize again", async () => {
59+
await expect(gateway.initialize(constants.AddressZero, constants.AddressZero)).to.revertedWith(
60+
"Initializable: contract is already initialized"
61+
);
62+
});
63+
64+
context("#updateFeeVault", async () => {
65+
it("should revert, when non-owner call", async () => {
66+
await expect(gateway.connect(signer).updateFeeVault(constants.AddressZero)).to.revertedWith(
67+
"Ownable: caller is not the owner"
68+
);
69+
});
70+
71+
it("should succeed", async () => {
72+
expect(await gateway.feeVault()).to.eq(feeVault.address);
73+
await expect(gateway.updateFeeVault(deployer.address))
74+
.to.emit(gateway, "UpdateFeeVault")
75+
.withArgs(feeVault.address, deployer.address);
76+
expect(await gateway.feeVault()).to.eq(deployer.address);
77+
});
78+
});
79+
80+
context("#setPaused", async () => {
81+
it("should revert, when non-owner call", async () => {
82+
await expect(gateway.connect(signer).setPaused(false)).to.revertedWith("Ownable: caller is not the owner");
83+
});
84+
85+
it("should succeed", async () => {
86+
expect(await gateway.paused()).to.eq(false);
87+
await expect(gateway.setPaused(true)).to.emit(gateway, "Paused").withArgs(deployer.address);
88+
expect(await gateway.paused()).to.eq(true);
89+
await expect(gateway.setPaused(false)).to.emit(gateway, "Unpaused").withArgs(deployer.address);
90+
expect(await gateway.paused()).to.eq(false);
91+
});
92+
});
93+
});
94+
95+
context("#sendTransaction, by EOA", async () => {
96+
it("should revert, when contract is paused", async () => {
97+
await gateway.setPaused(true);
98+
await expect(
99+
gateway.connect(signer)["sendTransaction(address,uint256,uint256,bytes)"](signer.address, 0, 0, "0x")
100+
).to.revertedWith("Pausable: paused");
101+
});
102+
103+
it("should revert, when call is not EOA", async () => {
104+
const tx = await gateway.populateTransaction["sendTransaction(address,uint256,uint256,bytes)"](
105+
signer.address,
106+
0,
107+
0,
108+
"0x"
109+
);
110+
await expect(caller.callTarget(gateway.address, tx.data!)).to.revertedWith(
111+
"Only EOA senders are allowed to send enforced transaction"
112+
);
113+
});
114+
115+
it("should revert, when insufficient value for fee", async () => {
116+
const fee = await queue.estimateCrossDomainMessageFee(1000000);
117+
await expect(
118+
gateway
119+
.connect(signer)
120+
["sendTransaction(address,uint256,uint256,bytes)"](signer.address, 0, 1000000, "0x", { value: fee.sub(1) })
121+
).to.revertedWith("Insufficient value for fee");
122+
});
123+
124+
it("should revert, when failed to deduct the fee", async () => {
125+
await gateway.updateFeeVault(gateway.address);
126+
const fee = await queue.estimateCrossDomainMessageFee(1000000);
127+
await expect(
128+
gateway
129+
.connect(signer)
130+
["sendTransaction(address,uint256,uint256,bytes)"](signer.address, 0, 1000000, "0x", { value: fee })
131+
).to.revertedWith("Failed to deduct the fee");
132+
});
133+
134+
it("should succeed, no refund", async () => {
135+
const fee = await queue.estimateCrossDomainMessageFee(1000000);
136+
const feeVaultBalanceBefore = await ethers.provider.getBalance(feeVault.address);
137+
await expect(
138+
gateway
139+
.connect(signer)
140+
["sendTransaction(address,uint256,uint256,bytes)"](deployer.address, 0, 1000000, "0x", { value: fee })
141+
)
142+
.to.emit(queue, "QueueTransaction")
143+
.withArgs(signer.address, deployer.address, 0, 0, 1000000, "0x");
144+
const feeVaultBalanceAfter = await ethers.provider.getBalance(feeVault.address);
145+
expect(feeVaultBalanceAfter.sub(feeVaultBalanceBefore)).to.eq(fee);
146+
});
147+
148+
it("should succeed, with refund", async () => {
149+
const fee = await queue.estimateCrossDomainMessageFee(1000000);
150+
const feeVaultBalanceBefore = await ethers.provider.getBalance(feeVault.address);
151+
const signerBalanceBefore = await ethers.provider.getBalance(signer.address);
152+
const tx = await gateway
153+
.connect(signer)
154+
["sendTransaction(address,uint256,uint256,bytes)"](deployer.address, 0, 1000000, "0x", { value: fee.add(100) });
155+
await expect(tx)
156+
.to.emit(queue, "QueueTransaction")
157+
.withArgs(signer.address, deployer.address, 0, 0, 1000000, "0x");
158+
const receipt = await tx.wait();
159+
const feeVaultBalanceAfter = await ethers.provider.getBalance(feeVault.address);
160+
const signerBalanceAfter = await ethers.provider.getBalance(signer.address);
161+
expect(feeVaultBalanceAfter.sub(feeVaultBalanceBefore)).to.eq(fee);
162+
expect(signerBalanceBefore.sub(signerBalanceAfter)).to.eq(
163+
receipt.gasUsed.mul(receipt.effectiveGasPrice).add(fee)
164+
);
165+
});
166+
});
167+
168+
context("#sendTransaction, with signatures", async () => {
169+
const getSignature = async (
170+
signer: SignerWithAddress,
171+
target: string,
172+
value: BigNumberish,
173+
gasLimit: BigNumberish,
174+
data: BytesLike
175+
): Promise<string> => {
176+
const queueIndex = await queue.nextCrossDomainMessageIndex();
177+
const txHash = await queue.computeTransactionHash(signer.address, queueIndex, value, target, gasLimit, data);
178+
return await signer.signMessage(utils.arrayify(txHash));
179+
};
180+
181+
it("should revert, when contract is paused", async () => {
182+
await gateway.setPaused(true);
183+
await expect(
184+
gateway
185+
.connect(deployer)
186+
["sendTransaction(address,address,uint256,uint256,bytes,bytes,address)"](
187+
signer.address,
188+
signer.address,
189+
0,
190+
0,
191+
"0x",
192+
"0x",
193+
constants.AddressZero
194+
)
195+
).to.revertedWith("Pausable: paused");
196+
});
197+
198+
it("should revert, when signatue is wrong", async () => {
199+
const signature = await signer.signMessage("0x00");
200+
await expect(
201+
gateway
202+
.connect(deployer)
203+
["sendTransaction(address,address,uint256,uint256,bytes,bytes,address)"](
204+
signer.address,
205+
signer.address,
206+
0,
207+
0,
208+
"0x",
209+
signature,
210+
constants.AddressZero
211+
)
212+
).to.revertedWith("Incorrect signature");
213+
});
214+
215+
it("should revert, when insufficient value for fee", async () => {
216+
const signature = await getSignature(signer, signer.address, 0, 1000000, "0x");
217+
const fee = await queue.estimateCrossDomainMessageFee(1000000);
218+
await expect(
219+
gateway
220+
.connect(deployer)
221+
["sendTransaction(address,address,uint256,uint256,bytes,bytes,address)"](
222+
signer.address,
223+
signer.address,
224+
0,
225+
1000000,
226+
"0x",
227+
signature,
228+
signer.address,
229+
{ value: fee.sub(1) }
230+
)
231+
).to.revertedWith("Insufficient value for fee");
232+
});
233+
234+
it("should revert, when failed to deduct the fee", async () => {
235+
await gateway.updateFeeVault(gateway.address);
236+
const signature = await getSignature(signer, signer.address, 0, 1000000, "0x");
237+
const fee = await queue.estimateCrossDomainMessageFee(1000000);
238+
await expect(
239+
gateway
240+
.connect(deployer)
241+
["sendTransaction(address,address,uint256,uint256,bytes,bytes,address)"](
242+
signer.address,
243+
signer.address,
244+
0,
245+
1000000,
246+
"0x",
247+
signature,
248+
signer.address,
249+
{ value: fee }
250+
)
251+
).to.revertedWith("Failed to deduct the fee");
252+
});
253+
254+
it("should succeed, no refund", async () => {
255+
const signature = await getSignature(signer, deployer.address, 0, 1000000, "0x");
256+
const fee = await queue.estimateCrossDomainMessageFee(1000000);
257+
const feeVaultBalanceBefore = await ethers.provider.getBalance(feeVault.address);
258+
await expect(
259+
gateway
260+
.connect(deployer)
261+
["sendTransaction(address,address,uint256,uint256,bytes,bytes,address)"](
262+
signer.address,
263+
deployer.address,
264+
0,
265+
1000000,
266+
"0x",
267+
signature,
268+
signer.address,
269+
{ value: fee }
270+
)
271+
)
272+
.to.emit(queue, "QueueTransaction")
273+
.withArgs(signer.address, deployer.address, 0, 0, 1000000, "0x");
274+
const feeVaultBalanceAfter = await ethers.provider.getBalance(feeVault.address);
275+
expect(feeVaultBalanceAfter.sub(feeVaultBalanceBefore)).to.eq(fee);
276+
});
277+
278+
it("should succeed, with refund", async () => {
279+
const signature = await getSignature(signer, deployer.address, 0, 1000000, "0x");
280+
const fee = await queue.estimateCrossDomainMessageFee(1000000);
281+
const feeVaultBalanceBefore = await ethers.provider.getBalance(feeVault.address);
282+
const signerBalanceBefore = await ethers.provider.getBalance(signer.address);
283+
await expect(
284+
gateway
285+
.connect(deployer)
286+
["sendTransaction(address,address,uint256,uint256,bytes,bytes,address)"](
287+
signer.address,
288+
deployer.address,
289+
0,
290+
1000000,
291+
"0x",
292+
signature,
293+
signer.address,
294+
{ value: fee.add(100) }
295+
)
296+
)
297+
.to.emit(queue, "QueueTransaction")
298+
.withArgs(signer.address, deployer.address, 0, 0, 1000000, "0x");
299+
const feeVaultBalanceAfter = await ethers.provider.getBalance(feeVault.address);
300+
const signerBalanceAfter = await ethers.provider.getBalance(signer.address);
301+
expect(feeVaultBalanceAfter.sub(feeVaultBalanceBefore)).to.eq(fee);
302+
expect(signerBalanceAfter.sub(signerBalanceBefore)).to.eq(100);
303+
});
304+
305+
it("should revert, when refund failed", async () => {
306+
const signature = await getSignature(signer, signer.address, 0, 1000000, "0x");
307+
const fee = await queue.estimateCrossDomainMessageFee(1000000);
308+
await expect(
309+
gateway
310+
.connect(deployer)
311+
["sendTransaction(address,address,uint256,uint256,bytes,bytes,address)"](
312+
signer.address,
313+
signer.address,
314+
0,
315+
1000000,
316+
"0x",
317+
signature,
318+
gateway.address,
319+
{ value: fee.add(100) }
320+
)
321+
).to.revertedWith("Failed to refund the fee");
322+
});
323+
});
324+
});

0 commit comments

Comments
 (0)