Skip to content

feat: euclid phase-2 contract changes #74

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 37 commits into from
Mar 24, 2025
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
90191fd
ScrollChain changes for Euclid upgrade
zimpha Jan 17, 2025
0a29b4e
add ZkEvmVerifierPostEuclid and fix old unit tests
zimpha Jan 20, 2025
ff5a39b
use verson=5 for initial fork batch
zimpha Jan 21, 2025
5a64965
use only function for commit batch
zimpha Jan 22, 2025
37c1566
fix v5 codec and add unit tests
zimpha Jan 23, 2025
20b8173
fix lcov error
zimpha Jan 23, 2025
1934e40
sanity check for v5 batch
zimpha Jan 23, 2025
42cc20e
fix _loadBatchHeader
zimpha Jan 24, 2025
0fd1107
use finalizeBundleWithProof for v4 and v6 batches
zimpha Jan 24, 2025
307224d
cleanup imports
zimpha Feb 10, 2025
81f0db7
fix failing unit tests in foundry v1.0
zimpha Feb 10, 2025
691609b
chained l1 message queue (#69)
zimpha Feb 13, 2025
e2a3e48
add system contract (#75)
zimpha Feb 13, 2025
19fdf2a
feat: enforced batch mode (#68)
zimpha Feb 13, 2025
c382953
feat: adjust deployment scripts (#76)
Thegaram Feb 14, 2025
4f2b9f7
add enforced messages
zimpha Feb 14, 2025
b5437c5
cleanup comments and useless errors
zimpha Feb 17, 2025
cc7de1d
remove EOA require in EnforcedTxGateway
zimpha Feb 18, 2025
f4fda1f
Merge branch 'feat/mpt_migration' into feat/euclid_phase_2
Thegaram Feb 18, 2025
d5b78fe
unit tests for L1MessageQueueV2 and SystemConfig
zimpha Feb 18, 2025
d82fcf9
fix remaining unit tests
zimpha Feb 18, 2025
9cbede1
feat(euclid-2): adjust test deployment scripts (#77)
Thegaram Feb 19, 2025
256416f
feat: euclid phase-2 nitpicks (spelling, wording, etc.) (#78)
Thegaram Feb 20, 2025
ab77ed4
fix comments
zimpha Feb 20, 2025
a6d7627
fix comments
zimpha Feb 20, 2025
9646f2c
add comments
Thegaram Feb 20, 2025
c0d6c65
move BatchNotCommitted check to _loadBatchHeader
zimpha Feb 21, 2025
46f920d
remove mock function; add parentBatchHash to commitBatches
zimpha Feb 26, 2025
744c764
emit RevertBatch when enter enforced mode
zimpha Feb 26, 2025
f56af34
change lastProcessedQueueIndex to totalL1MessagesPoppedOverall
zimpha Feb 26, 2025
e969619
clear committedBatches during revert
zimpha Feb 28, 2025
de8fa12
remove outdated comments
zimpha Feb 28, 2025
711723f
fix finalizeBundlePostEuclidV2NoProof in ScrollChainMockFinalize
zimpha Mar 3, 2025
da37b40
add ScrollChain interface containing all methods (#80)
zimpha Mar 5, 2025
22d3f76
add importGenesisBatch to ScrollChainInterface
zimpha Mar 10, 2025
0ca6632
add public view functions to ScrollChainInterface
zimpha Mar 11, 2025
1a44cc4
Merge branch 'main' into feat/euclid_phase_2
Thegaram Mar 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/contracts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ jobs:
run : forge coverage --evm-version cancun --report lcov

- name : Prune coverage
run : lcov --rc branch_coverage=1 --remove ./lcov.info -o ./lcov.info.pruned 'src/mocks/*' 'src/test/*' 'scripts/*' 'node_modules/*' 'lib/*'
run : lcov --rc branch_coverage=1 --remove ./lcov.info -o ./lcov.info.pruned 'src/mocks/*' 'src/test/*' 'scripts/*' 'node_modules/*' 'lib/*' --ignore-errors unused,unused

- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
Expand Down
226 changes: 120 additions & 106 deletions hardhat-test/EnforcedTxGateway.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@
/* eslint-disable node/no-missing-import */
import { HardhatEthersSigner, SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers";
import { expect } from "chai";
import { BigNumberish, BytesLike, MaxUint256, ZeroAddress, getBytes } from "ethers";
import { ethers } from "hardhat";
import { BigNumberish, BytesLike, MaxUint256, ZeroAddress, getAddress, getBytes, toBeHex } from "ethers";
import { ethers, network } from "hardhat";

import { EnforcedTxGateway, L1MessageQueue, L2GasPriceOracle, MockCaller } from "../typechain";
import { EnforcedTxGateway, L1MessageQueueV2, MockCaller, SystemConfig } from "../typechain";

describe("EnforcedTxGateway.spec", async () => {
let deployer: HardhatEthersSigner;
let feeVault: HardhatEthersSigner;
let signer: HardhatEthersSigner;

let caller: MockCaller;
let system: SystemConfig;
let gateway: EnforcedTxGateway;
let oracle: L2GasPriceOracle;
let queue: L1MessageQueue;
let queue: L1MessageQueueV2;

const deployProxy = async (name: string, admin: string, args: any[]): Promise<string> => {
const TransparentUpgradeableProxy = await ethers.getContractFactory("TransparentUpgradeableProxy", deployer);
Expand All @@ -28,44 +28,57 @@ describe("EnforcedTxGateway.spec", async () => {
beforeEach(async () => {
[deployer, feeVault, signer] = await ethers.getSigners();

const EmptyContract = await ethers.getContractFactory("EmptyContract", deployer);
const empty = await EmptyContract.deploy();

const ProxyAdmin = await ethers.getContractFactory("ProxyAdmin", deployer);
const admin = await ProxyAdmin.deploy();

gateway = await ethers.getContractAt(
"EnforcedTxGateway",
await deployProxy("EnforcedTxGateway", await admin.getAddress(), []),
deployer
);
const TransparentUpgradeableProxy = await ethers.getContractFactory("TransparentUpgradeableProxy", deployer);
const L1MessageQueueV1 = await ethers.getContractFactory("L1MessageQueueV1", deployer);
const L1MessageQueueV2 = await ethers.getContractFactory("L1MessageQueueV2", deployer);
const EnforcedTxGateway = await ethers.getContractFactory("EnforcedTxGateway", deployer);

queue = await ethers.getContractAt(
"L1MessageQueue",
await deployProxy("L1MessageQueue", await admin.getAddress(), [
deployer.address,
deployer.address,
await gateway.getAddress(),
]),
system = await ethers.getContractAt(
"SystemConfig",
await deployProxy("SystemConfig", await admin.getAddress(), []),
deployer
);

oracle = await ethers.getContractAt(
"L2GasPriceOracle",
await deployProxy("L2GasPriceOracle", await admin.getAddress(), []),
deployer
const queueV1Proxy = await TransparentUpgradeableProxy.deploy(empty.getAddress(), admin.getAddress(), "0x");
const queueV2Proxy = await TransparentUpgradeableProxy.deploy(empty.getAddress(), admin.getAddress(), "0x");
const gatewayProxy = await TransparentUpgradeableProxy.deploy(empty.getAddress(), admin.getAddress(), "0x");

const queueV1Impl = await L1MessageQueueV1.deploy(deployer.address, deployer.address, gatewayProxy.getAddress());
const queueV2Impl = await L1MessageQueueV2.deploy(
deployer.address,
deployer.address,
gatewayProxy.getAddress(),
queueV1Proxy.getAddress(),
system.getAddress()
);
const gatewayImpl = await EnforcedTxGateway.deploy(queueV2Proxy.getAddress(), feeVault.address);
await admin.upgrade(queueV1Proxy.getAddress(), queueV1Impl.getAddress());
await admin.upgrade(queueV2Proxy.getAddress(), queueV2Impl.getAddress());
await admin.upgrade(gatewayProxy.getAddress(), gatewayImpl.getAddress());

gateway = await ethers.getContractAt("EnforcedTxGateway", await gatewayProxy.getAddress(), deployer);
queue = await ethers.getContractAt("L1MessageQueueV2", await queueV2Proxy.getAddress(), deployer);

const MockCaller = await ethers.getContractFactory("MockCaller", deployer);
caller = await MockCaller.deploy();

await queue.initialize(ZeroAddress, ZeroAddress, ZeroAddress, oracle.getAddress(), 10000000);
await gateway.initialize(queue.getAddress(), feeVault.address);
await oracle.initialize(21000, 51000, 8, 16);

const Whitelist = await ethers.getContractFactory("Whitelist", deployer);
const whitelist = await Whitelist.deploy(deployer.address);

await whitelist.updateWhitelistStatus([deployer.address], true);
await oracle.updateWhitelist(whitelist.getAddress());
await oracle.setL2BaseFee(1);
await system.initialize(
deployer.address,
deployer.address,
{
maxGasLimit: 1000000,
baseFeeOverhead: 10n ** 9n,
baseFeeScalar: 10n ** 18n,
},
{ maxDelayEnterEnforcedMode: 0, maxDelayMessageQueue: 0 }
);
await queue.initialize();
await gateway.initialize();
});

context("auth", async () => {
Expand All @@ -77,25 +90,7 @@ describe("EnforcedTxGateway.spec", async () => {
});

it("should revert, when initialize again", async () => {
await expect(gateway.initialize(ZeroAddress, ZeroAddress)).to.revertedWith(
"Initializable: contract is already initialized"
);
});

context("#updateFeeVault", async () => {
it("should revert, when non-owner call", async () => {
await expect(gateway.connect(signer).updateFeeVault(ZeroAddress)).to.revertedWith(
"Ownable: caller is not the owner"
);
});

it("should succeed", async () => {
expect(await gateway.feeVault()).to.eq(feeVault.address);
await expect(gateway.updateFeeVault(deployer.address))
.to.emit(gateway, "UpdateFeeVault")
.withArgs(feeVault.address, deployer.address);
expect(await gateway.feeVault()).to.eq(deployer.address);
});
await expect(gateway.initialize()).to.revertedWith("Initializable: contract is already initialized");
});

context("#setPause", async () => {
Expand All @@ -121,66 +116,102 @@ describe("EnforcedTxGateway.spec", async () => {
).to.revertedWith("Pausable: paused");
});

it("should revert, when call is not EOA", async () => {
const calldata = gateway.interface.encodeFunctionData("sendTransaction(address,uint256,uint256,bytes)", [
signer.address,
0,
0,
"0x",
]);
await expect(caller.callTarget(gateway.getAddress(), calldata)).to.revertedWith(
"Only EOA senders are allowed to send enforced transaction"
);
});

it("should revert, when insufficient value for fee", async () => {
const fee = await queue.estimateCrossDomainMessageFee(1000000);
await network.provider.send("hardhat_setNextBlockBaseFeePerGas", ["0"]);
await expect(
gateway
.connect(signer)
["sendTransaction(address,uint256,uint256,bytes)"](signer.address, 0, 1000000, "0x", { value: fee - 1n })
).to.revertedWith("Insufficient value for fee");
});

it("should revert, when failed to deduct the fee", async () => {
await gateway.updateFeeVault(gateway.getAddress());
const fee = await queue.estimateCrossDomainMessageFee(1000000);
await expect(
gateway
.connect(signer)
["sendTransaction(address,uint256,uint256,bytes)"](signer.address, 0, 1000000, "0x", { value: fee })
).to.revertedWith("Failed to deduct the fee");
});

it("should succeed, no refund", async () => {
it("should succeed, with refund", async () => {
const fee = await queue.estimateCrossDomainMessageFee(1000000);
const feeVaultBalanceBefore = await ethers.provider.getBalance(feeVault.address);
await expect(
gateway
.connect(signer)
["sendTransaction(address,uint256,uint256,bytes)"](deployer.address, 0, 1000000, "0x", { value: fee })
)
const signerBalanceBefore = await ethers.provider.getBalance(signer.address);
await network.provider.send("hardhat_setNextBlockBaseFeePerGas", ["0"]);
const tx = await gateway
.connect(signer)
["sendTransaction(address,uint256,uint256,bytes)"](deployer.address, 0, 1000000, "0x", { value: fee + 100n });
await expect(tx)
.to.emit(queue, "QueueTransaction")
.withArgs(signer.address, deployer.address, 0, 0, 1000000, "0x");
const receipt = await tx.wait();
const feeVaultBalanceAfter = await ethers.provider.getBalance(feeVault.address);
const signerBalanceAfter = await ethers.provider.getBalance(signer.address);
expect(feeVaultBalanceAfter - feeVaultBalanceBefore).to.eq(fee);
expect(signerBalanceBefore - signerBalanceAfter).to.eq(receipt!.gasUsed * receipt!.gasPrice + fee);
});
});

context("#sendTransaction, by Contract", async () => {
it("should revert, when contract is paused", async () => {
await gateway.setPause(true);
await expect(
caller.callTarget(
gateway.getAddress(),
gateway.interface.encodeFunctionData("sendTransaction(address,uint256,uint256,bytes)", [
signer.address,
0,
0,
"0x",
])
)
).to.revertedWith("Pausable: paused");
});

it("should revert, when insufficient value for fee", async () => {
const fee = await queue.estimateCrossDomainMessageFee(1000000);
await network.provider.send("hardhat_setNextBlockBaseFeePerGas", ["0"]);
await expect(
caller.callTarget(
gateway.getAddress(),
gateway.interface.encodeFunctionData("sendTransaction(address,uint256,uint256,bytes)", [
signer.address,
0,
1000000,
"0x",
]),
{ value: fee - 1n }
)
).to.revertedWith("Insufficient value for fee");
});

it("should succeed, with refund", async () => {
const fee = await queue.estimateCrossDomainMessageFee(1000000);
const callerBalanceBefore = await ethers.provider.getBalance(caller.getAddress());
const feeVaultBalanceBefore = await ethers.provider.getBalance(feeVault.address);
const signerBalanceBefore = await ethers.provider.getBalance(signer.address);
const tx = await gateway
await network.provider.send("hardhat_setNextBlockBaseFeePerGas", ["0"]);
const sender = getAddress(
"0x" +
toBeHex(
(BigInt(await caller.getAddress()) + BigInt("0x1111000000000000000000000000000000001111")) % 2n ** 160n
)
.slice(2)
.padStart(40, "0")
);
const tx = await caller
.connect(signer)
["sendTransaction(address,uint256,uint256,bytes)"](deployer.address, 0, 1000000, "0x", { value: fee + 100n });
await expect(tx)
.to.emit(queue, "QueueTransaction")
.withArgs(signer.address, deployer.address, 0, 0, 1000000, "0x");
.callTarget(
gateway.getAddress(),
gateway.interface.encodeFunctionData("sendTransaction(address,uint256,uint256,bytes)", [
deployer.address,
0,
1000000,
"0x",
]),
{ value: fee + 100n }
);
await expect(tx).to.emit(queue, "QueueTransaction").withArgs(sender, deployer.address, 0, 0, 1000000, "0x");
const receipt = await tx.wait();
const callerBalanceAfter = await ethers.provider.getBalance(caller.getAddress());
const feeVaultBalanceAfter = await ethers.provider.getBalance(feeVault.address);
const signerBalanceAfter = await ethers.provider.getBalance(signer.address);
expect(callerBalanceAfter - callerBalanceBefore).to.eq(100n);
expect(feeVaultBalanceAfter - feeVaultBalanceBefore).to.eq(fee);
expect(signerBalanceBefore - signerBalanceAfter).to.eq(receipt!.gasUsed * receipt!.gasPrice + fee);
expect(signerBalanceBefore - signerBalanceAfter).to.eq(receipt!.gasUsed * receipt!.gasPrice + fee + 100n);
});
});

Expand Down Expand Up @@ -303,6 +334,7 @@ describe("EnforcedTxGateway.spec", async () => {
it("should revert, when insufficient value for fee", async () => {
const signature = await getSignature(signer, signer.address, 0, 1000000, "0x");
const fee = await queue.estimateCrossDomainMessageFee(1000000);
await network.provider.send("hardhat_setNextBlockBaseFeePerGas", ["0"]);
await expect(
gateway
.connect(deployer)
Expand All @@ -320,32 +352,12 @@ describe("EnforcedTxGateway.spec", async () => {
).to.revertedWith("Insufficient value for fee");
});

it("should revert, when failed to deduct the fee", async () => {
await gateway.updateFeeVault(gateway.getAddress());
const signature = await getSignature(signer, signer.address, 0, 1000000, "0x");
const fee = await queue.estimateCrossDomainMessageFee(1000000);
await expect(
gateway
.connect(deployer)
["sendTransaction(address,address,uint256,uint256,bytes,uint256,bytes,address)"](
signer.address,
signer.address,
0,
1000000,
"0x",
MaxUint256,
signature,
signer.address,
{ value: fee }
)
).to.revertedWith("Failed to deduct the fee");
});

it("should succeed, no refund", async () => {
const signature = await getSignature(signer, deployer.address, 0, 1000000, "0x");
const fee = await queue.estimateCrossDomainMessageFee(1000000);
const feeVaultBalanceBefore = await ethers.provider.getBalance(feeVault.address);
expect(await gateway.nonces(signer.address)).to.eq(0);
await network.provider.send("hardhat_setNextBlockBaseFeePerGas", ["0"]);
await expect(
gateway
.connect(deployer)
Expand Down Expand Up @@ -391,6 +403,7 @@ describe("EnforcedTxGateway.spec", async () => {
const feeVaultBalanceBefore = await ethers.provider.getBalance(feeVault.address);
const signerBalanceBefore = await ethers.provider.getBalance(signer.address);
expect(await gateway.nonces(signer.address)).to.eq(0);
await network.provider.send("hardhat_setNextBlockBaseFeePerGas", ["0"]);
await expect(
gateway
.connect(deployer)
Expand Down Expand Up @@ -435,6 +448,7 @@ describe("EnforcedTxGateway.spec", async () => {
it("should revert, when refund failed", async () => {
const signature = await getSignature(signer, signer.address, 0, 1000000, "0x1234");
const fee = await queue.estimateCrossDomainMessageFee(1000000);
await network.provider.send("hardhat_setNextBlockBaseFeePerGas", ["0"]);
await expect(
gateway
.connect(deployer)
Expand Down
Loading
Loading