diff --git a/contracts/docker/Dockerfile.deploy b/contracts/docker/Dockerfile.deploy new file mode 100644 index 0000000000..02301333b9 --- /dev/null +++ b/contracts/docker/Dockerfile.deploy @@ -0,0 +1,47 @@ +# Use the latest node Debian slim base image +# This makes installing yarn dep much easier +FROM node:20-bookworm-slim + +# Switch to bash shell +SHELL ["/bin/bash", "-c"] + +WORKDIR /root + +# Install dependencies +RUN apt update +RUN apt install --yes curl bash coreutils git jq + +# Download and run the Foundry installation script +RUN curl -L https://foundry.paradigm.xyz | bash + +# Set the environment variables to ensure Foundry tools are in the PATH +ENV PATH="/root/.foundry/bin:${PATH}" + +# Run foundryup to update Foundry +RUN foundryup -v nightly-56dbd20c7179570c53b6c17ff34daa7273a4ddae + +# copy dependencies +COPY ./lib /contracts/lib +COPY ./node_modules/@openzeppelin /contracts/node_modules/@openzeppelin + +# copy configurations +COPY foundry.toml /contracts/foundry.toml +COPY remappings.txt /contracts/remappings.txt + +# copy source code +COPY ./src /contracts/src +COPY ./scripts /contracts/scripts + +# compile contracts +ENV FOUNDRY_EVM_VERSION="cancun" +ENV FOUNDRY_BYTECODE_HASH="none" + +WORKDIR /contracts +RUN forge build + +# copy script configs +COPY ./docker/templates/config-contracts.toml /contracts/docker/templates/config-contracts.toml + +COPY ./docker/scripts/deploy.sh /contracts/docker/scripts/deploy.sh + +ENTRYPOINT ["/bin/bash", "/contracts/docker/scripts/deploy.sh"] diff --git a/contracts/docker/Dockerfile.gen-configs b/contracts/docker/Dockerfile.gen-configs new file mode 100644 index 0000000000..6a8ad51fc9 --- /dev/null +++ b/contracts/docker/Dockerfile.gen-configs @@ -0,0 +1,53 @@ +# Use the latest node Debian slim base image +# This makes installing yarn dep much easier +FROM node:20-bookworm-slim + +# Switch to bash shell +SHELL ["/bin/bash", "-c"] + +WORKDIR /root + +# Install dependencies +RUN apt update +RUN apt install --yes curl bash coreutils git jq + +# Download and run the Foundry installation script +RUN curl -L https://foundry.paradigm.xyz | bash + +# Set the environment variables to ensure Foundry tools are in the PATH +ENV PATH="/root/.foundry/bin:${PATH}" + +# Run foundryup to update Foundry +RUN foundryup -v nightly-56dbd20c7179570c53b6c17ff34daa7273a4ddae + +# copy dependencies +COPY ./lib /contracts/lib +COPY ./node_modules/@openzeppelin /contracts/node_modules/@openzeppelin + +# copy configurations +COPY foundry.toml /contracts/foundry.toml +COPY remappings.txt /contracts/remappings.txt + +# copy source code +COPY ./src /contracts/src +COPY ./scripts /contracts/scripts + +# compile contracts +ENV FOUNDRY_EVM_VERSION="cancun" +ENV FOUNDRY_BYTECODE_HASH="none" + +WORKDIR /contracts +RUN forge build + +# copy script configs +COPY ./docker/templates/balance-checker-config.json /contracts/docker/templates/balance-checker-config.json +COPY ./docker/templates/bridge-history-config.json /contracts/docker/templates/bridge-history-config.json +COPY ./docker/templates/chain-monitor-config.json /contracts/docker/templates/chain-monitor-config.json +COPY ./docker/templates/config-contracts.toml /contracts/docker/templates/config-contracts.toml +COPY ./docker/templates/coordinator-config.json /contracts/docker/templates/coordinator-config.json +COPY ./docker/templates/genesis.json /contracts/docker/templates/genesis.json +COPY ./docker/templates/rollup-config.json /contracts/docker/templates/rollup-config.json + +COPY ./docker/scripts/gen-configs.sh /contracts/docker/scripts/gen-configs.sh + +ENTRYPOINT ["/bin/bash", "/contracts/docker/scripts/gen-configs.sh"] diff --git a/contracts/docker/config-example.toml b/contracts/docker/config-example.toml new file mode 100644 index 0000000000..284236c6f6 --- /dev/null +++ b/contracts/docker/config-example.toml @@ -0,0 +1,75 @@ +[general] + +L1_RPC_ENDPOINT = "http://l1geth:8545" +L2_RPC_ENDPOINT = "http://l2geth:8545" + +CHAIN_ID_L1 = 111111 +CHAIN_ID_L2 = 222222 + +MAX_TX_IN_CHUNK = 100 +MAX_BLOCK_IN_CHUNK = 100 +MAX_L1_MESSAGE_GAS_LIMIT = 10000 + +L1_CONTRACT_DEPLOYMENT_BLOCK = 0 + +TEST_ENV_MOCK_FINALIZE_ENABLED = true +TEST_ENV_MOCK_FINALIZE_TIMEOUT_SEC = 3600 + + +[accounts] + +# note: for now we simply use Anvil's dev accounts + +DEPLOYER_PRIVATE_KEY = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" +OWNER_PRIVATE_KEY = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" + +L1_COMMIT_SENDER_PRIVATE_KEY = "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" +L1_FINALIZE_SENDER_PRIVATE_KEY = "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a" +L1_GAS_ORACLE_SENDER_PRIVATE_KEY = "0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6" +L2_GAS_ORACLE_SENDER_PRIVATE_KEY = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" + +DEPLOYER_ADDR = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" +OWNER_ADDR = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + +L1_COMMIT_SENDER_ADDR = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" +L1_FINALIZE_SENDER_ADDR = "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" +L1_GAS_ORACLE_SENDER_ADDR = "0x90F79bf6EB2c4f870365E785982E1f101E93b906" +L2_GAS_ORACLE_SENDER_ADDR = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + +L2GETH_SIGNER_0_ADDRESS = "0x756EA06BDEe36de11F22DCca45a31d8a178eF3c6" + +[db] + +SCROLL_DB_CONNECTION_STRING = "postgres://postgres:scroll2022@db:5432/scroll?sslmode=disable" +CHAIN_MONITOR_DB_CONNECTION_STRING = "postgres://postgres:scroll2022@db:5432/chain_monitor?sslmode=disable" +BRIDGE_HISTORY_DB_CONNECTION_STRING = "postgres://postgres:scroll2022@db:5432/bridge_history?sslmode=disable" + + +[genesis] + +L2_MAX_ETH_SUPPLY = "226156424291633194186662080095093570025917938800079226639565593765455331328" +L2_DEPLOYER_INITIAL_BALANCE = 1000000000000000000 + + +[contracts] + +DEPLOYMENT_SALT = "" + +# contracts deployed outside this script +L1_FEE_VAULT_ADDR = "0x0000000000000000000000000000000000000001" +L1_PLONK_VERIFIER_ADDR = "0x0000000000000000000000000000000000000001" + +[contracts.overrides] + +# L1_WETH = "0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14" + +L2_MESSAGE_QUEUE = "0x5300000000000000000000000000000000000000" +L1_GAS_PRICE_ORACLE = "0x5300000000000000000000000000000000000002" +L2_WHITELIST = "0x5300000000000000000000000000000000000003" +L2_WETH = "0x5300000000000000000000000000000000000004" +L2_TX_FEE_VAULT = "0x5300000000000000000000000000000000000005" + + +[coordinator] + +COORDINATOR_JWT_SECRET_KEY = "e788b62d39254928a821ac1c76b274a8c835aa1e20ecfb6f50eb10e87847de44" diff --git a/contracts/docker/scripts/build.sh b/contracts/docker/scripts/build.sh new file mode 100755 index 0000000000..93b15de06a --- /dev/null +++ b/contracts/docker/scripts/build.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +latest_commit=$(git log -1 --pretty=format:%h) +tag=${latest_commit:0:8} +echo "Using Docker image tag: $tag" +echo "" + +docker build -f docker/Dockerfile.gen-configs -t scrolltech/scroll-stack-contracts:gen-configs-$tag-amd64 --platform linux/amd64 . +echo +echo "built scrolltech/scroll-stack-contracts:gen-configs-$tag-amd64" +echo + +docker build -f docker/Dockerfile.gen-configs -t scrolltech/scroll-stack-contracts:gen-configs-$tag-arm64 --platform linux/arm64 . +echo +echo "built scrolltech/scroll-stack-contracts:gen-configs-$tag-arm64" +echo + +docker build -f docker/Dockerfile.deploy -t scrolltech/scroll-stack-contracts:deploy-$tag-amd64 --platform linux/amd64 . +echo +echo "built scrolltech/scroll-stack-contracts:deploy-$tag-amd64" +echo + +docker build -f docker/Dockerfile.deploy -t scrolltech/scroll-stack-contracts:deploy-$tag-arm64 --platform linux/arm64 . +echo +echo "built scrolltech/scroll-stack-contracts:deploy-$tag-arm64" +echo diff --git a/contracts/docker/scripts/deploy.sh b/contracts/docker/scripts/deploy.sh new file mode 100755 index 0000000000..64547f9462 --- /dev/null +++ b/contracts/docker/scripts/deploy.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +export FOUNDRY_EVM_VERSION="cancun" +export FOUNDRY_BYTECODE_HASH="none" + +if [ "${L1_RPC_ENDPOINT}" = "" ]; then + echo "L1_RPC_ENDPOINT is not set" + L1_RPC_ENDPOINT="http://host.docker.internal:8543" +fi + +if [ "$L2_RPC_ENDPOINT" = "" ]; then + echo "L2_RPC_ENDPOINT is not set" + L2_RPC_ENDPOINT="http://host.docker.internal:8545" +fi + +if [ "${L1_RPC_ENDPOINT}" = "" ]; then + echo "L1_RPC_ENDPOINT is not set" + L1_RPC_ENDPOINT="http://host.docker.internal:8543" +fi + +if [ "${BATCH_SIZE}" = "" ]; then + BATCH_SIZE="100" +fi + +echo "using L1_RPC_ENDPOINT = $L1_RPC_ENDPOINT" +echo "using L2_RPC_ENDPOINT = $L2_RPC_ENDPOINT" + +# simulate L1 +echo "" +echo "simulating on L1" +forge script scripts/foundry/DeployScroll.s.sol:DeployScroll --rpc-url "$L1_RPC_ENDPOINT" --sig "run(string,string)" "L1" "verify-config" || exit 1 + +# simulate L2 +echo "" +echo "simulating on L2" +forge script scripts/foundry/DeployScroll.s.sol:DeployScroll --rpc-url "$L2_RPC_ENDPOINT" --sig "run(string,string)" "L2" "verify-config" --legacy || exit 1 + +# deploy L1 +echo "" +echo "deploying on L1" +forge script scripts/foundry/DeployScroll.s.sol:DeployScroll --rpc-url "$L1_RPC_ENDPOINT" --batch-size "$BATCH_SIZE" --sig "run(string,string)" "L1" "verify-config" --broadcast || exit 1 + +# deploy L2 +echo "" +echo "deploying on L2" +forge script scripts/foundry/DeployScroll.s.sol:DeployScroll --rpc-url "$L2_RPC_ENDPOINT" --batch-size "$BATCH_SIZE" --sig "run(string,string)" "L2" "verify-config" --broadcast --legacy || exit 1 diff --git a/contracts/docker/scripts/gen-configs.sh b/contracts/docker/scripts/gen-configs.sh new file mode 100755 index 0000000000..988b6830c6 --- /dev/null +++ b/contracts/docker/scripts/gen-configs.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +echo "" +echo "generating config-contracts.toml" +forge script scripts/foundry/DeployScroll.s.sol:DeployScroll --sig "run(string,string)" "none" "write-config" || exit 1 + +echo "" +echo "generating genesis.json" +forge script scripts/foundry/DeployScroll.s.sol:GenerateGenesis || exit 1 + +echo "" +echo "generating rollup-config.json" +forge script scripts/foundry/DeployScroll.s.sol:GenerateRollupConfig || exit 1 + +echo "" +echo "generating coordinator-config.json" +forge script scripts/foundry/DeployScroll.s.sol:GenerateCoordinatorConfig || exit 1 + +echo "" +echo "generating chain-monitor-config.json" +forge script scripts/foundry/DeployScroll.s.sol:GenerateChainMonitorConfig || exit 1 + +echo "" +echo "generating bridge-history-config.json" +forge script scripts/foundry/DeployScroll.s.sol:GenerateBridgeHistoryConfig || exit 1 + +echo "" +echo "generating balance-checker-config.json" +forge script scripts/foundry/DeployScroll.s.sol:GenerateBalanceCheckerConfig || exit 1 + +echo "" +echo "generating .env.frontend" +forge script scripts/foundry/DeployScroll.s.sol:GenerateFrontendConfig || exit 1 diff --git a/contracts/docker/scripts/push.sh b/contracts/docker/scripts/push.sh new file mode 100755 index 0000000000..da28510e46 --- /dev/null +++ b/contracts/docker/scripts/push.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +latest_commit=$(git log -1 --pretty=format:%h) +tag=${latest_commit:0:8} +echo "Using Docker image tag: $tag" +echo "" + +docker push scrolltech/scroll-stack-contracts:gen-configs-$tag-amd64 +docker push scrolltech/scroll-stack-contracts:gen-configs-$tag-arm64 + +docker manifest create scrolltech/scroll-stack-contracts:gen-configs-$tag \ + --amend scrolltech/scroll-stack-contracts:gen-configs-$tag-amd64 \ + --amend scrolltech/scroll-stack-contracts:gen-configs-$tag-arm64 + +docker manifest push scrolltech/scroll-stack-contracts:gen-configs-$tag + +docker push scrolltech/scroll-stack-contracts:deploy-$tag-amd64 +docker push scrolltech/scroll-stack-contracts:deploy-$tag-arm64 + +docker manifest create scrolltech/scroll-stack-contracts:deploy-$tag \ + --amend scrolltech/scroll-stack-contracts:deploy-$tag-amd64 \ + --amend scrolltech/scroll-stack-contracts:deploy-$tag-arm64 + +docker manifest push scrolltech/scroll-stack-contracts:deploy-$tag diff --git a/contracts/docker/templates/balance-checker-config.json b/contracts/docker/templates/balance-checker-config.json new file mode 100644 index 0000000000..79011d9401 --- /dev/null +++ b/contracts/docker/templates/balance-checker-config.json @@ -0,0 +1,42 @@ +{ + "addresses": [ + { + "rpc_url": "${SCROLL_L1_RPC}", + "min_balance_ether": "10", + "address": "${L1_COMMIT_SENDER_ADDRESS}", + "name": "L1_COMMIT_SENDER" + }, + { + "rpc_url": "${SCROLL_L1_RPC}", + "min_balance_ether": "10", + "address": "${L1_FINALIZE_SENDER_ADDRESS}", + "name": "L1_FINALIZE_SENDER" + }, + { + "rpc_url": "${SCROLL_L1_RPC}", + "min_balance_ether": "1.1", + "address": "${L1_GAS_ORACLE_SENDER_ADDRESS}", + "name": "L1_GAS_ORACLE_SENDER" + }, + { + "rpc_url": "${SCROLL_L1_RPC}", + "min_balance_ether": "0", + "address": "${L1_SCROLL_FEE_VAULT_ADDRESS}", + "name": "L1_SCROLL_FEE_VAULT" + }, + { + "rpc_url": "${SCROLL_L2_RPC}", + "min_balance_ether": "1.1", + "address": "${L2_GAS_ORACLE_SENDER_ADDRESS}", + "name": "L2_GAS_ORACLE_SENDER" + }, + { + "rpc_url": "${SCROLL_L2_RPC}", + "min_balance_ether": "0", + "address": "${L2_TX_FEE_VAULT_ADDR}", + "name": "L2_TX_FEE_VAULT" + } + ], + "JOB_INTERVAL_SECS": 60, + "BIND_PORT": 8080 +} \ No newline at end of file diff --git a/contracts/docker/templates/bridge-history-config.json b/contracts/docker/templates/bridge-history-config.json new file mode 100644 index 0000000000..de8f0a6cc8 --- /dev/null +++ b/contracts/docker/templates/bridge-history-config.json @@ -0,0 +1,56 @@ +{ + "L1": { + "confirmation": 0, + "endpoint": null, + "startHeight": 0, + "blockTime": 12, + "fetchLimit": 16, + "MessageQueueAddr": null, + "MessengerAddr": null, + "ScrollChainAddr": null, + "GatewayRouterAddr": null, + "ETHGatewayAddr": null, + "WETHGatewayAddr": null, + "StandardERC20GatewayAddr": null, + "CustomERC20GatewayAddr": null, + "ERC721GatewayAddr": null, + "ERC1155GatewayAddr": null, + "USDCGatewayAddr": "0x0000000000000000000000000000000000000000", + "LIDOGatewayAddr": "0x0000000000000000000000000000000000000000", + "DAIGatewayAddr": "0x0000000000000000000000000000000000000000", + "PufferGatewayAddr": "0x0000000000000000000000000000000000000000" + }, + "L2": { + "confirmation": 0, + "endpoint": null, + "blockTime": 3, + "fetchLimit": 64, + "MessageQueueAddr": null, + "MessengerAddr": null, + "GatewayRouterAddr": null, + "ETHGatewayAddr": null, + "WETHGatewayAddr": null, + "StandardERC20GatewayAddr": null, + "CustomERC20GatewayAddr": null, + "ERC721GatewayAddr": null, + "ERC1155GatewayAddr": null, + "USDCGatewayAddr": "0x0000000000000000000000000000000000000000", + "LIDOGatewayAddr": "0x0000000000000000000000000000000000000000", + "DAIGatewayAddr": "0x0000000000000000000000000000000000000000", + "PufferGatewayAddr": "0x0000000000000000000000000000000000000000" + }, + "db": { + "dsn": null, + "driverName": "postgres", + "maxOpenNum": 200, + "maxIdleNum": 20 + }, + "redis": { + "address": "localhost:6379", + "username": "default", + "password": "", + "local": true, + "minIdleConns": 10, + "readTimeoutMs": 500 + } +} diff --git a/contracts/docker/templates/chain-monitor-config.json b/contracts/docker/templates/chain-monitor-config.json new file mode 100644 index 0000000000..09fb89bcb7 --- /dev/null +++ b/contracts/docker/templates/chain-monitor-config.json @@ -0,0 +1,56 @@ +{ + "l1_config": { + "l1_url": null, + "confirm": "0x20", + "start_number": null, + "l1_contracts": { + "l1_gateways": { + "eth_gateway": null, + "weth_gateway": null, + "standard_erc20_gateway": null, + "custom_erc20_gateway": null, + "erc721_gateway": null, + "erc1155_gateway": null, + "dai_gateway": "0x0000000000000000000000000000000000000000", + "usdc_gateway": "0x0000000000000000000000000000000000000000", + "lido_gateway": "0x0000000000000000000000000000000000000000", + "puffer_gateway": "0x0000000000000000000000000000000000000000" + }, + "scroll_messenger": null, + "message_queue": null, + "scroll_chain": null + }, + "start_messenger_balance": null + }, + "l2_config": { + "l2_url": null, + "confirm": "0x80", + "l2_contracts": { + "l2_gateways": { + "eth_gateway": null, + "weth_gateway": null, + "standard_erc20_gateway": null, + "custom_erc20_gateway": null, + "erc721_gateway": null, + "erc1155_gateway": null, + "dai_gateway": "0x0000000000000000000000000000000000000000", + "usdc_gateway": "0x0000000000000000000000000000000000000000", + "lido_gateway": "0x0000000000000000000000000000000000000000", + "puffer_gateway": "0x0000000000000000000000000000000000000000" + }, + "scroll_messenger": null, + "message_queue": null + } + }, + "slack_webhook_config": { + "webhook_url": "http://localhost:1234", + "worker_count": 5, + "worker_buffer_size": 1000 + }, + "db_config": { + "driver_name": "postgres", + "dsn": null, + "maxOpenNum": 100, + "maxIdleNum": 20 + } +} diff --git a/contracts/docker/templates/config-contracts.toml b/contracts/docker/templates/config-contracts.toml new file mode 100644 index 0000000000..b0681a0ec7 --- /dev/null +++ b/contracts/docker/templates/config-contracts.toml @@ -0,0 +1,55 @@ +L1_WETH_ADDR = "" +L1_PROXY_ADMIN_ADDR = "" +L1_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR = "" +L1_WHITELIST_ADDR = "" +L2_GAS_PRICE_ORACLE_IMPLEMENTATION_ADDR = "" +L2_GAS_PRICE_ORACLE_PROXY_ADDR = "" +L1_SCROLL_CHAIN_PROXY_ADDR = "" +L1_SCROLL_MESSENGER_PROXY_ADDR = "" +L1_ENFORCED_TX_GATEWAY_IMPLEMENTATION_ADDR = "" +L1_ENFORCED_TX_GATEWAY_PROXY_ADDR = "" +L1_ZKEVM_VERIFIER_V1_ADDR = "" +L1_MULTIPLE_VERSION_ROLLUP_VERIFIER_ADDR = "" +L1_MESSAGE_QUEUE_IMPLEMENTATION_ADDR = "" +L1_MESSAGE_QUEUE_PROXY_ADDR = "" +L1_SCROLL_CHAIN_IMPLEMENTATION_ADDR = "" +L1_GATEWAY_ROUTER_IMPLEMENTATION_ADDR = "" +L1_GATEWAY_ROUTER_PROXY_ADDR = "" +L1_ETH_GATEWAY_PROXY_ADDR = "" +L1_WETH_GATEWAY_PROXY_ADDR = "" +L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR = "" +L1_CUSTOM_ERC20_GATEWAY_PROXY_ADDR = "" +L1_ERC721_GATEWAY_PROXY_ADDR = "" +L1_ERC1155_GATEWAY_PROXY_ADDR = "" +L2_MESSAGE_QUEUE_ADDR = "" +L1_GAS_PRICE_ORACLE_ADDR = "" +L2_WHITELIST_ADDR = "" +L2_WETH_ADDR = "" +L2_TX_FEE_VAULT_ADDR = "" +L2_PROXY_ADMIN_ADDR = "" +L2_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR = "" +L2_SCROLL_MESSENGER_PROXY_ADDR = "" +L2_ETH_GATEWAY_PROXY_ADDR = "" +L2_WETH_GATEWAY_PROXY_ADDR = "" +L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR = "" +L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR = "" +L2_ERC721_GATEWAY_PROXY_ADDR = "" +L2_ERC1155_GATEWAY_PROXY_ADDR = "" +L2_SCROLL_STANDARD_ERC20_ADDR = "" +L2_SCROLL_STANDARD_ERC20_FACTORY_ADDR = "" +L1_SCROLL_MESSENGER_IMPLEMENTATION_ADDR = "" +L1_STANDARD_ERC20_GATEWAY_IMPLEMENTATION_ADDR = "" +L1_ETH_GATEWAY_IMPLEMENTATION_ADDR = "" +L1_WETH_GATEWAY_IMPLEMENTATION_ADDR = "" +L1_CUSTOM_ERC20_GATEWAY_IMPLEMENTATION_ADDR = "" +L1_ERC721_GATEWAY_IMPLEMENTATION_ADDR = "" +L1_ERC1155_GATEWAY_IMPLEMENTATION_ADDR = "" +L2_SCROLL_MESSENGER_IMPLEMENTATION_ADDR = "" +L2_GATEWAY_ROUTER_IMPLEMENTATION_ADDR = "" +L2_GATEWAY_ROUTER_PROXY_ADDR = "" +L2_STANDARD_ERC20_GATEWAY_IMPLEMENTATION_ADDR = "" +L2_ETH_GATEWAY_IMPLEMENTATION_ADDR = "" +L2_WETH_GATEWAY_IMPLEMENTATION_ADDR = "" +L2_CUSTOM_ERC20_GATEWAY_IMPLEMENTATION_ADDR = "" +L2_ERC721_GATEWAY_IMPLEMENTATION_ADDR = "" +L2_ERC1155_GATEWAY_IMPLEMENTATION_ADDR = "" diff --git a/contracts/docker/templates/coordinator-config.json b/contracts/docker/templates/coordinator-config.json new file mode 100644 index 0000000000..f495637ee7 --- /dev/null +++ b/contracts/docker/templates/coordinator-config.json @@ -0,0 +1,30 @@ +{ + "prover_manager": { + "provers_per_session": 1, + "session_attempts": 100, + "chunk_collection_time_sec": 3600, + "batch_collection_time_sec": 600, + "verifier": { + "fork_name": "bernoulli", + "mock_mode": false, + "params_path": "/verifier/params", + "assets_path": "/verifier/assets" + }, + "max_verifier_workers": 4, + "min_prover_version": "v4.3.41" + }, + "db": { + "driver_name": "postgres", + "dsn": null, + "maxOpenNum": 200, + "maxIdleNum": 20 + }, + "l2": { + "chain_id": null + }, + "auth": { + "secret": null, + "challenge_expire_duration_sec": 10, + "login_expire_duration_sec": 3600 + } +} \ No newline at end of file diff --git a/contracts/docker/templates/genesis.json b/contracts/docker/templates/genesis.json new file mode 100644 index 0000000000..27f8685071 --- /dev/null +++ b/contracts/docker/templates/genesis.json @@ -0,0 +1,48 @@ +{ + "config": { + "chainId": null, + "homesteadBlock": 0, + "eip150Block": 0, + "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "berlinBlock": 0, + "londonBlock": 0, + "archimedesBlock": 0, + "shanghaiBlock": 0, + "bernoulliBlock": 0, + "curieBlock": 0, + "clique": { + "period": 3, + "epoch": 30000 + }, + "scroll": { + "useZktrie": true, + "maxTxPerBlock": null, + "maxTxPayloadBytesPerBlock": 122880, + "feeVaultAddress": null, + "l1Config": { + "l1ChainId": null, + "l1MessageQueueAddress": null, + "scrollChainAddress": null, + "numL1MessagesPerBlock": "10" + } + } + }, + "nonce": "0x0", + "timestamp": null, + "extraData": null, + "gasLimit": "10000000", + "difficulty": "0x1", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0000000000000000000000000000000000000000", + "alloc": {}, + "number": "0x0", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "baseFeePerGas": null +} \ No newline at end of file diff --git a/contracts/docker/templates/rollup-config.json b/contracts/docker/templates/rollup-config.json new file mode 100644 index 0000000000..d080925ece --- /dev/null +++ b/contracts/docker/templates/rollup-config.json @@ -0,0 +1,87 @@ +{ + "l1_config": { + "confirmations": "0x0", + "endpoint": null, + "l1_message_queue_address": null, + "scroll_chain_address": null, + "start_height": 0, + "relayer_config": { + "gas_price_oracle_contract_address": null, + "sender_config": { + "endpoint": null, + "escalate_blocks": 100, + "escalate_multiple_num": 11, + "escalate_multiple_den": 10, + "max_gas_price": 10000000000000, + "tx_type": "LegacyTx", + "check_pending_time": 3, + "confirmations": "0x0" + }, + "gas_oracle_config": { + "min_gas_price": 0, + "gas_price_diff": 50000, + "l1_base_fee_weight": 0.086, + "l1_blob_base_fee_weight": 0.030 + }, + "gas_oracle_sender_private_key": null + } + }, + "l2_config": { + "confirmations": "0x0", + "endpoint": null, + "l2_message_queue_address": null, + "relayer_config": { + "rollup_contract_address": null, + "gas_price_oracle_contract_address": null, + "sender_config": { + "endpoint": null, + "escalate_blocks": 4, + "escalate_multiple_num": 12, + "escalate_multiple_den": 10, + "max_gas_price": 200000000000, + "max_blob_gas_price": 200000000000, + "tx_type": "DynamicFeeTx", + "check_pending_time": 10, + "confirmations": "0x0" + }, + "gas_oracle_config": { + "min_gas_price": 0, + "gas_price_diff": 50000 + }, + "chain_monitor": { + "enabled": true, + "timeout": 3, + "try_times": 5, + "base_url": "http://chain-monitorv2:8080" + }, + "enable_test_env_bypass_features": null, + "finalize_batch_without_proof_timeout_sec": null, + "gas_oracle_sender_private_key": null, + "commit_sender_private_key": null, + "finalize_sender_private_key": null, + "l1_commit_gas_limit_multiplier": 1.2 + }, + "chunk_proposer_config": { + "max_block_num_per_chunk": null, + "max_tx_num_per_chunk": null, + "max_l1_commit_gas_per_chunk": 5000000, + "max_l1_commit_calldata_size_per_chunk": 110000, + "chunk_timeout_sec": 2700, + "max_row_consumption_per_chunk": 1000000, + "gas_cost_increase_multiplier": 1.2 + }, + "batch_proposer_config": { + "max_chunk_num_per_batch": 15, + "max_l1_commit_gas_per_batch": 5000000, + "max_l1_commit_calldata_size_per_batch": 110000, + "batch_timeout_sec": 2700, + "gas_cost_increase_multiplier": 1.2 + } + }, + "db_config": { + "driver_name": "postgres", + "dsn": null, + "maxOpenNum": 50, + "maxIdleNum": 20 + } +} \ No newline at end of file diff --git a/contracts/foundry.toml b/contracts/foundry.toml index 21a95192f0..9e556d260f 100644 --- a/contracts/foundry.toml +++ b/contracts/foundry.toml @@ -7,15 +7,13 @@ libs = [] # a list of librar remappings = [] # a list of remappings libraries = [] # a list of deployed libraries to link against cache = true # whether to cache builds or not -force = true # whether to ignore the cache (clean build) +force = false # whether to ignore the cache (clean build) evm_version = 'cancun' # the evm version (by hardfork name) solc_version = '0.8.24' # override for the solc version (setting this ignores `auto_detect_solc`) optimizer = true # enable or disable the solc optimizer optimizer_runs = 200 # the number of optimizer runs verbosity = 2 # the verbosity of tests -ignored_error_codes = [] # a list of ignored solc error codes fuzz_runs = 256 # the number of fuzz runs for tests -ffi = false # whether to enable ffi or not sender = '0x00a329c0648769a73afac7f9381e08fb43dbea72' # the address of `msg.sender` in tests tx_origin = '0x00a329c0648769a73afac7f9381e08fb43dbea72' # the address of `tx.origin` in tests initial_balance = '0xffffffffffffffffffffffff' # the initial balance of the test contract @@ -27,4 +25,15 @@ block_coinbase = '0x0000000000000000000000000000000000000000' # the address of ` block_timestamp = 0 # the value of `block.timestamp` in tests block_difficulty = 0 # the value of `block.difficulty` in tests +# remove bytecode hash for reliable deterministic addresses +bytecode_hash = 'none' + +# file system permissions +ffi = true + +fs_permissions = [ + { access='read', path='./docker' }, + { access='read-write', path='./volume' }, +] + gas_reports = ["L2GasPriceOracle"] diff --git a/contracts/lib/forge-std b/contracts/lib/forge-std index 662ae0d693..bb4ceea94d 160000 --- a/contracts/lib/forge-std +++ b/contracts/lib/forge-std @@ -1 +1 @@ -Subproject commit 662ae0d6936654c5d1fb79fc15f521de28edb60e +Subproject commit bb4ceea94d6f10eeb5b41dc2391c6c8bf8e734ef diff --git a/contracts/scripts/foundry/DeployScroll.s.sol b/contracts/scripts/foundry/DeployScroll.s.sol new file mode 100644 index 0000000000..d92e8b5ef6 --- /dev/null +++ b/contracts/scripts/foundry/DeployScroll.s.sol @@ -0,0 +1,2291 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.8.24; + +import {Script, console} from "forge-std/Script.sol"; +import {VmSafe} from "forge-std/Vm.sol"; +import {stdToml} from "forge-std/StdToml.sol"; + +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import {TransparentUpgradeableProxy, ITransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; + +import {EmptyContract} from "../../src/misc/EmptyContract.sol"; + +import {EnforcedTxGateway} from "../../src/L1/gateways/EnforcedTxGateway.sol"; +import {L1CustomERC20Gateway} from "../../src/L1/gateways/L1CustomERC20Gateway.sol"; +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 {L1ScrollMessenger} from "../../src/L1/L1ScrollMessenger.sol"; +import {L1StandardERC20Gateway} from "../../src/L1/gateways/L1StandardERC20Gateway.sol"; +import {L1WETHGateway} from "../../src/L1/gateways/L1WETHGateway.sol"; +import {L2GasPriceOracle} from "../../src/L1/rollup/L2GasPriceOracle.sol"; +import {MultipleVersionRollupVerifier} from "../../src/L1/rollup/MultipleVersionRollupVerifier.sol"; +import {ScrollChain} from "../../src/L1/rollup/ScrollChain.sol"; +import {ZkEvmVerifierV1} from "../../src/libraries/verifier/ZkEvmVerifierV1.sol"; + +import {L2CustomERC20Gateway} from "../../src/L2/gateways/L2CustomERC20Gateway.sol"; +import {L2ERC1155Gateway} from "../../src/L2/gateways/L2ERC1155Gateway.sol"; +import {L2ERC721Gateway} from "../../src/L2/gateways/L2ERC721Gateway.sol"; +import {L2ETHGateway} from "../../src/L2/gateways/L2ETHGateway.sol"; +import {L2GatewayRouter} from "../../src/L2/gateways/L2GatewayRouter.sol"; +import {L2ScrollMessenger} from "../../src/L2/L2ScrollMessenger.sol"; +import {L2StandardERC20Gateway} from "../../src/L2/gateways/L2StandardERC20Gateway.sol"; +import {L2WETHGateway} from "../../src/L2/gateways/L2WETHGateway.sol"; +import {L1GasPriceOracle} from "../../src/L2/predeploys/L1GasPriceOracle.sol"; +import {L2MessageQueue} from "../../src/L2/predeploys/L2MessageQueue.sol"; +import {L2TxFeeVault} from "../../src/L2/predeploys/L2TxFeeVault.sol"; +import {Whitelist} from "../../src/L2/predeploys/Whitelist.sol"; +import {WrappedEther} from "../../src/L2/predeploys/WrappedEther.sol"; +import {ScrollStandardERC20} from "../../src/libraries/token/ScrollStandardERC20.sol"; +import {ScrollStandardERC20Factory} from "../../src/libraries/token/ScrollStandardERC20Factory.sol"; + +import {ScrollChainMockFinalize} from "../../src/mocks/ScrollChainMockFinalize.sol"; + +/// @dev The address of DeterministicDeploymentProxy. +/// See https://github.com/Arachnid/deterministic-deployment-proxy. +address constant DETERMINISTIC_DEPLOYMENT_PROXY_ADDR = 0x4e59b44847b379578588920cA78FbF26c0B4956C; + +/// @dev The default deterministic deployment salt prefix. +string constant DEFAULT_DEPLOYMENT_SALT = "ScrollStack"; + +/// @dev The default minimum withdraw amount configured on L2TxFeeVault. +uint256 constant FEE_VAULT_MIN_WITHDRAW_AMOUNT = 1 ether; + +/// @dev The minimum deployer account balance. +uint256 constant MINIMUM_DEPLOYER_BALANCE = 0.1 ether; + +// template files +string constant CONFIG_CONTRACTS_TEMPLATE_PATH = "./docker/templates/config-contracts.toml"; +string constant GENESIS_JSON_TEMPLATE_PATH = "./docker/templates/genesis.json"; +string constant ROLLUP_CONFIG_TEMPLATE_PATH = "./docker/templates/rollup-config.json"; +string constant COORDINATOR_CONFIG_TEMPLATE_PATH = "./docker/templates/coordinator-config.json"; +string constant CHAIN_MONITOR_CONFIG_TEMPLATE_PATH = "./docker/templates/chain-monitor-config.json"; +string constant BRIDGE_HISTORY_CONFIG_TEMPLATE_PATH = "./docker/templates/bridge-history-config.json"; +string constant BALANCE_CHECKER_CONFIG_TEMPLATE_PATH = "./docker/templates/balance-checker-config.json"; + +// input files +string constant CONFIG_PATH = "./volume/config.toml"; + +// output files +string constant CONFIG_CONTRACTS_PATH = "./volume/config-contracts.toml"; +string constant GENESIS_ALLOC_JSON_PATH = "./volume/__genesis-alloc.json"; +string constant GENESIS_JSON_PATH = "./volume/genesis.json"; +string constant ROLLUP_CONFIG_PATH = "./volume/rollup-config.json"; +string constant COORDINATOR_CONFIG_PATH = "./volume/coordinator-config.json"; +string constant CHAIN_MONITOR_CONFIG_PATH = "./volume/chain-monitor-config.json"; +string constant BRIDGE_HISTORY_CONFIG_PATH = "./volume/bridge-history-config.json"; +string constant BALANCE_CHECKER_CONFIG_PATH = "./volume/balance-checker-config.json"; +string constant FRONTEND_ENV_PATH = "./volume/.env.frontend"; + +contract ProxyAdminSetOwner is ProxyAdmin { + /// @dev allow setting the owner in the constructor, otherwise + /// DeterministicDeploymentProxy would become the owner. + constructor(address owner) { + _transferOwnership(owner); + } +} + +contract MultipleVersionRollupVerifierSetOwner is MultipleVersionRollupVerifier { + /// @dev allow setting the owner in the constructor, otherwise + /// DeterministicDeploymentProxy would become the owner. + constructor( + address owner, + uint256[] memory _versions, + address[] memory _verifiers + ) MultipleVersionRollupVerifier(_versions, _verifiers) { + _transferOwnership(owner); + } +} + +contract ScrollStandardERC20FactorySetOwner is ScrollStandardERC20Factory { + /// @dev allow setting the owner in the constructor, otherwise + /// DeterministicDeploymentProxy would become the owner. + constructor(address owner, address _implementation) ScrollStandardERC20Factory(_implementation) { + _transferOwnership(owner); + } +} + +/// @notice Configuration allows inheriting contracts to read the TOML configuration file. +abstract contract Configuration is Script { + using stdToml for string; + + /******************* + * State variables * + *******************/ + + string internal cfg; + string internal contractsCfg; + + /**************************** + * Configuration parameters * + ****************************/ + + // general + string internal L1_RPC_ENDPOINT; + string internal L2_RPC_ENDPOINT; + + uint64 internal CHAIN_ID_L1; + uint64 internal CHAIN_ID_L2; + + uint256 internal MAX_TX_IN_CHUNK; + uint256 internal MAX_BLOCK_IN_CHUNK; + uint256 internal MAX_L1_MESSAGE_GAS_LIMIT; + + uint256 internal L1_CONTRACT_DEPLOYMENT_BLOCK; + + bool internal TEST_ENV_MOCK_FINALIZE_ENABLED; + uint256 internal TEST_ENV_MOCK_FINALIZE_TIMEOUT_SEC; + + // accounts + uint256 internal DEPLOYER_PRIVATE_KEY; + uint256 internal L1_COMMIT_SENDER_PRIVATE_KEY; + uint256 internal L1_FINALIZE_SENDER_PRIVATE_KEY; + uint256 internal L1_GAS_ORACLE_SENDER_PRIVATE_KEY; + uint256 internal L2_GAS_ORACLE_SENDER_PRIVATE_KEY; + + address internal DEPLOYER_ADDR; + address internal L1_COMMIT_SENDER_ADDR; + address internal L1_FINALIZE_SENDER_ADDR; + address internal L1_GAS_ORACLE_SENDER_ADDR; + address internal L2_GAS_ORACLE_SENDER_ADDR; + + address internal OWNER_ADDR; + + address internal L2GETH_SIGNER_0_ADDRESS; + + // db + string internal SCROLL_DB_CONNECTION_STRING; + string internal CHAIN_MONITOR_DB_CONNECTION_STRING; + string internal BRIDGE_HISTORY_DB_CONNECTION_STRING; + + // genesis + uint256 internal L2_MAX_ETH_SUPPLY; + uint256 internal L2_DEPLOYER_INITIAL_BALANCE; + uint256 internal L2_SCROLL_MESSENGER_INITIAL_BALANCE; + + // contracts + string internal DEPLOYMENT_SALT; + + address internal L1_FEE_VAULT_ADDR; + address internal L1_PLONK_VERIFIER_ADDR; + + // coordinator + string internal COORDINATOR_JWT_SECRET_KEY; + + /*************** + * Constructor * + ***************/ + + constructor() { + if (!vm.exists(CONFIG_CONTRACTS_PATH)) { + string memory template = vm.readFile(CONFIG_CONTRACTS_TEMPLATE_PATH); + vm.writeFile(CONFIG_CONTRACTS_PATH, template); + } + + cfg = vm.readFile(CONFIG_PATH); + contractsCfg = vm.readFile(CONFIG_CONTRACTS_PATH); + + L1_RPC_ENDPOINT = cfg.readString(".general.L1_RPC_ENDPOINT"); + L2_RPC_ENDPOINT = cfg.readString(".general.L2_RPC_ENDPOINT"); + + CHAIN_ID_L1 = uint64(cfg.readUint(".general.CHAIN_ID_L1")); + CHAIN_ID_L2 = uint64(cfg.readUint(".general.CHAIN_ID_L2")); + + MAX_TX_IN_CHUNK = cfg.readUint(".general.MAX_TX_IN_CHUNK"); + MAX_BLOCK_IN_CHUNK = cfg.readUint(".general.MAX_BLOCK_IN_CHUNK"); + MAX_L1_MESSAGE_GAS_LIMIT = cfg.readUint(".general.MAX_L1_MESSAGE_GAS_LIMIT"); + + L1_CONTRACT_DEPLOYMENT_BLOCK = cfg.readUint(".general.L1_CONTRACT_DEPLOYMENT_BLOCK"); + + TEST_ENV_MOCK_FINALIZE_ENABLED = cfg.readBool(".general.TEST_ENV_MOCK_FINALIZE_ENABLED"); + TEST_ENV_MOCK_FINALIZE_TIMEOUT_SEC = cfg.readUint(".general.TEST_ENV_MOCK_FINALIZE_TIMEOUT_SEC"); + + DEPLOYER_PRIVATE_KEY = cfg.readUint(".accounts.DEPLOYER_PRIVATE_KEY"); + L1_COMMIT_SENDER_PRIVATE_KEY = cfg.readUint(".accounts.L1_COMMIT_SENDER_PRIVATE_KEY"); + L1_FINALIZE_SENDER_PRIVATE_KEY = cfg.readUint(".accounts.L1_FINALIZE_SENDER_PRIVATE_KEY"); + L1_GAS_ORACLE_SENDER_PRIVATE_KEY = cfg.readUint(".accounts.L1_GAS_ORACLE_SENDER_PRIVATE_KEY"); + L2_GAS_ORACLE_SENDER_PRIVATE_KEY = cfg.readUint(".accounts.L2_GAS_ORACLE_SENDER_PRIVATE_KEY"); + + DEPLOYER_ADDR = cfg.readAddress(".accounts.DEPLOYER_ADDR"); + L1_COMMIT_SENDER_ADDR = cfg.readAddress(".accounts.L1_COMMIT_SENDER_ADDR"); + L1_FINALIZE_SENDER_ADDR = cfg.readAddress(".accounts.L1_FINALIZE_SENDER_ADDR"); + L1_GAS_ORACLE_SENDER_ADDR = cfg.readAddress(".accounts.L1_GAS_ORACLE_SENDER_ADDR"); + L2_GAS_ORACLE_SENDER_ADDR = cfg.readAddress(".accounts.L2_GAS_ORACLE_SENDER_ADDR"); + + OWNER_ADDR = cfg.readAddress(".accounts.OWNER_ADDR"); + + L2GETH_SIGNER_0_ADDRESS = cfg.readAddress(".accounts.L2GETH_SIGNER_0_ADDRESS"); + + SCROLL_DB_CONNECTION_STRING = cfg.readString(".db.SCROLL_DB_CONNECTION_STRING"); + CHAIN_MONITOR_DB_CONNECTION_STRING = cfg.readString(".db.CHAIN_MONITOR_DB_CONNECTION_STRING"); + BRIDGE_HISTORY_DB_CONNECTION_STRING = cfg.readString(".db.BRIDGE_HISTORY_DB_CONNECTION_STRING"); + + L2_MAX_ETH_SUPPLY = cfg.readUint(".genesis.L2_MAX_ETH_SUPPLY"); + L2_DEPLOYER_INITIAL_BALANCE = cfg.readUint(".genesis.L2_DEPLOYER_INITIAL_BALANCE"); + L2_SCROLL_MESSENGER_INITIAL_BALANCE = L2_MAX_ETH_SUPPLY - L2_DEPLOYER_INITIAL_BALANCE; + + DEPLOYMENT_SALT = cfg.readString(".contracts.DEPLOYMENT_SALT"); + + L1_FEE_VAULT_ADDR = cfg.readAddress(".contracts.L1_FEE_VAULT_ADDR"); + L1_PLONK_VERIFIER_ADDR = cfg.readAddress(".contracts.L1_PLONK_VERIFIER_ADDR"); + + COORDINATOR_JWT_SECRET_KEY = cfg.readString(".coordinator.COORDINATOR_JWT_SECRET_KEY"); + + runSanityCheck(); + } + + /********************** + * Internal interface * + **********************/ + + /// @dev Ensure that `addr` is not the zero address. + /// This helps catch bugs arising from incorrect deployment order. + function notnull(address addr) internal pure returns (address) { + require(addr != address(0), "null address"); + return addr; + } + + function tryGetOverride(string memory name) internal returns (address) { + address addr; + string memory key = string(abi.encodePacked(".contracts.overrides.", name)); + + if (!vm.keyExistsToml(cfg, key)) { + return address(0); + } + + addr = cfg.readAddress(key); + + if (addr.code.length == 0) { + (VmSafe.CallerMode callerMode, , ) = vm.readCallers(); + + // if we're ready to start broadcasting transactions, then we + // must ensure that the override contract has been deployed. + if (callerMode == VmSafe.CallerMode.Broadcast || callerMode == VmSafe.CallerMode.RecurrentBroadcast) { + revert( + string( + abi.encodePacked( + "[ERROR] override ", + name, + " = ", + vm.toString(addr), + " not deployed in broadcast mode" + ) + ) + ); + } + } + + return addr; + } + + /********************* + * Private functions * + *********************/ + + function runSanityCheck() private view { + verifyAccount("DEPLOYER", DEPLOYER_PRIVATE_KEY, DEPLOYER_ADDR); + verifyAccount("L1_COMMIT_SENDER", L1_COMMIT_SENDER_PRIVATE_KEY, L1_COMMIT_SENDER_ADDR); + verifyAccount("L1_FINALIZE_SENDER", L1_FINALIZE_SENDER_PRIVATE_KEY, L1_FINALIZE_SENDER_ADDR); + verifyAccount("L1_GAS_ORACLE_SENDER", L1_GAS_ORACLE_SENDER_PRIVATE_KEY, L1_GAS_ORACLE_SENDER_ADDR); + verifyAccount("L2_GAS_ORACLE_SENDER", L2_GAS_ORACLE_SENDER_PRIVATE_KEY, L2_GAS_ORACLE_SENDER_ADDR); + } + + function verifyAccount( + string memory name, + uint256 privateKey, + address addr + ) private pure { + if (vm.addr(privateKey) != addr) { + revert( + string( + abi.encodePacked( + "[ERROR] ", + name, + "_ADDR (", + vm.toString(addr), + ") does not match ", + name, + "_PRIVATE_KEY" + ) + ) + ); + } + } +} + +/// @notice DeterminsticDeployment provides utilities for deterministic contract deployments. +abstract contract DeterminsticDeployment is Configuration { + using stdToml for string; + + /********* + * Types * + *********/ + + enum ScriptMode { + None, + LogAddresses, + WriteConfig, + VerifyConfig + } + + /******************* + * State variables * + *******************/ + + ScriptMode private mode; + string private saltPrefix; + bool private skipDeploy; + + /*************** + * Constructor * + ***************/ + + constructor() { + mode = ScriptMode.None; + skipDeploy = false; + + // salt prefix used for deterministic deployments + if (bytes(DEPLOYMENT_SALT).length != 0) { + saltPrefix = DEPLOYMENT_SALT; + } else { + saltPrefix = DEFAULT_DEPLOYMENT_SALT; + } + + // sanity check: make sure DeterministicDeploymentProxy exists + if (DETERMINISTIC_DEPLOYMENT_PROXY_ADDR.code.length == 0) { + revert( + string( + abi.encodePacked( + "[ERROR] DeterministicDeploymentProxy (", + vm.toString(DETERMINISTIC_DEPLOYMENT_PROXY_ADDR), + ") is not available" + ) + ) + ); + } + } + + /********************** + * Internal interface * + **********************/ + + function setScriptMode(ScriptMode scriptMode) internal { + mode = scriptMode; + } + + function setScriptMode(string memory scriptMode) internal { + if (keccak256(bytes(scriptMode)) == keccak256(bytes("log-addresses"))) { + mode = ScriptMode.WriteConfig; + } else if (keccak256(bytes(scriptMode)) == keccak256(bytes("write-config"))) { + mode = ScriptMode.WriteConfig; + } else if (keccak256(bytes(scriptMode)) == keccak256(bytes("verify-config"))) { + mode = ScriptMode.VerifyConfig; + } else { + mode = ScriptMode.None; + } + } + + function skipDeployment() internal { + skipDeploy = true; + } + + function deploy(string memory name, bytes memory codeWithArgs) internal returns (address) { + return _deploy(name, codeWithArgs); + } + + function deploy( + string memory name, + bytes memory code, + bytes memory args + ) internal returns (address) { + return _deploy(name, abi.encodePacked(code, args)); + } + + function predict(string memory name, bytes memory codeWithArgs) internal view returns (address) { + return _predict(name, codeWithArgs); + } + + function predict( + string memory name, + bytes memory code, + bytes memory args + ) internal view returns (address) { + return _predict(name, abi.encodePacked(code, args)); + } + + function upgrade( + address proxyAdminAddr, + address proxyAddr, + address implAddr + ) internal { + if (!skipDeploy) { + ProxyAdmin(notnull(proxyAdminAddr)).upgrade( + ITransparentUpgradeableProxy(notnull(proxyAddr)), + notnull(implAddr) + ); + } + } + + /********************* + * Private functions * + *********************/ + + function _getSalt(string memory name) internal view returns (bytes32) { + return keccak256(abi.encodePacked(saltPrefix, name)); + } + + function _deploy(string memory name, bytes memory codeWithArgs) private returns (address) { + // check override (mainly used with predeploys) + address addr = tryGetOverride(name); + + if (addr != address(0)) { + _label(name, addr); + return addr; + } + + // predict determinstic deployment address + addr = _predict(name, codeWithArgs); + _label(name, addr); + + if (skipDeploy) { + return addr; + } + + // revert if the contract is already deployed + if (addr.code.length > 0) { + revert( + string(abi.encodePacked("[ERROR] contract ", name, " (", vm.toString(addr), ") is already deployed")) + ); + } + + // deploy contract + bytes32 salt = _getSalt(name); + bytes memory data = abi.encodePacked(salt, codeWithArgs); + (bool success, ) = DETERMINISTIC_DEPLOYMENT_PROXY_ADDR.call(data); + require(success, "call failed"); + require(addr.code.length != 0, "deployment address mismatch"); + + return addr; + } + + function _predict(string memory name, bytes memory codeWithArgs) private view returns (address) { + bytes32 salt = _getSalt(name); + + return + address( + uint160( + uint256( + keccak256( + abi.encodePacked( + bytes1(0xff), + DETERMINISTIC_DEPLOYMENT_PROXY_ADDR, + salt, + keccak256(codeWithArgs) + ) + ) + ) + ) + ); + } + + function _label(string memory name, address addr) internal { + vm.label(addr, name); + + if (mode == ScriptMode.None) { + return; + } + + if (mode == ScriptMode.LogAddresses) { + console.log(string(abi.encodePacked(name, "_ADDR=", vm.toString(address(addr))))); + return; + } + + string memory tomlPath = string(abi.encodePacked(".", name, "_ADDR")); + + if (mode == ScriptMode.WriteConfig) { + vm.writeToml(vm.toString(addr), CONFIG_CONTRACTS_PATH, tomlPath); + return; + } + + if (mode == ScriptMode.VerifyConfig) { + address expectedAddr = contractsCfg.readAddress(tomlPath); + + if (addr != expectedAddr) { + revert( + string( + abi.encodePacked( + "[ERROR] unexpected address for ", + name, + ", expected = ", + vm.toString(expectedAddr), + " (from toml config), got = ", + vm.toString(addr) + ) + ) + ); + } + } + } +} + +contract DeployScroll is DeterminsticDeployment { + /********* + * Types * + *********/ + + enum Layer { + None, + L1, + L2 + } + + /******************* + * State variables * + *******************/ + + // general configurations + Layer private broadcastLayer = Layer.None; + + /*********************** + * Contracts to deploy * + ***********************/ + + // L1 addresses + address internal L1_CUSTOM_ERC20_GATEWAY_IMPLEMENTATION_ADDR; + address internal L1_CUSTOM_ERC20_GATEWAY_PROXY_ADDR; + address internal L1_ENFORCED_TX_GATEWAY_IMPLEMENTATION_ADDR; + address internal L1_ENFORCED_TX_GATEWAY_PROXY_ADDR; + address internal L1_ERC1155_GATEWAY_IMPLEMENTATION_ADDR; + address internal L1_ERC1155_GATEWAY_PROXY_ADDR; + address internal L1_ERC721_GATEWAY_IMPLEMENTATION_ADDR; + address internal L1_ERC721_GATEWAY_PROXY_ADDR; + address internal L1_ETH_GATEWAY_IMPLEMENTATION_ADDR; + address internal L1_ETH_GATEWAY_PROXY_ADDR; + address internal L1_GATEWAY_ROUTER_IMPLEMENTATION_ADDR; + address internal L1_GATEWAY_ROUTER_PROXY_ADDR; + address internal L1_MESSAGE_QUEUE_IMPLEMENTATION_ADDR; + address internal L1_MESSAGE_QUEUE_PROXY_ADDR; + address internal L1_MULTIPLE_VERSION_ROLLUP_VERIFIER_ADDR; + address internal L1_PROXY_ADMIN_ADDR; + address internal L1_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR; + address internal L1_SCROLL_CHAIN_IMPLEMENTATION_ADDR; + address internal L1_SCROLL_CHAIN_PROXY_ADDR; + address internal L1_SCROLL_MESSENGER_IMPLEMENTATION_ADDR; + address internal L1_SCROLL_MESSENGER_PROXY_ADDR; + address internal L1_STANDARD_ERC20_GATEWAY_IMPLEMENTATION_ADDR; + address internal L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR; + address internal L1_WETH_ADDR; + address internal L1_WETH_GATEWAY_IMPLEMENTATION_ADDR; + address internal L1_WETH_GATEWAY_PROXY_ADDR; + address internal L1_WHITELIST_ADDR; + address internal L1_ZKEVM_VERIFIER_V1_ADDR; + address internal L2_GAS_PRICE_ORACLE_IMPLEMENTATION_ADDR; + address internal L2_GAS_PRICE_ORACLE_PROXY_ADDR; + + // L2 addresses + address internal L1_GAS_PRICE_ORACLE_ADDR; + address internal L2_CUSTOM_ERC20_GATEWAY_IMPLEMENTATION_ADDR; + address internal L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR; + address internal L2_ERC1155_GATEWAY_IMPLEMENTATION_ADDR; + address internal L2_ERC1155_GATEWAY_PROXY_ADDR; + address internal L2_ERC721_GATEWAY_IMPLEMENTATION_ADDR; + address internal L2_ERC721_GATEWAY_PROXY_ADDR; + address internal L2_ETH_GATEWAY_IMPLEMENTATION_ADDR; + address internal L2_ETH_GATEWAY_PROXY_ADDR; + address internal L2_GATEWAY_ROUTER_IMPLEMENTATION_ADDR; + address internal L2_GATEWAY_ROUTER_PROXY_ADDR; + address internal L2_MESSAGE_QUEUE_ADDR; + address internal L2_PROXY_ADMIN_ADDR; + address internal L2_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR; + address internal L2_SCROLL_MESSENGER_IMPLEMENTATION_ADDR; + address internal L2_SCROLL_MESSENGER_PROXY_ADDR; + address internal L2_SCROLL_STANDARD_ERC20_ADDR; + address internal L2_SCROLL_STANDARD_ERC20_FACTORY_ADDR; + address internal L2_STANDARD_ERC20_GATEWAY_IMPLEMENTATION_ADDR; + address internal L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR; + address internal L2_TX_FEE_VAULT_ADDR; + address internal L2_WETH_ADDR; + address internal L2_WETH_GATEWAY_IMPLEMENTATION_ADDR; + address internal L2_WETH_GATEWAY_PROXY_ADDR; + address internal L2_WHITELIST_ADDR; + + /************* + * Utilities * + *************/ + + /// @dev Only broadcast code block if we run the script on the specified layer. + modifier broadcast(Layer layer) { + if (broadcastLayer == layer) { + vm.startBroadcast(DEPLOYER_PRIVATE_KEY); + } else { + // make sure we use the correct sender in simulation + vm.startPrank(DEPLOYER_ADDR); + } + + _; + + if (broadcastLayer == layer) { + vm.stopBroadcast(); + } else { + vm.stopPrank(); + } + } + + /// @dev Only execute block if we run the script on the specified layer. + modifier only(Layer layer) { + if (broadcastLayer != layer) { + return; + } + _; + } + + /*************** + * Entry point * + ***************/ + + function run(string memory layer, string memory scriptMode) public { + broadcastLayer = parseLayer(layer); + setScriptMode(scriptMode); + + checkDeployerBalance(); + deployAllContracts(); + initializeL1Contracts(); + initializeL2Contracts(); + } + + /********************** + * Internal interface * + **********************/ + + function predictAllContracts() internal { + skipDeployment(); + deployAllContracts(); + } + + /********************* + * Private functions * + *********************/ + + function parseLayer(string memory raw) private pure returns (Layer) { + if (keccak256(bytes(raw)) == keccak256(bytes("L1"))) { + return Layer.L1; + } else if (keccak256(bytes(raw)) == keccak256(bytes("L2"))) { + return Layer.L2; + } else { + return Layer.None; + } + } + + function checkDeployerBalance() private view { + // ignore balance during simulation + if (broadcastLayer == Layer.None) { + return; + } + + if (DEPLOYER_ADDR.balance < MINIMUM_DEPLOYER_BALANCE) { + revert( + string( + abi.encodePacked( + "[ERROR] insufficient funds on deployer account (", + vm.toString(DEPLOYER_ADDR), + ")" + ) + ) + ); + } + } + + function deployAllContracts() private { + deployL1Contracts1stPass(); + deployL2Contracts1stPass(); + deployL1Contracts2ndPass(); + deployL2Contracts2ndPass(); + } + + // @notice deployL1Contracts1stPass deploys L1 contracts whose initialization does not depend on any L2 addresses. + function deployL1Contracts1stPass() private broadcast(Layer.L1) { + deployL1Weth(); + deployL1ProxyAdmin(); + deployL1PlaceHolder(); + deployL1Whitelist(); + deployL2GasPriceOracle(); + deployL1ScrollChainProxy(); + deployL1ScrollMessengerProxy(); + deployL1EnforcedTxGateway(); + deployL1ZkEvmVerifierV1(); + deployL1MultipleVersionRollupVerifier(); + deployL1MessageQueue(); + deployL1ScrollChain(); + deployL1GatewayRouter(); + deployL1ETHGatewayProxy(); + deployL1WETHGatewayProxy(); + deployL1StandardERC20GatewayProxy(); + deployL1CustomERC20GatewayProxy(); + deployL1ERC721GatewayProxy(); + deployL1ERC1155GatewayProxy(); + } + + // @notice deployL2Contracts1stPass deploys L2 contracts whose initialization does not depend on any L1 addresses. + function deployL2Contracts1stPass() private broadcast(Layer.L2) { + deployL2MessageQueue(); + deployL1GasPriceOracle(); + deployL2Whitelist(); + deployL2Weth(); + deployTxFeeVault(); + deployL2ProxyAdmin(); + deployL2PlaceHolder(); + deployL2ScrollMessengerProxy(); + deployL2ETHGatewayProxy(); + deployL2WETHGatewayProxy(); + deployL2StandardERC20GatewayProxy(); + deployL2CustomERC20GatewayProxy(); + deployL2ERC721GatewayProxy(); + deployL2ERC1155GatewayProxy(); + deployScrollStandardERC20Factory(); + } + + // @notice deployL1Contracts2ndPass deploys L1 contracts whose initialization depends on some L2 addresses. + function deployL1Contracts2ndPass() private broadcast(Layer.L1) { + deployL1ScrollMessenger(); + deployL1StandardERC20Gateway(); + deployL1ETHGateway(); + deployL1WETHGateway(); + deployL1CustomERC20Gateway(); + deployL1ERC721Gateway(); + deployL1ERC1155Gateway(); + } + + // @notice deployL2Contracts2ndPass deploys L2 contracts whose initialization depends on some L1 addresses. + function deployL2Contracts2ndPass() private broadcast(Layer.L2) { + // upgradable + deployL2ScrollMessenger(); + deployL2GatewayRouter(); + deployL2StandardERC20Gateway(); + deployL2ETHGateway(); + deployL2WETHGateway(); + deployL2CustomERC20Gateway(); + deployL2ERC721Gateway(); + deployL2ERC1155Gateway(); + } + + // @notice initializeL1Contracts initializes contracts deployed on L1. + function initializeL1Contracts() private broadcast(Layer.L1) only(Layer.L1) { + initializeScrollChain(); + initializeL2GasPriceOracle(); + initializeL1MessageQueue(); + initializeL1ScrollMessenger(); + initializeEnforcedTxGateway(); + initializeL1GatewayRouter(); + initializeL1CustomERC20Gateway(); + initializeL1ERC1155Gateway(); + initializeL1ERC721Gateway(); + initializeL1ETHGateway(); + initializeL1StandardERC20Gateway(); + initializeL1WETHGateway(); + initializeL1Whitelist(); + + transferL1ContractOwnership(); + } + + // @notice initializeL2Contracts initializes contracts deployed on L2. + function initializeL2Contracts() private broadcast(Layer.L2) only(Layer.L2) { + initializeL2MessageQueue(); + initializeL2TxFeeVault(); + initializeL1GasPriceOracle(); + initializeL2ScrollMessenger(); + initializeL2GatewayRouter(); + initializeL2CustomERC20Gateway(); + initializeL2ERC1155Gateway(); + initializeL2ERC721Gateway(); + initializeL2ETHGateway(); + initializeL2StandardERC20Gateway(); + initializeL2WETHGateway(); + initializeScrollStandardERC20Factory(); + initializeL2Whitelist(); + + transferL2ContractOwnership(); + } + + /*************************** + * L1: 1st pass deployment * + ***************************/ + + function deployL1Weth() private { + L1_WETH_ADDR = deploy("L1_WETH", type(WrappedEther).creationCode); + } + + function deployL1ProxyAdmin() private { + bytes memory args = abi.encode(DEPLOYER_ADDR); + L1_PROXY_ADMIN_ADDR = deploy("L1_PROXY_ADMIN", type(ProxyAdminSetOwner).creationCode, args); + } + + function deployL1PlaceHolder() private { + L1_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR = deploy( + "L1_PROXY_IMPLEMENTATION_PLACEHOLDER", + type(EmptyContract).creationCode + ); + } + + function deployL1Whitelist() private { + bytes memory args = abi.encode(DEPLOYER_ADDR); + L1_WHITELIST_ADDR = deploy("L1_WHITELIST", type(Whitelist).creationCode, args); + } + + function deployL2GasPriceOracle() private { + L2_GAS_PRICE_ORACLE_IMPLEMENTATION_ADDR = deploy( + "L2_GAS_PRICE_ORACLE_IMPLEMENTATION", + type(L2GasPriceOracle).creationCode + ); + + bytes memory args = abi.encode( + notnull(L2_GAS_PRICE_ORACLE_IMPLEMENTATION_ADDR), + notnull(L1_PROXY_ADMIN_ADDR), + new bytes(0) + ); + + L2_GAS_PRICE_ORACLE_PROXY_ADDR = deploy( + "L2_GAS_PRICE_ORACLE_PROXY", + type(TransparentUpgradeableProxy).creationCode, + args + ); + } + + function deployL1ScrollChainProxy() private { + bytes memory args = abi.encode( + notnull(L1_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR), + notnull(L1_PROXY_ADMIN_ADDR), + new bytes(0) + ); + + L1_SCROLL_CHAIN_PROXY_ADDR = deploy( + "L1_SCROLL_CHAIN_PROXY", + type(TransparentUpgradeableProxy).creationCode, + args + ); + } + + function deployL1ScrollMessengerProxy() private { + bytes memory args = abi.encode( + notnull(L1_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR), + notnull(L1_PROXY_ADMIN_ADDR), + new bytes(0) + ); + + L1_SCROLL_MESSENGER_PROXY_ADDR = deploy( + "L1_SCROLL_MESSENGER_PROXY", + type(TransparentUpgradeableProxy).creationCode, + args + ); + } + + function deployL1EnforcedTxGateway() private { + L1_ENFORCED_TX_GATEWAY_IMPLEMENTATION_ADDR = deploy( + "L1_ENFORCED_TX_GATEWAY_IMPLEMENTATION", + type(EnforcedTxGateway).creationCode + ); + + bytes memory args = abi.encode( + notnull(L1_ENFORCED_TX_GATEWAY_IMPLEMENTATION_ADDR), + notnull(L1_PROXY_ADMIN_ADDR), + new bytes(0) + ); + + L1_ENFORCED_TX_GATEWAY_PROXY_ADDR = deploy( + "L1_ENFORCED_TX_GATEWAY_PROXY", + type(TransparentUpgradeableProxy).creationCode, + args + ); + } + + function deployL1ZkEvmVerifierV1() private { + bytes memory args = abi.encode(notnull(L1_PLONK_VERIFIER_ADDR)); + L1_ZKEVM_VERIFIER_V1_ADDR = deploy("L1_ZKEVM_VERIFIER_V1", type(ZkEvmVerifierV1).creationCode, args); + } + + function deployL1MultipleVersionRollupVerifier() private { + uint256[] memory _versions = new uint256[](1); + address[] memory _verifiers = new address[](1); + _versions[0] = 1; + _verifiers[0] = notnull(L1_ZKEVM_VERIFIER_V1_ADDR); + + bytes memory args = abi.encode(DEPLOYER_ADDR, _versions, _verifiers); + + L1_MULTIPLE_VERSION_ROLLUP_VERIFIER_ADDR = deploy( + "L1_MULTIPLE_VERSION_ROLLUP_VERIFIER", + type(MultipleVersionRollupVerifierSetOwner).creationCode, + args + ); + } + + function deployL1MessageQueue() private { + bytes memory args = abi.encode( + notnull(L1_SCROLL_MESSENGER_PROXY_ADDR), + notnull(L1_SCROLL_CHAIN_PROXY_ADDR), + notnull(L1_ENFORCED_TX_GATEWAY_PROXY_ADDR) + ); + + L1_MESSAGE_QUEUE_IMPLEMENTATION_ADDR = deploy( + "L1_MESSAGE_QUEUE_IMPLEMENTATION", + type(L1MessageQueueWithGasPriceOracle).creationCode, + args + ); + + bytes memory args2 = abi.encode( + notnull(L1_MESSAGE_QUEUE_IMPLEMENTATION_ADDR), + notnull(L1_PROXY_ADMIN_ADDR), + new bytes(0) + ); + + L1_MESSAGE_QUEUE_PROXY_ADDR = deploy( + "L1_MESSAGE_QUEUE_PROXY", + type(TransparentUpgradeableProxy).creationCode, + args2 + ); + } + + function deployL1ScrollChain() private { + bytes memory args = abi.encode( + CHAIN_ID_L2, + notnull(L1_MESSAGE_QUEUE_PROXY_ADDR), + notnull(L1_MULTIPLE_VERSION_ROLLUP_VERIFIER_ADDR) + ); + + bytes memory creationCode = type(ScrollChain).creationCode; + + if (TEST_ENV_MOCK_FINALIZE_ENABLED) { + creationCode = type(ScrollChainMockFinalize).creationCode; + } + + L1_SCROLL_CHAIN_IMPLEMENTATION_ADDR = deploy("L1_SCROLL_CHAIN_IMPLEMENTATION", creationCode, args); + + upgrade(L1_PROXY_ADMIN_ADDR, L1_SCROLL_CHAIN_PROXY_ADDR, L1_SCROLL_CHAIN_IMPLEMENTATION_ADDR); + } + + function deployL1GatewayRouter() private { + L1_GATEWAY_ROUTER_IMPLEMENTATION_ADDR = deploy( + "L1_GATEWAY_ROUTER_IMPLEMENTATION", + type(L1GatewayRouter).creationCode + ); + + bytes memory args = abi.encode( + notnull(L1_GATEWAY_ROUTER_IMPLEMENTATION_ADDR), + notnull(L1_PROXY_ADMIN_ADDR), + new bytes(0) + ); + + L1_GATEWAY_ROUTER_PROXY_ADDR = deploy( + "L1_GATEWAY_ROUTER_PROXY", + type(TransparentUpgradeableProxy).creationCode, + args + ); + } + + function deployL1ETHGatewayProxy() private { + bytes memory args = abi.encode( + notnull(L1_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR), + notnull(L1_PROXY_ADMIN_ADDR), + new bytes(0) + ); + + L1_ETH_GATEWAY_PROXY_ADDR = deploy( + "L1_ETH_GATEWAY_PROXY", + type(TransparentUpgradeableProxy).creationCode, + args + ); + } + + function deployL1WETHGatewayProxy() private { + bytes memory args = abi.encode( + notnull(L1_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR), + notnull(L1_PROXY_ADMIN_ADDR), + new bytes(0) + ); + + L1_WETH_GATEWAY_PROXY_ADDR = deploy( + "L1_WETH_GATEWAY_PROXY", + type(TransparentUpgradeableProxy).creationCode, + args + ); + } + + function deployL1StandardERC20GatewayProxy() private { + bytes memory args = abi.encode( + notnull(L1_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR), + notnull(L1_PROXY_ADMIN_ADDR), + new bytes(0) + ); + + L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR = deploy( + "L1_STANDARD_ERC20_GATEWAY_PROXY", + type(TransparentUpgradeableProxy).creationCode, + args + ); + } + + function deployL1CustomERC20GatewayProxy() private { + bytes memory args = abi.encode( + notnull(L1_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR), + notnull(L1_PROXY_ADMIN_ADDR), + new bytes(0) + ); + + L1_CUSTOM_ERC20_GATEWAY_PROXY_ADDR = deploy( + "L1_CUSTOM_ERC20_GATEWAY_PROXY", + type(TransparentUpgradeableProxy).creationCode, + args + ); + } + + function deployL1ERC721GatewayProxy() private { + bytes memory args = abi.encode( + notnull(L1_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR), + notnull(L1_PROXY_ADMIN_ADDR), + new bytes(0) + ); + + L1_ERC721_GATEWAY_PROXY_ADDR = deploy( + "L1_ERC721_GATEWAY_PROXY", + type(TransparentUpgradeableProxy).creationCode, + args + ); + } + + function deployL1ERC1155GatewayProxy() private { + bytes memory args = abi.encode( + notnull(L1_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR), + notnull(L1_PROXY_ADMIN_ADDR), + new bytes(0) + ); + + L1_ERC1155_GATEWAY_PROXY_ADDR = deploy( + "L1_ERC1155_GATEWAY_PROXY", + type(TransparentUpgradeableProxy).creationCode, + args + ); + } + + /*************************** + * L2: 1st pass deployment * + ***************************/ + + function deployL2MessageQueue() private { + bytes memory args = abi.encode(DEPLOYER_ADDR); + L2_MESSAGE_QUEUE_ADDR = deploy("L2_MESSAGE_QUEUE", type(L2MessageQueue).creationCode, args); + } + + function deployL1GasPriceOracle() private { + bytes memory args = abi.encode(DEPLOYER_ADDR); + L1_GAS_PRICE_ORACLE_ADDR = deploy("L1_GAS_PRICE_ORACLE", type(L1GasPriceOracle).creationCode, args); + } + + function deployL2Whitelist() private { + bytes memory args = abi.encode(DEPLOYER_ADDR); + L2_WHITELIST_ADDR = deploy("L2_WHITELIST", type(Whitelist).creationCode, args); + } + + function deployL2Weth() private { + L2_WETH_ADDR = deploy("L2_WETH", type(WrappedEther).creationCode); + } + + function deployTxFeeVault() private { + bytes memory args = abi.encode(DEPLOYER_ADDR, L1_FEE_VAULT_ADDR, FEE_VAULT_MIN_WITHDRAW_AMOUNT); + L2_TX_FEE_VAULT_ADDR = deploy("L2_TX_FEE_VAULT", type(L2TxFeeVault).creationCode, args); + } + + function deployL2ProxyAdmin() private { + bytes memory args = abi.encode(DEPLOYER_ADDR); + L2_PROXY_ADMIN_ADDR = deploy("L2_PROXY_ADMIN", type(ProxyAdminSetOwner).creationCode, args); + } + + function deployL2PlaceHolder() private { + L2_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR = deploy( + "L2_PROXY_IMPLEMENTATION_PLACEHOLDER", + type(EmptyContract).creationCode + ); + } + + function deployL2ScrollMessengerProxy() private { + bytes memory args = abi.encode( + notnull(L2_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR), + notnull(L2_PROXY_ADMIN_ADDR), + new bytes(0) + ); + + L2_SCROLL_MESSENGER_PROXY_ADDR = deploy( + "L2_SCROLL_MESSENGER_PROXY", + type(TransparentUpgradeableProxy).creationCode, + args + ); + } + + function deployL2StandardERC20GatewayProxy() private { + bytes memory args = abi.encode( + notnull(L2_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR), + notnull(L2_PROXY_ADMIN_ADDR), + new bytes(0) + ); + + L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR = deploy( + "L2_STANDARD_ERC20_GATEWAY_PROXY", + type(TransparentUpgradeableProxy).creationCode, + args + ); + } + + function deployL2ETHGatewayProxy() private { + bytes memory args = abi.encode( + notnull(L2_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR), + notnull(L2_PROXY_ADMIN_ADDR), + new bytes(0) + ); + + L2_ETH_GATEWAY_PROXY_ADDR = deploy( + "L2_ETH_GATEWAY_PROXY", + type(TransparentUpgradeableProxy).creationCode, + args + ); + } + + function deployL2WETHGatewayProxy() private { + bytes memory args = abi.encode( + notnull(L2_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR), + notnull(L2_PROXY_ADMIN_ADDR), + new bytes(0) + ); + + L2_WETH_GATEWAY_PROXY_ADDR = deploy( + "L2_WETH_GATEWAY_PROXY", + type(TransparentUpgradeableProxy).creationCode, + args + ); + } + + function deployL2CustomERC20GatewayProxy() private { + bytes memory args = abi.encode( + notnull(L2_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR), + notnull(L2_PROXY_ADMIN_ADDR), + new bytes(0) + ); + + L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR = deploy( + "L2_CUSTOM_ERC20_GATEWAY_PROXY", + type(TransparentUpgradeableProxy).creationCode, + args + ); + } + + function deployL2ERC721GatewayProxy() private { + bytes memory args = abi.encode( + notnull(L2_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR), + notnull(L2_PROXY_ADMIN_ADDR), + new bytes(0) + ); + + L2_ERC721_GATEWAY_PROXY_ADDR = deploy( + "L2_ERC721_GATEWAY_PROXY", + type(TransparentUpgradeableProxy).creationCode, + args + ); + } + + function deployL2ERC1155GatewayProxy() private { + bytes memory args = abi.encode( + notnull(L2_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR), + notnull(L2_PROXY_ADMIN_ADDR), + new bytes(0) + ); + + L2_ERC1155_GATEWAY_PROXY_ADDR = deploy( + "L2_ERC1155_GATEWAY_PROXY", + type(TransparentUpgradeableProxy).creationCode, + args + ); + } + + function deployScrollStandardERC20Factory() private { + L2_SCROLL_STANDARD_ERC20_ADDR = deploy("L2_SCROLL_STANDARD_ERC20", type(ScrollStandardERC20).creationCode); + bytes memory args = abi.encode(DEPLOYER_ADDR, notnull(L2_SCROLL_STANDARD_ERC20_ADDR)); + + L2_SCROLL_STANDARD_ERC20_FACTORY_ADDR = deploy( + "L2_SCROLL_STANDARD_ERC20_FACTORY", + type(ScrollStandardERC20FactorySetOwner).creationCode, + args + ); + } + + /*************************** + * L1: 2nd pass deployment * + ***************************/ + + function deployL1ScrollMessenger() private { + bytes memory args = abi.encode( + notnull(L2_SCROLL_MESSENGER_PROXY_ADDR), + notnull(L1_SCROLL_CHAIN_PROXY_ADDR), + notnull(L1_MESSAGE_QUEUE_PROXY_ADDR) + ); + + L1_SCROLL_MESSENGER_IMPLEMENTATION_ADDR = deploy( + "L1_SCROLL_MESSENGER_IMPLEMENTATION", + type(L1ScrollMessenger).creationCode, + args + ); + + upgrade(L1_PROXY_ADMIN_ADDR, L1_SCROLL_MESSENGER_PROXY_ADDR, L1_SCROLL_MESSENGER_IMPLEMENTATION_ADDR); + } + + function deployL1ETHGateway() private { + bytes memory args = abi.encode( + notnull(L2_ETH_GATEWAY_PROXY_ADDR), + notnull(L1_GATEWAY_ROUTER_PROXY_ADDR), + notnull(L1_SCROLL_MESSENGER_PROXY_ADDR) + ); + + L1_ETH_GATEWAY_IMPLEMENTATION_ADDR = deploy( + "L1_ETH_GATEWAY_IMPLEMENTATION", + type(L1ETHGateway).creationCode, + args + ); + + upgrade(L1_PROXY_ADMIN_ADDR, L1_ETH_GATEWAY_PROXY_ADDR, L1_ETH_GATEWAY_IMPLEMENTATION_ADDR); + } + + function deployL1WETHGateway() private { + bytes memory args = abi.encode( + notnull(L1_WETH_ADDR), + notnull(L2_WETH_ADDR), + notnull(L2_WETH_GATEWAY_PROXY_ADDR), + notnull(L1_GATEWAY_ROUTER_PROXY_ADDR), + notnull(L1_SCROLL_MESSENGER_PROXY_ADDR) + ); + + L1_WETH_GATEWAY_IMPLEMENTATION_ADDR = deploy( + "L1_WETH_GATEWAY_IMPLEMENTATION", + type(L1WETHGateway).creationCode, + args + ); + + upgrade(L1_PROXY_ADMIN_ADDR, L1_WETH_GATEWAY_PROXY_ADDR, L1_WETH_GATEWAY_IMPLEMENTATION_ADDR); + } + + function deployL1StandardERC20Gateway() private { + bytes memory args = abi.encode( + notnull(L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR), + notnull(L1_GATEWAY_ROUTER_PROXY_ADDR), + notnull(L1_SCROLL_MESSENGER_PROXY_ADDR), + notnull(L2_SCROLL_STANDARD_ERC20_ADDR), + notnull(L2_SCROLL_STANDARD_ERC20_FACTORY_ADDR) + ); + + L1_STANDARD_ERC20_GATEWAY_IMPLEMENTATION_ADDR = deploy( + "L1_STANDARD_ERC20_GATEWAY_IMPLEMENTATION", + type(L1StandardERC20Gateway).creationCode, + args + ); + + upgrade( + L1_PROXY_ADMIN_ADDR, + L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR, + L1_STANDARD_ERC20_GATEWAY_IMPLEMENTATION_ADDR + ); + } + + function deployL1CustomERC20Gateway() private { + bytes memory args = abi.encode( + notnull(L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR), + notnull(L1_GATEWAY_ROUTER_PROXY_ADDR), + notnull(L1_SCROLL_MESSENGER_PROXY_ADDR) + ); + + L1_CUSTOM_ERC20_GATEWAY_IMPLEMENTATION_ADDR = deploy( + "L1_CUSTOM_ERC20_GATEWAY_IMPLEMENTATION", + type(L1CustomERC20Gateway).creationCode, + args + ); + + upgrade(L1_PROXY_ADMIN_ADDR, L1_CUSTOM_ERC20_GATEWAY_PROXY_ADDR, L1_CUSTOM_ERC20_GATEWAY_IMPLEMENTATION_ADDR); + } + + function deployL1ERC721Gateway() private { + bytes memory args = abi.encode(notnull(L2_ERC721_GATEWAY_PROXY_ADDR), notnull(L1_SCROLL_MESSENGER_PROXY_ADDR)); + + L1_ERC721_GATEWAY_IMPLEMENTATION_ADDR = deploy( + "L1_ERC721_GATEWAY_IMPLEMENTATION", + type(L1ERC721Gateway).creationCode, + args + ); + + upgrade(L1_PROXY_ADMIN_ADDR, L1_ERC721_GATEWAY_PROXY_ADDR, L1_ERC721_GATEWAY_IMPLEMENTATION_ADDR); + } + + function deployL1ERC1155Gateway() private { + bytes memory args = abi.encode(notnull(L2_ERC1155_GATEWAY_PROXY_ADDR), notnull(L1_SCROLL_MESSENGER_PROXY_ADDR)); + + L1_ERC1155_GATEWAY_IMPLEMENTATION_ADDR = deploy( + "L1_ERC1155_GATEWAY_IMPLEMENTATION", + type(L1ERC1155Gateway).creationCode, + args + ); + + upgrade(L1_PROXY_ADMIN_ADDR, L1_ERC1155_GATEWAY_PROXY_ADDR, L1_ERC1155_GATEWAY_IMPLEMENTATION_ADDR); + } + + /*************************** + * L2: 2nd pass deployment * + ***************************/ + + function deployL2ScrollMessenger() private { + bytes memory args = abi.encode(notnull(L1_SCROLL_MESSENGER_PROXY_ADDR), notnull(L2_MESSAGE_QUEUE_ADDR)); + + L2_SCROLL_MESSENGER_IMPLEMENTATION_ADDR = deploy( + "L2_SCROLL_MESSENGER_IMPLEMENTATION", + type(L2ScrollMessenger).creationCode, + args + ); + + upgrade(L2_PROXY_ADMIN_ADDR, L2_SCROLL_MESSENGER_PROXY_ADDR, L2_SCROLL_MESSENGER_IMPLEMENTATION_ADDR); + } + + function deployL2GatewayRouter() private { + L2_GATEWAY_ROUTER_IMPLEMENTATION_ADDR = deploy( + "L2_GATEWAY_ROUTER_IMPLEMENTATION", + type(L2GatewayRouter).creationCode + ); + + bytes memory args = abi.encode( + notnull(L2_GATEWAY_ROUTER_IMPLEMENTATION_ADDR), + notnull(L2_PROXY_ADMIN_ADDR), + new bytes(0) + ); + + L2_GATEWAY_ROUTER_PROXY_ADDR = deploy( + "L2_GATEWAY_ROUTER_PROXY", + type(TransparentUpgradeableProxy).creationCode, + args + ); + } + + function deployL2StandardERC20Gateway() private { + bytes memory args = abi.encode( + notnull(L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR), + notnull(L2_GATEWAY_ROUTER_PROXY_ADDR), + notnull(L2_SCROLL_MESSENGER_PROXY_ADDR), + notnull(L2_SCROLL_STANDARD_ERC20_FACTORY_ADDR) + ); + + L2_STANDARD_ERC20_GATEWAY_IMPLEMENTATION_ADDR = deploy( + "L2_STANDARD_ERC20_GATEWAY_IMPLEMENTATION", + type(L2StandardERC20Gateway).creationCode, + args + ); + + upgrade( + L2_PROXY_ADMIN_ADDR, + L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR, + L2_STANDARD_ERC20_GATEWAY_IMPLEMENTATION_ADDR + ); + } + + function deployL2ETHGateway() private { + bytes memory args = abi.encode( + notnull(L1_ETH_GATEWAY_PROXY_ADDR), + notnull(L2_GATEWAY_ROUTER_PROXY_ADDR), + notnull(L2_SCROLL_MESSENGER_PROXY_ADDR) + ); + + L2_ETH_GATEWAY_IMPLEMENTATION_ADDR = deploy( + "L2_ETH_GATEWAY_IMPLEMENTATION", + type(L2ETHGateway).creationCode, + args + ); + + upgrade(L2_PROXY_ADMIN_ADDR, L2_ETH_GATEWAY_PROXY_ADDR, L2_ETH_GATEWAY_IMPLEMENTATION_ADDR); + } + + function deployL2WETHGateway() private { + bytes memory args = abi.encode( + notnull(L2_WETH_ADDR), + notnull(L1_WETH_ADDR), + notnull(L1_WETH_GATEWAY_PROXY_ADDR), + notnull(L2_GATEWAY_ROUTER_PROXY_ADDR), + notnull(L2_SCROLL_MESSENGER_PROXY_ADDR) + ); + + L2_WETH_GATEWAY_IMPLEMENTATION_ADDR = deploy( + "L2_WETH_GATEWAY_IMPLEMENTATION", + type(L2WETHGateway).creationCode, + args + ); + + upgrade(L2_PROXY_ADMIN_ADDR, L2_WETH_GATEWAY_PROXY_ADDR, L2_WETH_GATEWAY_IMPLEMENTATION_ADDR); + } + + function deployL2CustomERC20Gateway() private { + bytes memory args = abi.encode( + notnull(L1_CUSTOM_ERC20_GATEWAY_PROXY_ADDR), + notnull(L2_GATEWAY_ROUTER_PROXY_ADDR), + notnull(L2_SCROLL_MESSENGER_PROXY_ADDR) + ); + + L2_CUSTOM_ERC20_GATEWAY_IMPLEMENTATION_ADDR = deploy( + "L2_CUSTOM_ERC20_GATEWAY_IMPLEMENTATION", + type(L2CustomERC20Gateway).creationCode, + args + ); + + upgrade(L2_PROXY_ADMIN_ADDR, L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR, L2_CUSTOM_ERC20_GATEWAY_IMPLEMENTATION_ADDR); + } + + function deployL2ERC721Gateway() private { + bytes memory args = abi.encode(notnull(L1_ERC721_GATEWAY_PROXY_ADDR), notnull(L2_SCROLL_MESSENGER_PROXY_ADDR)); + + L2_ERC721_GATEWAY_IMPLEMENTATION_ADDR = deploy( + "L2_ERC721_GATEWAY_IMPLEMENTATION", + type(L2ERC721Gateway).creationCode, + args + ); + + upgrade(L2_PROXY_ADMIN_ADDR, L2_ERC721_GATEWAY_PROXY_ADDR, L2_ERC721_GATEWAY_IMPLEMENTATION_ADDR); + } + + function deployL2ERC1155Gateway() private { + bytes memory args = abi.encode(notnull(L1_ERC1155_GATEWAY_PROXY_ADDR), notnull(L2_SCROLL_MESSENGER_PROXY_ADDR)); + + L2_ERC1155_GATEWAY_IMPLEMENTATION_ADDR = deploy( + "L2_ERC1155_GATEWAY_IMPLEMENTATION", + type(L2ERC1155Gateway).creationCode, + args + ); + + upgrade(L2_PROXY_ADMIN_ADDR, L2_ERC1155_GATEWAY_PROXY_ADDR, L2_ERC1155_GATEWAY_IMPLEMENTATION_ADDR); + } + + /********************** + * L1: initialization * + **********************/ + + function initializeScrollChain() private { + ScrollChain(L1_SCROLL_CHAIN_PROXY_ADDR).initialize( + notnull(L1_MESSAGE_QUEUE_PROXY_ADDR), + notnull(L1_MULTIPLE_VERSION_ROLLUP_VERIFIER_ADDR), + MAX_TX_IN_CHUNK + ); + + ScrollChain(L1_SCROLL_CHAIN_PROXY_ADDR).addSequencer(L1_COMMIT_SENDER_ADDR); + ScrollChain(L1_SCROLL_CHAIN_PROXY_ADDR).addProver(L1_FINALIZE_SENDER_ADDR); + } + + function initializeL2GasPriceOracle() private { + L2GasPriceOracle(L2_GAS_PRICE_ORACLE_PROXY_ADDR).initialize( + 21000, // _txGas + 53000, // _txGasContractCreation + 4, // _zeroGas + 16 // _nonZeroGas + ); + + L2GasPriceOracle(L2_GAS_PRICE_ORACLE_PROXY_ADDR).updateWhitelist(L1_WHITELIST_ADDR); + } + + function initializeL1MessageQueue() private { + L1MessageQueueWithGasPriceOracle(L1_MESSAGE_QUEUE_PROXY_ADDR).initialize( + notnull(L1_SCROLL_MESSENGER_PROXY_ADDR), + notnull(L1_SCROLL_CHAIN_PROXY_ADDR), + notnull(L1_ENFORCED_TX_GATEWAY_PROXY_ADDR), + notnull(L2_GAS_PRICE_ORACLE_PROXY_ADDR), + MAX_L1_MESSAGE_GAS_LIMIT + ); + + L1MessageQueueWithGasPriceOracle(L1_MESSAGE_QUEUE_PROXY_ADDR).initializeV2(); + } + + function initializeL1ScrollMessenger() private { + L1ScrollMessenger(payable(L1_SCROLL_MESSENGER_PROXY_ADDR)).initialize( + notnull(L2_SCROLL_MESSENGER_PROXY_ADDR), + notnull(L1_FEE_VAULT_ADDR), + notnull(L1_SCROLL_CHAIN_PROXY_ADDR), + notnull(L1_MESSAGE_QUEUE_PROXY_ADDR) + ); + } + + function initializeEnforcedTxGateway() private { + EnforcedTxGateway(payable(L1_ENFORCED_TX_GATEWAY_PROXY_ADDR)).initialize( + notnull(L1_MESSAGE_QUEUE_PROXY_ADDR), + notnull(L1_FEE_VAULT_ADDR) + ); + + // disable gateway + EnforcedTxGateway(payable(L1_ENFORCED_TX_GATEWAY_PROXY_ADDR)).setPause(true); + } + + function initializeL1GatewayRouter() private { + L1GatewayRouter(L1_GATEWAY_ROUTER_PROXY_ADDR).initialize( + notnull(L1_ETH_GATEWAY_PROXY_ADDR), + notnull(L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR) + ); + } + + function initializeL1CustomERC20Gateway() private { + L1CustomERC20Gateway(L1_CUSTOM_ERC20_GATEWAY_PROXY_ADDR).initialize( + notnull(L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR), + notnull(L1_GATEWAY_ROUTER_PROXY_ADDR), + notnull(L1_SCROLL_MESSENGER_PROXY_ADDR) + ); + } + + function initializeL1ERC1155Gateway() private { + L1ERC1155Gateway(L1_ERC1155_GATEWAY_PROXY_ADDR).initialize( + notnull(L2_ERC1155_GATEWAY_PROXY_ADDR), + notnull(L1_SCROLL_MESSENGER_PROXY_ADDR) + ); + } + + function initializeL1ERC721Gateway() private { + L1ERC721Gateway(L1_ERC721_GATEWAY_PROXY_ADDR).initialize( + notnull(L2_ERC721_GATEWAY_PROXY_ADDR), + notnull(L1_SCROLL_MESSENGER_PROXY_ADDR) + ); + } + + function initializeL1ETHGateway() private { + L1ETHGateway(L1_ETH_GATEWAY_PROXY_ADDR).initialize( + notnull(L2_ETH_GATEWAY_PROXY_ADDR), + notnull(L1_GATEWAY_ROUTER_PROXY_ADDR), + notnull(L1_SCROLL_MESSENGER_PROXY_ADDR) + ); + } + + function initializeL1StandardERC20Gateway() private { + L1StandardERC20Gateway(L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR).initialize( + notnull(L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR), + notnull(L1_GATEWAY_ROUTER_PROXY_ADDR), + notnull(L1_SCROLL_MESSENGER_PROXY_ADDR), + notnull(L2_SCROLL_STANDARD_ERC20_ADDR), + notnull(L2_SCROLL_STANDARD_ERC20_FACTORY_ADDR) + ); + } + + function initializeL1WETHGateway() private { + L1WETHGateway(payable(L1_WETH_GATEWAY_PROXY_ADDR)).initialize( + notnull(L2_WETH_GATEWAY_PROXY_ADDR), + notnull(L1_GATEWAY_ROUTER_PROXY_ADDR), + notnull(L1_SCROLL_MESSENGER_PROXY_ADDR) + ); + + // set WETH gateway in router + { + address[] memory _tokens = new address[](1); + _tokens[0] = notnull(L1_WETH_ADDR); + address[] memory _gateways = new address[](1); + _gateways[0] = notnull(L1_WETH_GATEWAY_PROXY_ADDR); + L1GatewayRouter(L1_GATEWAY_ROUTER_PROXY_ADDR).setERC20Gateway(_tokens, _gateways); + } + } + + function initializeL1Whitelist() private { + address[] memory accounts = new address[](1); + accounts[0] = L1_GAS_ORACLE_SENDER_ADDR; + Whitelist(L1_WHITELIST_ADDR).updateWhitelistStatus(accounts, true); + } + + function transferL1ContractOwnership() private { + if (DEPLOYER_ADDR == OWNER_ADDR) { + return; + } + + Ownable(L1_ENFORCED_TX_GATEWAY_PROXY_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L1_CUSTOM_ERC20_GATEWAY_PROXY_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L1_ERC1155_GATEWAY_PROXY_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L1_ERC721_GATEWAY_PROXY_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L1_ETH_GATEWAY_PROXY_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L1_GATEWAY_ROUTER_PROXY_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L1_MESSAGE_QUEUE_PROXY_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L1_SCROLL_MESSENGER_PROXY_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L1_WETH_GATEWAY_PROXY_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L2_GAS_PRICE_ORACLE_PROXY_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L1_MULTIPLE_VERSION_ROLLUP_VERIFIER_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L1_PROXY_ADMIN_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L1_SCROLL_CHAIN_PROXY_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L1_WHITELIST_ADDR).transferOwnership(OWNER_ADDR); + } + + /********************** + * L2: initialization * + **********************/ + + function initializeL2MessageQueue() private { + L2MessageQueue(L2_MESSAGE_QUEUE_ADDR).initialize(notnull(L2_SCROLL_MESSENGER_PROXY_ADDR)); + } + + function initializeL2TxFeeVault() private { + L2TxFeeVault(payable(L2_TX_FEE_VAULT_ADDR)).updateMessenger(notnull(L2_SCROLL_MESSENGER_PROXY_ADDR)); + } + + function initializeL1GasPriceOracle() private { + L1GasPriceOracle(L1_GAS_PRICE_ORACLE_ADDR).updateWhitelist(notnull(L2_WHITELIST_ADDR)); + } + + function initializeL2ScrollMessenger() private { + L2ScrollMessenger(payable(L2_SCROLL_MESSENGER_PROXY_ADDR)).initialize(notnull(L1_SCROLL_MESSENGER_PROXY_ADDR)); + } + + function initializeL2GatewayRouter() private { + L2GatewayRouter(L2_GATEWAY_ROUTER_PROXY_ADDR).initialize( + notnull(L2_ETH_GATEWAY_PROXY_ADDR), + notnull(L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR) + ); + } + + function initializeL2CustomERC20Gateway() private { + L2CustomERC20Gateway(L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR).initialize( + notnull(L1_CUSTOM_ERC20_GATEWAY_PROXY_ADDR), + notnull(L2_GATEWAY_ROUTER_PROXY_ADDR), + notnull(L2_SCROLL_MESSENGER_PROXY_ADDR) + ); + } + + function initializeL2ERC1155Gateway() private { + L2ERC1155Gateway(L2_ERC1155_GATEWAY_PROXY_ADDR).initialize( + notnull(L1_ERC1155_GATEWAY_PROXY_ADDR), + notnull(L2_SCROLL_MESSENGER_PROXY_ADDR) + ); + } + + function initializeL2ERC721Gateway() private { + L2ERC721Gateway(L2_ERC721_GATEWAY_PROXY_ADDR).initialize( + notnull(L1_ERC721_GATEWAY_PROXY_ADDR), + notnull(L2_SCROLL_MESSENGER_PROXY_ADDR) + ); + } + + function initializeL2ETHGateway() private { + L2ETHGateway(L2_ETH_GATEWAY_PROXY_ADDR).initialize( + notnull(L1_ETH_GATEWAY_PROXY_ADDR), + notnull(L2_GATEWAY_ROUTER_PROXY_ADDR), + notnull(L2_SCROLL_MESSENGER_PROXY_ADDR) + ); + } + + function initializeL2StandardERC20Gateway() private { + L2StandardERC20Gateway(L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR).initialize( + notnull(L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR), + notnull(L2_GATEWAY_ROUTER_PROXY_ADDR), + notnull(L2_SCROLL_MESSENGER_PROXY_ADDR), + notnull(L2_SCROLL_STANDARD_ERC20_FACTORY_ADDR) + ); + } + + function initializeL2WETHGateway() private { + L2WETHGateway(payable(L2_WETH_GATEWAY_PROXY_ADDR)).initialize( + notnull(L1_WETH_GATEWAY_PROXY_ADDR), + notnull(L2_GATEWAY_ROUTER_PROXY_ADDR), + notnull(L2_SCROLL_MESSENGER_PROXY_ADDR) + ); + + // set WETH gateway in router + { + address[] memory _tokens = new address[](1); + _tokens[0] = notnull(L2_WETH_ADDR); + address[] memory _gateways = new address[](1); + _gateways[0] = notnull(L2_WETH_GATEWAY_PROXY_ADDR); + L2GatewayRouter(L2_GATEWAY_ROUTER_PROXY_ADDR).setERC20Gateway(_tokens, _gateways); + } + } + + function initializeScrollStandardERC20Factory() private { + ScrollStandardERC20Factory(L2_SCROLL_STANDARD_ERC20_FACTORY_ADDR).transferOwnership( + notnull(L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR) + ); + } + + function initializeL2Whitelist() private { + address[] memory accounts = new address[](1); + accounts[0] = L2_GAS_ORACLE_SENDER_ADDR; + Whitelist(L2_WHITELIST_ADDR).updateWhitelistStatus(accounts, true); + } + + function transferL2ContractOwnership() private { + if (DEPLOYER_ADDR == OWNER_ADDR) { + return; + } + + Ownable(L1_GAS_PRICE_ORACLE_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L2_ERC1155_GATEWAY_PROXY_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L2_ERC721_GATEWAY_PROXY_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L2_ETH_GATEWAY_PROXY_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L2_GATEWAY_ROUTER_PROXY_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L2_MESSAGE_QUEUE_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L2_SCROLL_MESSENGER_PROXY_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L2_TX_FEE_VAULT_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L2_WETH_GATEWAY_PROXY_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L2_PROXY_ADMIN_ADDR).transferOwnership(OWNER_ADDR); + Ownable(L2_WHITELIST_ADDR).transferOwnership(OWNER_ADDR); + } +} + +contract GenerateGenesis is DeployScroll { + /*************** + * Entry point * + ***************/ + + function run() public { + setScriptMode(ScriptMode.VerifyConfig); + predictAllContracts(); + + generateGenesisAlloc(); + generateGenesisJson(); + + // clean up temporary files + vm.removeFile(GENESIS_ALLOC_JSON_PATH); + } + + /********************* + * Private functions * + *********************/ + + function generateGenesisAlloc() private { + if (vm.exists(GENESIS_ALLOC_JSON_PATH)) { + vm.removeFile(GENESIS_ALLOC_JSON_PATH); + } + + // Scroll predeploys + setL2MessageQueue(); + setL1GasPriceOracle(); + setL2Whitelist(); + setL2Weth(); + setL2FeeVault(); + + // other predeploys + setDeterministicDeploymentProxy(); + + // reset sender + vm.resetNonce(msg.sender); + + // prefunded accounts + setL2ScrollMessenger(); + setL2Deployer(); + + // write to file + vm.dumpState(GENESIS_ALLOC_JSON_PATH); + sortJsonByKeys(GENESIS_ALLOC_JSON_PATH); + } + + function setL2MessageQueue() internal { + address predeployAddr = tryGetOverride("L2_MESSAGE_QUEUE"); + + if (predeployAddr == address(0)) { + return; + } + + // set code + L2MessageQueue _queue = new L2MessageQueue(OWNER_ADDR); + vm.etch(predeployAddr, address(_queue).code); + + // set storage + bytes32 _ownerSlot = hex"0000000000000000000000000000000000000000000000000000000000000052"; + vm.store(predeployAddr, _ownerSlot, vm.load(address(_queue), _ownerSlot)); + + // reset so its not included state dump + vm.etch(address(_queue), ""); + vm.resetNonce(address(_queue)); + } + + function setL1GasPriceOracle() internal { + address predeployAddr = tryGetOverride("L1_GAS_PRICE_ORACLE"); + + if (predeployAddr == address(0)) { + return; + } + + // set code + L1GasPriceOracle _oracle = new L1GasPriceOracle(OWNER_ADDR); + vm.etch(predeployAddr, address(_oracle).code); + + // set storage + bytes32 _ownerSlot = hex"0000000000000000000000000000000000000000000000000000000000000000"; + vm.store(predeployAddr, _ownerSlot, vm.load(address(_oracle), _ownerSlot)); + + bytes32 _isCurieSlot = hex"0000000000000000000000000000000000000000000000000000000000000008"; + vm.store(predeployAddr, _isCurieSlot, bytes32(uint256(1))); + + // reset so its not included state dump + vm.etch(address(_oracle), ""); + vm.resetNonce(address(_oracle)); + } + + function setL2Whitelist() internal { + address predeployAddr = tryGetOverride("L2_WHITELIST"); + + if (predeployAddr == address(0)) { + return; + } + + // set code + Whitelist _whitelist = new Whitelist(OWNER_ADDR); + vm.etch(predeployAddr, address(_whitelist).code); + + // set storage + bytes32 _ownerSlot = hex"0000000000000000000000000000000000000000000000000000000000000000"; + vm.store(predeployAddr, _ownerSlot, vm.load(address(_whitelist), _ownerSlot)); + + // reset so its not included state dump + vm.etch(address(_whitelist), ""); + vm.resetNonce(address(_whitelist)); + } + + function setL2Weth() internal { + address predeployAddr = tryGetOverride("L2_WETH"); + + if (predeployAddr == address(0)) { + return; + } + + // set code + WrappedEther _weth = new WrappedEther(); + vm.etch(predeployAddr, address(_weth).code); + + // set storage + bytes32 _nameSlot = hex"0000000000000000000000000000000000000000000000000000000000000003"; + vm.store(predeployAddr, _nameSlot, vm.load(address(_weth), _nameSlot)); + + bytes32 _symbolSlot = hex"0000000000000000000000000000000000000000000000000000000000000004"; + vm.store(predeployAddr, _symbolSlot, vm.load(address(_weth), _symbolSlot)); + + // reset so its not included state dump + vm.etch(address(_weth), ""); + vm.resetNonce(address(_weth)); + } + + function setL2FeeVault() internal { + address predeployAddr = tryGetOverride("L2_TX_FEE_VAULT"); + + if (predeployAddr == address(0)) { + return; + } + + // set code + L2TxFeeVault _vault = new L2TxFeeVault(OWNER_ADDR, L1_FEE_VAULT_ADDR, FEE_VAULT_MIN_WITHDRAW_AMOUNT); + vm.etch(predeployAddr, address(_vault).code); + + vm.prank(OWNER_ADDR); + _vault.updateMessenger(L2_SCROLL_MESSENGER_PROXY_ADDR); + + // set storage + bytes32 _ownerSlot = hex"0000000000000000000000000000000000000000000000000000000000000000"; + vm.store(predeployAddr, _ownerSlot, vm.load(address(_vault), _ownerSlot)); + + bytes32 _minWithdrawAmountSlot = hex"0000000000000000000000000000000000000000000000000000000000000001"; + vm.store(predeployAddr, _minWithdrawAmountSlot, vm.load(address(_vault), _minWithdrawAmountSlot)); + + bytes32 _messengerSlot = hex"0000000000000000000000000000000000000000000000000000000000000002"; + vm.store(predeployAddr, _messengerSlot, vm.load(address(_vault), _messengerSlot)); + + bytes32 _recipientSlot = hex"0000000000000000000000000000000000000000000000000000000000000003"; + vm.store(predeployAddr, _recipientSlot, vm.load(address(_vault), _recipientSlot)); + + // reset so its not included state dump + vm.etch(address(_vault), ""); + vm.resetNonce(address(_vault)); + } + + function setDeterministicDeploymentProxy() internal { + bytes + memory code = hex"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3"; + vm.etch(DETERMINISTIC_DEPLOYMENT_PROXY_ADDR, code); + } + + function setL2ScrollMessenger() internal { + vm.deal(L2_SCROLL_MESSENGER_PROXY_ADDR, L2_SCROLL_MESSENGER_INITIAL_BALANCE); + } + + function setL2Deployer() internal { + vm.deal(OWNER_ADDR, L2_DEPLOYER_INITIAL_BALANCE); + } + + function generateGenesisJson() private { + // initialize template file + if (vm.exists(GENESIS_JSON_PATH)) { + vm.removeFile(GENESIS_JSON_PATH); + } + + string memory template = vm.readFile(GENESIS_JSON_TEMPLATE_PATH); + vm.writeFile(GENESIS_JSON_PATH, template); + + // general config + vm.writeJson(vm.toString(CHAIN_ID_L2), GENESIS_JSON_PATH, ".config.chainId"); + + uint256 timestamp = vm.unixTime() / 1000; + vm.writeJson(vm.toString(bytes32(timestamp)), GENESIS_JSON_PATH, ".timestamp"); + + string memory extraData = string( + abi.encodePacked( + "0x0000000000000000000000000000000000000000000000000000000000000000", + vm.replace(vm.toString(L2GETH_SIGNER_0_ADDRESS), "0x", ""), + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ) + ); + + vm.writeJson(extraData, GENESIS_JSON_PATH, ".extraData"); + + // scroll-specific config + vm.writeJson(vm.toString(MAX_TX_IN_CHUNK), GENESIS_JSON_PATH, ".config.scroll.maxTxPerBlock"); + vm.writeJson(vm.toString(L2_TX_FEE_VAULT_ADDR), GENESIS_JSON_PATH, ".config.scroll.feeVaultAddress"); + + // serialize explicitly as string, otherwise foundry will serialize it as number + string memory l1ChainId = string(abi.encodePacked('"', vm.toString(CHAIN_ID_L1), '"')); + vm.writeJson(l1ChainId, GENESIS_JSON_PATH, ".config.scroll.l1Config.l1ChainId"); + + vm.writeJson( + vm.toString(L1_MESSAGE_QUEUE_PROXY_ADDR), + GENESIS_JSON_PATH, + ".config.scroll.l1Config.l1MessageQueueAddress" + ); + + vm.writeJson( + vm.toString(L1_SCROLL_CHAIN_PROXY_ADDR), + GENESIS_JSON_PATH, + ".config.scroll.l1Config.scrollChainAddress" + ); + + // predeploys and prefunded accounts + string memory alloc = vm.readFile(GENESIS_ALLOC_JSON_PATH); + vm.writeJson(alloc, GENESIS_JSON_PATH, ".alloc"); + } + + /// @notice Sorts the allocs by address + // source: https://github.com/ethereum-optimism/optimism/blob/develop/packages/contracts-bedrock/scripts/L2Genesis.s.sol + function sortJsonByKeys(string memory _path) private { + string[] memory commands = new string[](3); + commands[0] = "/bin/bash"; + commands[1] = "-c"; + commands[2] = string.concat("cat <<< $(jq -S '.' ", _path, ") > ", _path); + vm.ffi(commands); + } +} + +contract GenerateRollupConfig is DeployScroll { + /*************** + * Entry point * + ***************/ + + function run() public { + setScriptMode(ScriptMode.VerifyConfig); + predictAllContracts(); + + generateRollupConfig(); + } + + /********************* + * Private functions * + *********************/ + + // prettier-ignore + function generateRollupConfig() private { + // initialize template file + if (vm.exists(ROLLUP_CONFIG_PATH)) { + vm.removeFile(ROLLUP_CONFIG_PATH); + } + + string memory template = vm.readFile(ROLLUP_CONFIG_TEMPLATE_PATH); + vm.writeFile(ROLLUP_CONFIG_PATH, template); + + // endpoints + vm.writeJson(L1_RPC_ENDPOINT, ROLLUP_CONFIG_PATH, ".l1_config.endpoint"); + vm.writeJson(L2_RPC_ENDPOINT, ROLLUP_CONFIG_PATH, ".l1_config.relayer_config.sender_config.endpoint"); + vm.writeJson(L2_RPC_ENDPOINT, ROLLUP_CONFIG_PATH, ".l2_config.endpoint"); + vm.writeJson(L1_RPC_ENDPOINT, ROLLUP_CONFIG_PATH, ".l2_config.relayer_config.sender_config.endpoint"); + + // contracts + vm.writeJson(vm.toString(L1_MESSAGE_QUEUE_PROXY_ADDR), ROLLUP_CONFIG_PATH, ".l1_config.l1_message_queue_address"); + vm.writeJson(vm.toString(L1_SCROLL_CHAIN_PROXY_ADDR), ROLLUP_CONFIG_PATH, ".l1_config.scroll_chain_address"); + vm.writeJson(vm.toString(L1_GAS_PRICE_ORACLE_ADDR), ROLLUP_CONFIG_PATH, ".l1_config.relayer_config.gas_price_oracle_contract_address"); + vm.writeJson(vm.toString(L2_MESSAGE_QUEUE_ADDR), ROLLUP_CONFIG_PATH, ".l2_config.l2_message_queue_address"); + vm.writeJson(vm.toString(L1_SCROLL_CHAIN_PROXY_ADDR), ROLLUP_CONFIG_PATH, ".l2_config.relayer_config.rollup_contract_address"); + vm.writeJson(vm.toString(L2_GAS_PRICE_ORACLE_PROXY_ADDR), ROLLUP_CONFIG_PATH, ".l2_config.relayer_config.gas_price_oracle_contract_address"); + + // private keys + vm.writeJson(vm.toString(bytes32(L1_GAS_ORACLE_SENDER_PRIVATE_KEY)), ROLLUP_CONFIG_PATH, ".l2_config.relayer_config.gas_oracle_sender_private_key"); + vm.writeJson(vm.toString(bytes32(L1_COMMIT_SENDER_PRIVATE_KEY)), ROLLUP_CONFIG_PATH, ".l2_config.relayer_config.commit_sender_private_key"); + vm.writeJson(vm.toString(bytes32(L1_FINALIZE_SENDER_PRIVATE_KEY)), ROLLUP_CONFIG_PATH, ".l2_config.relayer_config.finalize_sender_private_key"); + vm.writeJson(vm.toString(bytes32(L2_GAS_ORACLE_SENDER_PRIVATE_KEY)), ROLLUP_CONFIG_PATH, ".l1_config.relayer_config.gas_oracle_sender_private_key"); + + // other + vm.writeJson(vm.toString(TEST_ENV_MOCK_FINALIZE_ENABLED), ROLLUP_CONFIG_PATH, ".l2_config.relayer_config.enable_test_env_bypass_features"); + vm.writeJson(vm.toString(TEST_ENV_MOCK_FINALIZE_TIMEOUT_SEC), ROLLUP_CONFIG_PATH, ".l2_config.relayer_config.finalize_batch_without_proof_timeout_sec"); + + vm.writeJson(vm.toString(MAX_BLOCK_IN_CHUNK), ROLLUP_CONFIG_PATH, ".l2_config.chunk_proposer_config.max_block_num_per_chunk"); + vm.writeJson(vm.toString(MAX_TX_IN_CHUNK), ROLLUP_CONFIG_PATH, ".l2_config.chunk_proposer_config.max_tx_num_per_chunk"); + + vm.writeJson(SCROLL_DB_CONNECTION_STRING, ROLLUP_CONFIG_PATH, ".db_config.dsn"); + } +} + +contract GenerateCoordinatorConfig is DeployScroll { + /*************** + * Entry point * + ***************/ + + function run() public { + setScriptMode(ScriptMode.VerifyConfig); + predictAllContracts(); + + generateCoordinatorConfig(); + } + + /********************* + * Private functions * + *********************/ + + function generateCoordinatorConfig() private { + // initialize template file + if (vm.exists(COORDINATOR_CONFIG_PATH)) { + vm.removeFile(COORDINATOR_CONFIG_PATH); + } + + string memory template = vm.readFile(COORDINATOR_CONFIG_TEMPLATE_PATH); + vm.writeFile(COORDINATOR_CONFIG_PATH, template); + + vm.writeJson(vm.toString(CHAIN_ID_L2), COORDINATOR_CONFIG_PATH, ".l2.chain_id"); + vm.writeJson(SCROLL_DB_CONNECTION_STRING, COORDINATOR_CONFIG_PATH, ".db.dsn"); + vm.writeJson(COORDINATOR_JWT_SECRET_KEY, COORDINATOR_CONFIG_PATH, ".auth.secret"); + } +} + +contract GenerateChainMonitorConfig is DeployScroll { + /*************** + * Entry point * + ***************/ + + function run() public { + setScriptMode(ScriptMode.VerifyConfig); + predictAllContracts(); + + generateChainMonitorConfig(); + } + + /********************* + * Private functions * + *********************/ + + // prettier-ignore + function generateChainMonitorConfig() private { + // initialize template file + if (vm.exists(CHAIN_MONITOR_CONFIG_PATH)) { + vm.removeFile(CHAIN_MONITOR_CONFIG_PATH); + } + + string memory template = vm.readFile(CHAIN_MONITOR_CONFIG_TEMPLATE_PATH); + vm.writeFile(CHAIN_MONITOR_CONFIG_PATH, template); + + // L1 + vm.writeJson(L1_RPC_ENDPOINT, CHAIN_MONITOR_CONFIG_PATH, ".l1_config.l1_url"); + vm.writeJson(vm.toString(L1_CONTRACT_DEPLOYMENT_BLOCK), CHAIN_MONITOR_CONFIG_PATH, ".l1_config.start_number"); + vm.writeJson(vm.toString(L1_ETH_GATEWAY_PROXY_ADDR), CHAIN_MONITOR_CONFIG_PATH, ".l1_config.l1_contracts.l1_gateways.eth_gateway"); + vm.writeJson(vm.toString(L1_WETH_GATEWAY_PROXY_ADDR), CHAIN_MONITOR_CONFIG_PATH, ".l1_config.l1_contracts.l1_gateways.weth_gateway"); + vm.writeJson(vm.toString(L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR), CHAIN_MONITOR_CONFIG_PATH, ".l1_config.l1_contracts.l1_gateways.standard_erc20_gateway"); + vm.writeJson(vm.toString(L1_CUSTOM_ERC20_GATEWAY_PROXY_ADDR), CHAIN_MONITOR_CONFIG_PATH, ".l1_config.l1_contracts.l1_gateways.custom_erc20_gateway"); + vm.writeJson(vm.toString(L1_ERC721_GATEWAY_PROXY_ADDR), CHAIN_MONITOR_CONFIG_PATH, ".l1_config.l1_contracts.l1_gateways.erc721_gateway"); + vm.writeJson(vm.toString(L1_ERC1155_GATEWAY_PROXY_ADDR), CHAIN_MONITOR_CONFIG_PATH, ".l1_config.l1_contracts.l1_gateways.erc1155_gateway"); + vm.writeJson(vm.toString(L1_SCROLL_MESSENGER_PROXY_ADDR), CHAIN_MONITOR_CONFIG_PATH, ".l1_config.l1_contracts.scroll_messenger"); + vm.writeJson(vm.toString(L1_MESSAGE_QUEUE_PROXY_ADDR), CHAIN_MONITOR_CONFIG_PATH, ".l1_config.l1_contracts.message_queue"); + vm.writeJson(vm.toString(L1_SCROLL_CHAIN_PROXY_ADDR), CHAIN_MONITOR_CONFIG_PATH, ".l1_config.l1_contracts.scroll_chain"); + vm.writeJson(vm.toString(L2_DEPLOYER_INITIAL_BALANCE), CHAIN_MONITOR_CONFIG_PATH, ".l1_config.start_messenger_balance"); + + // L2 + vm.writeJson(L2_RPC_ENDPOINT, CHAIN_MONITOR_CONFIG_PATH, ".l2_config.l2_url"); + vm.writeJson(vm.toString(L2_ETH_GATEWAY_PROXY_ADDR), CHAIN_MONITOR_CONFIG_PATH, ".l2_config.l2_contracts.l2_gateways.eth_gateway"); + vm.writeJson(vm.toString(L2_WETH_GATEWAY_PROXY_ADDR), CHAIN_MONITOR_CONFIG_PATH, ".l2_config.l2_contracts.l2_gateways.weth_gateway"); + vm.writeJson(vm.toString(L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR), CHAIN_MONITOR_CONFIG_PATH, ".l2_config.l2_contracts.l2_gateways.standard_erc20_gateway"); + vm.writeJson(vm.toString(L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR), CHAIN_MONITOR_CONFIG_PATH, ".l2_config.l2_contracts.l2_gateways.custom_erc20_gateway"); + vm.writeJson(vm.toString(L2_ERC721_GATEWAY_PROXY_ADDR), CHAIN_MONITOR_CONFIG_PATH, ".l2_config.l2_contracts.l2_gateways.erc721_gateway"); + vm.writeJson(vm.toString(L2_ERC1155_GATEWAY_PROXY_ADDR), CHAIN_MONITOR_CONFIG_PATH, ".l2_config.l2_contracts.l2_gateways.erc1155_gateway"); + vm.writeJson(vm.toString(L2_SCROLL_MESSENGER_PROXY_ADDR), CHAIN_MONITOR_CONFIG_PATH, ".l2_config.l2_contracts.scroll_messenger"); + vm.writeJson(vm.toString(L2_MESSAGE_QUEUE_ADDR), CHAIN_MONITOR_CONFIG_PATH, ".l2_config.l2_contracts.message_queue"); + + // misc + vm.writeJson(CHAIN_MONITOR_DB_CONNECTION_STRING, CHAIN_MONITOR_CONFIG_PATH, ".db_config.dsn"); + } +} + +contract GenerateBridgeHistoryConfig is DeployScroll { + /*************** + * Entry point * + ***************/ + + function run() public { + setScriptMode(ScriptMode.VerifyConfig); + predictAllContracts(); + + generateBridgeHistoryConfig(); + } + + /********************* + * Private functions * + *********************/ + + // prettier-ignore + function generateBridgeHistoryConfig() private { + // initialize template file + if (vm.exists(BRIDGE_HISTORY_CONFIG_PATH)) { + vm.removeFile(BRIDGE_HISTORY_CONFIG_PATH); + } + + string memory template = vm.readFile(BRIDGE_HISTORY_CONFIG_TEMPLATE_PATH); + vm.writeFile(BRIDGE_HISTORY_CONFIG_PATH, template); + + // L1 contracts + vm.writeJson(vm.toString(L1_MESSAGE_QUEUE_PROXY_ADDR), BRIDGE_HISTORY_CONFIG_PATH, ".L1.MessageQueueAddr"); + vm.writeJson(vm.toString(L1_SCROLL_MESSENGER_PROXY_ADDR), BRIDGE_HISTORY_CONFIG_PATH, ".L1.MessengerAddr"); + vm.writeJson(vm.toString(L1_SCROLL_CHAIN_PROXY_ADDR), BRIDGE_HISTORY_CONFIG_PATH, ".L1.ScrollChainAddr"); + vm.writeJson(vm.toString(L1_GATEWAY_ROUTER_PROXY_ADDR), BRIDGE_HISTORY_CONFIG_PATH, ".L1.GatewayRouterAddr"); + vm.writeJson(vm.toString(L1_ETH_GATEWAY_PROXY_ADDR), BRIDGE_HISTORY_CONFIG_PATH, ".L1.ETHGatewayAddr"); + vm.writeJson(vm.toString(L1_WETH_GATEWAY_PROXY_ADDR), BRIDGE_HISTORY_CONFIG_PATH, ".L1.WETHGatewayAddr"); + vm.writeJson(vm.toString(L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR), BRIDGE_HISTORY_CONFIG_PATH, ".L1.StandardERC20GatewayAddr"); + vm.writeJson(vm.toString(L1_CUSTOM_ERC20_GATEWAY_PROXY_ADDR), BRIDGE_HISTORY_CONFIG_PATH, ".L1.CustomERC20GatewayAddr"); + vm.writeJson(vm.toString(L1_ERC721_GATEWAY_PROXY_ADDR), BRIDGE_HISTORY_CONFIG_PATH, ".L1.ERC721GatewayAddr"); + vm.writeJson(vm.toString(L1_ERC1155_GATEWAY_PROXY_ADDR), BRIDGE_HISTORY_CONFIG_PATH, ".L1.ERC1155GatewayAddr"); + + // L2 contracts + vm.writeJson(vm.toString(L2_MESSAGE_QUEUE_ADDR), BRIDGE_HISTORY_CONFIG_PATH, ".L2.MessageQueueAddr"); + vm.writeJson(vm.toString(L2_SCROLL_MESSENGER_PROXY_ADDR), BRIDGE_HISTORY_CONFIG_PATH, ".L2.MessengerAddr"); + vm.writeJson(vm.toString(L2_GATEWAY_ROUTER_PROXY_ADDR), BRIDGE_HISTORY_CONFIG_PATH, ".L2.GatewayRouterAddr"); + vm.writeJson(vm.toString(L2_ETH_GATEWAY_PROXY_ADDR), BRIDGE_HISTORY_CONFIG_PATH, ".L2.ETHGatewayAddr"); + vm.writeJson(vm.toString(L2_WETH_GATEWAY_PROXY_ADDR), BRIDGE_HISTORY_CONFIG_PATH, ".L2.WETHGatewayAddr"); + vm.writeJson(vm.toString(L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR), BRIDGE_HISTORY_CONFIG_PATH, ".L2.StandardERC20GatewayAddr"); + vm.writeJson(vm.toString(L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR), BRIDGE_HISTORY_CONFIG_PATH, ".L2.CustomERC20GatewayAddr"); + vm.writeJson(vm.toString(L2_ERC721_GATEWAY_PROXY_ADDR), BRIDGE_HISTORY_CONFIG_PATH, ".L2.ERC721GatewayAddr"); + vm.writeJson(vm.toString(L2_ERC1155_GATEWAY_PROXY_ADDR), BRIDGE_HISTORY_CONFIG_PATH, ".L2.ERC1155GatewayAddr"); + + // endpoints + vm.writeJson(L1_RPC_ENDPOINT, BRIDGE_HISTORY_CONFIG_PATH, ".L1.endpoint"); + vm.writeJson(L2_RPC_ENDPOINT, BRIDGE_HISTORY_CONFIG_PATH, ".L2.endpoint"); + + // others + vm.writeJson(BRIDGE_HISTORY_DB_CONNECTION_STRING, BRIDGE_HISTORY_CONFIG_PATH, ".db.dsn"); + } +} + +contract GenerateBalanceCheckerConfig is DeployScroll { + /*************** + * Entry point * + ***************/ + + function run() public { + setScriptMode(ScriptMode.VerifyConfig); + predictAllContracts(); + + generateBalanceCheckerConfig(); + } + + /********************* + * Private functions * + *********************/ + + function generateBalanceCheckerConfig() private { + // initialize template file + if (vm.exists(BALANCE_CHECKER_CONFIG_PATH)) { + vm.removeFile(BALANCE_CHECKER_CONFIG_PATH); + } + + string memory template = vm.readFile(BALANCE_CHECKER_CONFIG_TEMPLATE_PATH); + vm.writeFile(BALANCE_CHECKER_CONFIG_PATH, template); + + vm.writeJson(L1_RPC_ENDPOINT, BALANCE_CHECKER_CONFIG_PATH, ".addresses[0].rpc_url"); + vm.writeJson(vm.toString(L1_COMMIT_SENDER_ADDR), BALANCE_CHECKER_CONFIG_PATH, ".addresses[0].address"); + + vm.writeJson(L1_RPC_ENDPOINT, BALANCE_CHECKER_CONFIG_PATH, ".addresses[1].rpc_url"); + vm.writeJson(vm.toString(L1_FINALIZE_SENDER_ADDR), BALANCE_CHECKER_CONFIG_PATH, ".addresses[1].address"); + + vm.writeJson(L1_RPC_ENDPOINT, BALANCE_CHECKER_CONFIG_PATH, ".addresses[2].rpc_url"); + vm.writeJson(vm.toString(L1_GAS_ORACLE_SENDER_ADDR), BALANCE_CHECKER_CONFIG_PATH, ".addresses[2].address"); + + vm.writeJson(L1_RPC_ENDPOINT, BALANCE_CHECKER_CONFIG_PATH, ".addresses[3].rpc_url"); + vm.writeJson(vm.toString(L1_FEE_VAULT_ADDR), BALANCE_CHECKER_CONFIG_PATH, ".addresses[3].address"); + + vm.writeJson(L2_RPC_ENDPOINT, BALANCE_CHECKER_CONFIG_PATH, ".addresses[4].rpc_url"); + vm.writeJson(vm.toString(L2_GAS_ORACLE_SENDER_ADDR), BALANCE_CHECKER_CONFIG_PATH, ".addresses[4].address"); + + vm.writeJson(L2_RPC_ENDPOINT, BALANCE_CHECKER_CONFIG_PATH, ".addresses[5].rpc_url"); + vm.writeJson(vm.toString(L2_TX_FEE_VAULT_ADDR), BALANCE_CHECKER_CONFIG_PATH, ".addresses[5].address"); + } +} + +contract GenerateFrontendConfig is DeployScroll { + /*************** + * Entry point * + ***************/ + + function run() public { + setScriptMode(ScriptMode.VerifyConfig); + predictAllContracts(); + + generateFrontendConfig(); + } + + /********************* + * Private functions * + *********************/ + + // prettier-ignore + function generateFrontendConfig() private { + // use writeFile to start a new file + vm.writeFile(FRONTEND_ENV_PATH, "REACT_APP_ETH_SYMBOL = \"ETH\"\n"); + vm.writeLine(FRONTEND_ENV_PATH, "REACT_APP_BASE_CHAIN = \"Ethereum\""); + vm.writeLine(FRONTEND_ENV_PATH, "REACT_APP_ROLLUP = \"Scroll Stack\""); + vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_CHAIN_ID_L1 = \"", vm.toString(CHAIN_ID_L1), "\"")); + vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_CHAIN_ID_L2 = \"", vm.toString(CHAIN_ID_L2), "\"")); + vm.writeLine(FRONTEND_ENV_PATH, "REACT_APP_CONNECT_WALLET_PROJECT_ID = \"14efbaafcf5232a47d93a68229b71028\""); + + // API endpoints + vm.writeLine(FRONTEND_ENV_PATH, ""); + vm.writeLine(FRONTEND_ENV_PATH, "REACT_APP_EXTERNAL_RPC_URI_L1 = \"http://l1-devnet.scrollsdk\""); + vm.writeLine(FRONTEND_ENV_PATH, "REACT_APP_EXTERNAL_RPC_URI_L2 = \"http://l2-rpc.scrollsdk\""); + vm.writeLine(FRONTEND_ENV_PATH, "REACT_APP_BRIDGE_API_URI = \"http://bridge-history-api.scrollsdk/api\""); + vm.writeLine(FRONTEND_ENV_PATH, "REACT_APP_ROLLUPSCAN_API_URI = \"https://rollup-explorer-backend.scrollsdk/api\""); + vm.writeLine(FRONTEND_ENV_PATH, "REACT_APP_EXTERNAL_EXPLORER_URI_L1 = \"http://l1-explorer.scrollsdk\""); + vm.writeLine(FRONTEND_ENV_PATH, "REACT_APP_EXTERNAL_EXPLORER_URI_L2 = \"http://blockscout.scrollsdk\""); + + // L1 contracts + vm.writeLine(FRONTEND_ENV_PATH, ""); + vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_L1_CUSTOM_ERC20_GATEWAY_PROXY_ADDR = \"", vm.toString(L1_CUSTOM_ERC20_GATEWAY_PROXY_ADDR), "\"")); + vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_L1_ETH_GATEWAY_PROXY_ADDR = \"", vm.toString(L1_ETH_GATEWAY_PROXY_ADDR), "\"")); + vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_L1_GAS_PRICE_ORACLE = \"", vm.toString(L1_GAS_PRICE_ORACLE_ADDR), "\"")); + vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_L1_GATEWAY_ROUTER_PROXY_ADDR = \"", vm.toString(L1_GATEWAY_ROUTER_PROXY_ADDR), "\"")); + vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_L1_MESSAGE_QUEUE = \"", vm.toString(L1_MESSAGE_QUEUE_PROXY_ADDR), "\"")); + vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_L1_MESSAGE_QUEUE_WITH_GAS_PRICE_ORACLE = \"", vm.toString(L1_MESSAGE_QUEUE_PROXY_ADDR), "\"")); + vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_L1_SCROLL_MESSENGER = \"", vm.toString(L1_SCROLL_MESSENGER_PROXY_ADDR), "\"")); + vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR = \"", vm.toString(L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR), "\"")); + vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_L1_WETH_GATEWAY_PROXY_ADDR = \"", vm.toString(L1_WETH_GATEWAY_PROXY_ADDR), "\"")); + vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_SCROLL_CHAIN = \"", vm.toString(L1_SCROLL_CHAIN_PROXY_ADDR), "\"")); + + // L2 contracts + vm.writeLine(FRONTEND_ENV_PATH, ""); + vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR = \"", vm.toString(L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR), "\"")); + vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_L2_ETH_GATEWAY_PROXY_ADDR = \"", vm.toString(L2_ETH_GATEWAY_PROXY_ADDR), "\"")); + vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_L2_GATEWAY_ROUTER_PROXY_ADDR = \"", vm.toString(L2_GATEWAY_ROUTER_PROXY_ADDR), "\"")); + vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_L2_SCROLL_MESSENGER = \"", vm.toString(L2_SCROLL_MESSENGER_PROXY_ADDR), "\"")); + vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR = \"", vm.toString(L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR), "\"")); + vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_L2_WETH_GATEWAY_PROXY_ADDR = \"", vm.toString(L2_WETH_GATEWAY_PROXY_ADDR), "\"")); + + // custom token gateways (currently not set) + vm.writeLine(FRONTEND_ENV_PATH, ""); + vm.writeLine(FRONTEND_ENV_PATH, "REACT_APP_L1_USDC_GATEWAY_PROXY_ADDR = \"\""); + vm.writeLine(FRONTEND_ENV_PATH, "REACT_APP_L2_USDC_GATEWAY_PROXY_ADDR = \"\""); + vm.writeLine(FRONTEND_ENV_PATH, "REACT_APP_L1_DAI_GATEWAY_PROXY_ADDR = \"\""); + vm.writeLine(FRONTEND_ENV_PATH, "REACT_APP_L2_DAI_GATEWAY_PROXY_ADDR = \"\""); + vm.writeLine(FRONTEND_ENV_PATH, "REACT_APP_L1_LIDO_GATEWAY_PROXY_ADDR = \"\""); + vm.writeLine(FRONTEND_ENV_PATH, "REACT_APP_L2_LIDO_GATEWAY_PROXY_ADDR = \"\""); + vm.writeLine(FRONTEND_ENV_PATH, "REACT_APP_L1_PUFFER_GATEWAY_PROXY_ADDR = \"\""); + vm.writeLine(FRONTEND_ENV_PATH, "REACT_APP_L2_PUFFER_GATEWAY_PROXY_ADDR = \"\""); + + // misc + vm.writeLine(FRONTEND_ENV_PATH, ""); + vm.writeLine(FRONTEND_ENV_PATH, "REACT_APP_SCROLL_ORIGINS_NFT = \"\""); + vm.writeLine(FRONTEND_ENV_PATH, "REACT_APP_SCROLL_ORIGINS_NFT_V2 = \"\""); + vm.writeLine(FRONTEND_ENV_PATH, "REACT_APP_L1_BATCH_BRIDGE_GATEWAY_PROXY_ADDR = \"\""); + } +} diff --git a/contracts/src/L1/rollup/ScrollChain.sol b/contracts/src/L1/rollup/ScrollChain.sol index c2fe3252c4..db7dd5ad2f 100644 --- a/contracts/src/L1/rollup/ScrollChain.sol +++ b/contracts/src/L1/rollup/ScrollChain.sol @@ -112,11 +112,11 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { *************/ /// @dev Address of the point evaluation precompile used for EIP-4844 blob verification. - address private constant POINT_EVALUATION_PRECOMPILE_ADDR = address(0x0A); + address internal constant POINT_EVALUATION_PRECOMPILE_ADDR = address(0x0A); /// @dev BLS Modulus value defined in EIP-4844 and the magic value returned from a successful call to the /// point evaluation precompile - uint256 private constant BLS_MODULUS = + uint256 internal constant BLS_MODULUS = 52435875175126190479447740508185965837690552500527637822603658699938581184513; /// @notice The chain id of the corresponding layer 2 chain. diff --git a/contracts/src/L2/predeploys/L1GasPriceOracle.sol b/contracts/src/L2/predeploys/L1GasPriceOracle.sol index fe3c37e4e1..de03d9dad2 100644 --- a/contracts/src/L2/predeploys/L1GasPriceOracle.sol +++ b/contracts/src/L2/predeploys/L1GasPriceOracle.sol @@ -60,7 +60,7 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle { /// commit_scalar = commit_gas_per_tx * fluctuation_multiplier * 1e9 /// ``` /// So, the value should not exceed 10^9 * 1e9 normally. - uint256 private constant MAX_COMMIT_SCALAR = 10 ** 9 * PRECISION; + uint256 private constant MAX_COMMIT_SCALAR = 10**9 * PRECISION; /// @dev The maximum possible l1 blob fee scalar after Curie. /// We derive the blob scalar by @@ -68,7 +68,7 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle { /// blob_scalar = fluctuation_multiplier / compression_ratio / blob_util_ratio * 1e9 /// ``` /// So, the value should not exceed 10^9 * 1e9 normally. - uint256 private constant MAX_BLOB_SCALAR = 10 ** 9 * PRECISION; + uint256 private constant MAX_BLOB_SCALAR = 10**9 * PRECISION; /************* * Variables * @@ -113,6 +113,9 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle { constructor(address _owner) { _transferOwnership(_owner); + + // by default we enable Curie from genesis + isCurie = true; } /************************* @@ -150,10 +153,11 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle { } /// @inheritdoc IL1GasPriceOracle - function setL1BaseFeeAndBlobBaseFee( - uint256 _l1BaseFee, - uint256 _l1BlobBaseFee - ) external override onlyWhitelistedSender { + function setL1BaseFeeAndBlobBaseFee(uint256 _l1BaseFee, uint256 _l1BlobBaseFee) + external + override + onlyWhitelistedSender + { l1BaseFee = _l1BaseFee; l1BlobBaseFee = _l1BlobBaseFee; diff --git a/contracts/src/mocks/ScrollChainMockFinalize.sol b/contracts/src/mocks/ScrollChainMockFinalize.sol new file mode 100644 index 0000000000..a7d7a8ecaf --- /dev/null +++ b/contracts/src/mocks/ScrollChainMockFinalize.sol @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: MIT + +pragma solidity =0.8.24; + +import {ScrollChain} from "../L1/rollup/ScrollChain.sol"; + +import {BatchHeaderV0Codec} from "../libraries/codec/BatchHeaderV0Codec.sol"; +import {BatchHeaderV1Codec} from "../libraries/codec/BatchHeaderV1Codec.sol"; + +contract ScrollChainMockFinalize is ScrollChain { + /*************** + * Constructor * + ***************/ + + /// @notice Constructor for `ScrollChain` implementation contract. + /// + /// @param _chainId The chain id of L2. + /// @param _messageQueue The address of `L1MessageQueue` contract. + /// @param _verifier The address of zkevm verifier contract. + constructor( + uint64 _chainId, + address _messageQueue, + address _verifier + ) ScrollChain(_chainId, _messageQueue, _verifier) {} + + /***************************** + * Public Mutating Functions * + *****************************/ + + function finalizeBatch( + bytes calldata _batchHeader, + bytes32 _prevStateRoot, + bytes32 _postStateRoot, + bytes32 _withdrawRoot + ) external OnlyProver whenNotPaused { + require(_prevStateRoot != bytes32(0), "previous state root is zero"); + require(_postStateRoot != bytes32(0), "new state root is zero"); + + // compute batch hash and verify + (uint256 memPtr, bytes32 _batchHash, uint256 _batchIndex, ) = _loadBatchHeader(_batchHeader); + + // verify previous state root. + require(finalizedStateRoots[_batchIndex - 1] == _prevStateRoot, "incorrect previous state root"); + + // avoid duplicated verification + require(finalizedStateRoots[_batchIndex] == bytes32(0), "batch already verified"); + + // check and update lastFinalizedBatchIndex + unchecked { + require(lastFinalizedBatchIndex + 1 == _batchIndex, "incorrect batch index"); + lastFinalizedBatchIndex = _batchIndex; + } + + // record state root and withdraw root + finalizedStateRoots[_batchIndex] = _postStateRoot; + withdrawRoots[_batchIndex] = _withdrawRoot; + + // Pop finalized and non-skipped message from L1MessageQueue. + _popL1Messages( + BatchHeaderV0Codec.getSkippedBitmapPtr(memPtr), + BatchHeaderV0Codec.getTotalL1MessagePopped(memPtr), + BatchHeaderV0Codec.getL1MessagePopped(memPtr) + ); + + emit FinalizeBatch(_batchIndex, _batchHash, _postStateRoot, _withdrawRoot); + } + + function finalizeBatch4844( + bytes calldata _batchHeader, + bytes32 _prevStateRoot, + bytes32 _postStateRoot, + bytes32 _withdrawRoot, + bytes calldata _blobDataProof + ) external OnlyProver whenNotPaused { + if (_prevStateRoot == bytes32(0)) revert ErrorPreviousStateRootIsZero(); + if (_postStateRoot == bytes32(0)) revert ErrorStateRootIsZero(); + + // compute batch hash and verify + (uint256 memPtr, bytes32 _batchHash, uint256 _batchIndex, ) = _loadBatchHeader(_batchHeader); + bytes32 _blobVersionedHash = BatchHeaderV1Codec.getBlobVersionedHash(memPtr); + + // Calls the point evaluation precompile and verifies the output + { + (bool success, bytes memory data) = POINT_EVALUATION_PRECOMPILE_ADDR.staticcall( + abi.encodePacked(_blobVersionedHash, _blobDataProof) + ); + // We verify that the point evaluation precompile call was successful by testing the latter 32 bytes of the + // response is equal to BLS_MODULUS as defined in https://eips.ethereum.org/EIPS/eip-4844#point-evaluation-precompile + if (!success) revert ErrorCallPointEvaluationPrecompileFailed(); + (, uint256 result) = abi.decode(data, (uint256, uint256)); + if (result != BLS_MODULUS) revert ErrorUnexpectedPointEvaluationPrecompileOutput(); + } + + // verify previous state root. + if (finalizedStateRoots[_batchIndex - 1] != _prevStateRoot) revert ErrorIncorrectPreviousStateRoot(); + + // avoid duplicated verification + if (finalizedStateRoots[_batchIndex] != bytes32(0)) revert ErrorBatchIsAlreadyVerified(); + + // check and update lastFinalizedBatchIndex + unchecked { + if (lastFinalizedBatchIndex + 1 != _batchIndex) revert ErrorIncorrectBatchIndex(); + lastFinalizedBatchIndex = _batchIndex; + } + + // record state root and withdraw root + finalizedStateRoots[_batchIndex] = _postStateRoot; + withdrawRoots[_batchIndex] = _withdrawRoot; + + // Pop finalized and non-skipped message from L1MessageQueue. + _popL1Messages( + BatchHeaderV1Codec.getSkippedBitmapPtr(memPtr), + BatchHeaderV1Codec.getTotalL1MessagePopped(memPtr), + BatchHeaderV1Codec.getL1MessagePopped(memPtr) + ); + + emit FinalizeBatch(_batchIndex, _batchHash, _postStateRoot, _withdrawRoot); + } +} diff --git a/contracts/src/test/L1GasPriceOracle.t.sol b/contracts/src/test/L1GasPriceOracle.t.sol index d6b4c834bb..3d845ad9d4 100644 --- a/contracts/src/test/L1GasPriceOracle.t.sol +++ b/contracts/src/test/L1GasPriceOracle.t.sol @@ -11,8 +11,8 @@ contract L1GasPriceOracleTest is DSTestPlus { uint256 private constant PRECISION = 1e9; uint256 private constant MAX_OVERHEAD = 30000000 / 16; uint256 private constant MAX_SCALAR = 1000 * PRECISION; - uint256 private constant MAX_COMMIT_SCALAR = 10 ** 9 * PRECISION; - uint256 private constant MAX_BLOB_SCALAR = 10 ** 9 * PRECISION; + uint256 private constant MAX_COMMIT_SCALAR = 10**9 * PRECISION; + uint256 private constant MAX_BLOB_SCALAR = 10**9 * PRECISION; L1GasPriceOracle private oracle; Whitelist private whitelist;