Feat/122 Add TokenBridge e2e tests (#210)

* fix: add mutex in account manager to avoid nonce issue

* fix: optimize global setup

* Limiting number of concurrent traces API requests for the local stack to avoid occasional OOM-s

* Limiting number of verticles for Traces API node

* Add E2E TokenBridge tests

* fixing test and adding concurency

* fixing test and adding concurency

* fixing test and adding concurency

* fixing test and adding concurency

* fixing nonce management

* deploying l2token for the L2 -> L1 test

* adjusting accounts for L2->L1 test

* adjusting l2TestContractAddress

* use nonce management for L1->L2 test

* adjusting the TestERC20 contract and tests

* rebasing with fix/133-improve-e2e-tests-performance

* fix: update jest config to exit even if there are open handles

* Trying out Besu untuned and raising limit per endpoint to 2 for traces

* Trying out Besu untuned and raising limit per endpoint to 2 for traces and Shomei node

* Using besu untuned for arithmetization as well

* Compile once and parallelise setRemoteTokenBridge

* feat: deploy smart contracts from artifacts + change e2e tests setup

* fix: update pnpm

* fix: remove compile contracts gradle task

* fix: remove compileContracts gradle task

* fix: refactor genesis generator dockerfile + downgrade l1-el-node besu version

* fix: move abi from e2e folder to contract folder + refactor contracts deployments scripts

* feat: add deployment script from artifacts for LineaRollupV6

* update pnpm version in get-started.md

* fix: update console log in deployment scripts

* fix: update besu version + fix deployment scripts

* correct addresses

* fix import

* use abi and bytecode for deployments

* use upgradable beacon for BridgedToken ABI deploys

* use saved abi and bytecode for TestERC20 deploy

* correct deployBridgedTokenAndTokenBridge casing

* optimize token bridge e2e calls

* use explicit message event data

* use precomputed nonces for e2e stack

---------

Co-authored-by: VGau <victorien.gauch@consensys.net>
Co-authored-by: Victorien Gauch <85494462+VGau@users.noreply.github.com>
Co-authored-by: Roman <4833306+Filter94@users.noreply.github.com>
Co-authored-by: thedarkjester <grant.southey@consensys.net>
Co-authored-by: The Dark Jester <thedarkjester@users.noreply.github.com>
This commit is contained in:
Andrei A.
2024-10-31 19:03:53 +02:00
committed by GitHub
parent bf2a760824
commit 390d6ff860
26 changed files with 3237 additions and 18 deletions

View File

@@ -108,6 +108,53 @@ deploy-l2messageservice:
L2MSGSERVICE_RATE_LIMIT_AMOUNT=1000000000000000000000 \
npx ts-node local-deployments-artifacts/deployL2MessageService.ts
deploy-token-bridge-l1:
# WARNING: FOR LOCAL DEV ONLY - DO NOT REUSE THESE KEYS ELSEWHERE
cd contracts/; \
PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
RPC_URL=http:\\localhost:8445/ \
REMOTE_CHAIN_ID=1337 \
TOKEN_BRIDGE_L1=true \
TOKEN_BRIDGE_SECURITY_COUNCIL=0x90F79bf6EB2c4f870365E785982E1f101E93b906 \
L2_MESSAGE_SERVICE_ADDRESS=0xe537D669CA013d86EBeF1D64e40fC74CADC91987 \
LINEA_ROLLUP_ADDRESS=0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9 \
npx ts-node local-deployments-artifacts/deployBridgedTokenAndTokenBridge.ts
deploy-token-bridge-l2:
# WARNING: FOR LOCAL DEV ONLY - DO NOT REUSE THESE KEYS ELSEWHERE
cd contracts/; \
SAVE_ADDRESS=true \
PRIVATE_KEY=0x1dd171cec7e2995408b5513004e8207fe88d6820aeff0d82463b3e41df251aae \
RPC_URL=http:\\localhost:8545/ \
REMOTE_CHAIN_ID=31648428 \
TOKEN_BRIDGE_L1=false \
TOKEN_BRIDGE_SECURITY_COUNCIL=0xf17f52151EbEF6C7334FAD080c5704D77216b732 \
L2_MESSAGE_SERVICE_ADDRESS=0xe537D669CA013d86EBeF1D64e40fC74CADC91987 \
LINEA_ROLLUP_ADDRESS=0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9 \
npx ts-node local-deployments-artifacts/deployBridgedTokenAndTokenBridge.ts
deploy-l1-test-erc20:
# WARNING: FOR LOCAL DEV ONLY - DO NOT REUSE THESE KEYS ELSEWHERE
cd contracts/; \
PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
RPC_URL=http:\\localhost:8445/ \
TEST_ERC20_L1=true \
TEST_ERC20_NAME=TestERC20 \
TEST_ERC20_SYMBOL=TERC20 \
TEST_ERC20_INITIAL_SUPPLY=100000 \
npx ts-node local-deployments-artifacts/deployTestERC20.ts
deploy-l2-test-erc20:
# WARNING: FOR LOCAL DEV ONLY - DO NOT REUSE THESE KEYS ELSEWHERE
cd contracts/; \
PRIVATE_KEY=0x1dd171cec7e2995408b5513004e8207fe88d6820aeff0d82463b3e41df251aae \
RPC_URL=http:\\localhost:8545/ \
TEST_ERC20_L1=false \
TEST_ERC20_NAME=TestERC20 \
TEST_ERC20_SYMBOL=TERC20 \
TEST_ERC20_INITIAL_SUPPLY=100000 \
npx ts-node local-deployments-artifacts/deployTestERC20.ts
upgrade-linea-rollup-on-uat:
cd contracts/; \
rm -f .openzeppelin/goerli.json; \
@@ -153,7 +200,11 @@ deploy-contracts-v4:
$(MAKE) -j2 deploy-linea-rollup-v4 deploy-l2messageservice
deploy-contracts:
$(MAKE) -j2 deploy-linea-rollup-v5 deploy-l2messageservice
cd contracts/; \
export L1_NONCE=$$(npx ts-node local-deployments-artifacts/get-wallet-nonce.ts --wallet-priv-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 --rpc-url http://localhost:8445) && \
export L2_NONCE=$$(npx ts-node local-deployments-artifacts/get-wallet-nonce.ts --wallet-priv-key 0x1dd171cec7e2995408b5513004e8207fe88d6820aeff0d82463b3e41df251aae --rpc-url http://localhost:8545) && \
cd .. && \
$(MAKE) -j6 deploy-linea-rollup-v5 deploy-token-bridge-l1 deploy-l1-test-erc20 deploy-l2messageservice deploy-token-bridge-l2 deploy-l2-test-erc20
testnet-start-l2:
docker compose -f docker/compose.yml -f docker/compose-testnet-sync.overrides.yml --profile l2 up -d

View File

@@ -15,7 +15,7 @@ export const tryStoreAddress = async (
const network = await ethers.provider.getNetwork();
const ContractFactory = await ethers.getContractFactory(contractName);
const dirPath = path.join(__dirname, "..", "deployments", `${networkName}`);
const dirPath = path.join(__dirname, "..", "..", "deployments", `${networkName}`);
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });

View File

@@ -0,0 +1,40 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
/**
* @title TestERC20
* @dev Simple ERC20 Token example.
*/
contract TestERC20 is ERC20, Ownable {
/**
* @dev Constructor that gives msg.sender all of existing tokens.
*/
constructor(string memory _name, string memory _symbol, uint256 _initialSupply) ERC20(_name, _symbol) {
_mint(msg.sender, _initialSupply);
}
/**
* @dev Function to mint tokens
* @param _to The address that will receive the minted tokens.
* @param _amount The amount of tokens to mint.
*/
function mint(address _to, uint256 _amount) public {
_mint(_to, _amount);
}
/**
* @dev Function to burn tokens
* @param _amount The amount of tokens to burn.
*/
function burn(uint256 _amount) public {
_burn(msg.sender, _amount);
}
}

View File

@@ -43,7 +43,11 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
await tryStoreAddress(network.name, contractName, bridgedTokenAddress, deployTx.hash);
console.log(`BridgedToken beacon deployed on ${network.name}, at address:`, bridgedTokenAddress);
if (process.env.TOKEN_BRIDGE_L1 === "true") {
console.log(`L1 BridgedToken beacon deployed on ${network.name}, at address:`, bridgedTokenAddress);
} else {
console.log(`L2 BridgedToken beacon deployed on ${network.name}, at address:`, bridgedTokenAddress);
}
await tryVerifyContract(bridgedTokenAddress);
};

View File

@@ -113,8 +113,11 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
await tryStoreProxyAdminAddress(network.name, contractName, proxyAdminAddress);
console.log(`TokenBridge deployed on ${network.name}, at address: ${tokenBridgeAddress}`);
if (process.env.TOKEN_BRIDGE_L1 === "true") {
console.log(`L1 TokenBridge deployed on ${network.name}, at address: ${tokenBridgeAddress}`);
} else {
console.log(`L2 TokenBridge deployed on ${network.name}, at address: ${tokenBridgeAddress}`);
}
await tryVerifyContract(tokenBridgeAddress);
};
export default func;

View File

@@ -0,0 +1,48 @@
import { ethers, network } from "hardhat";
import { DeployFunction } from "hardhat-deploy/types";
import { HardhatRuntimeEnvironment } from "hardhat/types";
import {
getRequiredEnvVar,
tryVerifyContract,
tryStoreAddress,
validateDeployBranchAndTags,
getDeployedContractAddress,
} from "../common/helpers";
const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const { deployments } = hre;
validateDeployBranchAndTags(hre.network.name);
const contractName = "TestERC20";
const existingContractAddress = await getDeployedContractAddress(contractName, deployments);
const tokenName = getRequiredEnvVar("TEST_ERC20_NAME");
const tokenSymbol = getRequiredEnvVar("TEST_ERC20_SYMBOL");
const initialSupply = getRequiredEnvVar("TEST_ERC20_INITIAL_SUPPLY");
if (!existingContractAddress) {
console.log(`Deploying initial version, NB: the address will be saved if env SAVE_ADDRESS=true.`);
} else {
console.log(`Deploying new version, NB: ${existingContractAddress} will be overwritten if env SAVE_ADDRESS=true.`);
}
const TestERC20Factory = await ethers.getContractFactory(contractName);
const contract = await TestERC20Factory.deploy(tokenName, tokenSymbol, ethers.parseEther(initialSupply));
await contract.waitForDeployment();
const contractAddress = await contract.getAddress();
const deployTx = contract.deploymentTransaction();
if (!deployTx) {
throw "Deployment transaction not found.";
}
await tryStoreAddress(network.name, contractName, contractAddress, deployTx.hash);
const chainId = (await ethers.provider.getNetwork()).chainId;
console.log(`${contractName} deployed on ${network.name}, chainId=${chainId}, at address: ${contractAddress}`);
await tryVerifyContract(contractAddress);
};
export default func;
func.tags = ["TestERC20"];

View File

@@ -0,0 +1,141 @@
import { abi as ProxyAdminAbi, bytecode as ProxyAdminBytecode } from "./static-artifacts/ProxyAdmin.json";
import { abi as BridgedTokenAbi, bytecode as BridgedTokenBytecode } from "./dynamic-artifacts/BridgedToken.json";
import { abi as TokenBridgeAbi, bytecode as TokenBridgeBytecode } from "./dynamic-artifacts/TokenBridge.json";
import {
abi as UpgradeableBeaconAbi,
bytecode as UpgradeableBeaconBytecode,
} from "./static-artifacts/UpgradeableBeacon.json";
import {
abi as TransparentUpgradeableProxyAbi,
bytecode as TransparentUpgradeableProxyBytecode,
} from "./static-artifacts/TransparentUpgradeableProxy.json";
import { getEnvVarOrDefault, getRequiredEnvVar } from "../common/helpers/environment";
import { generateRoleAssignments } from "../common/helpers/roles";
import {
TOKEN_BRIDGE_PAUSE_TYPES_ROLES,
TOKEN_BRIDGE_ROLES,
TOKEN_BRIDGE_UNPAUSE_TYPES_ROLES,
} from "contracts/common/constants";
import { ethers } from "ethers";
import { deployContractFromArtifacts, getInitializerData } from "../common/helpers/deployments";
async function main() {
const ORDERED_NONCE_POST_L2MESSAGESERVICE = 3;
const ORDERED_NONCE_POST_LINEAROLLUP = 4;
const bridgedTokenName = "BridgedToken";
const tokenBridgeName = "TokenBridge";
const l2MessageServiceAddress = process.env.L2_MESSAGE_SERVICE_ADDRESS;
const lineaRollupAddress = process.env.LINEA_ROLLUP_ADDRESS;
const remoteChainId = getRequiredEnvVar("REMOTE_CHAIN_ID");
const tokenBridgeSecurityCouncil = getRequiredEnvVar("TOKEN_BRIDGE_SECURITY_COUNCIL");
const pauseTypeRoles = getEnvVarOrDefault("TOKEN_BRIDGE_PAUSE_TYPES_ROLES", TOKEN_BRIDGE_PAUSE_TYPES_ROLES);
const unpauseTypeRoles = getEnvVarOrDefault("TOKEN_BRIDGE_UNPAUSE_TYPES_ROLES", TOKEN_BRIDGE_UNPAUSE_TYPES_ROLES);
const defaultRoleAddresses = generateRoleAssignments(TOKEN_BRIDGE_ROLES, tokenBridgeSecurityCouncil, []);
const roleAddresses = getEnvVarOrDefault("TOKEN_BRIDGE_ROLE_ADDRESSES", defaultRoleAddresses);
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
let walletNonce;
if (process.env.TOKEN_BRIDGE_L1 === "true") {
if (process.env.L1_NONCE === undefined) {
walletNonce = await wallet.getNonce();
} else {
walletNonce = parseInt(process.env.L1_NONCE) + ORDERED_NONCE_POST_LINEAROLLUP;
}
} else {
if (process.env.L2_NONCE === undefined) {
walletNonce = await wallet.getNonce();
} else {
walletNonce = parseInt(process.env.L2_NONCE) + ORDERED_NONCE_POST_L2MESSAGESERVICE;
}
}
const [bridgedToken, tokenBridgeImplementation, proxyAdmin] = await Promise.all([
deployContractFromArtifacts(BridgedTokenAbi, BridgedTokenBytecode, wallet, { nonce: walletNonce }),
deployContractFromArtifacts(TokenBridgeAbi, TokenBridgeBytecode, wallet, { nonce: walletNonce + 1 }),
deployContractFromArtifacts(ProxyAdminAbi, ProxyAdminBytecode, wallet, { nonce: walletNonce + 2 }),
]);
const bridgedTokenAddress = await bridgedToken.getAddress();
const tokenBridgeImplementationAddress = await tokenBridgeImplementation.getAddress();
const proxyAdminAddress = await proxyAdmin.getAddress();
const chainId = (await provider.getNetwork()).chainId;
console.log(`${bridgedTokenName} contract deployed at ${bridgedTokenAddress}`);
console.log(`${tokenBridgeName} Implementation contract deployed at ${tokenBridgeImplementationAddress}`);
console.log(`L1 ProxyAdmin deployed: address=${proxyAdminAddress}`);
console.log(`Deploying UpgradeableBeacon: chainId=${chainId} bridgedTokenAddress=${bridgedTokenAddress}`);
const beaconProxy = await deployContractFromArtifacts(
UpgradeableBeaconAbi,
UpgradeableBeaconBytecode,
wallet,
bridgedTokenAddress,
);
const beaconProxyAddress = await beaconProxy.getAddress();
let deployingChainMessageService = l2MessageServiceAddress;
let reservedAddresses = process.env.L2_RESERVED_TOKEN_ADDRESSES
? process.env.L2_RESERVED_TOKEN_ADDRESSES.split(",")
: [];
if (process.env.TOKEN_BRIDGE_L1 === "true") {
console.log(
`TOKEN_BRIDGE_L1=${process.env.TOKEN_BRIDGE_L1}. Deploying TokenBridge on L1, using L1_RESERVED_TOKEN_ADDRESSES environment variable`,
);
deployingChainMessageService = lineaRollupAddress;
reservedAddresses = process.env.L1_RESERVED_TOKEN_ADDRESSES
? process.env.L1_RESERVED_TOKEN_ADDRESSES.split(",")
: [];
} else {
console.log(
`TOKEN_BRIDGE_L1=${process.env.TOKEN_BRIDGE_L1}. Deploying TokenBridge on L2, using L2_RESERVED_TOKEN_ADDRESSES environment variable`,
);
}
const initializer = getInitializerData(TokenBridgeAbi, "initialize", [
{
defaultAdmin: tokenBridgeSecurityCouncil,
messageService: deployingChainMessageService,
tokenBeacon: beaconProxyAddress,
sourceChainId: chainId,
targetChainId: remoteChainId,
reservedTokens: reservedAddresses,
roleAddresses,
pauseTypeRoles,
unpauseTypeRoles,
},
]);
const proxyContract = await deployContractFromArtifacts(
TransparentUpgradeableProxyAbi,
TransparentUpgradeableProxyBytecode,
wallet,
tokenBridgeImplementationAddress,
proxyAdminAddress,
initializer,
);
const proxyContractAddress = await proxyContract.getAddress();
const txReceipt = await proxyContract.deploymentTransaction()?.wait();
if (!txReceipt) {
throw "Contract deployment transaction receipt not found.";
}
console.log(
`${tokenBridgeName} deployed: chainId=${chainId} address=${proxyContractAddress} blockNumber=${txReceipt.blockNumber}`,
);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});

View File

@@ -30,7 +30,13 @@ async function main() {
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
const walletNonce = await wallet.getNonce();
let walletNonce;
if (!process.env.L2_NONCE) {
walletNonce = await wallet.getNonce();
} else {
walletNonce = parseInt(process.env.L2_NONCE);
}
const [l2MessageServiceImplementation, proxyAdmin] = await Promise.all([
deployContractFromArtifacts(L2MessageServiceAbi, L2MessageServiceBytecode, wallet, { nonce: walletNonce }),

View File

@@ -52,7 +52,15 @@ async function main() {
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
const [walletNonce, { gasPrice }] = await Promise.all([wallet.getNonce(), get1559Fees(provider)]);
const { gasPrice } = await get1559Fees(provider);
let walletNonce;
if (!process.env.L1_NONCE) {
walletNonce = await wallet.getNonce();
} else {
walletNonce = parseInt(process.env.L1_NONCE);
}
const [verifier, lineaRollupImplementation, proxyAdmin] = await Promise.all([
deployContractFromArtifacts(verifierArtifacts.abi, verifierArtifacts.bytecode, wallet, {

View File

@@ -65,9 +65,18 @@ async function main() {
const verifierArtifacts = findContractArtifacts(path.join(__dirname, "./dynamic-artifacts"), verifierName);
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
const [walletNonce, { gasPrice }] = await Promise.all([wallet.getNonce(), get1559Fees(provider)]);
const { gasPrice } = await get1559Fees(provider);
let walletNonce;
if (!process.env.L1_NONCE) {
walletNonce = await wallet.getNonce();
} else {
walletNonce = parseInt(process.env.L1_NONCE);
}
const [verifier, lineaRollupImplementation, proxyAdmin] = await Promise.all([
deployContractFromArtifacts(verifierArtifacts.abi, verifierArtifacts.bytecode, wallet, {

View File

@@ -0,0 +1,61 @@
import { ethers } from "ethers";
import { abi as TestERC20Abi, bytecode as TestERC20Bytecode } from "./static-artifacts/TestERC20.json";
import { deployContractFromArtifacts } from "../common/helpers/deployments";
import { get1559Fees } from "../scripts/utils";
import { getRequiredEnvVar } from "../common/helpers/environment";
async function main() {
const ORDERED_NONCE_POST_LINEAROLLUP = 4;
const ORDERED_NONCE_POST_TOKENBRIDGE = 5;
const ORDERED_NONCE_POST_L2MESSAGESERVICE = 3;
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);
const erc20Name = getRequiredEnvVar("TEST_ERC20_NAME");
const erc20Symbol = getRequiredEnvVar("TEST_ERC20_SYMBOL");
const erc20Supply = getRequiredEnvVar("TEST_ERC20_INITIAL_SUPPLY");
const { gasPrice } = await get1559Fees(provider);
let walletNonce;
if (process.env.TEST_ERC20_L1 === "true") {
if (!process.env.L1_NONCE) {
walletNonce = await wallet.getNonce();
} else {
walletNonce = parseInt(process.env.L1_NONCE) + ORDERED_NONCE_POST_LINEAROLLUP + ORDERED_NONCE_POST_TOKENBRIDGE;
}
} else {
if (!process.env.L2_NONCE) {
walletNonce = await wallet.getNonce();
} else {
walletNonce =
parseInt(process.env.L2_NONCE) + ORDERED_NONCE_POST_L2MESSAGESERVICE + ORDERED_NONCE_POST_TOKENBRIDGE;
}
}
const testERC20 = await deployContractFromArtifacts(
TestERC20Abi,
TestERC20Bytecode,
wallet,
erc20Name,
erc20Symbol,
erc20Supply,
{
nonce: walletNonce,
gasPrice,
},
);
const testERC20Address = await testERC20.getAddress();
const chainId = (await provider.getNetwork()).chainId;
console.log(`testERC20 deployed: address=${testERC20Address} chainId=${chainId}`);
}
main().catch((error) => {
console.error(error);
process.exit(1);
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,33 @@
import { JsonRpcProvider, Wallet } from "ethers";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { sanitizePrivKey } from "../scripts/cli";
const argv = yargs(hideBin(process.argv))
.option("rpc-url", {
describe: "RPC url",
type: "string",
demandOption: true,
})
.option("wallet-priv-key", {
describe: "deployer private key",
type: "string",
demandOption: true,
coerce: sanitizePrivKey("priv-key"),
})
.parseSync();
async function main(args: typeof argv) {
const provider = new JsonRpcProvider(args.rpcUrl);
const wallet = new Wallet(args.walletPrivKey, provider);
const nonce = await wallet.getNonce();
process.env.L1_NONCE = nonce.toString();
process.stdout.write(process.env.L1_NONCE);
}
main(argv).catch((error) => {
console.error(error);
process.exit(1);
});

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,113 @@
{
"_format": "hh-sol-artifact-1",
"contractName": "UpgradeableBeacon",
"sourceName": "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol",
"abi": [
{
"inputs": [
{
"internalType": "address",
"name": "implementation_",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "implementation",
"type": "address"
}
],
"name": "Upgraded",
"type": "event"
},
{
"inputs": [],
"name": "implementation",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newImplementation",
"type": "address"
}
],
"name": "upgradeTo",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
],
"bytecode": "0x608060405234801561001057600080fd5b506040516104e43803806104e483398101604081905261002f91610151565b61003833610047565b61004181610097565b50610181565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6100aa8161014260201b6101a01760201c565b6101205760405162461bcd60e51b815260206004820152603360248201527f5570677261646561626c65426561636f6e3a20696d706c656d656e746174696f60448201527f6e206973206e6f74206120636f6e747261637400000000000000000000000000606482015260840160405180910390fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b03163b151590565b60006020828403121561016357600080fd5b81516001600160a01b038116811461017a57600080fd5b9392505050565b610354806101906000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80633659cfe61461005c5780635c60da1b14610071578063715018a61461009a5780638da5cb5b146100a2578063f2fde38b146100b3575b600080fd5b61006f61006a3660046102ee565b6100c6565b005b6001546001600160a01b03165b6040516001600160a01b03909116815260200160405180910390f35b61006f61010e565b6000546001600160a01b031661007e565b61006f6100c13660046102ee565b610122565b6100ce6101af565b6100d781610209565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6101166101af565b610120600061029e565b565b61012a6101af565b6001600160a01b0381166101945760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b61019d8161029e565b50565b6001600160a01b03163b151590565b6000546001600160a01b031633146101205760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161018b565b6001600160a01b0381163b61027c5760405162461bcd60e51b815260206004820152603360248201527f5570677261646561626c65426561636f6e3a20696d706c656d656e746174696f6044820152721b881a5cc81b9bdd08184818dbdb9d1c9858dd606a1b606482015260840161018b565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006020828403121561030057600080fd5b81356001600160a01b038116811461031757600080fd5b939250505056fea264697066735822122037f5e761a584b5cfedfedcacdd35de142872479851fbaecf66f55a175ffa1d1d64736f6c63430008090033",
"deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100575760003560e01c80633659cfe61461005c5780635c60da1b14610071578063715018a61461009a5780638da5cb5b146100a2578063f2fde38b146100b3575b600080fd5b61006f61006a3660046102ee565b6100c6565b005b6001546001600160a01b03165b6040516001600160a01b03909116815260200160405180910390f35b61006f61010e565b6000546001600160a01b031661007e565b61006f6100c13660046102ee565b610122565b6100ce6101af565b6100d781610209565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6101166101af565b610120600061029e565b565b61012a6101af565b6001600160a01b0381166101945760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b61019d8161029e565b50565b6001600160a01b03163b151590565b6000546001600160a01b031633146101205760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161018b565b6001600160a01b0381163b61027c5760405162461bcd60e51b815260206004820152603360248201527f5570677261646561626c65426561636f6e3a20696d706c656d656e746174696f6044820152721b881a5cc81b9bdd08184818dbdb9d1c9858dd606a1b606482015260840161018b565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60006020828403121561030057600080fd5b81356001600160a01b038116811461031757600080fd5b939250505056fea264697066735822122037f5e761a584b5cfedfedcacdd35de142872479851fbaecf66f55a175ffa1d1d64736f6c63430008090033",
"linkReferences": {},
"deployedLinkReferences": {}
}

View File

@@ -48,7 +48,7 @@
"f17f52151EbEF6C7334FAD080c5704D77216b732": {
"@WARNING": "FOR LOCAL DEV ONLY - DO NOT REUSE THESE KEYS ELSEWHERE",
"privateKey": "ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f",
"comment": "account 3, can be used as end user",
"comment": "L2 Security Council",
"balance": "90000000000000000000003"
},
"6d26dcc30a1693043aefa35ed9171c16da53f275": {

View File

@@ -0,0 +1,211 @@
import { ethers } from "ethers";
import { describe, expect, it } from "@jest/globals";
import { config } from "./config/tests-config";
import { waitForEvents, etherToWei } from "./common/utils";
import { MESSAGE_SENT_EVENT_SIGNATURE } from "./common/constants";
const l1AccountManager = config.getL1AccountManager();
const l2AccountManager = config.getL2AccountManager();
const bridgeAmount = ethers.parseEther("100");
const messageSentEventMessageNumberIndex = 4;
const messageSentEventMessageHashIndex = 6;
describe("Bridge ERC20 Tokens L1 -> L2 and L2 -> L1", () => {
it.concurrent("Bridge a token from L1 to L2", async () => {
const [l1Account, l2Account] = await Promise.all([
l1AccountManager.generateAccount(),
l2AccountManager.generateAccount(),
]);
const lineaRollup = config.getLineaRollupContract();
const l2MessageService = config.getL2MessageServiceContract();
const l1TokenBridge = config.getL1TokenBridgeContract();
const l2TokenBridge = config.getL2TokenBridgeContract();
const l1Token = config.getL1TokenContract();
const l1Provider = config.getL1Provider();
console.log("Minting ERC20 tokens to L1 Account");
let { maxPriorityFeePerGas: l1MaxPriorityFeePerGas, maxFeePerGas: l1MaxFeePerGas } = await l1Provider.getFeeData();
let nonce = await l1Provider.getTransactionCount(l1Account.address, "pending");
console.log("Minting and approving tokens to L1 TokenBridge");
await Promise.all([
(
await l1Token.connect(l1Account).mint(l1Account.address, bridgeAmount, {
nonce: nonce,
maxPriorityFeePerGas: l1MaxPriorityFeePerGas,
maxFeePerGas: l1MaxFeePerGas,
})
).wait(),
(
await l1Token.connect(l1Account).approve(l1TokenBridge.getAddress(), bridgeAmount, {
maxPriorityFeePerGas: l1MaxPriorityFeePerGas,
maxFeePerGas: l1MaxFeePerGas,
nonce: nonce + 1,
})
).wait(),
]);
const l1TokenBridgeAddress = await l1TokenBridge.getAddress();
const l1TokenAddress = await l1Token.getAddress();
const allowanceL1Account = await l1Token.allowance(l1Account.address, l1TokenBridgeAddress);
console.log("Current allowance of L1 account to L1 TokenBridge is :", allowanceL1Account.toString());
console.log("Calling the bridgeToken function on the L1 TokenBridge contract");
({ maxPriorityFeePerGas: l1MaxPriorityFeePerGas, maxFeePerGas: l1MaxFeePerGas } = await l1Provider.getFeeData());
nonce = await l1Provider.getTransactionCount(l1Account.address, "pending");
const bridgeTokenTx = await l1TokenBridge
.connect(l1Account)
.bridgeToken(l1TokenAddress, bridgeAmount, l2Account.address, {
value: etherToWei("0.01"),
maxPriorityFeePerGas: l1MaxPriorityFeePerGas,
maxFeePerGas: l1MaxFeePerGas,
nonce: nonce,
});
const bridgedTxReceipt = await bridgeTokenTx.wait();
const sentEventLog = bridgedTxReceipt?.logs.find((log) => log.topics[0] == MESSAGE_SENT_EVENT_SIGNATURE);
const messageSentEvent = lineaRollup.interface.decodeEventLog(
"MessageSent",
sentEventLog!.data,
sentEventLog!.topics,
);
const l1TokenBalance = await l1Token.balanceOf(l1Account.address);
console.log("Token balance of L1 account :", l1TokenBalance.toString());
expect(l1TokenBalance).toEqual(0n);
console.log("Waiting for MessageSent event on L1.");
const messageNumber = messageSentEvent[messageSentEventMessageNumberIndex];
const messageHash = messageSentEvent[messageSentEventMessageHashIndex];
console.log(`Message sent on L1 : messageHash=${messageHash}`);
console.log("Waiting for anchoring...");
const [rollingHashUpdatedEvent] = await waitForEvents(
l2MessageService,
l2MessageService.filters.RollingHashUpdated(),
1_000,
0,
"latest",
async (events) => events.filter((event) => event.args.messageNumber >= messageNumber),
);
expect(rollingHashUpdatedEvent).not.toBeNull();
const anchoredStatus = await l2MessageService.inboxL1L2MessageStatus(messageHash);
expect(anchoredStatus).toBeGreaterThan(0);
console.log(`Message anchored : ${JSON.stringify(rollingHashUpdatedEvent)}`);
console.log("Waiting for MessageClaimed event on L2...");
const [claimedEvent] = await waitForEvents(l2MessageService, l2MessageService.filters.MessageClaimed(messageHash));
expect(claimedEvent).not.toBeNull();
const [newTokenDeployed] = await waitForEvents(l2TokenBridge, l2TokenBridge.filters.NewTokenDeployed());
expect(newTokenDeployed).not.toBeNull();
console.log(`Message claimed on L2 : ${JSON.stringify(claimedEvent)}.`);
const l2Token = config.getL2BridgedTokenContract(newTokenDeployed.args.bridgedToken);
console.log("Verify the token balance on L2");
const l2TokenBalance = await l2Token.balanceOf(l2Account.address);
console.log("Token balance of L2 account :", l2TokenBalance.toString());
expect(l2TokenBalance).toEqual(bridgeAmount);
});
it.concurrent("Bridge a token from L2 to L1", async () => {
const [l1Account, l2Account] = await Promise.all([
l1AccountManager.generateAccount(),
l2AccountManager.generateAccount(),
]);
const lineaRollup = config.getLineaRollupContract();
const l2MessageService = config.getL2MessageServiceContract();
const l1TokenBridge = config.getL1TokenBridgeContract();
const l2TokenBridge = config.getL2TokenBridgeContract();
const l2Token = config.getL2TokenContract();
const l2Provider = config.getL2Provider();
const { maxPriorityFeePerGas: l2MaxPriorityFeePerGas, maxFeePerGas: l2MaxFeePerGas } =
await l2Provider.getFeeData();
let nonce = await l2Provider.getTransactionCount(l2Account.address, "pending");
await Promise.all([
(
await l2Token.connect(l2Account).mint(l2Account.address, bridgeAmount, {
nonce: nonce,
maxPriorityFeePerGas: l2MaxPriorityFeePerGas,
maxFeePerGas: l2MaxFeePerGas,
})
).wait(),
(
await l2Token.connect(l2Account).approve(l2TokenBridge.getAddress(), ethers.parseEther("100"), {
maxPriorityFeePerGas: l2MaxPriorityFeePerGas,
maxFeePerGas: l2MaxFeePerGas,
nonce: nonce + 1,
})
).wait(),
]);
const allowanceL2Account = await l2Token.allowance(l2Account.address, l2TokenBridge.getAddress());
console.log("Current allowance of L2 account to L2 TokenBridge is :", allowanceL2Account.toString());
console.log("Current balance of L2 account is :", await l2Token.balanceOf(l2Account));
console.log("Calling the bridgeToken function on the L2 TokenBridge contract");
nonce = await l2Provider.getTransactionCount(l2Account.address, "pending");
const bridgeTokenTx = await l2TokenBridge
.connect(l2Account)
.bridgeToken(await l2Token.getAddress(), bridgeAmount, l1Account.address, {
value: etherToWei("0.01"),
maxPriorityFeePerGas: l2MaxPriorityFeePerGas,
maxFeePerGas: l2MaxFeePerGas,
nonce: nonce,
});
const receipt = await bridgeTokenTx.wait();
const sentEventLog = receipt?.logs.find((log) => log.topics[0] == MESSAGE_SENT_EVENT_SIGNATURE);
const messageSentEvent = l2MessageService.interface.decodeEventLog(
"MessageSent",
sentEventLog!.data,
sentEventLog!.topics,
);
const messageHash = messageSentEvent[messageSentEventMessageHashIndex];
console.log("Waiting for L1 MessageClaimed event.");
const [claimedEvent] = await waitForEvents(lineaRollup, lineaRollup.filters.MessageClaimed(messageHash));
expect(claimedEvent).not.toBeNull();
console.log(`Message claimed on L1 : ${JSON.stringify(claimedEvent)}`);
const [newTokenDeployed] = await waitForEvents(l1TokenBridge, l1TokenBridge.filters.NewTokenDeployed());
expect(newTokenDeployed).not.toBeNull();
const l1BridgedToken = config.getL1BridgedTokenContract(newTokenDeployed.args.bridgedToken);
console.log("Verify the token balance on L1");
const l1BridgedTokenBalance = await l1BridgedToken.balanceOf(l1Account.address);
console.log("Token balance of L1 account :", l1BridgedTokenBalance.toString());
expect(l1BridgedTokenBalance).toEqual(bridgeAmount);
});
});

View File

@@ -3,7 +3,7 @@ import assert from "assert";
import { AbstractSigner, BaseContract, BlockTag, TransactionReceipt, TransactionRequest, Wallet, ethers } from "ethers";
import path from "path";
import { exec } from "child_process";
import { L2MessageService, LineaRollupV5 } from "../typechain";
import { L2MessageService, TokenBridge, LineaRollupV5 } from "../typechain";
import { PayableOverrides, TypedContractEvent, TypedDeferredTopicFilter, TypedEventLog } from "../typechain/common";
import { MessageEvent, SendMessageArgs } from "./types";
@@ -147,7 +147,10 @@ export async function getBlockByNumberOrBlockTag(rpcUrl: URL, blockTag: BlockTag
}
}
export async function getEvents<TContract extends LineaRollupV5 | L2MessageService, TEvent extends TypedContractEvent>(
export async function getEvents<
TContract extends LineaRollupV5 | L2MessageService | TokenBridge,
TEvent extends TypedContractEvent,
>(
contract: TContract,
eventFilter: TypedDeferredTopicFilter<TEvent>,
fromBlock?: BlockTag,
@@ -168,7 +171,7 @@ export async function getEvents<TContract extends LineaRollupV5 | L2MessageServi
}
export async function waitForEvents<
TContract extends LineaRollupV5 | L2MessageService,
TContract extends LineaRollupV5 | L2MessageService | TokenBridge,
TEvent extends TypedContractEvent,
>(
contract: TContract,

View File

@@ -9,10 +9,18 @@ declare global {
}
export default async (): Promise<void> => {
const l1JsonRpcProvider = config.getL1Provider();
const l1AccountManager = config.getL1AccountManager();
const l2AccountManager = config.getL2AccountManager();
const account = config.getL1AccountManager().whaleAccount(0);
const l2Account = config.getL2AccountManager().whaleAccount(0);
const lineaRollup = config.getLineaRollupContract(account);
const l1JsonRpcProvider = config.getL1Provider();
const l1TokenBridge = config.getL1TokenBridgeContract();
const l2TokenBridge = config.getL2TokenBridgeContract();
const l1SecurityCouncil = l1AccountManager.whaleAccount(3);
const l2SecurityCouncil = l2AccountManager.whaleAccount(3);
const [l1AccountNonce, l2AccountNonce, { maxPriorityFeePerGas, maxFeePerGas }] = await Promise.all([
account.getNonce(),
@@ -37,6 +45,8 @@ export default async (): Promise<void> => {
nonce: l1AccountNonce + 1,
})
).wait(),
(await l1TokenBridge.connect(l1SecurityCouncil).setRemoteTokenBridge(await l2TokenBridge.getAddress())).wait(),
(await l2TokenBridge.connect(l2SecurityCouncil).setRemoteTokenBridge(await l1TokenBridge.getAddress())).wait(),
]);
console.log(`L1 Dummy contract deployed at address: ${await dummyContract.getAddress()}`);

View File

@@ -26,6 +26,8 @@ const config: Config = {
rpcUrl: L1_RPC_URL,
chainId: L1_CHAIN_ID,
lineaRollupAddress: "0x2A5CDCfc38856e2590E9Bd32F54Fa348e5De5f48",
tokenBridgeAddress: "",
l1TokenAddress: "",
accountManager: new EnvironmentBasedAccountManager(
new ethers.JsonRpcProvider(L1_RPC_URL.toString()),
L1_WHALE_ACCOUNTS,
@@ -37,6 +39,8 @@ const config: Config = {
rpcUrl: L2_RPC_URL,
chainId: L2_CHAIN_ID,
l2MessageServiceAddress: "0x33bf916373159A8c1b54b025202517BfDbB7863D",
tokenBridgeAddress: "",
l2TokenAddress: "",
l2TestContractAddress: "",
accountManager: new EnvironmentBasedAccountManager(
new ethers.JsonRpcProvider(L2_RPC_URL.toString()),

View File

@@ -16,7 +16,9 @@ const config: Config = {
rpcUrl: L1_RPC_URL,
chainId: 31648428,
lineaRollupAddress: "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9",
dummyContractAddress: "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9",
dummyContractAddress: "0x610178dA211FEF7D417bC0e6FeD39F05609AD788",
tokenBridgeAddress: "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6",
l1TokenAddress: "0x8A791620dd6260079BF849Dc5567aDC3F2FdC318",
accountManager: new GenesisBasedAccountManager(
new ethers.JsonRpcProvider(L1_RPC_URL.toString()),
path.resolve(
@@ -30,8 +32,10 @@ const config: Config = {
besuNodeRpcUrl: L2_BESU_NODE_RPC_URL,
chainId: 1337,
l2MessageServiceAddress: "0xe537D669CA013d86EBeF1D64e40fC74CADC91987",
l2TestContractAddress: "0xeB0b0a14F92e3BA35aEF3a2B6A24D7ED1D11631B",
dummyContractAddress: "0x2f6dAaF8A81AB675fbD37Ca6Ed5b72cf86237453",
l2TestContractAddress: "0x997FC3aF1F193Cbdc013060076c67A13e218980e",
dummyContractAddress: "0xE4392c8ecC46b304C83cDB5edaf742899b1bda93",
tokenBridgeAddress: "0x5C95Bcd50E6D1B4E3CDC478484C9030Ff0a7D493",
l2TokenAddress: "0xCC1B08B17301e090cbb4c1F5598Cbaa096d591FB",
accountManager: new GenesisBasedAccountManager(
new ethers.JsonRpcProvider(L2_RPC_URL.toString()),
path.resolve(

View File

@@ -25,6 +25,8 @@ const config: Config = {
rpcUrl: L1_RPC_URL,
chainId: L1_CHAIN_ID,
lineaRollupAddress: "0xB218f8A4Bc926cF1cA7b3423c154a0D627Bdb7E5",
tokenBridgeAddress: "0x5A0a48389BB0f12E5e017116c1105da97E129142",
l1TokenAddress: "",
accountManager: new EnvironmentBasedAccountManager(
new ethers.JsonRpcProvider(L1_RPC_URL.toString()),
L1_WHALE_ACCOUNTS,
@@ -36,6 +38,8 @@ const config: Config = {
rpcUrl: L2_RPC_URL,
chainId: L2_CHAIN_ID,
l2MessageServiceAddress: "0x971e727e956690b9957be6d51Ec16E73AcAC83A7",
tokenBridgeAddress: "0x93DcAdf238932e6e6a85852caC89cBd71798F463",
l2TokenAddress: "",
l2TestContractAddress: "",
accountManager: new EnvironmentBasedAccountManager(
new ethers.JsonRpcProvider(L2_RPC_URL.toString()),

View File

@@ -1,6 +1,8 @@
import { AbstractSigner, JsonRpcProvider, Wallet } from "ethers";
import { Config, L2Config, LocalL2Config } from "./types";
import {
BridgedToken,
BridgedToken__factory,
DummyContract,
DummyContract__factory,
L2MessageService,
@@ -9,6 +11,10 @@ import {
LineaRollupV5__factory,
TestContract,
TestContract__factory,
TestERC20,
TestERC20__factory,
TokenBridge,
TokenBridge__factory,
} from "../../typechain";
import { AccountManager } from "./accounts/account-manager";
@@ -99,6 +105,71 @@ export default class TestSetup {
return l2MessageService;
}
public getL1TokenBridgeContract(signer?: Wallet): TokenBridge {
const l1TokenBridge: TokenBridge = TokenBridge__factory.connect(
this.config.L1.tokenBridgeAddress,
this.getL1Provider(),
);
if (signer) {
return l1TokenBridge.connect(signer);
}
return l1TokenBridge;
}
public getL2TokenBridgeContract(signer?: Wallet): TokenBridge {
const l2TokenBridge: TokenBridge = TokenBridge__factory.connect(
this.config.L2.tokenBridgeAddress,
this.getL2Provider(),
);
if (signer) {
return l2TokenBridge.connect(signer);
}
return l2TokenBridge;
}
public getL1TokenContract(signer?: Wallet): TestERC20 {
const l1Token: TestERC20 = TestERC20__factory.connect(this.config.L1.l1TokenAddress, this.getL1Provider());
if (signer) {
return l1Token.connect(signer);
}
return l1Token;
}
public getL2TokenContract(signer?: Wallet): TestERC20 {
const l2Token: TestERC20 = TestERC20__factory.connect(this.config.L2.l2TokenAddress, this.getL2Provider());
if (signer) {
return l2Token.connect(signer);
}
return l2Token;
}
public getL1BridgedTokenContract(bridgedTokenAddress: string, signer?: Wallet): BridgedToken {
const l1BridgedToken: BridgedToken = BridgedToken__factory.connect(bridgedTokenAddress, this.getL1Provider());
if (signer) {
return l1BridgedToken.connect(signer);
}
return l1BridgedToken;
}
public getL2BridgedTokenContract(bridgedTokenAddress: string, signer?: Wallet): BridgedToken {
const l2BridgedToken: BridgedToken = BridgedToken__factory.connect(bridgedTokenAddress, this.getL2Provider());
if (signer) {
return l2BridgedToken.connect(signer);
}
return l2BridgedToken;
}
public getL1DummyContract(signer?: Wallet): DummyContract {
const dummyContract = DummyContract__factory.connect(this.config.L1.dummyContractAddress, this.getL1Provider());

View File

@@ -9,11 +9,20 @@ export type BaseConfig = {
export type L1Config = BaseConfig & {
lineaRollupAddress: string;
tokenBridgeAddress: string;
l1TokenAddress: string;
};
export type BaseL2Config = BaseConfig & {
l2MessageServiceAddress: string;
l2TestContractAddress: string;
l2TestContractAddress?: string;
besuNodeRpcUrl?: URL;
tokenBridgeAddress: string;
l2TokenAddress: string;
shomeiEndpoint?: URL;
shomeiFrontendEndpoint?: URL;
sequencerEndpoint?: URL;
transactionExclusionEndpoint?: URL;
};
export type LocalL2Config = BaseL2Config & {

View File

@@ -7,6 +7,8 @@ import { TRANSACTION_CALLDATA_LIMIT } from "./common/constants";
const l2AccountManager = config.getL2AccountManager();
describe("Layer 2 test suite", () => {
const l2Provider = config.getL2Provider();
it.concurrent("Should revert if transaction data size is above the limit", async () => {
const account = await l2AccountManager.generateAccount();
const dummyContract = config.getL2DummyContract(account);
@@ -19,8 +21,14 @@ describe("Layer 2 test suite", () => {
it.concurrent("Should succeed if transaction data size is below the limit", async () => {
const account = await l2AccountManager.generateAccount();
const dummyContract = config.getL2DummyContract(account);
const nonce = await l2Provider.getTransactionCount(account.address, "pending");
const { maxPriorityFeePerGas, maxFeePerGas } = await l2Provider.getFeeData();
const tx = await dummyContract.connect(account).setPayload(ethers.randomBytes(1000));
const tx = await dummyContract.connect(account).setPayload(ethers.randomBytes(1000), {
nonce: nonce,
maxPriorityFeePerGas: maxPriorityFeePerGas,
maxFeePerGas: maxFeePerGas,
});
const receipt = await tx.wait();
expect(receipt?.status).toEqual(1);