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 17 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
72 changes: 44 additions & 28 deletions hardhat-test/EnforcedTxGateway.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ import { expect } from "chai";
import { BigNumberish, BytesLike, MaxUint256, ZeroAddress, getBytes } from "ethers";
import { ethers } from "hardhat";

import { EnforcedTxGateway, L1MessageQueue, L2GasPriceOracle, MockCaller } from "../typechain";
import { EnforcedTxGateway, L1MessageQueueV2, L2GasPriceOracle, 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 @@ -37,12 +38,30 @@ describe("EnforcedTxGateway.spec", async () => {
deployer
);

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

const queueV1 = await ethers.getContractAt(
"L1MessageQueueV1",
await deployProxy("L1MessageQueueV1", await admin.getAddress(), [
deployer.address,
deployer.address,
await gateway.getAddress(),
]),
deployer
);

queue = await ethers.getContractAt(
"L1MessageQueue",
await deployProxy("L1MessageQueue", await admin.getAddress(), [
"L1MessageQueueV2",
await deployProxy("L1MessageQueueV2", await admin.getAddress(), [
deployer.address,
deployer.address,
await gateway.getAddress(),
await queueV1.getAddress(),
await system.getAddress(),
]),
deployer
);
Expand All @@ -56,7 +75,17 @@ describe("EnforcedTxGateway.spec", async () => {
const MockCaller = await ethers.getContractFactory("MockCaller", deployer);
caller = await MockCaller.deploy();

await queue.initialize(ZeroAddress, ZeroAddress, ZeroAddress, oracle.getAddress(), 10000000);
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(queue.getAddress(), feeVault.address);
await oracle.initialize(21000, 51000, 8, 16);

Expand Down Expand Up @@ -133,7 +162,7 @@ describe("EnforcedTxGateway.spec", async () => {
);
});

it("should revert, when insufficient value for fee", async () => {
it.skip("should revert, when insufficient value for fee", async () => {
const fee = await queue.estimateCrossDomainMessageFee(1000000);
await expect(
gateway
Expand All @@ -142,31 +171,18 @@ describe("EnforcedTxGateway.spec", async () => {
).to.revertedWith("Insufficient value for fee");
});

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

it("should succeed, no 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 })
)
.to.emit(queue, "QueueTransaction")
.withArgs(signer.address, deployer.address, 0, 0, 1000000, "0x");
const feeVaultBalanceAfter = await ethers.provider.getBalance(feeVault.address);
expect(feeVaultBalanceAfter - feeVaultBalanceBefore).to.eq(fee);
});

it("should succeed, with refund", async () => {
it.skip("should succeed, with refund", async () => {
const fee = await queue.estimateCrossDomainMessageFee(1000000);
const feeVaultBalanceBefore = await ethers.provider.getBalance(feeVault.address);
const signerBalanceBefore = await ethers.provider.getBalance(signer.address);
Expand Down Expand Up @@ -300,7 +316,7 @@ describe("EnforcedTxGateway.spec", async () => {
).to.revertedWith("Incorrect signature");
});

it("should revert, when insufficient value for fee", async () => {
it.skip("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 expect(
Expand All @@ -320,7 +336,7 @@ describe("EnforcedTxGateway.spec", async () => {
).to.revertedWith("Insufficient value for fee");
});

it("should revert, when failed to deduct the fee", async () => {
it.skip("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);
Expand All @@ -341,7 +357,7 @@ describe("EnforcedTxGateway.spec", async () => {
).to.revertedWith("Failed to deduct the fee");
});

it("should succeed, no refund", async () => {
it.skip("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);
Expand Down Expand Up @@ -385,7 +401,7 @@ describe("EnforcedTxGateway.spec", async () => {
).to.revertedWith("Incorrect signature");
});

it("should succeed, with refund", async () => {
it.skip("should succeed, with 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);
Expand Down Expand Up @@ -432,7 +448,7 @@ describe("EnforcedTxGateway.spec", async () => {
).to.revertedWith("Incorrect signature");
});

it("should revert, when refund failed", async () => {
it.skip("should revert, when refund failed", async () => {
const signature = await getSignature(signer, signer.address, 0, 1000000, "0x1234");
const fee = await queue.estimateCrossDomainMessageFee(1000000);
await expect(
Expand Down
8 changes: 4 additions & 4 deletions hardhat-test/L1MessageQueue.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { expect } from "chai";
import { ethers } from "hardhat";

import { L1MessageQueue, L2GasPriceOracle } from "../typechain";
import { L1MessageQueueV1, L2GasPriceOracle } from "../typechain";
import { MaxUint256, ZeroAddress, concat, encodeRlp, hexlify, keccak256, randomBytes, toBeHex } from "ethers";

describe("L1MessageQueue", async () => {
Expand All @@ -14,7 +14,7 @@ describe("L1MessageQueue", async () => {
let gateway: HardhatEthersSigner;

let oracle: L2GasPriceOracle;
let queue: L1MessageQueue;
let queue: L1MessageQueueV1;

const deployProxy = async (name: string, admin: string, args: any[]): Promise<string> => {
const TransparentUpgradeableProxy = await ethers.getContractFactory("TransparentUpgradeableProxy", deployer);
Expand All @@ -31,8 +31,8 @@ describe("L1MessageQueue", async () => {
const admin = await ProxyAdmin.deploy();

queue = await ethers.getContractAt(
"L1MessageQueue",
await deployProxy("L1MessageQueue", await admin.getAddress(), [
"L1MessageQueueV1",
await deployProxy("L1MessageQueueV1", await admin.getAddress(), [
messenger.address,
scrollChain.address,
gateway.address,
Expand Down
7 changes: 6 additions & 1 deletion hardhat-test/ZkEvmVerifierV2.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,12 @@ describe("ZkEvmVerifierV2", async () => {
const chainProxy = await TransparentUpgradeableProxy.deploy(empty.getAddress(), admin.getAddress(), "0x");

const ScrollChainMockBlob = await ethers.getContractFactory("ScrollChainMockBlob", deployer);
const chainImpl = await ScrollChainMockBlob.deploy(layer2ChainId, deployer.address, verifier.getAddress());
const chainImpl = await ScrollChainMockBlob.deploy(
layer2ChainId,
deployer.address,
deployer.address,
verifier.getAddress()
);
await admin.upgrade(chainProxy.getAddress(), chainImpl.getAddress());

chain = await ethers.getContractAt("ScrollChainMockBlob", await chainProxy.getAddress(), deployer);
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"eslint-plugin-promise": "^6.1.1",
"ethereum-waffle": "^3.0.0",
"ethers": "^6.11.1",
"hardhat": "^2.22.6",
"hardhat": "^2.22.17",
"hardhat-gas-reporter": "^1.0.4",
"husky": "^8.0.1",
"lint-staged": "^13.0.3",
Expand Down
13 changes: 10 additions & 3 deletions scripts/foundry/DeployL1BridgeContracts.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {L1ERC1155Gateway} from "../../src/L1/gateways/L1ERC1155Gateway.sol";
import {L1ERC721Gateway} from "../../src/L1/gateways/L1ERC721Gateway.sol";
import {L1ETHGateway} from "../../src/L1/gateways/L1ETHGateway.sol";
import {L1GatewayRouter} from "../../src/L1/gateways/L1GatewayRouter.sol";
import {L1MessageQueueWithGasPriceOracle} from "../../src/L1/rollup/L1MessageQueueWithGasPriceOracle.sol";
import {L1MessageQueueV1WithGasPriceOracle} from "../../src/L1/rollup/L1MessageQueueV1WithGasPriceOracle.sol";
import {L1ScrollMessenger} from "../../src/L1/L1ScrollMessenger.sol";
import {L1StandardERC20Gateway} from "../../src/L1/gateways/L1StandardERC20Gateway.sol";
import {L1WETHGateway} from "../../src/L1/gateways/L1WETHGateway.sol";
Expand Down Expand Up @@ -109,13 +109,19 @@ contract DeployL1BridgeContracts is Script {
}

function deployScrollChain() internal {
ScrollChain impl = new ScrollChain(CHAIN_ID_L2, L1_MESSAGE_QUEUE_PROXY_ADDR, address(rollupVerifier));
ScrollChain impl = new ScrollChain(
CHAIN_ID_L2,
L1_MESSAGE_QUEUE_PROXY_ADDR,
L1_MESSAGE_QUEUE_PROXY_ADDR,
address(rollupVerifier),
address(0)
);

logAddress("L1_SCROLL_CHAIN_IMPLEMENTATION_ADDR", address(impl));
}

function deployL1MessageQueue() internal {
L1MessageQueueWithGasPriceOracle impl = new L1MessageQueueWithGasPriceOracle(
L1MessageQueueV1WithGasPriceOracle impl = new L1MessageQueueV1WithGasPriceOracle(
L1_SCROLL_MESSENGER_PROXY_ADDR,
L1_SCROLL_CHAIN_PROXY_ADDR,
address(enforcedTxGateway)
Expand All @@ -127,6 +133,7 @@ contract DeployL1BridgeContracts is Script {
L1ScrollMessenger impl = new L1ScrollMessenger(
L2_SCROLL_MESSENGER_PROXY_ADDR,
L1_SCROLL_CHAIN_PROXY_ADDR,
L1_MESSAGE_QUEUE_PROXY_ADDR,
L1_MESSAGE_QUEUE_PROXY_ADDR
);

Expand Down
22 changes: 22 additions & 0 deletions scripts/foundry/DeployL1SystemConfig.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.24;

import {Script} from "forge-std/Script.sol";
import {SystemConfig} from "../../src/L1/system-contract/SystemConfig.sol"; // adjust the relative path as necessary
import {console} from "forge-std/console.sol";

contract DeployL1SystemConfig is Script {
function run() external {
// Retrieve the deployer private key from environment variables
uint256 deployerKey = vm.envUint("L1_DEPLOYER_PRIVATE_KEY");

vm.startBroadcast(deployerKey);

// Deploy the SystemConfig contract with the specified owner.
SystemConfig sysConfig = new SystemConfig();

console.log("Deployed SystemConfig Implementation at address:", address(sysConfig));

vm.stopBroadcast();
}
}
10 changes: 5 additions & 5 deletions scripts/foundry/InitializeL1BridgeContracts.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import {L1StandardERC20Gateway} from "../../src/L1/gateways/L1StandardERC20Gatew
import {L1WETHGateway} from "../../src/L1/gateways/L1WETHGateway.sol";
import {MultipleVersionRollupVerifier} from "../../src/L1/rollup/MultipleVersionRollupVerifier.sol";
import {ScrollChain} from "../../src/L1/rollup/ScrollChain.sol";
import {L1MessageQueue} from "../../src/L1/rollup/L1MessageQueue.sol";
import {L1MessageQueueWithGasPriceOracle} from "../../src/L1/rollup/L1MessageQueueWithGasPriceOracle.sol";
import {L1MessageQueueV1} from "../../src/L1/rollup/L1MessageQueueV1.sol";
import {L1MessageQueueV1WithGasPriceOracle} from "../../src/L1/rollup/L1MessageQueueV1WithGasPriceOracle.sol";
import {L2GasPriceOracle} from "../../src/L1/rollup/L2GasPriceOracle.sol";
import {EnforcedTxGateway} from "../../src/L1/gateways/EnforcedTxGateway.sol";

Expand Down Expand Up @@ -105,21 +105,21 @@ contract InitializeL1BridgeContracts is Script {
);
L2GasPriceOracle(L2_GAS_PRICE_ORACLE_PROXY_ADDR).updateWhitelist(L1_WHITELIST_ADDR);

// initialize L1MessageQueueWithGasPriceOracle
// initialize L1MessageQueueV1WithGasPriceOracle
proxyAdmin.upgrade(
ITransparentUpgradeableProxy(L1_MESSAGE_QUEUE_PROXY_ADDR),
L1_MESSAGE_QUEUE_IMPLEMENTATION_ADDR
);

L1MessageQueueWithGasPriceOracle(L1_MESSAGE_QUEUE_PROXY_ADDR).initialize(
L1MessageQueueV1WithGasPriceOracle(L1_MESSAGE_QUEUE_PROXY_ADDR).initialize(
L1_SCROLL_MESSENGER_PROXY_ADDR,
L1_SCROLL_CHAIN_PROXY_ADDR,
L1_ENFORCED_TX_GATEWAY_PROXY_ADDR,
L2_GAS_PRICE_ORACLE_PROXY_ADDR,
MAX_L1_MESSAGE_GAS_LIMIT
);

L1MessageQueueWithGasPriceOracle(L1_MESSAGE_QUEUE_PROXY_ADDR).initializeV2();
L1MessageQueueV1WithGasPriceOracle(L1_MESSAGE_QUEUE_PROXY_ADDR).initializeV2();

// initialize L1ScrollMessenger
proxyAdmin.upgrade(
Expand Down
8 changes: 5 additions & 3 deletions scripts/foundry/InitializeL1ScrollOwner.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {L1CustomERC20Gateway} from "../../src/L1/gateways/L1CustomERC20Gateway.s
import {L1ERC1155Gateway} from "../../src/L1/gateways/L1ERC1155Gateway.sol";
import {L1ERC721Gateway} from "../../src/L1/gateways/L1ERC721Gateway.sol";
import {L1GatewayRouter} from "../../src/L1/gateways/L1GatewayRouter.sol";
import {L1MessageQueue} from "../../src/L1/rollup/L1MessageQueue.sol";
import {L1MessageQueueV1} from "../../src/L1/rollup/L1MessageQueueV1.sol";
import {ScrollMessengerBase} from "../../src/libraries/ScrollMessengerBase.sol";
import {L2GasPriceOracle} from "../../src/L1/rollup/L2GasPriceOracle.sol";
import {MultipleVersionRollupVerifier} from "../../src/L1/rollup/MultipleVersionRollupVerifier.sol";
Expand Down Expand Up @@ -63,6 +63,8 @@ contract InitializeL1ScrollOwner is Script {
address L1_ENFORCED_TX_GATEWAY_PROXY_ADDR = vm.envAddress("L1_ENFORCED_TX_GATEWAY_PROXY_ADDR");
address L1_WHITELIST_ADDR = vm.envAddress("L1_WHITELIST_ADDR");

address SYSTEM_CONTRACT_ADDR = vm.envAddress("SYSTEM_CONTRACT_ADDR");

ScrollOwner owner;

function run() external {
Expand Down Expand Up @@ -163,8 +165,8 @@ contract InitializeL1ScrollOwner is Script {

// delay 1 day, scroll multisig
_selectors = new bytes4[](2);
_selectors[0] = L1MessageQueue.updateGasOracle.selector;
_selectors[1] = L1MessageQueue.updateMaxGasLimit.selector;
_selectors[0] = L1MessageQueueV1.updateGasOracle.selector;
_selectors[1] = L1MessageQueueV1.updateMaxGasLimit.selector;
owner.updateAccess(L1_MESSAGE_QUEUE_PROXY_ADDR, _selectors, TIMELOCK_1DAY_DELAY_ROLE, true);
}

Expand Down
47 changes: 47 additions & 0 deletions scripts/foundry/InitializeL1SystemConfig.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.8.24;

import {Script} from "forge-std/Script.sol";
import {SystemConfig} from "../../src/L1/system-contract/SystemConfig.sol";
import {ScrollOwner} from "../../src/misc/ScrollOwner.sol"; // Adjust this path as needed

/**
* @title InitializeL1SystemConfig
* @notice Configures the deployed SystemConfig contract.
* This script grants the Security Council (as defined by L1_SECURITY_COUNCIL_ADDR)
* access to call updateSigner() on the SystemConfig contract with no delay.
*/
contract InitializeL1SystemConfig is Script {
function run() external {
// Retrieve required environment variables.
uint256 deployerKey = vm.envUint("L1_DEPLOYER_PRIVATE_KEY");
address systemConfigAddr = vm.envAddress("SYSTEM_CONTRACT_ADDR");
address securityCouncilAddr = vm.envAddress("L1_SECURITY_COUNCIL_ADDR");
address scrollOwnerAddr = vm.envAddress("L1_SCROLL_OWNER_ADDR");

// Compute the role hash for the Security Council with no delay.
bytes32 SECURITY_COUNCIL_NO_DELAY_ROLE = keccak256("SECURITY_COUNCIL_NO_DELAY_ROLE");

vm.startBroadcast(deployerKey);

// Instantiate the ScrollOwner contract instance which manages access control.
ScrollOwner owner = ScrollOwner(payable(scrollOwnerAddr));
// Instantiate the already-deployed SystemConfig contract.
SystemConfig sys = SystemConfig(systemConfigAddr);

// Prepare a single-element array containing the function selector for updateSigner.
bytes4[] memory selectors = new bytes4[](1);
selectors[0] = sys.updateSigner.selector;

// Grant the SECURITY_COUNCIL_NO_DELAY_ROLE permission on SystemConfig,
// so that the Security Council address can call updateSigner() with no delay.
owner.updateAccess(
systemConfigAddr, // Address of the SystemConfig contract.
selectors, // The function selectors (only updateSigner here).
SECURITY_COUNCIL_NO_DELAY_ROLE,
true // Grant access.
);

vm.stopBroadcast();
}
}
Loading