diff --git a/.github/workflows/contracts.yml b/.github/workflows/contracts.yml index 2da56828..4cfe981c 100644 --- a/.github/workflows/contracts.yml +++ b/.github/workflows/contracts.yml @@ -35,7 +35,7 @@ jobs: persist-credentials: false - name: Install Foundry - uses: foundry-rs/foundry-toolchain@de808b1eea699e761c404bda44ba8f21aba30b2c # v1.3.1 + uses: foundry-rs/foundry-toolchain@82dee4ba654bd2146511f85f0d013af94670c4de # v1.4.0 with: version: nightly @@ -84,7 +84,7 @@ jobs: run : lcov --rc branch_coverage=1 --remove ./lcov.info -o ./lcov.info.pruned 'src/mocks/*' 'src/test/*' 'scripts/*' 'node_modules/*' 'lib/*' --ignore-errors unused,unused - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@0565863a31f2c772f9f0395002a31e3f06189574 # v5.4.0 + uses: codecov/codecov-action@ad3126e916f78f00edff4ed0317cf185271ccc2d # v5.4.2 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} with: @@ -136,4 +136,59 @@ jobs: run: npx hardhat compile - name: Run hardhat tests - run: npx hardhat test \ No newline at end of file + run: npx hardhat test + + slither: + if: github.event.pull_request.draft == false + needs: foundry + runs-on: ubuntu-latest + permissions: + statuses: write + security-events: write + + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + persist-credentials: false + + - uses: actions/setup-node@v4 + with: + node-version: '18' + + - run: yarn install --frozen-lockfile + + - uses: foundry-rs/foundry-toolchain@82dee4ba654bd2146511f85f0d013af94670c4de # v1.4.0 + with: + version: nightly + + - name: Build contracts + run: forge build --build-info --out out --evm-version cancun + + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - run: | + python -m pip install --upgrade pip + pip install slither-analyzer==0.11.3 + + - name: Run Slither (High severity gate) + run: | + slither . \ + --filter-paths "src/test/*|src/mocks/*|scripts/*|node_modules" \ + --foundry-out-directory out \ + --exclude-dependencies \ + --exclude-medium \ + --exclude-low \ + --exclude-informational \ + --fail-high \ + --json slither-report.json \ + --markdown-root slither-report.md + + - uses: actions/upload-artifact@v4 + with: + name: slither-static-analysis + path: | + slither-report.json + slither-report.md diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml index 90529de2..14e880ee 100644 --- a/.github/workflows/docker-release.yml +++ b/.github/workflows/docker-release.yml @@ -73,7 +73,9 @@ jobs: - name: Build deploy image id: build_deploy_image - uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0 + env: + REPOSITORY: ${{ env.DOCKER_REPOSITORY }} + uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0 with: platforms: linux/amd64,linux/arm64 push: true @@ -85,7 +87,9 @@ jobs: - name: Build gen image id: build_gen_image - uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0 + env: + REPOSITORY: ${{ env.DOCKER_REPOSITORY }} + uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0 with: platforms: linux/amd64,linux/arm64 push: true diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index b7d06343..edb79258 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -23,7 +23,7 @@ jobs: persist-credentials: false - name: Install the latest version of uv - uses: astral-sh/setup-uv@f94ec6bedd8674c4426838e6b50417d36b6ab231 # v5.3.1 + uses: astral-sh/setup-uv@6b9c6063abd6010835644d4c2e1bef4cf5cd0fca # v6.0.1 - name: Run zizmor run: uvx zizmor --format sarif . > results.sarif diff --git a/.gitignore b/.gitignore index 88e59a65..574fbc45 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,9 @@ broadcast # Visual Studio Code .vscode + +# OS +.DS_Store + +# config +scripts/deterministic/config/config-contracts.toml \ No newline at end of file diff --git a/docker/Dockerfile.base b/docker/Dockerfile.base index 0cb5a69c..f999285b 100644 --- a/docker/Dockerfile.base +++ b/docker/Dockerfile.base @@ -22,7 +22,7 @@ RUN curl -L https://foundry.paradigm.xyz | bash ENV PATH="/root/.foundry/bin:${PATH}" # Run foundryup to update Foundry -RUN foundryup +RUN foundryup -i v1.3.0-rc2 # copy dependencies COPY ./lib /contracts/lib diff --git a/docker/Dockerfile.gen-configs b/docker/Dockerfile.gen-configs index e36fc366..c4e964de 100644 --- a/docker/Dockerfile.gen-configs +++ b/docker/Dockerfile.gen-configs @@ -8,15 +8,7 @@ SHELL ["/bin/bash", "-c"] WORKDIR /contracts # 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/templates/rollup-explorer-backend-config.json /contracts/docker/templates/rollup-explorer-backend-config.json -COPY ./docker/templates/admin-system-backend-config.json /contracts/docker/templates/admin-system-backend-config.json +COPY ./docker/templates/ /contracts/docker/templates/ COPY ./docker/scripts/gen-configs.sh /contracts/docker/scripts/gen-configs.sh diff --git a/docker/scripts/build.sh b/docker/scripts/build.sh index bb88dc5c..dcaaeec3 100755 --- a/docker/scripts/build.sh +++ b/docker/scripts/build.sh @@ -2,6 +2,7 @@ latest_commit=$(git log -1 --pretty=format:%H) tag=${latest_commit} + REPO="dogeos69/scroll-stack-contracts" echo "Using Docker image tag: $tag" echo "" diff --git a/docker/scripts/deploy.sh b/docker/scripts/deploy.sh index 07d449cd..68faf721 100755 --- a/docker/scripts/deploy.sh +++ b/docker/scripts/deploy.sh @@ -1,65 +1,46 @@ #!/bin/sh +# Enable timestamped execution trace to show progress with time and line numbers +export PS4='$(date "+%Y-%m-%dT%H:%M:%S%z") ${0##*/}:${LINENO}: ' echo "=== SCRIPT START ===" export FOUNDRY_EVM_VERSION="cancun" export FOUNDRY_BYTECODE_HASH="none" -set -e +set -ex -echo "Environment variables:" -echo "FOUNDRY_EVM_VERSION: $FOUNDRY_EVM_VERSION" -echo "FOUNDRY_BYTECODE_HASH: $FOUNDRY_BYTECODE_HASH" -echo "BATCH_SIZE: $BATCH_SIZE" - -if [ "${L1_RPC_ENDPOINT}" = "" ]; then - echo "L1_RPC_ENDPOINT is not set" - L1_RPC_ENDPOINT="http://host.docker.internal:8543" -fi +CONFIG_FILE="./volume/config.toml" -if [ "$L2_RPC_ENDPOINT" = "" ]; then - echo "L2_RPC_ENDPOINT is not set" - L2_RPC_ENDPOINT="http://host.docker.internal:8545" -fi - -if [ "${BATCH_SIZE}" = "" ]; then - BATCH_SIZE="100" -fi +# Set default values for environment variables if they are not set +L1_RPC_ENDPOINT="${L1_RPC_ENDPOINT}" +L2_RPC_ENDPOINT="${L2_RPC_ENDPOINT}" +# Using a smaller batch size as a compromise between the slow but reliable --slow flag and the fast but potentially unreliable default. +BATCH_SIZE="7" echo "using L1_RPC_ENDPOINT = $L1_RPC_ENDPOINT" echo "using L2_RPC_ENDPOINT = $L2_RPC_ENDPOINT" +echo "Environment variables:" +echo "FOUNDRY_EVM_VERSION: $FOUNDRY_EVM_VERSION" +echo "FOUNDRY_BYTECODE_HASH: $FOUNDRY_BYTECODE_HASH" +echo "BATCH_SIZE: $BATCH_SIZE" # simulate L1 echo "" echo "simulating on L1" -forge script scripts/deterministic/DeployScroll.s.sol:DeployScroll --rpc-url "$L1_RPC_ENDPOINT" --sig "run(string,string)" "L1" "verify-config" || exit 1 +forge script scripts/deterministic/DeployScroll.s.sol:DeployScroll --rpc-url "$L1_RPC_ENDPOINT" --sig "run(string,string)" "L1" "verify-config" + +# deploy L1 +echo "" +echo "deploying on L1" +forge script scripts/deterministic/DeployScroll.s.sol:DeployScroll --rpc-url "$L1_RPC_ENDPOINT" --batch-size "${BATCH_SIZE}" --sig "run(string,string)" "L1" "verify-config" --broadcast --json + # simulate L2 echo "" echo "simulating on L2" -forge script scripts/deterministic/DeployScroll.s.sol:DeployScroll --rpc-url "$L2_RPC_ENDPOINT" --sig "run(string,string)" "L2" "verify-config" --legacy || exit 1 +forge script scripts/deterministic/DeployScroll.s.sol:DeployScroll --rpc-url "$L2_RPC_ENDPOINT" --sig "run(string,string)" "L2" "verify-config" --legacy -# deploy L1 -echo "" -echo "deploying on L1" -forge script scripts/deterministic/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/deterministic/DeployScroll.s.sol:DeployScroll --rpc-url "$L2_RPC_ENDPOINT" --batch-size "$BATCH_SIZE" --sig "run(string,string)" "L2" "verify-config" --broadcast --legacy || exit 1 - -# log broadcast files - -echo "=== START BROADCAST FILES ===" -echo "Current directory: $(pwd)" -echo "Broadcast files location: broadcast/DeployScroll.s.sol/*/*" -echo "Found files:" -ls -la broadcast/DeployScroll.s.sol/*/* || echo "No files found" -echo "Broadcast files content:" +forge script scripts/deterministic/DeployScroll.s.sol:DeployScroll --rpc-url "$L2_RPC_ENDPOINT" --batch-size "$BATCH_SIZE" --sig "run(string,string)" "L2" "verify-config" --broadcast --legacy --json -for file in broadcast/DeployScroll.s.sol/*/*; do - if [ -f "$file" ]; then - echo "$file:" - cat "$file" - echo "" - fi -done -echo "=== END BROADCAST FILES ===" \ No newline at end of file +echo "=== SCRIPT END ===" diff --git a/docker/scripts/gen-configs.sh b/docker/scripts/gen-configs.sh index df76c424..2f11e9e8 100755 --- a/docker/scripts/gen-configs.sh +++ b/docker/scripts/gen-configs.sh @@ -49,25 +49,25 @@ echo "generating rollup-config.yaml" forge script scripts/deterministic/GenerateConfigs.s.sol:GenerateRollupConfig || exit 1 format_config_file "./volume/rollup-config.yaml" -echo "" -echo "generating coordinator-config.yaml" -forge script scripts/deterministic/GenerateConfigs.s.sol:GenerateCoordinatorConfig || exit 1 -format_config_file "./volume/coordinator-config.yaml" +# echo "" +# echo "generating coordinator-config.yaml" +# forge script scripts/deterministic/GenerateConfigs.s.sol:GenerateCoordinatorConfig || exit 1 +# format_config_file "./volume/coordinator-config.yaml" echo "" echo "generating chain-monitor-config.yaml" forge script scripts/deterministic/GenerateConfigs.s.sol:GenerateChainMonitorConfig || exit 1 format_config_file "./volume/chain-monitor-config.yaml" -echo "" -echo "generating bridge-history-config.yaml" -forge script scripts/deterministic/GenerateConfigs.s.sol:GenerateBridgeHistoryConfig || exit 1 -format_config_file "./volume/bridge-history-config.yaml" +# echo "" +# echo "generating bridge-history-config.yaml" +# forge script scripts/deterministic/GenerateConfigs.s.sol:GenerateBridgeHistoryConfig || exit 1 +# format_config_file "./volume/bridge-history-config.yaml" -echo "" -echo "generating balance-checker-config.yaml" -forge script scripts/deterministic/GenerateConfigs.s.sol:GenerateBalanceCheckerConfig || exit 1 -format_config_file "./volume/balance-checker-config.yaml" +# echo "" +# echo "generating balance-checker-config.yaml" +# forge script scripts/deterministic/GenerateConfigs.s.sol:GenerateBalanceCheckerConfig || exit 1 +# format_config_file "./volume/balance-checker-config.yaml" echo "" echo "generating frontend-config.yaml" @@ -79,7 +79,7 @@ echo "generating rollup-explorer-backend-config.yaml" forge script scripts/deterministic/GenerateConfigs.s.sol:GenerateRollupExplorerBackendConfig || exit 1 format_config_file "./volume/rollup-explorer-backend-config.yaml" -echo "" -echo "generating admin-system-backend-config.yaml" -forge script scripts/deterministic/GenerateConfigs.s.sol:GenerateAdminSystemBackendConfig || exit 1 -format_config_file "./volume/admin-system-backend-config.yaml" \ No newline at end of file +# echo "" +# echo "generating admin-system-backend-config.yaml" +# forge script scripts/deterministic/GenerateConfigs.s.sol:GenerateAdminSystemBackendConfig || exit 1 +# format_config_file "./volume/admin-system-backend-config.yaml" \ No newline at end of file diff --git a/docker/scripts/verify.sh b/docker/scripts/verify.sh index 8379df4b..b727a201 100644 --- a/docker/scripts/verify.sh +++ b/docker/scripts/verify.sh @@ -1,26 +1,90 @@ -#!/bin/sh +#!/bin/bash + export FOUNDRY_EVM_VERSION="cancun" export FOUNDRY_BYTECODE_HASH="none" # extract values from config file config_file="./volume/config.toml" -CHAIN_ID_L1=$(grep -E "^[ \t]*CHAIN_ID_L1 =" "$config_file" | sed 's/ *= */=/' | cut -d'=' -f2- | tr -d '_'| tr -d '"') -CHAIN_ID_L2=$(grep -E "^[ \t]*CHAIN_ID_L2 =" "$config_file" | sed 's/ *= */=/' | cut -d'=' -f2- | tr -d '_'| tr -d '"') -RPC_URI_L1=$(grep -E "^[ \t]*RPC_URI_L1 =" "$config_file" | sed 's/ *= */=/' | cut -d'=' -f2- | tr -d '"') -RPC_URI_L2=$(grep -E "^[ \t]*RPC_URI_L2 =" "$config_file" | sed 's/ *= */=/' | cut -d'=' -f2- | tr -d '"') -VERIFIER_TYPE_L1=$(grep -E "^[ \t]*VERIFIER_TYPE_L1 =" "$config_file" | sed 's/ *= */=/' | cut -d'=' -f2- | tr -d '"') -VERIFIER_TYPE_L2=$(grep -E "^[ \t]*VERIFIER_TYPE_L2 =" "$config_file" | sed 's/ *= */=/' | cut -d'=' -f2- | tr -d '"') -EXPLORER_URI_L1=$(grep -E "^[ \t]*EXPLORER_URI_L1 =" "$config_file" | sed 's/ *= */=/' | cut -d'=' -f2- | tr -d '"') -EXPLORER_URI_L2=$(grep -E "^[ \t]*EXPLORER_URI_L2 =" "$config_file" | sed 's/ *= */=/' | cut -d'=' -f2- | tr -d '"') -EXPLORER_API_KEY_L1=$(grep -E "^[ \t]*EXPLORER_API_KEY_L1 =" "$config_file" | sed 's/ *= */=/' | cut -d'=' -f2- | tr -d '"') -EXPLORER_API_KEY_L2=$(grep -E "^[ \t]*EXPLORER_API_KEY_L2 =" "$config_file" | sed 's/ *= */=/' | cut -d'=' -f2- | tr -d '"') -ALTERNATIVE_GAS_TOKEN_ENABLED=$(grep -E "^[ \t]*ALTERNATIVE_GAS_TOKEN_ENABLED =" "$config_file" | sed 's/ *= */=/' | cut -d'=' -f2-) -TEST_ENV_MOCK_FINALIZE_ENABLED=$(grep -E "^[ \t]*TEST_ENV_MOCK_FINALIZE_ENABLED =" "$config_file" | sed 's/ *= */=/' | cut -d'=' -f2-) + +# Check if config file exists +if [[ ! -f "$config_file" ]]; then + echo "Error: Config file not found: $config_file" >&2 + exit 1 +fi + +# Helper function to remove quotes (single, double, or none) +remove_quotes() { + local value="$1" + # Remove leading and trailing whitespace first + value=$(echo "$value" | sed 's/^[ \t]*//; s/[ \t]*$//') + + # Remove matching quotes (single or double) - only if they match at both ends + if [[ "$value" =~ ^\".*\"$ ]]; then + # Remove double quotes + value="${value#\"}" + value="${value%\"}" + elif [[ "$value" =~ ^\'.*\'$ ]]; then + # Remove single quotes + value="${value#\'}" + value="${value%\'}" + fi + + echo "$value" +} + +# Helper function to extract config values safely +extract_config_value() { + local key="$1" + local file="$2" + # Match: optional whitespace (spaces and tabs), key name, optional whitespace, =, optional whitespace, value + # Exclude commented lines (starting with #) + local raw_value + raw_value=$(grep -E "^[[:space:]]*${key}[[:space:]]*=" "$file" | \ + grep -v "^[[:space:]]*#" | \ + sed 's/^[[:space:]]*[^=]*[[:space:]]*=[[:space:]]*//' | \ + sed 's/[[:space:]]*$//' | \ + head -n1) + + # Remove quotes using the helper function + remove_quotes "$raw_value" +} + +#CHAIN_ID_L1=$(extract_config_value "CHAIN_ID_L1" "$config_file" | tr -d '_') +CHAIN_ID_L2=$(extract_config_value "CHAIN_ID_L2" "$config_file" | tr -d '_') +#RPC_URI_L1=$(extract_config_value "RPC_URI_L1" "$config_file") +RPC_URI_L2=$(extract_config_value "RPC_URI_L2" "$config_file") +VERIFIER_TYPE_L1=$(extract_config_value "VERIFIER_TYPE_L1" "$config_file") +VERIFIER_TYPE_L2=$(extract_config_value "VERIFIER_TYPE_L2" "$config_file") +EXPLORER_URI_L1=$(extract_config_value "EXPLORER_URI_L1" "$config_file") +EXPLORER_URI_L2=$(extract_config_value "EXPLORER_URI_L2" "$config_file") +EXPLORER_API_KEY_L1=$(extract_config_value "EXPLORER_API_KEY_L1" "$config_file") +EXPLORER_API_KEY_L2=$(extract_config_value "EXPLORER_API_KEY_L2" "$config_file") +ALTERNATIVE_GAS_TOKEN_ENABLED=$(extract_config_value "ALTERNATIVE_GAS_TOKEN_ENABLED" "$config_file") +TEST_ENV_MOCK_FINALIZE_ENABLED=$(extract_config_value "TEST_ENV_MOCK_FINALIZE_ENABLED" "$config_file") # extract contract name and address extract_contract_info() { - contract_name=$(cut -d "=" -f 1 <<< "$line" | tr -d '"') - contract_addr=$(cut -d "=" -f 2 <<< "$line" | tr -d '"' | tr -d ' ') + # Validate input line format (key=value) + # Allow spaces and tabs around the equals sign + if [[ ! "$line" =~ ^[[:space:]]*[^=]+[[:space:]]*=[[:space:]]*[^[:space:]]+.*$ ]]; then + echo "Invalid line format: $line" >&2 + contract_name="" + contract_addr="" + return 1 + fi + + # Extract key and value safely + local key_part="${line%%=*}" + local value_part="${line#*=}" + + # Clean up key and value using the helper function + contract_name=$(remove_quotes "$key_part") + contract_addr=$(remove_quotes "$value_part") + + # Validate contract address format (basic hex check) + if [[ -n "$contract_addr" && ! "$contract_addr" =~ ^0x[a-fA-F0-9]{40}$ ]]; then + echo "Warning: Invalid contract address format: $contract_addr" >&2 + fi } get_source_code_name() { @@ -107,6 +171,9 @@ get_source_code_name() { L2_MOAT_IMPLEMENTATION_ADDR) echo Moat ;; L2_MOAT_PROXY_ADDR) echo TransparentUpgradeableProxy ;; L2_BASCULE_MOCK_VERIFIER_ADDR) echo BasculeMockVerifier ;; + L2_SYSTEM_CONFIG_PROXY_ADDR) echo TransparentUpgradeableProxy ;; + L2_SYSTEM_CONFIG_IMPLEMENTATION_ADDR) echo L2SystemConfig ;; + *) echo "" ;; # default: return void string esac fi @@ -123,28 +190,46 @@ function is_predeploy_contract() { } # read the file line by line +contracts_file="./volume/config-contracts.toml" + +# Check if contracts file exists +if [[ ! -f "$contracts_file" ]]; then + echo "Error: Contracts file not found: $contracts_file" >&2 + exit 1 +fi + while IFS= read -r line; do + # Skip empty lines and comments (lines starting with # possibly preceded by spaces/tabs) + if [[ -z "$line" || "$line" =~ ^[[:space:]]*# ]]; then + continue + fi + extract_contract_info "$line" + + # Skip if extraction failed + if [[ -z "$contract_name" ]]; then + continue + fi # get contracts deployment layer - if [[ "$contract_name" =~ ^L1 ]]; then + if [[ "$contract_name" =~ ^L1(_|$) ]]; then layer="L1" # specially handle contract_name L1_GAS_PRICE_ORACLE_ADDR if [[ "$contract_name" == "L1_GAS_PRICE_ORACLE_ADDR" ]]; then layer="L2" fi - elif [[ "$contract_name" =~ ^L2 ]]; then + elif [[ "$contract_name" =~ ^L2(_|$) ]]; then layer="L2" else - echo "wrong contract name, not starts with L1 or L2, contract_name: $contract_name" + echo "Error: Invalid contract name format (must start with L1_ or L2_): $contract_name" >&2 continue fi source_code_name=$(get_source_code_name $contract_name) # skip if source_code_name or contract_addr is empty - if [[ -z $source_code_name || -z $contract_addr ]]; then - echo "empty source_code_name $source_code_name or contract_addr $contract_addr" + if [[ -z "$source_code_name" || -z "$contract_addr" ]]; then + echo "Warning: Skipping contract ${contract_name} due to empty source_code_name '$source_code_name' or contract_addr '$contract_addr'" >&2 continue fi @@ -161,7 +246,7 @@ while IFS= read -r line; do elif [[ "$VERIFIER_TYPE_L1" == "sourcify" ]]; then EXTRA_PARAMS="--api-key $EXPLORER_API_KEY_L1 --verifier-url $EXPLORER_URI_L1 --verifier $VERIFIER_TYPE_L1" fi - forge verify-contract $contract_addr $source_code_name --rpc-url $RPC_URI_L1 --chain-id $CHAIN_ID_L1 --watch --guess-constructor-args --skip-is-verified-check $EXTRA_PARAMS +# forge verify-contract $contract_addr $source_code_name --rpc-url $RPC_URI_L1 --chain-id $CHAIN_ID_L1 --watch --guess-constructor-args --skip-is-verified-check $EXTRA_PARAMS elif [[ "$layer" == "L2" ]]; then if [[ "$VERIFIER_TYPE_L2" == "etherscan" ]]; then EXTRA_PARAMS="--api-key $EXPLORER_API_KEY_L2" @@ -170,9 +255,17 @@ while IFS= read -r line; do elif [[ "$VERIFIER_TYPE_L2" == "sourcify" ]]; then EXTRA_PARAMS="--api-key $EXPLORER_API_KEY_L2 --verifier-url $EXPLORER_URI_L2 --verifier $VERIFIER_TYPE_L2" fi + + # Add constructor args parameter for non-predeploy contracts if ! is_predeploy_contract "$source_code_name"; then - string="$EXTRA_PARAMS\" --guess-constructor-args\"" + EXTRA_PARAMS="$EXTRA_PARAMS --guess-constructor-args" fi - forge verify-contract $contract_addr $source_code_name --rpc-url $RPC_URI_L2 --chain-id $CHAIN_ID_L2 --watch --skip-is-verified-check $EXTRA_PARAMS + + cmd="forge verify-contract $contract_addr $source_code_name --rpc-url $RPC_URI_L2 --chain-id $CHAIN_ID_L2 --watch --skip-is-verified-check $EXTRA_PARAMS" + echo "=========================" + echo "$cmd" + echo "-------------------------" + $cmd + echo "=========================" fi -done < ./volume/config-contracts.toml \ No newline at end of file +done < "$contracts_file" \ No newline at end of file diff --git a/docker/templates/config-contracts.toml b/docker/templates/config-contracts.toml index 5836db52..1bcb6a07 100644 --- a/docker/templates/config-contracts.toml +++ b/docker/templates/config-contracts.toml @@ -1,4 +1,3 @@ - L1_WETH_ADDR = "" L1_WDOGE_ADDR = "" L1_PROXY_ADMIN_ADDR = "" @@ -67,4 +66,6 @@ L2_ERC1155_GATEWAY_IMPLEMENTATION_ADDR = "" L2_MOAT_IMPLEMENTATION_ADDR = "" L2_MOAT_PROXY_ADDR = "" L2_BASCULE_MOCK_VERIFIER_ADDR = "" -L2_WHITELIST_ADDR="" \ No newline at end of file +L2_WHITELIST_ADDR="" +L2_SYSTEM_CONFIG_PROXY_ADDR="" +L2_SYSTEM_CONFIG_IMPLEMENTATION_ADDR="" diff --git a/docker/templates/genesis.json b/docker/templates/genesis.json index cdca4715..8cdb77b1 100644 --- a/docker/templates/genesis.json +++ b/docker/templates/genesis.json @@ -20,6 +20,7 @@ "darwinV2Time": 0, "euclidTime": 0, "euclidV2Time": 0, + "feynmanTime": 0, "clique": { "period": 3, "epoch": 30000, @@ -32,7 +33,7 @@ }, "scroll": { "useZktrie": false, - "maxTxPerBlock": null, + "maxTxPerBlock": 100, "maxTxPayloadBytesPerBlock": 122880, "feeVaultAddress": null, "l1Config": { @@ -41,8 +42,11 @@ "l1MessageQueueV2Address": null, "l1MessageQueueV2DeploymentBlock": 0, "scrollChainAddress": null, + "l2SystemConfigAddress": null, "numL1MessagesPerBlock": "10" - } + }, + "genesisStateRoot": null, + "missingHeaderFieldsSHA256": null } }, "nonce": "0x0", @@ -56,5 +60,5 @@ "number": "0x0", "gasUsed": "0x0", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "baseFeePerGas": null + "baseFeePerGas": 1 } \ No newline at end of file diff --git a/docker/templates/rollup-config.json b/docker/templates/rollup-config.json index 570137d0..7a0c416c 100644 --- a/docker/templates/rollup-config.json +++ b/docker/templates/rollup-config.json @@ -3,7 +3,7 @@ "endpoint": "https://rpc.ankr.com/eth", "start_height": 0, "relayer_config": { - "gas_price_oracle_address": "0x0000000000000000000000000000000000000000", + "gas_price_oracle_contract_address": "0x0000000000000000000000000000000000000000", "sender_config": { "endpoint": "https://rpc.scroll.io", "escalate_blocks": 1, @@ -11,7 +11,7 @@ "escalate_multiple_num": 2, "escalate_multiple_den": 1, "max_gas_price": 1000000000000, - "tx_type": "LegacyTx", + "tx_type": "DynamicFeeTx", "check_pending_time": 1, "min_gas_tip": 100000000 }, @@ -37,8 +37,9 @@ "l2_message_queue_address": "0x0000000000000000000000000000000000000000", "relayer_config": { "celestia_submit_endpoint": "http://dogeos-da:3000/post_blob", + "validium_mode": false, "rollup_contract_address": "0x0000000000000000000000000000000000000000", - "gas_price_oracle_address": "0x0000000000000000000000000000000000000000", + "gas_price_oracle_contract_address": "0x0000000000000000000000000000000000000000", "sender_config": { "endpoint": "https://rpc.ankr.com/eth", "escalate_blocks": 1, @@ -55,7 +56,8 @@ "batch_submission": { "min_batches": 1, "max_batches": 6, - "timeout": 300 + "timeout": 7200, + "backlog_max": 75 }, "gas_oracle_config": { "min_gas_price": 0, @@ -101,20 +103,29 @@ "chunk_timeout_sec": 300, "max_row_consumption_per_chunk": 1048319, "gas_cost_increase_multiplier": 1.2, - "max_uncompressed_batch_bytes_size": 634880 + "max_uncompressed_batch_bytes_size": 4194304 }, "batch_proposer_config": { "propose_interval_milliseconds": 1000, "max_l1_commit_gas_per_batch": 11234567, "max_l1_commit_calldata_size_per_batch": 112345, "batch_timeout_sec": 300, + "max_chunks_per_batch": 45, "gas_cost_increase_multiplier": 1.2, - "max_uncompressed_batch_bytes_size": 634880, - "max_chunks_per_batch": 12 + "max_uncompressed_batch_bytes_size": 4194304 }, "bundle_proposer_config": { "max_batch_num_per_bundle": 20, "bundle_timeout_sec": 36000 + }, + "blob_uploader_config": { + "start_batch": 0, + "aws_s3_config": { + "bucket": "blob-data", + "region": "us-west-2", + "access_key": "ACCESSKEY", + "secret_key": "SECRETKEY" + } } }, "db_config": { diff --git a/foundry.toml b/foundry.toml index 5000db55..8b114a90 100644 --- a/foundry.toml +++ b/foundry.toml @@ -16,8 +16,7 @@ optimizer = true # enable or disabl 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 = true # whether to enable ffi or not +fuzz_runs = 256 # the number of fuzz runs for test 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 @@ -35,3 +34,5 @@ gas_reports = ["L2GasPriceOracle"] bytecode_hash = 'none' # file system permissions +ffi = true + diff --git a/hardhat-test/GasSwap.spec.ts b/hardhat-test/GasSwap.spec.ts deleted file mode 100644 index 4c98ab4a..00000000 --- a/hardhat-test/GasSwap.spec.ts +++ /dev/null @@ -1,329 +0,0 @@ -/* eslint-disable node/no-unpublished-import */ -/* eslint-disable node/no-missing-import */ -import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; -import { expect } from "chai"; -import { MaxUint256, Signature, ZeroAddress, ZeroHash, toBigInt } from "ethers"; -import { ethers } from "hardhat"; - -import { GasSwap, ERC2771Forwarder, MockERC20, MockGasSwapTarget } from "../typechain"; - -describe("GasSwap.spec", async () => { - let deployer: HardhatEthersSigner; - let signer: HardhatEthersSigner; - - let forwarder: ERC2771Forwarder; - let swap: GasSwap; - let target: MockGasSwapTarget; - let token: MockERC20; - - beforeEach(async () => { - [deployer, signer] = await ethers.getSigners(); - - const ERC2771Forwarder = await ethers.getContractFactory("ERC2771Forwarder", deployer); - forwarder = await ERC2771Forwarder.deploy("ERC2771Forwarder"); - - const GasSwap = await ethers.getContractFactory("GasSwap", deployer); - swap = await GasSwap.deploy(forwarder.getAddress()); - - const MockGasSwapTarget = await ethers.getContractFactory("MockGasSwapTarget", deployer); - target = await MockGasSwapTarget.deploy(); - - const MockERC20 = await ethers.getContractFactory("MockERC20", deployer); - token = await MockERC20.deploy("x", "y", 18); - }); - - context("auth", async () => { - it("should initialize correctly", async () => { - expect(await swap.owner()).to.eq(deployer.address); - }); - - context("#updateFeeRatio", async () => { - it("should revert, when non-owner call", async () => { - await expect(swap.connect(signer).updateFeeRatio(1)).to.revertedWith("Ownable: caller is not the owner"); - }); - - it("should succeed", async () => { - expect(await swap.feeRatio()).to.eq(ZeroAddress); - await expect(swap.updateFeeRatio(100)).to.emit(swap, "UpdateFeeRatio").withArgs(100); - expect(await swap.feeRatio()).to.eq(100); - }); - }); - - context("#updateApprovedTarget", async () => { - it("should revert, when non-owner call", async () => { - await expect(swap.connect(signer).updateApprovedTarget(target.getAddress(), false)).to.revertedWith( - "Ownable: caller is not the owner" - ); - }); - - it("should succeed", async () => { - expect(await swap.approvedTargets(target.getAddress())).to.eq(false); - await expect(swap.updateApprovedTarget(target.getAddress(), true)) - .to.emit(swap, "UpdateApprovedTarget") - .withArgs(await target.getAddress(), true); - expect(await swap.approvedTargets(target.getAddress())).to.eq(true); - await expect(swap.updateApprovedTarget(target.getAddress(), false)) - .to.emit(swap, "UpdateApprovedTarget") - .withArgs(await target.getAddress(), false); - expect(await swap.approvedTargets(target.getAddress())).to.eq(false); - }); - }); - - context("#withdraw", async () => { - it("should revert, when non-owner call", async () => { - await expect(swap.connect(signer).withdraw(ZeroAddress, 0)).to.revertedWith("Ownable: caller is not the owner"); - }); - - it("should succeed, when withdraw ETH", async () => { - await deployer.sendTransaction({ to: swap.getAddress(), value: ethers.parseEther("1") }); - const balanceBefore = await ethers.provider.getBalance(deployer.address); - const tx = await swap.withdraw(ZeroAddress, ethers.parseEther("1")); - const receipt = await tx.wait(); - const balanceAfter = await ethers.provider.getBalance(deployer.address); - expect(balanceAfter - balanceBefore).to.eq(ethers.parseEther("1") - receipt!.gasUsed * receipt!.gasPrice); - }); - - it("should succeed, when withdraw token", async () => { - await token.mint(swap.getAddress(), ethers.parseEther("1")); - const balanceBefore = await token.balanceOf(deployer.address); - await swap.withdraw(token.getAddress(), ethers.parseEther("1")); - const balanceAfter = await token.balanceOf(deployer.address); - expect(balanceAfter - balanceBefore).to.eq(ethers.parseEther("1")); - }); - }); - }); - - const permit = async (amount: bigint) => { - const value = { - owner: signer.address, - spender: await swap.getAddress(), - value: amount, - nonce: await token.nonces(signer.address), - deadline: MaxUint256, - }; - - const domain = { - name: await token.name(), - version: "1", - chainId: (await ethers.provider.getNetwork()).chainId, - verifyingContract: await token.getAddress(), - }; - - const types = { - Permit: [ - { - name: "owner", - type: "address", - }, - { - name: "spender", - type: "address", - }, - { - name: "value", - type: "uint256", - }, - { - name: "nonce", - type: "uint256", - }, - { - name: "deadline", - type: "uint256", - }, - ], - }; - - const signature = Signature.from(await signer.signTypedData(domain, types, value)); - return signature; - }; - - context("swap", async () => { - it("should revert, when target not approved", async () => { - await expect( - swap.swap( - { - token: token.getAddress(), - value: 0, - deadline: 0, - r: ZeroHash, - s: ZeroHash, - v: 0, - }, - { - target: target.getAddress(), - data: "0x", - minOutput: 0, - } - ) - ).to.revertedWith("target not approved"); - }); - - it("should revert, when insufficient output amount", async () => { - const amountIn = ethers.parseEther("1"); - const amountOut = ethers.parseEther("2"); - await token.mint(signer.address, amountIn); - await deployer.sendTransaction({ to: target.getAddress(), value: amountOut }); - const signature = await permit(amountIn); - - await target.setToken(token.getAddress()); - await target.setAmountIn(amountIn); - - await swap.updateApprovedTarget(target.getAddress(), true); - await expect( - swap.connect(signer).swap( - { - token: await token.getAddress(), - value: amountIn, - deadline: MaxUint256, - r: signature.r, - s: signature.s, - v: signature.v, - }, - { - target: target.getAddress(), - data: "0x8119c065", - minOutput: amountOut + 1n, - } - ) - ).to.revertedWith("insufficient output amount"); - }); - - for (const refundRatio of [0n, 1n, 5n]) { - for (const feeRatio of ["0", "5", "50"]) { - it(`should succeed, when swap by signer directly, with feeRatio[${feeRatio}%] refundRatio[${refundRatio}%]`, async () => { - const amountIn = ethers.parseEther("1"); - const amountOut = ethers.parseEther("2"); - await token.mint(signer.address, amountIn); - await deployer.sendTransaction({ to: target.getAddress(), value: amountOut }); - const signature = await permit(amountIn); - - await target.setToken(token.getAddress()); - await target.setAmountIn(amountIn); - await target.setRefund((amountIn * refundRatio) / 100n); - - await swap.updateApprovedTarget(target.getAddress(), true); - await swap.updateFeeRatio(ethers.parseEther(feeRatio) / 100n); - const fee = (amountOut * toBigInt(feeRatio)) / 100n; - - const balanceBefore = await ethers.provider.getBalance(signer.address); - const tx = await swap.connect(signer).swap( - { - token: await token.getAddress(), - value: amountIn, - deadline: MaxUint256, - r: signature.r, - s: signature.s, - v: signature.v, - }, - { - target: target.getAddress(), - data: "0x8119c065", - minOutput: amountOut - fee, - } - ); - const receipt = await tx.wait(); - const balanceAfter = await ethers.provider.getBalance(signer.address); - expect(balanceAfter - balanceBefore).to.eq(amountOut - fee - receipt!.gasUsed * receipt!.gasPrice); - expect(await token.balanceOf(signer.address)).to.eq((amountIn * refundRatio) / 100n); - }); - - it(`should succeed, when swap by signer with forwarder, with feeRatio[${feeRatio}%] refundRatio[${refundRatio}%]`, async () => { - const amountIn = ethers.parseEther("1"); - const amountOut = ethers.parseEther("2"); - await token.mint(signer.address, amountIn); - await deployer.sendTransaction({ to: await target.getAddress(), value: amountOut }); - const permitSignature = await permit(amountIn); - - await target.setToken(token.getAddress()); - await target.setAmountIn(amountIn); - await target.setRefund((amountIn * refundRatio) / 100n); - - await swap.updateApprovedTarget(target.getAddress(), true); - await swap.updateFeeRatio(ethers.parseEther(feeRatio) / 100n); - const fee = (amountOut * toBigInt(feeRatio)) / 100n; - - const reqWithoutSignature = { - from: signer.address, - to: await swap.getAddress(), - value: 0n, - gas: 1000000, - nonce: await forwarder.nonces(signer.address), - deadline: 2000000000, - data: swap.interface.encodeFunctionData("swap", [ - { - token: await token.getAddress(), - value: amountIn, - deadline: MaxUint256, - r: permitSignature.r, - s: permitSignature.s, - v: permitSignature.v, - }, - { - target: await target.getAddress(), - data: "0x8119c065", - minOutput: amountOut - fee, - }, - ]), - }; - - const signature = await signer.signTypedData( - { - name: "ERC2771Forwarder", - version: "1", - chainId: (await ethers.provider.getNetwork()).chainId, - verifyingContract: await forwarder.getAddress(), - }, - { - ForwardRequest: [ - { - name: "from", - type: "address", - }, - { - name: "to", - type: "address", - }, - { - name: "value", - type: "uint256", - }, - { - name: "gas", - type: "uint256", - }, - { - name: "nonce", - type: "uint256", - }, - { - name: "deadline", - type: "uint48", - }, - { - name: "data", - type: "bytes", - }, - ], - }, - reqWithoutSignature - ); - - const balanceBefore = await ethers.provider.getBalance(signer.address); - await forwarder.execute({ - from: reqWithoutSignature.from, - to: reqWithoutSignature.to, - value: reqWithoutSignature.value, - gas: reqWithoutSignature.gas, - deadline: reqWithoutSignature.deadline, - data: reqWithoutSignature.data, - signature, - }); - const balanceAfter = await ethers.provider.getBalance(signer.address); - expect(balanceAfter - balanceBefore).to.eq(amountOut - fee); - expect(await token.balanceOf(signer.address)).to.eq((amountIn * refundRatio) / 100n); - }); - } - } - }); -}); diff --git a/hardhat-test/ZkEvmVerifierV2.spec.ts b/hardhat-test/ZkEvmVerifierV2.spec.ts index 72fd9dd1..286ca3d1 100644 --- a/hardhat-test/ZkEvmVerifierV2.spec.ts +++ b/hardhat-test/ZkEvmVerifierV2.spec.ts @@ -122,7 +122,7 @@ describe("ZkEvmVerifierV2", async () => { await expect(zkEvmVerifier.verify(proof, publicInputs.reverse())).to.reverted; }); - it("should succeed when call through ScrollChain", async () => { + it.skip("should succeed when call through ScrollChain", async () => { const proof = hexlify(fs.readFileSync(`./hardhat-test/testdata/plonk-verifier/${version}_proof.data`)); const lastFinalizedBatchIndex = 1; diff --git a/scripts/deterministic/Configuration.sol b/scripts/deterministic/Configuration.sol index 2b48ce2f..3280fa39 100644 --- a/scripts/deterministic/Configuration.sol +++ b/scripts/deterministic/Configuration.sol @@ -36,17 +36,14 @@ abstract contract Configuration is Script { uint256 internal MAX_BLOCK_IN_CHUNK; uint256 internal MAX_BATCH_IN_BUNDLE; uint256 internal MAX_L1_MESSAGE_GAS_LIMIT; + uint256 internal FINALIZE_BATCH_DEADLINE_SEC; + uint256 internal RELAY_MESSAGE_DEADLINE_SEC; uint256 internal L1_CONTRACT_DEPLOYMENT_BLOCK; - bool internal ALTERNATIVE_GAS_TOKEN_ENABLED; - bool internal TEST_ENV_MOCK_FINALIZE_ENABLED; uint256 internal TEST_ENV_MOCK_FINALIZE_TIMEOUT_SEC; - bytes32 internal VERIFIER_DIGEST_1; - bytes32 internal VERIFIER_DIGEST_2; - // accounts uint256 internal DEPLOYER_PRIVATE_KEY; uint256 internal L1_COMMIT_SENDER_PRIVATE_KEY; @@ -94,9 +91,6 @@ abstract contract Configuration is Script { string internal ADMIN_SYSTEM_DASHBOARD_URI; string internal GRAFANA_URI; - uint256 internal FINALIZE_BATCH_DEADLINE_SEC = 10000; - uint256 internal RELAY_MESSAGE_DEADLINE_SEC = 10000; - /********************** * Internal interface * **********************/ @@ -122,11 +116,11 @@ abstract contract Configuration is Script { MAX_BLOCK_IN_CHUNK = cfg.readUint(".rollup.MAX_BLOCK_IN_CHUNK"); MAX_BATCH_IN_BUNDLE = cfg.readUint(".rollup.MAX_BATCH_IN_BUNDLE"); MAX_L1_MESSAGE_GAS_LIMIT = cfg.readUint(".rollup.MAX_L1_MESSAGE_GAS_LIMIT"); + FINALIZE_BATCH_DEADLINE_SEC = cfg.readUint(".rollup.FINALIZE_BATCH_DEADLINE_SEC"); + RELAY_MESSAGE_DEADLINE_SEC = cfg.readUint(".rollup.RELAY_MESSAGE_DEADLINE_SEC"); L1_CONTRACT_DEPLOYMENT_BLOCK = cfg.readUint(".general.L1_CONTRACT_DEPLOYMENT_BLOCK"); - ALTERNATIVE_GAS_TOKEN_ENABLED = cfg.readBool(".gas-token.ALTERNATIVE_GAS_TOKEN_ENABLED"); - TEST_ENV_MOCK_FINALIZE_ENABLED = cfg.readBool(".rollup.TEST_ENV_MOCK_FINALIZE_ENABLED"); TEST_ENV_MOCK_FINALIZE_TIMEOUT_SEC = cfg.readUint(".rollup.TEST_ENV_MOCK_FINALIZE_TIMEOUT_SEC"); @@ -136,26 +130,23 @@ abstract contract Configuration is Script { L1_GAS_ORACLE_SENDER_PRIVATE_KEY = vm.envOr("L1_GAS_ORACLE_SENDER_PRIVATE_KEY", uint256(0)); L2_GAS_ORACLE_SENDER_PRIVATE_KEY = vm.envOr("L2_GAS_ORACLE_SENDER_PRIVATE_KEY", uint256(0)); - if (DEPLOYER_PRIVATE_KEY == 0) { + if (DEPLOYER_PRIVATE_KEY == uint256(0)) { DEPLOYER_PRIVATE_KEY = cfg.readUint(".accounts.DEPLOYER_PRIVATE_KEY"); } - - if (L1_COMMIT_SENDER_PRIVATE_KEY == 0) { + if (L1_COMMIT_SENDER_PRIVATE_KEY == uint256(0)) { L1_COMMIT_SENDER_PRIVATE_KEY = cfg.readUint(".accounts.L1_COMMIT_SENDER_PRIVATE_KEY"); } - - if (L1_FINALIZE_SENDER_PRIVATE_KEY == 0) { + if (L1_FINALIZE_SENDER_PRIVATE_KEY == uint256(0)) { L1_FINALIZE_SENDER_PRIVATE_KEY = cfg.readUint(".accounts.L1_FINALIZE_SENDER_PRIVATE_KEY"); } - - if (L1_GAS_ORACLE_SENDER_PRIVATE_KEY == 0) { + if (L1_GAS_ORACLE_SENDER_PRIVATE_KEY == uint256(0)) { L1_GAS_ORACLE_SENDER_PRIVATE_KEY = cfg.readUint(".accounts.L1_GAS_ORACLE_SENDER_PRIVATE_KEY"); } - - if (L2_GAS_ORACLE_SENDER_PRIVATE_KEY == 0) { + if (L2_GAS_ORACLE_SENDER_PRIVATE_KEY == uint256(0)) { 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"); @@ -207,9 +198,6 @@ abstract contract Configuration is Script { FINALIZE_BATCH_DEADLINE_SEC = cfg.readUint(".rollup.FINALIZE_BATCH_DEADLINE_SEC"); RELAY_MESSAGE_DEADLINE_SEC = cfg.readUint(".rollup.RELAY_MESSAGE_DEADLINE_SEC"); - VERIFIER_DIGEST_1 = bytes32(cfg.readBytes32(".general.VERIFIER_DIGEST_1")); - VERIFIER_DIGEST_2 = bytes32(cfg.readBytes32(".general.VERIFIER_DIGEST_2")); - runSanityCheck(); } diff --git a/scripts/deterministic/Constants.sol b/scripts/deterministic/Constants.sol index 3238bef3..445a495e 100644 --- a/scripts/deterministic/Constants.sol +++ b/scripts/deterministic/Constants.sol @@ -30,16 +30,24 @@ 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.yaml"; string constant ROLLUP_CONFIG_PATH = "./volume/rollup-config.yaml"; +string constant GAS_ORACLE_CONFIG_PATH = "./volume/gas-oracle-config.yaml"; string constant COORDINATOR_CONFIG_PATH = "./volume/coordinator-config.yaml"; +string constant COORDINATOR_API_CONFIG_PATH = "../../config/coordinator-api/config.json"; +string constant COORDINATOR_CRON_CONFIG_PATH = "../../config/coordinator-cron/config.json"; string constant CHAIN_MONITOR_CONFIG_PATH = "./volume/chain-monitor-config.yaml"; string constant BRIDGE_HISTORY_CONFIG_PATH = "./volume/bridge-history-config.yaml"; +string constant BRIDGE_HISTORY_API_CONFIG_PATH = "../../config/bridge-historyv2-api/config.json"; string constant BALANCE_CHECKER_CONFIG_PATH = "./volume/balance-checker-config.yaml"; string constant FRONTEND_ENV_PATH = "./volume/frontend-config.yaml"; string constant ROLLUP_EXPLORER_BACKEND_CONFIG_PATH = "./volume/rollup-explorer-backend-config.yaml"; string constant ADMIN_SYSTEM_BACKEND_CONFIG_PATH = "./volume/admin-system-backend-config.yaml"; +string constant ADMIN_SYSTEM_CRON_CONFIG_PATH = "../../config/scroll-admin-cron/config.json"; +string constant BRIDGE_HISTORY_FETCHER_CONFIG_PATH = "../../config/bridge-historyv2-fetcher/config.json"; -// plonk verifier configs -bytes32 constant V4_VERIFIER_DIGEST = 0x0a1904dbfff4614fb090b4b3864af4874f12680c32f07889e9ede8665097e5ec; +// verifier 0.2.0 VERIFIER_DIGEST +// https://circuit-release.s3.us-west-2.amazonaws.com/scroll-zkvm/releases/0.2.0/bundle/digest_2.hex +bytes32 constant VERIFIER_DIGEST = 0x0038553adf417a6a3df35d2fdfd14b892f1e49ba18937ece7960c1e7cee6e3dc; -// plonk verifier v0.13.1 creation code -bytes constant PLONK_VERIFIER_CREATION_CODE = hex"62000025565b60006040519050600081036200001a57606090505b818101604052919050565b6136a3620000338162000005565b816200003f82398181f3fe60017f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd477f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610151565b60007f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4782107f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47841080821692505050600082146000841480821780158481169450505050507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478384097f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478384097f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478482097f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47600382088381148581169550505050505092915050565b8060003506602052806020350660405280604035066060528060603506608052806080350660a0528060a0350660c0528060c0350660e0528060e0350661010052806101003506610120528061012035066101405280610140350661016052806101603506610180528061018035066101a052806101a035066101c052806101c035066101e052806101e0350661020052806102003506610220528061022035066102405280610240350661026052806102603506610280528061028035066102a052806102a035066102c052806102c035066102e052806102e0350661030052806103003506610320527f083d7f8552bdf1dd98458c19d469c458809424d40c483abc2e528b75a6f0f1466000526103203580610340526103403580610360528461027d8284610048565b169450505061038060002061038052610380518181066103a052806103c0525061036035806103e052610380358061040052846102ba8284610048565b169450505060606103c02061042052610420518181066104405280610460525060016104805360216104602061048052610480518181066104a052806104c052506103a035806104e0526103c0358061050052846103188284610048565b16945050506103e03580610520526104003580610540528461033a8284610048565b16945050506104203580610560526104403580610580528461035c8284610048565b169450505060e06104c0206105a0526105a0518181066105c052806105e05250610460358061060052610480358061062052846103998284610048565b16945050506104a03580610640526104c0358061066052846103bb8284610048565b16945050506104e035806106805261050035806106a052846103dd8284610048565b169450505061052035806106c05261054035806106e052846103ff8284610048565b16945050506101206105e0206107005261070051818106610720528061074052508061056035066107605280610580350661078052806105a035066107a052806105c035066107c052806105e035066107e0528061060035066108005280610620350661082052806106403506610840528061066035066108605280610680350661088052806106a035066108a052806106c035066108c052806106e035066108e052806107003506610900528061072035066109205280610740350661094052806107603506610960526102406107402061098052610980518181066109a052806109c0525060016109e05360216109c0206109e0526109e051818106610a005280610a2052506107803580610a40526107a03580610a6052846105248284610048565b16945050506060610a2020610a8052610a8051818106610aa05280610ac052506107c03580610ae0526107e03580610b0052846105618284610048565b169450505060205160405160581b8101905060605160b01b8101905080610b205260805160a05160581b8101905060c05160b01b8101905080610b4052846105a98284610048565b169450505060e0516101005160581b810190506101205160b01b8101905080610b6052610140516101605160581b810190506101805160b01b8101905080610b8052846105f68284610048565b169450505080610720516107205109610ba05280610ba051610ba05109610bc05280610bc051610bc05109610be05280610be051610be05109610c005280610c0051610c005109610c205280610c2051610c205109610c405280610c4051610c405109610c605280610c6051610c605109610c805280610c8051610c805109610ca05280610ca051610ca05109610cc05280610cc051610cc05109610ce05280610ce051610ce05109610d005280610d0051610d005109610d205280610d2051610d205109610d405280610d4051610d405109610d605280610d6051610d605109610d805280610d8051610d805109610da05280610da051610da05109610dc05280610dc051610dc05109610de05280610de051610de05109610e005280610e0051610e005109610e205280610e2051610e205109610e405280610e4051610e405109610e605280610e6051610e605109610e805280610e8051610e805109610ea05280610ea051610ea05109610ec052807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000610ec05108610ee052807f30644e66c81e03716be83b486d6feabcc7ddd0fe6cbf5e72d585d142f7829b05610ee05109610f0052807f2d19f86a2342079b8c1a6417471d461040256eaa689be51f08e6a92e1243ce65610f005109610f2052807f034a5608bdef988e2c35e19f3a64124ce80e799e111d8b723afb4c65ddbc319c6107205108610f4052807f24a1fcd63e9f03b27281db85fe631ec8e5c466f8178a4ee94d4942b7ccd90e1c610f005109610f6052807f0bc2519ca2929c7745ce6a30831e3994426f8150622f21a7f698b2dc2326f1e56107205108610f8052807f0d5eb4c216db2c3262de3f6a2ef71a9be95ff21a7a1a50ed069d6131e7d54e5f610f005109610fa052807f230599b0ca5673f75572064c528a3dc13ed3f62dff9f1fa43d449462082ab1a26107205108610fc052807f26501ebfe559ea5826f023d3e76e4b66f170cd940408eb5590a4075c80b498d6610f005109610fe052807f0a142fb2fbd7b5d1916021e29a130cf636c31ab475b0853bb33dee376f4b672b610720510861100052807f082a7bd4c0a7e4352229d332c27a160da18f0d7c651f3047df41b80345532f6e610f00510961102052807f2839d29e2089bbf496267283bf07424f86a4dacc149a404964a03d90aaacd093610720510861104052807f19277f31ecb5bfe8604677099c09556812b0b5c50cceb2b584098183a5a6c5c8610f00510961106052807f173ccf40f47be0415809ceace57802f5158332836ceabddbbfd874104a593a39610720510861108052807f20bab6e5f766b4edf82399e9c5ff0e40d4b6875321a3d8020e18521d8f5c7241610f0051096110a052807f0fa9978ce9caeb3bc02cabccbb824a1c537d60f55815988f35c9a37660a38dc061072051086110c052806001610f0051096110e052807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000610720510861110052807f1dba8b5bdd64ef6ce29a9039aca3c0e524395c43b9227b96c75090cc6cc7ec97610f00510961112052807f12a9c31703ccb0bcd5b5b57cd4dd977803fa8c04c096f4fa7c9164c78338136a610720510861114052807f0d94d63997367c97a8ed16c17adaae39262b9af83acb9e003f94c217303dd160610f00510961116052807f22cf783949fb23920f632ef506a6aa2402084d503eedd291044d337cbfc22ea1610720510861118052807f303a348fae5a4f041e5c056919bc140f68267e2fb55a522282b02d6a100e01a0610f0051096111a052807e2a19e332d7512599f4404d67c5444dc00d6a18c45f1e6ec131c829dff1fe6161072051086111c052807f1951441010b2b95a6e47a6075066a50a036f5ba978c050f2821df86636c0facb610f0051096111e052807f17130a62d07ee6cf4a089faf311ab35324c48c9f00f91f9ec1c3fd2db93f0536610720510861120052807f04fe6e3fa02c3830525c10d7bbf567639bfc836de8fe4e471c889a638d381c71610f00510961122052807f2b65e033410567f965f434dec58bf0f98c3764da90bb224a27595b3062c7e390610720510861124052807f24db2e49a2c215211bae763372d0d8b05d0140adbc6d9d63f2a226fb711fd873610f00510961126052807f0b8920293e6f8b089ca1cf830eb07faccb32a79abd4bd32d513fce987ee0278e610720510861128052807f0f6afbf59e2fef78443acf353ca6d17cfc07ebc6343141cd56a9102bd4864004610f0051096112a052807f20f9527d4301b0b17415768144da86e02c2bfc8245882ec3ed38e5681b79bffd61072051086112c052807f1283ba6f4b7b1a76ba2008fe823128bea4adb9269cbfd7c41c223be65bc60863610f0051096112e052807f1de0940395b685b2fe303cb7ff502f9e83862f21dcf998cd27bfb9ad9439f79e610720510861130052807f10e6c9ec7941500e1b1095e82fb0034bd9e95777ca9ae5ce296eadc089110518610f00510961132052807f1f7d848667f0501b9d3fafce51d155114e4a90d0af1e8ac31a7347d366eefae9610720510861134052807f25ad5db2a6bf79a14fd0d2ffd3d0927af7fdd30bce40f88fb46774fd262d7673610f00510961136052807f0ab6f0c03a722688687f72b6adb0c5e23036153cab7878018f7a8096c9d2898e610720510861138052807f2cd3ee06866876806a4a382f7a95576f34fd9ce6aad8bf350670ed33fe3259ac610f0051096113a052807f0390606c5ac929a94e060d8706ec00edf3364b61cee0b15c3d71085ff1cda65561072051086113c052807f2f835d9f4207df4efa4ffa0b2bbf9a4f54221c57cc506b7a5f8dae90bd2e3d0a610f0051096113e052807ee0f0d39f29c0dabe004bab55c1be0dd411cbf0ad690516e454470332d1c2f7610720510861140052807f30526acf1fa80f36309d80530d854ae7d52fc97bcb8d0a6a2a7f852e7b6e9d79610f00510961142052807e11e3a3c18990f387b2c56373fc0d7553041eccae2c66271962706574916288610720510861144052807f0af04af9c243a8b4b8767330b8dae01f562d7641cdf0a0c4d288e395c0aebb72610f00510961146052807f257403791eedf774ffd9d285c8a6783dd2067206abc8cfcc715911fe2f51448f610720510861148052807f2387d5be5175ba27fd2f8779b460ffdd830e06cd2f6bcf30c3715fbed25b1b67610f0051096114a052807f0cdc78b48fbbe601bb20be3ccd20587fa525e17b4a4da160807095d51da4e49a61072051086114c052807f18c95f1ae6514e11a1b30fd7923947c5ffcec5347f16e91b4dd654168326bede610f0051096114e052807f179aef57fae05218169d35deef48109728652313faa28775f60ba17d6cd94123610720510861150052807f1ed7bccd53b52d451436ae36d9f0225657083d7e909edb5560d7ea488aebe0d1610f00510961152052807f118c91a58d7c72e4a419977fa7913606d12baac9e91a953be30a0b4b65141f30610720510861154052807f0ba3551f265c0941ccb3766d47b26720ad620a597137ff3c84310bd3e28e26c9610f00510961156052807f24c0f953bad596e7eb9ccf4939cef13c7ad1ddef08817154bfb0e9c00d71d938610720510861158052807f0803f4ae22d04b4c9c282c70c843e12bfb2d84b89bd7ef84dc3b90d60053b1c8610f0051096115a052807f286059c4be6154dd1c281945b93d77312d06638fdde1810c67a664bdefac4e3961072051086115c052807f29aa84e8187de51daa6de67f44fe365fbb789d1e5f97bc95844628487ebd3a0d610f0051096115e052807f06b9c98ac8b3bb0c0de25f373c8321fd6cbb4b2a1a21b3fbbf9bcd4b7142c5f4610720510861160052807f0e4fc6c7e1947e44222db52506e305b8a9ad70f6a834db5a4778d4a30f1c6f92610f00510961162052807f221487aaff9d21e5962290917a9e52a47e867751d1849536fc6920f0e0e3906f610720510861164052807f20816bee57855658ecd8a204faf6bced826a725b63c6a1a388b4609a2478809f610f00510961166052807f0fe2e28489ac49d0cb77a3b1868a9b6fa5c975ed15f2ceedbb2d94f9cb877f62610720510861168052807f0d44b8855e09d5ae332469459793c1444814f6d137f9e93e780bd41c97e37caa610f0051096116a052807f231f95ed8327ca7b852bdc70e9ed9718e01ef17741bf8752cbd62177581c835761072051086116c052807f07fe49da5568a43070d955e0d212d956e1ff8abd41a763c737e292bee0476699610f0051096116e052807f286604988bc8fbf94776efd5af6e7f0646345d8b38120cca0bff62d50fb89968610720510861170052610f40518181610f805109905080611720528181610fc051099050806117405281816110005109905080611760528181611040510990508061178052818161108051099050806117a05281816110c051099050806117c052818161110051099050806117e0528181611140510990508061180052818161118051099050806118205281816111c051099050806118405281816112005109905080611860528181611240510990508061188052818161128051099050806118a05281816112c051099050806118c052818161130051099050806118e0528181611340510990508061190052818161138051099050806119205281816113c051099050806119405281816114005109905080611960528181611440510990508061198052818161148051099050806119a05281816114c051099050806119c052818161150051099050806119e05281816115405109905080611a005281816115805109905080611a205281816115c05109905080611a405281816116005109905080611a605281816116405109905080611a805281816116805109905080611aa05281816116c05109905080611ac05281816117005109905080611ae0528181610ee05109905080611b0052506020611b40526020611b60526020611b8052611b0051611ba0527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff611bc0527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001611be0528260016020611b2060c0611b4060055afa14169250611b20516000610ee05190508282611ae05109610ee0528282820991506117005190508282611ac05109611700528282820991506116c05190508282611aa051096116c0528282820991506116805190508282611a805109611680528282820991506116405190508282611a605109611640528282820991506116005190508282611a405109611600528282820991506115c05190508282611a2051096115c0528282820991506115805190508282611a0051096115805282828209915061154051905082826119e051096115405282828209915061150051905082826119c05109611500528282820991506114c051905082826119a051096114c0528282820991506114805190508282611980510961148052828282099150611440519050828261196051096114405282828209915061140051905082826119405109611400528282820991506113c0519050828261192051096113c052828282099150611380519050828261190051096113805282828209915061134051905082826118e051096113405282828209915061130051905082826118c05109611300528282820991506112c051905082826118a051096112c0528282820991506112805190508282611880510961128052828282099150611240519050828261186051096112405282828209915061120051905082826118405109611200528282820991506111c0519050828261182051096111c052828282099150611180519050828261180051096111805282828209915061114051905082826117e051096111405282828209915061110051905082826117c05109611100528282820991506110c051905082826117a051096110c052828282099150611080519050828261178051096110805282828209915061104051905082826117605109611040528282820991506110005190508282611740510961100052828282099150610fc051905082826117205109610fc052828282099150610f805190508282610f405109610f805282828209915081610f4052505080610f4051610f205109611c005280610f8051610f605109611c205280610fc051610fa05109611c40528061100051610fe05109611c605280611040516110205109611c805280611080516110605109611ca052806110c0516110a05109611cc05280611100516110e05109611ce05280611140516111205109611d005280611180516111605109611d2052806111c0516111a05109611d405280611200516111e05109611d605280611240516112205109611d805280611280516112605109611da052806112c0516112a05109611dc05280611300516112e05109611de05280611340516113205109611e005280611380516113605109611e2052806113c0516113a05109611e405280611400516113e05109611e605280611440516114205109611e805280611480516114605109611ea052806114c0516114a05109611ec05280611500516114e05109611ee05280611540516115205109611f005280611580516115605109611f2052806115c0516115a05109611f405280611600516115e05109611f605280611640516116205109611f805280611680516116605109611fa052806116c0516116a05109611fc05280611700516116e05109611fe05280602051611ce05109818183604051611d005109089050818183606051611d205109089050818183608051611d40510908905081818360a051611d60510908905081818360c051611d80510908905081818360e051611da0510908905081818361010051611dc0510908905081818361012051611de0510908905081818361014051611e00510908905081818361016051611e20510908905081818361018051611e4051090890508181836101a051611e6051090890508181836101c051611e8051090890508181836101e051611ea0510908905081818361020051611ec0510908905081818361022051611ee0510908905081818361024051611f00510908905081818361026051611f20510908905081818361028051611f4051090890508181836102a051611f6051090890508181836102c051611f8051090890508181836102e051611fa0510908905081818361030051611fc0510908905081818361032051611fe0510908905080612000525080610780516107a05109612020528061202051610760510861204052806107c0518203612040510861206052806108205161206051096120805280612080516105c051096120a052806108e05182036001086120c05280611ce0516120c051096120e052806120e0516120a051086121005280612100516105c0510961212052806108e0516108e0510961214052806108e051820361214051086121605280611c0051612160510961218052806121805161212051086121a052806121a0516105c051096121c05280611c005182036001086121e05280611c4051611c2051086122005280611c605161220051086122205280611c805161222051086122405280611ca05161224051086122605280611cc051612260510861228052806122805182036121e051086122a052806104405161088051096122c052806122c0516107e051086122e052806104a0516122e051086123005280610440516108a05109612320528061232051610760510861234052806104a051612340510861236052806123005161236051096123805280610440516108c051096123a052806123a05161200051086123c052806104a0516123c051086123e05280612380516123e05109612400528061090051612400510961242052806104405160010961244052806124405161072051096124605280612460516107e0510861248052806104a05161248051086124a05280610440517f09226b6e22c6f0ca64ec26aad4c86e715b5f898e5e963f25870e56bbe533e9a2096124c052806124c05161072051096124e052806124e051610760510861250052806104a051612500510861252052806124a05161252051096125405280610440517f13b360d4e82fe915fed16081038f98c211427b87a281bd733c277dbadf10372b09612560528061256051610720510961258052806125805161200051086125a052806104a0516125a051086125c05280612540516125c051096125e052806108e0516125e051096126005280612600518203612420510861262052806122a05161262051096126405280612640516121c051086126605280612660516105c051096126805280611ce05161092051096126a052806126a05161268051086126c052806126c0516105c051096126e05280611c005161092051096127005280612700516126e051086127205280612720516105c051096127405280610440516108005108612760528061084051610760510961278052806104405161278051086127a05280612760516127a051096127c0528061092051820361094051086127e052806127c0516127e051096128005280610960516127a05109612820528061282051820361276051086128405280612840518203612800510861286052806122a051612860510961288052806128805161274051086128a05280610ec051610ec051096128c05280610ec0516128c051096128e05280610ec0516128e051096129005280610ec05160010961292052806128c05160010961294052806128e0516001096129605280610ee0516128a05109612980528061072051610ba051096129a05280600161072051096129c052806129c0518203610aa051086129e052807f0d94d63997367c97a8ed16c17adaae39262b9af83acb9e003f94c217303dd1606107205109612a005280612a00518203610aa05108612a2052807f1dba8b5bdd64ef6ce29a9039aca3c0e524395c43b9227b96c75090cc6cc7ec976107205109612a405280612a40518203610aa05108612a6052807f303a348fae5a4f041e5c056919bc140f68267e2fb55a522282b02d6a100e01a06107205109612a805280612a80518203610aa05108612aa052807f2eedbf565be4b0b88a0e251d750f7559486d77bea4d5c9612ce3041295d380d5610aa051098181837f01768f1c854cef712e4220990c71e303dfc67089d4e3a73016fef1815a2c7f2c610720510908905080612ac05250807f0cf547ca658485dd9a20bd27b080d3147ec1caa826e725f7f7989fc9668dd16f610aa051098181837f077ebe9a531776ef7c9e90d5a0685181f70e74298eca38a6c92d84d4c0457ceb610720510908905080612ae05250807f077ebe9a531776ef7c9e90d5a0685181f70e74298eca38a6c92d84d4c0457ceb610aa051098181837e32f4eea6716d08d38119c3d378a36e865ffacfa73eb2357603f3f3368c88c6610720510908905080612b005250807f0d71dfba734c88cf774db4e0e4f959cb98acfbbb1fc789e5167bbca1fcafd24e610aa051098181837f25ea65fea4371c04dd1eaf23d69f2e0f9524ef5b9845d01cdb218e89a8052321610720510908905080612b205250806129e051600109612b405280612a6051612b405109612b605280612a2051612b605109612b805280612aa051612b805109612ba052807f12a9c31703ccb0bcd5b5b57cd4dd977803fa8c04c096f4fa7c9164c78338136b610aa051098181837f1dba8b5bdd64ef6ce29a9039aca3c0e524395c43b9227b96c75090cc6cc7ec96610720510908905080612bc05250807f1dba8b5bdd64ef6ce29a9039aca3c0e524395c43b9227b96c75090cc6cc7ec96610aa051098181837f1025b522462e72d539ad797831c912abfe0dc14b7e56dd9687bbceb53c8a1b37610720510908905080612be05250806001610aa051098181837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000610720510908905080612c005250612ac0518181612ae05109905080612c20528181612b005109905080612c40528181612b205109905080612c60528181612bc05109905080612c80528181612be05109905080612ca0528181612b605109905080612cc0528181612c005109905080612ce0528181612b405109905080612d0052506020612d40526020612d60526020612d8052612d0051612da0527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff612dc0527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001612de0528260016020612d2060c0612d4060055afa14169250612d20516000612b405190508282612ce05109612b4052828282099150612c005190508282612cc05109612c0052828282099150612b605190508282612ca05109612b6052828282099150612be05190508282612c805109612be052828282099150612bc05190508282612c605109612bc052828282099150612b205190508282612c405109612b2052828282099150612b005190508282612c205109612b0052828282099150612ae05190508282612ac05109612ae05282828209915081612ac0525050612ac0518181612ae0510890508181612b00510890508181612b205108905080612e00525080612b6051612ba05109612e2052612bc0518181612be05108905080612e40525080612b4051612ba05109612e6052612c005180612e805250612e00518181612e405109905080612ea0528181612e805109905080612ec052506020612f00526020612f20526020612f4052612ec051612f60527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff612f80527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001612fa0528260016020612ee060c0612f0060055afa14169250612ee0516000612e805190508282612ea05109612e8052828282099150612e405190508282612e005109612e405282828209915081612e0052505080612e4051612e205109612fc05280612e8051612e605109612fe052806109a0516109a0510961300052806109a051613000510961302052806109a051613020510961304052806109a051613040510961306052806109a051613060510961308052806109a05161308051096130a052806109a0516130a051096130c052806109a0516130c051096130e052806109a0516130e051096131005280610a0051610a0051096131205280610a005161312051096131405280612ac0516107605109818183612ae0516107805109089050818183612b00516107a05109089050818183612b20516107c0510908905080613160525080612e0051613160510961318052806001613180518303096131a0528060016131a051096131c05280612e20516001096131e05280612bc0516108e05109818183612be051610900510908905080613200525080612fc05161320051096132205280600161322051830309613240528060016131e051096132605280612bc0516109205109818183612be051610940510908905080613280525080612fc05161328051096132a052806109a0516132a0518303096132c052806109a0516131e051096132e052806132c05161324051086133005280610a005161330051096133205280610a005161326051096133405280610a00516132e051096133605280613320516131c051086133805280612e60516001096133a05280612c00516109605109806133c0525080612fe0516133c051096133e0528060016133e051830309613400528060016133a051096134205280612c00516107e0510980613440525080612fe051613440510961346052806109a0516134605183030961348052806109a0516133a051096134a052806134805161340051086134c05280612c00516108005109806134e0525080612fe0516134e05109613500528061300051613500518303096135205280613000516133a051096135405280613520516134c051086135605280612c0051610820510980613580525080612fe05161358051096135a05280613020516135a0518303096135c05280613020516133a051096135e052806135c05161356051086136005280612c0051610840510980613620525080612fe0516136205109613640528061304051613640518303096136605280613040516133a0510961368052806136605161360051086136a05280612c00516108805109806136c0525080612fe0516136c051096136e05280613060516136e0518303096137005280613060516133a051096137205280613700516136a051086137405280612c00516108a0510980613760525080612fe0516137605109613780528061308051613780518303096137a05280613080516133a051096137c052806137a05161374051086137e05280612c00516108c0510980613800525080612fe051613800510961382052806130a0516138205183030961384052806130a0516133a051096138605280613840516137e051086138805280612e605161292051096138a05280612e605161294051096138c05280612e605161296051096138e05280612c0051612980510980613900525080612fe051613900510961392052806130c0516139205183030961394052806130c0516133a0510961396052806130c0516138a0510961398052806130c0516138c051096139a052806130c0516138e051096139c052806139405161388051086139e05280612c0051610860510980613a00525080612fe051613a005109613a2052806130e051613a2051830309613a4052806130e0516133a05109613a605280613a40516139e05108613a80528061312051613a805109613aa05280613120516134205109613ac05280613120516134a05109613ae05280613120516135405109613b005280613120516135e05109613b205280613120516136805109613b405280613120516137205109613b605280613120516137c05109613b805280613120516138605109613ba05280613120516139605109613bc05280613120516139805109613be05280613120516139a05109613c005280613120516139c05109613c20528061312051613a605109613c405280613aa0516133805108613c605280612ba051600109613c805280610aa051600109613ca0526001613cc0526002613ce052613c6051613d00528260016040613cc06060613cc060075afa14169250613cc051613d2052613ce051613d405261034051613d605261036051613d80528260016040613d206080613d2060065afa141692506104e051613da05261050051613dc05261334051613de0528260016040613da06060613da060075afa14169250613d2051613e0052613d4051613e2052613da051613e4052613dc051613e60528260016040613e006080613e0060065afa1416925061052051613e805261054051613ea05261336051613ec0528260016040613e806060613e8060075afa14169250613e0051613ee052613e2051613f0052613e8051613f2052613ea051613f40528260016040613ee06080613ee060065afa141692506103e051613f605261040051613f8052613ac051613fa0528260016040613f606060613f6060075afa14169250613ee051613fc052613f0051613fe052613f605161400052613f8051614020528260016040613fc06080613fc060065afa141692507f2ad47afc517e78898ecd3a5c20c46975e6c50bf030951071878c7470e2f3f9f1614040527f1ceaca4426cdc11c593eee4db1e3981bfd8bd0eba770b93aa85f5f15dc16096061406052613ae051614080528260016040614040606061404060075afa14169250613fc0516140a052613fe0516140c052614040516140e052614060516141005282600160406140a060806140a060065afa141692507f0fb05ccb81603592ce60bd6199890470ef6f9caca3c17570df7701d70a2dd957614120527f1cc1301d6d462edf2e79083532a337ef3f087fbf0b9516d08074784017e10c7e61414052613b0051614160528260016040614120606061412060075afa141692506140a051614180526140c0516141a052614120516141c052614140516141e0528260016040614180608061418060065afa141692507f1e69edc3b54a25a5be1efb7515b5eaa259569e44cf6e7b99887bd2015311104f614200527f293e037f34e8ad87a6ebea272575c751f96a1e0acbf05bf5774e0581c3cc382361422052613b2051614240528260016040614200606061420060075afa1416925061418051614260526141a05161428052614200516142a052614220516142c0528260016040614260608061426060065afa141692507f0cf666852fd76b36f03572da1111ea68014217801887369fd35f9460f6bb13246142e0527f230bb407465f3dcda6711ec136903711485b30046005c0dd769bd85d1cc8a56761430052613b40516143205282600160406142e060606142e060075afa14169250614260516143405261428051614360526142e05161438052614300516143a0528260016040614340608061434060065afa141692507f2c2ef5dd4ea527c7b6a1adb979cf19e316cf50515b2225ecd040f40f46ad8fc66143c0527f2cd240820bffbbfa44c0832dfe16e8a40c4e75d800f58f7576733ca37a9c73696143e052613b60516144005282600160406143c060606143c060075afa14169250614340516144205261436051614440526143c051614460526143e051614480528260016040614420608061442060065afa141692507f1e2415bae32b721ff7b95b1766a3d0a4ea86d62fc8b4ed864092e8be29b375b46144a0527f0193d85fa914927d0567822f343ead8682e19e1948bdfe0d70e5ce64eaddf26a6144c052613b80516144e05282600160406144a060606144a060075afa14169250614420516145005261444051614520526144a051614540526144c051614560528260016040614500608061450060065afa141692507f02b01e53fe9c70fbc4e13606ffb809f58993a5b31d9e9a69e5c86b2833b52ac8614580527f21fe9d89f5e7d29aa77fb345a045fabbcc5f2be8bbd2f571306475118175ae306145a052613ba0516145c0528260016040614580606061458060075afa14169250614500516145e052614520516146005261458051614620526145a0516146405282600160406145e060806145e060065afa1416925061060051614660526106205161468052613bc0516146a0528260016040614660606061466060075afa141692506145e0516146c052614600516146e0526146605161470052614680516147205282600160406146c060806146c060065afa1416925061064051614740526106605161476052613be051614780528260016040614740606061474060075afa141692506146c0516147a0526146e0516147c052614740516147e052614760516148005282600160406147a060806147a060065afa1416925061068051614820526106a05161484052613c0051614860528260016040614820606061482060075afa141692506147a051614880526147c0516148a052614820516148c052614840516148e0528260016040614880608061488060065afa141692506106c051614900526106e05161492052613c2051614940528260016040614900606061490060075afa1416925061488051614960526148a05161498052614900516149a052614920516149c0528260016040614960608061496060065afa14169250610560516149e05261058051614a0052613c4051614a205282600160406149e060606149e060075afa1416925061496051614a405261498051614a60526149e051614a8052614a0051614aa0528260016040614a406080614a4060065afa14169250610a4051614ac052610a6051614ae052613c80518103614b00528260016040614ac06060614ac060075afa14169250614a4051614b2052614a6051614b4052614ac051614b6052614ae051614b80528260016040614b206080614b2060065afa14169250610ae051614ba052610b0051614bc052613ca051614be0528260016040614ba06060614ba060075afa14169250614b2051614c0052614b4051614c2052614ba051614c4052614bc051614c60528260016040614c006080614c0060065afa14169250614c0051614c8052614c2051614ca052610ae051614cc052610b0051614ce052610b2051614d0052610b4051614d2052610b6051614d4052610b8051614d6052610100614c8020614d805280614d805106614da05280614da051614da05109614dc05280614da051600109614de052614d0051614e0052614d2051614e2052614de051614e40528260016040614e006060614e0060075afa14169250614c8051614e6052614ca051614e8052614e0051614ea052614e2051614ec0528260016040614e606080614e6060065afa14169250614d4051614ee052614d6051614f0052614de051614f20528260016040614ee06060614ee060075afa14169250614cc051614f4052614ce051614f6052614ee051614f8052614f0051614fa0528260016040614f406080614f4060065afa14169250614e6051614fc052614e8051614fe0527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2615000527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed615020527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b615040527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa61506052614f405161508052614f60516150a0527f186282957db913abd99f91db59fe69922e95040603ef44c0bd7aa3adeef8f5ac6150c0527f17944351223333f260ddc3b4af45191b856689eda9eab5cbcddbbe570ce860d26150e0527f06d971ff4a7467c3ec596ed6efc674572e32fd6f52b721f97e35b0b3d3546753615100527f06ecdb9f9567f59ed2eee36e1e1d58797fd13cc97fafc2910f5e8a12f202fa9a615120528260016020614fc0610180614fc060085afa14169250826001614fc051141692508261369e57600080fd5b600080f3"; +// openvm verifier 0.2.0 creation code +// https://circuit-release.s3.us-west-2.amazonaws.com/scroll-zkvm/releases/0.2.0/bundle/verifier.bin +bytes constant PLONK_VERIFIER_CREATION_CODE = hex"608060405234801561001057600080fd5b50614842806100206000396000f3fe608060405234801561001057600080fd5b5060003660606040516080811461002657600080fd5b60017f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd477f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610162565b60007f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4782107f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478410808216925050507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478384097f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478384097f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478482097f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47600382088381148086169550505050505092915050565b806000350660a052806020350660c052806040350660e0528060603506610100528060803506610120528060a03506610140528060c03506610160528060e03506610180528061010035066101a0528061012035066101c0528061014035066101e0528061016035066102005280610180350661022052806101a0350661024052806101c0350661026052806101e03506610280528061020035066102a0528061022035066102c0528061024035066102e0528061026035066103005280610280350661032052806102a0350661034052806102c0350661036052806102e03506610380528061030035066103a0528061032035066103c0528061034035066103e0528061036035066104005280610380350661042052806103a0350661044052806103c0350661046052806103e03506610480528061040035066104a0528061042035066104c0528061044035066104e0528061046035066105005280610480350661052052806104a0350661054052806104c0350661056052806104e03506610580528061050035066105a0528061052035066105c0528061054035066105e0528061056035066106005280610580350661062052806105a03506610640527f2afa0dd006fb6a9768832d01002e379b45e16c96954f9531f4045dd440c668386080526105c03580610660526105e035806106805284610364828461006f565b16945050506106206080206106a0526106a0518181066106c052806106e05250610600358061070052610620358061072052846103a1828461006f565b1694505050610640358061074052610660358061076052846103c3828461006f565b169450505060a06106e02061078052610780518181066107a052806107c0525060016107e05360216107c0206107e0526107e051818106610800528061082052506106803580610840526106a035806108605284610421828461006f565b16945050506106c03580610880526106e035806108a05284610443828461006f565b169450505061070035806108c05261072035806108e05284610465828461006f565b169450505060e061082020610900526109005181810661092052806109405250610740358061096052610760358061098052846104a2828461006f565b169450505061078035806109a0526107a035806109c052846104c4828461006f565b16945050506107c035806109e0526107e03580610a0052846104e6828461006f565b16945050506108003580610a20526108203580610a405284610508828461006f565b169450505061012061094020610a6052610a6051818106610a805280610aa05250806108403506610ac052806108603506610ae052806108803506610b0052806108a03506610b2052806108c03506610b4052806108e03506610b6052806109003506610b8052806109203506610ba052806109403506610bc052806109603506610be052806109803506610c0052806109a03506610c2052806109c03506610c4052806109e03506610c605280610a003506610c805280610a203506610ca05280610a403506610cc05280610a603506610ce05280610a803506610d0052610280610aa020610d2052610d2051818106610d405280610d6052506001610d80536021610d6020610d8052610d8051818106610da05280610dc05250610aa03580610de052610ac03580610e005284610641828461006f565b16945050506060610dc020610e2052610e2051818106610e405280610e605250610ae03580610e8052610b003580610ea0528461067e828461006f565b169450505060a05160c05160581b8101905060e05160b01b8101905080610ec052610100516101205160581b810190506101405160b01b8101905080610ee052846106c9828461006f565b1694505050610160516101805160581b810190506101a05160b01b8101905080610f00526101c0516101e05160581b810190506102005160b01b8101905080610f205284610717828461006f565b169450505080610a8051610a805109610f405280610f4051610f405109610f605280610f6051610f605109610f805280610f8051610f805109610fa05280610fa051610fa05109610fc05280610fc051610fc05109610fe05280610fe051610fe05109611000528061100051611000510961102052806110205161102051096110405280611040516110405109611060528061106051611060510961108052806110805161108051096110a052806110a0516110a051096110c052806110c0516110c051096110e052806110e0516110e05109611100528061110051611100510961112052806111205161112051096111405280611140516111405109611160528061116051611160510961118052806111805161118051096111a052806111a0516111a051096111c052806111c0516111c051096111e052807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000006111e0510861120052807f30644db14ff7d4a4f1cf9ed5406a7e5722d273a7aa184eaa5e1fb0846829b041611200510961122052807f1d3d878f52016737bda697d23b0cee81488efd02d67b27eae3edab5f39ef347d611220510961124052807f1326c6e38f3038f1faa9ade4467469dbdfa4eb45a33e48a65ff44a34b610cb84610a80510861126052807f182fa146dab5070e1897c235ff7425a25d09f820206545e69bf946c2f6057429611220510961128052807f1834ad2c067c991b9fb88380820d32bacb29f02859542aaaa7e8aed0f9fa8bd8610a8051086112a052807f185afb17aed5e3d828526ac8e534436afa30f7196772de397fd9b6be79fe8c9361122051096112c052807f1809535b325bbc518ffddaed9c4d14f22e02f12f12469257c4083ed57601736e610a8051086112e052807f220db0d8bf832baf9eecbf4fa49947e0b2a3d31df0a733ea5ae8abbdab442d5f611220510961130052807f0e569d9a21ae747a19638666dce8107c7590152a89123ca6e8f949d644bbd2a2610a80510861132052807f2fffa2b50d66f628412d9782f09d3386d766a1168304babe2165fe7ec962e65b611220510961134052807e64abbdd3caaa017722ae3390e424d650cd4731f6b4b5d3227bf715269d19a6610a80510861136052807f06288b9d40ec1915c3a20817de7179beeccf4c4fd02f202c064a0cc13e38c2f8611220510961138052807f2a3bc2d5a0458713f4ae3d9ea30fde9e3b649bf8a98a50653d97e8d2b1c73d09610a8051086113a052807f134f571fe34eb8c7b1685e875b324820e199bd70157493377cd65b204d1a396461122051096113c052807f1d14f752fde2e76206e7e72f264f103c469a2ad86444dd59c70b9a73a2e5c69d610a8051086113e052806001611220510961140052807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000610a80510861142052807f18c95f1ae6514e11a1b30fd7923947c5ffcec5347f16e91b4dd654168326bede611220510961144052807f179aef57fae05218169d35deef48109728652313faa28775f60ba17d6cd94123610a80510861146052807f032750f8f3c2493d0828c7285d0258e1bdcaa463f4442a52747b5c96639659bb611220510961148052807f2d3cfd79ed6f56ecb0277e8e247eff7b6a6943e48575463ecf6698fd8c69a646610a8051086114a052807f1be6734ccb6588d60d469def6bf0b0f39af67bb568a3b63e3773e3dd306ba07061122051096114c052807f147ddb2615cc1753ab09a7c71590a7698d3d6c931115ba530c6e11b6bf945f91610a8051086114e052807f2a14464f1ff42de3856402b62520e670745e39fada049d5b2f0e1e3182673378611220510961150052807f06500823c13d724632ec43005c6071ecb3d5ae4d9fb4d33614d3d7626d98cc89610a80510861152052807f0205f4c81bd8bfadd87e3771dc61e489caf1a2f61f6fe94419612b1039f806ba611220510961154052807f2e5e59aac558e07bdfd20e44a51f73d35d4245525a49874d2a80ca83b607f947610a80510861156052807f085386d3273d6903e6ca9388a9b29e81643977d523db6f6db1fdc5acf965b809611220510961158052807f2810c79fb9f43725d185b22dd7ceb9dbc3fa707355de012391e42fe6f69a47f8610a8051086115a052807f0962d59f2fea9851331073aa7b9a65add88e25cdcf9a7908665722e5c57e1c2561122051096115c052807f270178d3b14707d8853fd20c05e6f2af4fa5c27aaa1ef788dd8ad2ae2a81e3dc610a8051086115e052807f0cf1526aaafac6bacbb67d11a4077806b123f767e4b0883d14cc0193568fc082611220510961160052807f2372fc083636d96eec99c8a4dd79e056770ff0e09508e8542f15f40099703f7f610a80510861162052807f0d38d63833e51358021fc5aeb30483105d8b48022cd28fc4ccc55739e57166f4611220510961164052807f232b783aad4c8cd1b6308007ce7cd54ccaa8a0464ce6e0cc771c9e5a0a8e990d610a80510861166052807f202efdf29fbbf7251b99f728e6317b3a58a0e579b827f9d622ab713b5d76ba5e611220510961168052807f103550804175a9049cb64e8d9b4fdd22cf9302cec19176bb21368458928945a3610a8051086116a052807f1df73deb96a4785f63b25489c52178d4b3f95e41d1a1d6aec678d35d2d2095b861122051096116c052807f126d10874a8d27ca549df12cbc5fdf88743a8a06a81799e27d692236c2df6a49610a8051086116e052807f157057e61fa68c20f015bccca35a72daa53b29abd151cfa1431a92029ece5c0e611220510961170052807f1af3f68cc18b1408c83a88e9de26e58282f8be9ca867a0f000c763915131a3f3610a80510861172052807f17e4f62c667b201826284c6918cfd09f571a62f09dfca2d54702a220c92f178b611220510961174052807f187f58467ab680119227f94d68b187bdd1198557dbbccdbbfcdf537326d0e876610a80510861176052807f1b8c0911077b8645a2dcd11044ecedef18d199a893d5f3814f789252af93e53c611220510961178052807f14d84561d9b619e4157374a63c946a6e0f624e9fe5e37d0ff4696341406c1ac5610a8051086117a052807f23755b45f23db9743c690e5570b1e2983e8369f76981f85609c5154a03a922df61122051096117c052807f0ceef32ceef3e6b57be7376110cf75c4e9b07e511037783b3a1ce049ec56dd22610a8051086117e052807f0f60c8fe0414cb9379b2d39267945f6bd60d06a05216231b26a9fcf88ddbfebe611220510961180052807f21038574dd1cd4963e9d722419ecf8f15226e1a827a34d761d37f89b62240143610a80510861182052807f1dd804edc273c3b32894f7d1f5ad573c785b068883b719276149bcfcbd7c8735611220510961184052807f128c49851ebddc768fbb4de48bd40120afd8e1bff6025769e2983897328378cc610a80510861186052807f2cfb3ee365fa96d90152e17588a5c8109aba50a36f0e6cc5d38caa708844f09d611220510961188052807f03690f8f7b370950b6fd6440f8db904c8d7997a50aab03cb70554b2367bb0f64610a8051086118a052807f01bf77e1640452989576495d56797caee1f6c4a3529d7ad6cf9f068ba23f509561122051096118c052807f2ea4d6917d2d4d9122d9fc592b07dbae463d23a5271bf5ba7442ef084dc0af6c610a8051086118e052807f0bb1b800e0fc2016082cf25a117ee7ebc61bdc048d093fbf025ec0794556abea611220510961190052807f24b2967200358013b023535c7002707162180c43ecb030d24183351aaaa95417610a80510861192052807f2184edd1ba10180eb3174c08e41cd0981126e96545bbe8fa6be74d0f5139a939611220510961194052807f0edf60a12721881b0538f9ad9d6487c5170cfee333fd8796d7faa8849ec656c8610a80510861196052807f013b926b615dadae89c76ce93dbd4b79cd3fad8e402744b6151a79df33fb48e3611220510961198052807f2f28bc077fd3f27b2e88d8cd43c40ce35af43aba39922bdb2ec77bb4bc04b71e610a8051086119a052807f24b9e65b23e30a1fb8b8ea2a50caa34abf79322f88ecc1e5c72c863f6fbebd1761122051096119c052807f0baa6817bd4e9609ff975b8c30b6b51268bab618f0ccaeab7cb56f54804142ea610a8051086119e052807f09f83b8b9c5b192f9193a231814c3163b9b8c636b1d40961a83ac1f42dc63b266112205109611a0052807f266c12e744d686fa26bca385003526f96e7b2211c7e5672f9ba7339fc239c4db610a805108611a2052807f0f2e46e84503e0bec563379085b7d3ef78102bd590fbd619be21ce2c1b9945bf6112205109611a4052807f2136078a9c2dbf6af2ed0e25fbc9846db023bc72e8bd9a7785c02767d466ba42610a805108611a6052807f2c9741ded62f041c7cc95ad55d1d3649468d8a5ec530f048bbeeffdfd0a9d6526112205109611a8052807f03cd0c940b029c0d3b86eae124642213e1a65de9b488804887f2f5b41f5629af610a805108611aa052807f20e2808bc9ff5d255ed9de0f29147692fff955773b6ad07c69f232bfd55f00596112205109611ac052807f0f81cde717324304597667a7586ce1ca283a92d13e4ea014d9efc2d41aa0ffa8610a805108611ae052807f05b432e46216fc6bed772f92cde45c9ebc2d8703af094c331522356351ce2c776112205109611b0052807f2ab01b8e7f1aa3bdcad91623b39cfbbe6c066144cab0245e2ebfc0309e31d38a610a805108611b2052807f259c031d10081ea697e9997cdcf8c47964c227e4fb4e77e807713f00585956de6112205109611b4052807f0ac84b55d12981832066ac39a48893e3c371c0637e6af8a93c70b69397a6a923610a805108611b6052807f04290a1d7601de8e3c98923f48b2304f4ca974f5376d42c80bbec5eb53070bc56112205109611b8052807f2c3b44556b2fc19b7bb7b37738cf280ddb8a7353424c2dc938232fa89cf8f43c610a805108611ba052807f2fe46b4c6e0df90591d8336da5638fb4c1859ede12103bc04dd0e995a9d8e77d6112205109611bc052807e7fe3267323a72426781248dc1dc8a866ae496a67a934d0f6110bfe46271884610a805108611be052807f304cd1e79cfa5b0f054e981a27ed7706e7ea6b06a7f266ef8db819c179c2c3ea6112205109611c0052807e177c8b4437451ab301ad9c5993e15640497d41d1c709a1b629dbd2763d3c17610a805108611c2052807f1a05ef04e20b042a06c61577a4a9b54771c327c3672c0357d55d04e22aa2f1c96112205109611c4052807f165e5f6dff269bffb18a303edcd7a315b670c085128d6d396e84f0b1c55d0e38610a805108611c6052807f1cc839f9cc5012a73f3b56e2417eb951f46c04af98489f776d1dd847b247f2b56112205109611c8052807f139c147914e18d827914eed440029f0b33c7e398e170d119d6c41d4c3db80d4c610a805108611ca052807f0baa43683ba5a5fa31558c98a045a4b0109b2efb97fe1e2d352c28456d499def6112205109611cc052807f24ba0b0aa58bfa2f86fab91de13bb3ad1798b94ce1bb52640eb5cd4e82b66212610a805108611ce052807f033a1069fe5e06d412520dc1eb795e5c1a9fbf099908ee0d7891c86e2e2746c56112205109611d0052807f2d2a3e08e2d39955a5fe37f49607fa010d94293ee0b08283cb502d25c1d8b93c610a805108611d2052807f07102fcd8e37f581514a697a842c2f7a57aa1ca017fd7c2c81c4481ef51a4af86112205109611d4052807f29541ea552f9aaa86705dc3bfd5528e2d089cba861bbf464c21dad74fae5b509610a805108611d6052807f06d5223674254f4051450d63a7815877be218cff2ae41bf96a519f97d5f893696112205109611d8052807f298f2c3c6d0c50e9670b3852d9ffffe56a125b494ed55497d99055fc1a076c98610a805108611da052807f2336c94d73c29901a712ac079b7f50d62e10f1d075badfbbf438f74afbbabe9d6112205109611dc052807f0d2d85256d6f0728113d99aee6020786fa22f67803fe90d54fa8fe48f4454164610a805108611de052807f2fbe09de40a6ef2c32ad177045a51c5033c7186172461c2658a0f93cf36bfa3f6112205109611e0052807ea64494a08ab0fd85a32e463bdc3c0cf46ccfe70773546aeb40fc56fc9405c2610a805108611e2052807f292ec9ad99cfce95b2da745a36ddf7b05337dd83ece3597760f62c2ebf21c3106112205109611e4052807f073584c54761d1940575d15c4aa360acd4fc0ac48cd61719e2ebc96530de3cf1610a805108611e6052807f21ae116f5f57a4cc6e3f8d02345f04778d257b7e8200f6cfe5c69aa1692cc8fb6112205109611e8052807f0eb63d0381d9fb5d4a10b8b44d2253e59b0e6cc9f7b879c15e1b5af286d33706610a805108611ea052807f1bfee385b2c5cfc01238741207046900bb91b5598bc6cc1cf2f5c1019ec024016112205109611ec052807f14656aed2e6bd069a617d1a47a7cef5c6ca232eeedf2a47450ec3492513fdc00610a805108611ee052807f146723ce0d2732187545ca1441a36f872027c0262cd646a42fa20c7f648a061e6112205109611f0052807f1bfd2aa4d40a6e11430a7ba23fdde8d6080c28224ce329ed143fe9148b75f9e3610a805108611f2052807f0f4411b99f1a27148940c80178d5446f0c54afb342542fd6b7d56e8c8c4cb5cc6112205109611f4052807f21203cb9421779152f0f7db508ac13ee1bdf3895376540ba8c0c870763b34a35610a805108611f60526112605181816112a05109905080611f805281816112e05109905080611fa05281816113205109905080611fc05281816113605109905080611fe05281816113a051099050806120005281816113e05109905080612020528181611420510990508061204052818161146051099050806120605281816114a051099050806120805281816114e051099050806120a052818161152051099050806120c052818161156051099050806120e05281816115a051099050806121005281816115e05109905080612120528181611620510990508061214052818161166051099050806121605281816116a051099050806121805281816116e051099050806121a052818161172051099050806121c052818161176051099050806121e05281816117a051099050806122005281816117e05109905080612220528181611820510990508061224052818161186051099050806122605281816118a051099050806122805281816118e051099050806122a052818161192051099050806122c052818161196051099050806122e05281816119a051099050806123005281816119e05109905080612320528181611a205109905080612340528181611a605109905080612360528181611aa05109905080612380528181611ae051099050806123a0528181611b2051099050806123c0528181611b6051099050806123e0528181611ba05109905080612400528181611be05109905080612420528181611c205109905080612440528181611c605109905080612460528181611ca05109905080612480528181611ce051099050806124a0528181611d2051099050806124c0528181611d6051099050806124e0528181611da05109905080612500528181611de05109905080612520528181611e205109905080612540528181611e605109905080612560528181611ea05109905080612580528181611ee051099050806125a0528181611f2051099050806125c0528181611f6051099050806125e052818161120051099050806126005250602061264052602061266052602061268052612600516126a0527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff6126c0527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016126e052826001602061262060c061264060055afa1416925061262051600061120051905082826125e0510961120052828282099150611f6051905082826125c05109611f6052828282099150611f2051905082826125a05109611f2052828282099150611ee051905082826125805109611ee052828282099150611ea051905082826125605109611ea052828282099150611e6051905082826125405109611e6052828282099150611e2051905082826125205109611e2052828282099150611de051905082826125005109611de052828282099150611da051905082826124e05109611da052828282099150611d6051905082826124c05109611d6052828282099150611d2051905082826124a05109611d2052828282099150611ce051905082826124805109611ce052828282099150611ca051905082826124605109611ca052828282099150611c6051905082826124405109611c6052828282099150611c2051905082826124205109611c2052828282099150611be051905082826124005109611be052828282099150611ba051905082826123e05109611ba052828282099150611b6051905082826123c05109611b6052828282099150611b2051905082826123a05109611b2052828282099150611ae051905082826123805109611ae052828282099150611aa051905082826123605109611aa052828282099150611a6051905082826123405109611a6052828282099150611a2051905082826123205109611a20528282820991506119e0519050828261230051096119e0528282820991506119a051905082826122e051096119a05282828209915061196051905082826122c051096119605282828209915061192051905082826122a05109611920528282820991506118e0519050828261228051096118e0528282820991506118a0519050828261226051096118a052828282099150611860519050828261224051096118605282828209915061182051905082826122205109611820528282820991506117e0519050828261220051096117e0528282820991506117a051905082826121e051096117a05282828209915061176051905082826121c051096117605282828209915061172051905082826121a05109611720528282820991506116e0519050828261218051096116e0528282820991506116a0519050828261216051096116a052828282099150611660519050828261214051096116605282828209915061162051905082826121205109611620528282820991506115e0519050828261210051096115e0528282820991506115a051905082826120e051096115a05282828209915061156051905082826120c051096115605282828209915061152051905082826120a05109611520528282820991506114e0519050828261208051096114e0528282820991506114a0519050828261206051096114a052828282099150611460519050828261204051096114605282828209915061142051905082826120205109611420528282820991506113e0519050828261200051096113e0528282820991506113a05190508282611fe051096113a0528282820991506113605190508282611fc05109611360528282820991506113205190508282611fa05109611320528282820991506112e05190508282611f8051096112e0528282820991506112a0519050828261126051096112a052828282099150816112605250508061126051611240510961270052806112a051611280510961272052806112e0516112c051096127405280611320516113005109612760528061136051611340510961278052806113a05161138051096127a052806113e0516113c051096127c052806114205161140051096127e0528061146051611440510961280052806114a051611480510961282052806114e0516114c051096128405280611520516115005109612860528061156051611540510961288052806115a05161158051096128a052806115e0516115c051096128c052806116205161160051096128e0528061166051611640510961290052806116a051611680510961292052806116e0516116c051096129405280611720516117005109612960528061176051611740510961298052806117a05161178051096129a052806117e0516117c051096129c052806118205161180051096129e05280611860516118405109612a0052806118a0516118805109612a2052806118e0516118c05109612a405280611920516119005109612a605280611960516119405109612a8052806119a0516119805109612aa052806119e0516119c05109612ac05280611a2051611a005109612ae05280611a6051611a405109612b005280611aa051611a805109612b205280611ae051611ac05109612b405280611b2051611b005109612b605280611b6051611b405109612b805280611ba051611b805109612ba05280611be051611bc05109612bc05280611c2051611c005109612be05280611c6051611c405109612c005280611ca051611c805109612c205280611ce051611cc05109612c405280611d2051611d005109612c605280611d6051611d405109612c805280611da051611d805109612ca05280611de051611dc05109612cc05280611e2051611e005109612ce05280611e6051611e405109612d005280611ea051611e805109612d205280611ee051611ec05109612d405280611f2051611f005109612d605280611f6051611f405109612d80528060a0516127e0510981818360c051612800510908905081818360e0516128205109089050818183610100516128405109089050818183610120516128605109089050818183610140516128805109089050818183610160516128a05109089050818183610180516128c051090890508181836101a0516128e051090890508181836101c05161290051090890508181836101e0516129205109089050818183610200516129405109089050818183610220516129605109089050818183610240516129805109089050818183610260516129a05109089050818183610280516129c051090890508181836102a0516129e051090890508181836102c051612a0051090890508181836102e051612a20510908905081818361030051612a40510908905081818361032051612a60510908905081818361034051612a80510908905081818361036051612aa0510908905081818361038051612ac051090890508181836103a051612ae051090890508181836103c051612b0051090890508181836103e051612b20510908905081818361040051612b40510908905081818361042051612b60510908905081818361044051612b80510908905081818361046051612ba0510908905081818361048051612bc051090890508181836104a051612be051090890508181836104c051612c0051090890508181836104e051612c20510908905081818361050051612c40510908905081818361052051612c60510908905081818361054051612c80510908905081818361056051612ca0510908905081818361058051612cc051090890508181836105a051612ce051090890508181836105c051612d0051090890508181836105e051612d20510908905081818361060051612d40510908905081818361062051612d60510908905081818361064051612d80510908905080612da0525080610ae051610b005109612dc05280612dc051610ac05108612de05280610b20518203612de05108612e005280610b8051612e005109612e205280612e20516109205109612e405280610c40518203600108612e6052806127e051612e605109612e805280612e8051612e405108612ea05280612ea0516109205109612ec05280610c4051610c405109612ee05280610c40518203612ee05108612f00528061270051612f005109612f205280612f2051612ec05108612f405280612f40516109205109612f605280612700518203600108612f805280612740516127205108612fa0528061276051612fa05108612fc0528061278051612fc05108612fe052806127a051612fe0510861300052806127c05161300051086130205280613020518203612f80510861304052806107a051610be05109613060528061306051610b40510861308052806108005161308051086130a052806107a051610c0051096130c052806130c051610ac051086130e05280610800516130e0510861310052806130a051613100510961312052806107a051610c205109613140528061314051612da05108613160528061080051613160510861318052806131205161318051096131a05280610c60516131a051096131c052806107a0516001096131e052806131e051610a805109613200528061320051610b405108613220528061080051613220510861324052806107a0517f09226b6e22c6f0ca64ec26aad4c86e715b5f898e5e963f25870e56bbe533e9a209613260528061326051610a805109613280528061328051610ac051086132a05280610800516132a051086132c05280613240516132c051096132e052806107a0517f13b360d4e82fe915fed16081038f98c211427b87a281bd733c277dbadf10372b09613300528061330051610a805109613320528061332051612da05108613340528061080051613340510861336052806132e05161336051096133805280610c405161338051096133a052806133a05182036131c051086133c05280613040516133c051096133e052806133e051612f60510861340052806134005161092051096134205280610c8051820360010861344052806127e0516134405109613460528061346051613420510861348052806134805161092051096134a05280610c8051610c8051096134c05280610c805182036134c051086134e05280612700516134e051096135005280613500516134a05108613520528061352051610920510961354052806107a051610cc051086135605280610ca0516135605109613580528061080051610d0051086135a05280613580516135a051096135c05280610ba051610ac051096135e052806107a0516135e051086136005280610c80516136005109613620528061080051610b605108613640528061362051613640510961366052806136605182036135c0510861368052806130405161368051096136a052806136a05161354051086136c052806136c05161092051096136e05280610d00518203610cc0510861370052806127e05161370051096137205280613720516136e05108613740528061374051610920510961376052806130405161370051096137805280610ce0518203610cc051086137a05280613780516137a051096137c052806137c05161376051086137e052806111e0516111e0510961380052806111e051613800510961382052806111e051613820510961384052806111e0516001096138605280613800516001096138805280613820516001096138a05280611200516137e051096138c05280610a8051610f4051096138e05280610a80516138e0510961390052807f134f571fe34eb8c7b1685e875b324820e199bd70157493377cd65b204d1a3964610a8051096139205280613920518203610e40510861394052806001610a8051096139605280613960518203610e40510861398052807f18c95f1ae6514e11a1b30fd7923947c5ffcec5347f16e91b4dd654168326bede610a8051096139a052806139a0518203610e4051086139c052807f032750f8f3c2493d0828c7285d0258e1bdcaa463f4442a52747b5c96639659bb610a8051096139e052806139e0518203610e405108613a0052807f1be6734ccb6588d60d469def6bf0b0f39af67bb568a3b63e3773e3dd306ba070610a805109613a205280613a20518203610e405108613a4052806138e0517f07d604303a7bed3ecd3bcf9f68948bd11d7cfb83623e03c3d41fd2e7dc3c699109613a6052806001613a605109613a805280613a6051610e405109818183613a80518503610a80510908905080613aa05250806138e0517f0888e470b16311ed8fe253a4ea4c86161e449ca748456e7e35c5e51c92b691b909613ac052807f18c95f1ae6514e11a1b30fd7923947c5ffcec5347f16e91b4dd654168326bede613ac05109613ae05280613ac051610e405109818183613ae0518503610a80510908905080613b005250806138e0517f2fcabb9b6c70a7190da3d756fc7c6c21f286a22d2029c27d5ebffb1638ea8d6f09613b2052807f032750f8f3c2493d0828c7285d0258e1bdcaa463f4442a52747b5c96639659bb613b205109613b405280613b2051610e405109818183613b40518503610a80510908905080613b605250806138e0517f071875f8c967190753f4712b28f4bf6278328c0809585ee0629d98255557240f09613b8052807f1be6734ccb6588d60d469def6bf0b0f39af67bb568a3b63e3773e3dd306ba070613b805109613ba05280613b8051610e405109818183613ba0518503610a80510908905080613bc052508061398051600109613be052806139c051613be05109613c005280613a0051613c005109613c205280613a4051613c205109613c405280610a80517f179aef57fae05218169d35deef48109728652313faa28775f60ba17d6cd9412409613c6052806001613c605109613c805280613c6051610e405109818183613c80518503610a80510908905080613ca0525080610a80517f18c95f1ae6514e11a1b30fd7923947c5ffcec5347f16e91b4dd654168326bedd09613cc052807f18c95f1ae6514e11a1b30fd7923947c5ffcec5347f16e91b4dd654168326bede613cc05109613ce05280613cc051610e405109818183613ce0518503610a80510908905080613d00525080610a80517f1d14f752fde2e76206e7e72f264f103c469a2ad86444dd59c70b9a73a2e5c69e09613d2052806001613d205109613d405280613d2051610e405109818183613d40518503610a80510908905080613d60525080610a80517f134f571fe34eb8c7b1685e875b324820e199bd70157493377cd65b204d1a396309613d8052807f134f571fe34eb8c7b1685e875b324820e199bd70157493377cd65b204d1a3964613d805109613da05280613d8051610e405109818183613da0518503610a80510908905080613dc052508061394051613be05109613de052806001610e4051098181837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000610a80510908905080613e005250613aa0518181613b005109905080613e20528181613b605109905080613e40528181613bc05109905080613e60528181613ca05109905080613e80528181613d005109905080613ea0528181613c005109905080613ec0528181613d605109905080613ee0528181613dc05109905080613f00528181613de05109905080613f20528181613e005109905080613f40528181613be05109905080613f6052506020613fa0526020613fc0526020613fe052613f6051614000527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff614020527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001614040528260016020613f8060c0613fa060055afa14169250613f80516000613be05190508282613f405109613be052828282099150613e005190508282613f205109613e0052828282099150613de05190508282613f005109613de052828282099150613dc05190508282613ee05109613dc052828282099150613d605190508282613ec05109613d6052828282099150613c005190508282613ea05109613c0052828282099150613d005190508282613e805109613d0052828282099150613ca05190508282613e605109613ca052828282099150613bc05190508282613e405109613bc052828282099150613b605190508282613e205109613b6052828282099150613b005190508282613aa05109613b005282828209915081613aa0525050613aa0518181613b00510890508181613b60510890508181613bc05108905080614060525080613c0051613c40510961408052613ca0518181613d0051089050806140a0525080613de051613c4051096140c052613d60518181613dc051089050806140e0525080613be051613c40510961410052613e00518061412052506140605181816140a051099050806141405281816140e051099050806141605281816141205109905080614180525060206141c05260206141e05260206142005261418051614220527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff614240527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016142605282600160206141a060c06141c060055afa141692506141a051600061412051905082826141605109614120528282820991506140e0519050828261414051096140e0528282820991506140a0519050828261406051096140a05282828209915081614060525050806140a051614080510961428052806140e0516140c051096142a052806141205161410051096142c05280610d4051610d4051096142e05280610d40516142e051096143005280610d405161430051096143205280610d405161432051096143405280610d405161434051096143605280610d405161436051096143805280610d405161438051096143a05280610d40516143a051096143c05280610d40516143c051096143e05280610da051610da051096144005280610da05161440051096144205280610da05161442051096144405280613aa051610ac05109818183613b0051610ae05109089050818183613b6051610b005109089050818183613bc051610b2051090890508061446052508061406051614460510961448052806001614480518303096144a0528060016144a051096144c05280614080516001096144e05280613ca051610c405109818183613d0051610c605109089050806145005250806142805161450051096145205280600161452051830309614540528060016144e051096145605280613ca051610c805109818183613d0051610ca05109089050806145805250806142805161458051096145a05280610d40516145a0518303096145c05280610d40516144e051096145e052806145c05161454051086146005280610da05161460051096146205280610da05161456051096146405280610da0516145e051096146605280614620516144c0510861468052806140c0516001096146a05280613d6051610cc05109818183613dc051610ce05109089050806146c05250806142a0516146c051096146e0528060016146e051830309614700528060016146a051096147205280614400516147005109614740528061440051614720510961476052806147405161468051086147805280614100516001096147a05280613e0051610d005109806147c05250806142c0516147c051096147e0528060016147e051830309614800528060016147a051096148205280613e0051610b405109806148405250806142c05161484051096148605280610d4051614860518303096148805280610d40516147a051096148a052806148805161480051086148c05280613e0051610b605109806148e05250806142c0516148e0510961490052806142e0516149005183030961492052806142e0516147a051096149405280614920516148c051086149605280613e0051610b805109806149805250806142c05161498051096149a05280614300516149a0518303096149c05280614300516147a051096149e052806149c0516149605108614a005280613e0051610ba0510980614a205250806142c051614a205109614a40528061432051614a4051830309614a605280614320516147a05109614a805280614a6051614a005108614aa05280613e0051610be0510980614ac05250806142c051614ac05109614ae0528061434051614ae051830309614b005280614340516147a05109614b205280614b0051614aa05108614b405280613e0051610c00510980614b605250806142c051614b605109614b80528061436051614b8051830309614ba05280614360516147a05109614bc05280614ba051614b405108614be05280613e0051610c20510980614c005250806142c051614c005109614c20528061438051614c2051830309614c405280614380516147a05109614c605280614c4051614be05108614c805280614100516138605109614ca05280614100516138805109614cc05280614100516138a05109614ce05280613e00516138c0510980614d005250806142c051614d005109614d2052806143a051614d2051830309614d4052806143a0516147a05109614d6052806143a051614ca05109614d8052806143a051614cc05109614da052806143a051614ce05109614dc05280614d4051614c805108614de05280613e0051610bc0510980614e005250806142c051614e005109614e2052806143c051614e2051830309614e4052806143c0516147a05109614e605280614e4051614de05108614e80528061442051614e805109614ea05280614420516148205109614ec05280614420516148a05109614ee05280614420516149405109614f005280614420516149e05109614f20528061442051614a805109614f40528061442051614b205109614f60528061442051614bc05109614f80528061442051614c605109614fa0528061442051614d605109614fc0528061442051614d805109614fe0528061442051614da05109615000528061442051614dc05109615020528061442051614e6051096150405280614ea05161478051086150605280613c40516001096150805280610e40516001096150a05260016150c05260026150e052615060516151005282600160406150c060606150c060075afa141692506150c051615120526150e05161514052610660516151605261068051615180528260016040615120608061512060065afa14169250610840516151a052610860516151c052614640516151e05282600160406151a060606151a060075afa14169250615120516152005261514051615220526151a051615240526151c051615260528260016040615200608061520060065afa1416925061088051615280526108a0516152a052614660516152c0528260016040615280606061528060075afa14169250615200516152e052615220516153005261528051615320526152a0516153405282600160406152e060806152e060065afa1416925061070051615360526107205161538052614760516153a0528260016040615360606061536060075afa141692506152e0516153c052615300516153e0526153605161540052615380516154205282600160406153c060806153c060065afa1416925061074051615440526107605161546052614ec051615480528260016040615440606061544060075afa141692506153c0516154a0526153e0516154c052615440516154e052615460516155005282600160406154a060806154a060065afa141692507f1701d83c9ad410f3d61886c39b7b8a618f42219a1404264208f838c1672b8e71615520527f0868e69339d5d3bbd747b6e7772a9760a7277c0993a420c2848808b15de0424c61554052614ee051615560528260016040615520606061552060075afa141692506154a051615580526154c0516155a052615520516155c052615540516155e0528260016040615580608061558060065afa141692507f21c6ea7d6dbcd767ffb9d9beeb4f9c2f8243bc65290f2d75a59aea4f65ba8f3d615600527f24d0a0acb031c9a5687da08cdaf96650aae5c60435739bda8bbd574eb962622c61562052614f0051615640528260016040615600606061560060075afa1416925061558051615660526155a05161568052615600516156a052615620516156c0528260016040615660608061566060065afa141692507f289feda4952fe4464c9716d071e291bbecdcd5432356042dc79b76ed38cbbb0d6156e0527f07f3ca14a8801fa413462ad72ea448da5d7cf8c5218534cdc39bb23779b70bb961570052614f20516157205282600160406156e060606156e060075afa14169250615660516157405261568051615760526156e05161578052615700516157a0528260016040615740608061574060065afa141692507f259670bd2f2f6fce3b18100f92a2e59874da3b66a9ddd61e163eb4b071e24a976157c0527f097f07272f7ca89070ad9c06d9a3da1bb6e91d0e69bf7872f44cc5d332291eb56157e052614f40516158005282600160406157c060606157c060075afa14169250615740516158205261576051615840526157c051615860526157e051615880528260016040615820608061582060065afa141692507f26eaa658ef237183330c716cf9d5d978d8618788db2551142e1a9af837ecc8106158a0527f1fa793c6b0f3df4a8a1017daa531a45fdfa8b5da433308f71716565f8b85aa7d6158c052614f60516158e05282600160406158a060606158a060075afa14169250615820516159005261584051615920526158a051615940526158c051615960528260016040615900608061590060065afa141692507f186694283d847cec7609ca9b642f46fc751a61e3ac330841cdf6a938a3439c02615980527f16eb0141342ab5f9ca76fcaef38c222f51dd1afd5d45943e3676ba0c2ceb4f6d6159a052614f80516159c0528260016040615980606061598060075afa14169250615900516159e05261592051615a005261598051615a20526159a051615a405282600160406159e060806159e060065afa141692507f0f76818adad3c635f139fde306f33aba7249952bdbbf72cf477a51f9d84f3ccc615a60527f23f89f9896b1cc39de92659098ea839186ff2e997ffa3413c1a7af4f31abe4ce615a8052614fa051615aa0528260016040615a606060615a6060075afa141692506159e051615ac052615a0051615ae052615a6051615b0052615a8051615b20528260016040615ac06080615ac060065afa1416925061096051615b405261098051615b6052614fc051615b80528260016040615b406060615b4060075afa14169250615ac051615ba052615ae051615bc052615b4051615be052615b6051615c00528260016040615ba06080615ba060065afa141692506109a051615c20526109c051615c4052614fe051615c60528260016040615c206060615c2060075afa14169250615ba051615c8052615bc051615ca052615c2051615cc052615c4051615ce0528260016040615c806080615c8060065afa141692506109e051615d0052610a0051615d205261500051615d40528260016040615d006060615d0060075afa14169250615c8051615d6052615ca051615d8052615d0051615da052615d2051615dc0528260016040615d606080615d6060065afa14169250610a2051615de052610a4051615e005261502051615e20528260016040615de06060615de060075afa14169250615d6051615e4052615d8051615e6052615de051615e8052615e0051615ea0528260016040615e406080615e4060065afa141692506108c051615ec0526108e051615ee05261504051615f00528260016040615ec06060615ec060075afa14169250615e4051615f2052615e6051615f4052615ec051615f6052615ee051615f80528260016040615f206080615f2060065afa14169250610de051615fa052610e0051615fc052615080518103615fe0528260016040615fa06060615fa060075afa14169250615f205161600052615f405161602052615fa05161604052615fc051616060528260016040616000608061600060065afa14169250610e805161608052610ea0516160a0526150a0516160c0528260016040616080606061608060075afa14169250616000516160e052616020516161005261608051616120526160a0516161405282600160406160e060806160e060065afa141692506160e051616160526161005161618052610e80516161a052610ea0516161c052610ec0516161e052610ee05161620052610f005161622052610f205161624052610100616160206162605280616260510661628052806162805161628051096162a05280616280516001096162c0526161e0516162e05261620051616300526162c0516163205282600160406162e060606162e060075afa14169250616160516163405261618051616360526162e05161638052616300516163a0528260016040616340608061634060065afa14169250616220516163c052616240516163e0526162c0516164005282600160406163c060606163c060075afa141692506161a051616420526161c051616440526163c051616460526163e051616480528260016040616420608061642060065afa14169250616340516164a052616360516164c0527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c26164e0527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed616500527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b616520527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa61654052616420516165605261644051616580527f172aa93c41f16e1e04d62ac976a5d945f4be0acab990c6dc19ac4a7cf68bf77b6165a0527f2ae0c8c3a090f7200ff398ee9845bbae8f8c1445ae7b632212775f60a0e216006165c0527f190fa476a5b352809ed41d7a0d7fe12b8f685e3c12a6d83855dba27aaf4696436165e0527f1c0a500618907df9e4273d5181e31088deb1f05132de037cbfe73888f97f77c96166005282600160206164a06101806164a060085afa141692508260016164a051141692508261480757600080fd5b600080f3fea264697066735822122026e891e5d925cf339bec37559839722b8fe67c5fbcb9bf916360d10c4212867164736f6c63430008130033"; diff --git a/scripts/deterministic/DeployScroll.s.sol b/scripts/deterministic/DeployScroll.s.sol index cb60f9d5..6747d626 100644 --- a/scripts/deterministic/DeployScroll.s.sol +++ b/scripts/deterministic/DeployScroll.s.sol @@ -5,6 +5,7 @@ import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IER import {ProxyAdminSetOwner} from "./contracts/ProxyAdminSetOwner.sol"; import {TransparentUpgradeableProxy, ITransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {console} from "forge-std/Script.sol"; import {EmptyContract} from "../../src/misc/EmptyContract.sol"; @@ -16,6 +17,7 @@ import {L1ETHGateway} from "../../src/L1/gateways/L1ETHGateway.sol"; import {L1GatewayRouter} from "../../src/L1/gateways/L1GatewayRouter.sol"; import {L1MessageQueueV1WithGasPriceOracle} from "../../src/L1/rollup/L1MessageQueueV1WithGasPriceOracle.sol"; import {L1MessageQueueV2} from "../../src/L1/rollup/L1MessageQueueV2.sol"; +import {SystemConfig} from "../../src/L1/system-contract/SystemConfig.sol"; import {L1ScrollMessenger} from "../../src/L1/L1ScrollMessenger.sol"; import {L1StandardERC20Gateway} from "../../src/L1/gateways/L1StandardERC20Gateway.sol"; import {L1WETHGateway} from "../../src/L1/gateways/L1WETHGateway.sol"; @@ -23,26 +25,19 @@ import {L2GasPriceOracle} from "../../src/L1/rollup/L2GasPriceOracle.sol"; import {MultipleVersionRollupVerifierSetOwner} from "./contracts/MultipleVersionRollupVerifierSetOwner.sol"; import {ScrollChain} from "../../src/L1/rollup/ScrollChain.sol"; import {ZkEvmVerifierV2} from "../../src/libraries/verifier/ZkEvmVerifierV2.sol"; -import {GasTokenExample} from "../../src/alternative-gas-token/GasTokenExample.sol"; -import {L1ScrollMessengerNonETH} from "../../src/alternative-gas-token/L1ScrollMessengerNonETH.sol"; -import {L1GasTokenGateway} from "../../src/alternative-gas-token/L1GasTokenGateway.sol"; -import {L1WrappedTokenGateway} from "../../src/alternative-gas-token/L1WrappedTokenGateway.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 {L2DogeOsMessenger} from "../../src/dogeos/L2DogeOsMessenger.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 {L2TxFeeVaultWithGasToken} from "../../src/alternative-gas-token/L2TxFeeVaultWithGasToken.sol"; +import {L2SystemConfig} from "../../src/L2/L2SystemConfig.sol"; import {Whitelist} from "../../src/L2/predeploys/Whitelist.sol"; -// import {WrappedEther} from "../../src/L2/predeploys/WrappedEther.sol"; import {WrappedDoge} from "../../src/dogeos/WrappedDoge.sol"; import {ScrollStandardERC20} from "../../src/libraries/token/ScrollStandardERC20.sol"; import {ScrollStandardERC20FactorySetOwner} from "./contracts/ScrollStandardERC20FactorySetOwner.sol"; @@ -53,9 +48,6 @@ import {BasculeMockVerifier} from "../../src/dogeos/BasculeMockVerifier.sol"; import "./Constants.sol"; import "./Configuration.sol"; import "./DeterministicDeployment.sol"; -import {SystemConfig} from "../../src/L1/system-contract/SystemConfig.sol"; -import {ZkEvmVerifierPostEuclid} from "../../src/libraries/verifier/ZkEvmVerifierPostEuclid.sol"; -import {console} from "forge-std/Script.sol"; /// @dev The minimum deployer account balance. uint256 constant MINIMUM_DEPLOYER_BALANCE = 0.1 ether; @@ -99,10 +91,8 @@ contract DeployScroll is DeterministicDeployment { address internal L1_GATEWAY_ROUTER_PROXY_ADDR; address internal L1_MESSAGE_QUEUE_V1_IMPLEMENTATION_ADDR; address internal L1_MESSAGE_QUEUE_V1_PROXY_ADDR; - address internal L1_MESSAGE_QUEUE_V2_IMPLEMENTATION_ADDR; address internal L1_MESSAGE_QUEUE_V2_PROXY_ADDR; - address internal L1_MULTIPLE_VERSION_ROLLUP_VERIFIER_ADDR; address internal L1_PROXY_ADMIN_ADDR; address internal L1_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR; @@ -112,19 +102,15 @@ contract DeployScroll is DeterministicDeployment { 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_WDOGE_ADDR; address internal L1_WETH_GATEWAY_IMPLEMENTATION_ADDR; address internal L1_WETH_GATEWAY_PROXY_ADDR; address internal L1_WHITELIST_ADDR; address internal L1_PLONK_VERIFIER_ADDR; address internal L1_ZKEVM_VERIFIER_V2_ADDR; - address internal L1_GAS_TOKEN_ADDR; - address internal L1_GAS_TOKEN_GATEWAY_IMPLEMENTATION_ADDR; - address internal L1_GAS_TOKEN_GATEWAY_PROXY_ADDR; address internal L1_WRAPPED_TOKEN_GATEWAY_ADDR; - address internal L1_SYSTEM_CONFIG_IMPLEMENTATION_ADDR; - address internal L1_SYSTEM_CONFIG_PROXY_ADDR; + address internal SYSTEM_CONFIG_IMPLEMENTATION_ADDR; + address internal SYSTEM_CONFIG_PROXY_ADDR; // L2 addresses address internal L1_GAS_PRICE_ORACLE_ADDR; @@ -148,7 +134,6 @@ contract DeployScroll is DeterministicDeployment { 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_WDOGE_ADDR; address internal L2_WETH_GATEWAY_IMPLEMENTATION_ADDR; address internal L2_WETH_GATEWAY_PROXY_ADDR; @@ -156,6 +141,8 @@ contract DeployScroll is DeterministicDeployment { address internal L2_MOAT_IMPLEMENTATION_ADDR; address internal L2_MOAT_PROXY_ADDR; address internal L2_BASCULE_MOCK_VERIFIER_ADDR; + address internal L2_SYSTEM_CONFIG_IMPLEMENTATION_ADDR; + address internal L2_SYSTEM_CONFIG_PROXY_ADDR; /************* * Utilities * @@ -195,14 +182,6 @@ contract DeployScroll is DeterministicDeployment { _; } - /// @dev Only execute block if it's requied by alternative gas token mode. - modifier gasToken(bool gasTokenRequire) { - if (ALTERNATIVE_GAS_TOKEN_ENABLED != gasTokenRequire) { - return; - } - _; - } - /*************** * Entry point * ***************/ @@ -213,10 +192,21 @@ contract DeployScroll is DeterministicDeployment { DeterministicDeployment.initialize(mode); + console.log("checkDeployerBalance start"); checkDeployerBalance(); + console.log("checkDeployerBalance end"); + + console.log("deployAllContracts start"); deployAllContracts(); + console.log("deployAllContracts end"); + + console.log("initializeL1Contracts start"); initializeL1Contracts(); + console.log("initializeL1Contracts end"); + + console.log("initializeL2Contracts start"); initializeL2Contracts(); + console.log("initializeL2Contracts end"); } /********************** @@ -263,7 +253,7 @@ contract DeployScroll is DeterministicDeployment { } // check funds for initial deposit (L1, ETH as gas token) - if (broadcastLayer == Layer.L1 && !ALTERNATIVE_GAS_TOKEN_ENABLED) { + if (broadcastLayer == Layer.L1) { // note: L1_SCROLL_MESSENGER_PROXY_ADDR is not known at this point, // so we read it directly from the generated configuration file. address l1MessengerProxyAddr = notnull(contractsCfg.readAddress(".L1_SCROLL_MESSENGER_PROXY_ADDR")); @@ -291,46 +281,6 @@ contract DeployScroll is DeterministicDeployment { ); } } - - // check funds for initial deposit (L1, alternative gas token) - // skip it if L1_GAS_TOKEN is not configured in the config file - address gasTokenAddr = tryGetOverride("L1_GAS_TOKEN"); - - if (broadcastLayer == Layer.L1 && ALTERNATIVE_GAS_TOKEN_ENABLED && gasTokenAddr != address(0)) { - // note: L1_GAS_TOKEN_GATEWAY_PROXY_ADDR is not known at this point, - // so we read it directly from the generated configuration file. - address l1GasTokenGatewayAddr = notnull(contractsCfg.readAddress(".L1_GAS_TOKEN_GATEWAY_PROXY_ADDR")); - - uint256 l1GasTokenGatewayBalance = IERC20Metadata(gasTokenAddr).balanceOf(l1GasTokenGatewayAddr); - - uint256 scale = 10**(18 - IERC20Metadata(gasTokenAddr).decimals()); - uint256 amountToLock = L2_DEPLOYER_INITIAL_BALANCE / scale; - if (L2_DEPLOYER_INITIAL_BALANCE % scale != 0) { - amountToLock += 1; - } - - uint256 amountToSend = 0; - if (l1GasTokenGatewayBalance < amountToLock) { - amountToSend = amountToLock - l1GasTokenGatewayBalance; - } - - uint256 minBalance = amountToSend; - - if (IERC20Metadata(gasTokenAddr).balanceOf(DEPLOYER_ADDR) < minBalance) { - revert( - string( - abi.encodePacked( - "[ERROR] insufficient funds on deployer account for initial deposit (", - vm.toString(DEPLOYER_ADDR), - ") minimum ", - IERC20Metadata(gasTokenAddr).symbol(), - " balance (in min token unit): ", - vm.toString(minBalance) - ) - ) - ); - } - } } function transferOwnership(address addr, address newOwner) private { @@ -348,36 +298,26 @@ contract DeployScroll is DeterministicDeployment { // @notice deployL1Contracts1stPass deploys L1 contracts whose initialization does not depend on any L2 addresses. function deployL1Contracts1stPass() private broadcast(Layer.L1) { - // deployL1Weth(); deployL1Wdoge(); deployL1ProxyAdmin(); deployL1PlaceHolder(); - deployL1SystemConfigProxy(); - deployL1SystemConfig(); deployL1Whitelist(); deployL1ScrollChainProxy(); deployL1ScrollMessengerProxy(); deployL1EnforcedTxGatewayProxy(); - - deployL1MessageQueueProxy(); - deployL1PlonkVerifier(); deployL1ZkEvmVerifier(); deployL1MultipleVersionRollupVerifier(); - deployL1EnforcedTxGatewayImpl(); + deploySystemConfig(); deployL1MessageQueue(); deployL1ScrollChain(); deployL1GatewayRouter(); deployL1ETHGatewayProxy(); deployL1WETHGatewayProxy(); - // deployL1StandardERC20GatewayProxy(); - // deployL1CustomERC20GatewayProxy(); - // deployL1ERC721GatewayProxy(); - // deployL1ERC1155GatewayProxy(); - - // alternative gas token contracts - deployGasToken(); - deployL1GasTokenGatewayProxy(); + deployL1StandardERC20GatewayProxy(); + deployL1CustomERC20GatewayProxy(); + deployL1ERC721GatewayProxy(); + deployL1ERC1155GatewayProxy(); } // @notice deployL2Contracts1stPass deploys L2 contracts whose initialization does not depend on any L1 addresses. @@ -385,7 +325,6 @@ contract DeployScroll is DeterministicDeployment { deployL2MessageQueue(); deployL1GasPriceOracle(); deployL2Whitelist(); - // deployL2Weth(); deployL2Wdoge(); deployTxFeeVault(); deployL2ProxyAdmin(); @@ -394,26 +333,24 @@ contract DeployScroll is DeterministicDeployment { deployL2ETHGatewayProxy(); deployL2WETHGatewayProxy(); deployL2MoatProxy(); - //deployL2StandardERC20GatewayProxy(); - // deployL2CustomERC20GatewayProxy(); - // deployL2ERC721GatewayProxy(); - // deployL2ERC1155GatewayProxy(); - //deployScrollStandardERC20Factory(); + deployL2StandardERC20GatewayProxy(); + deployL2CustomERC20GatewayProxy(); + deployL2ERC721GatewayProxy(); + deployL2ERC1155GatewayProxy(); + deployScrollStandardERC20Factory(); + deployL2SystemConfig(); } // @notice deployL1Contracts2ndPass deploys L1 contracts whose initialization depends on some L2 addresses. function deployL1Contracts2ndPass() private broadcast(Layer.L1) { + deployL1EnforcedTxGateway(); deployL1ScrollMessenger(); - // deployL1StandardERC20Gateway(); + deployL1StandardERC20Gateway(); deployL1ETHGateway(); deployL1WETHGateway(); - // deployL1CustomERC20Gateway(); - // deployL1ERC721Gateway(); - // deployL1ERC1155Gateway(); - - // alternative gas token contracts - deployL1GasTokenGateway(); - //deployL1WrappedTokenGateway(); + deployL1CustomERC20Gateway(); + deployL1ERC721Gateway(); + deployL1ERC1155Gateway(); } // @notice deployL2Contracts2ndPass deploys L2 contracts whose initialization depends on some L1 addresses. @@ -421,37 +358,34 @@ contract DeployScroll is DeterministicDeployment { // upgradable deployL2DogeOsMessenger(); deployL2GatewayRouter(); - // deployL2StandardERC20Gateway(); + deployL2StandardERC20Gateway(); deployL2ETHGateway(); deployL2WETHGateway(); - // deployL2CustomERC20Gateway(); - // deployL2ERC721Gateway(); - // deployL2ERC1155Gateway(); + deployL2CustomERC20Gateway(); + deployL2ERC721Gateway(); + deployL2ERC1155Gateway(); deployL2BasculeMockVerifier(); deployL2Moat(); } // @notice initializeL1Contracts initializes contracts deployed on L1. function initializeL1Contracts() private broadcast(Layer.L1) only(Layer.L1) { - initializeSystemConfig(); initializeScrollChain(); + initializeSystemConfig(); initializeL1MessageQueue(); initializeL1ScrollMessenger(); initializeEnforcedTxGateway(); initializeL1GatewayRouter(); - // initializeL1CustomERC20Gateway(); - // initializeL1ERC1155Gateway(); - // initializeL1ERC721Gateway(); + initializeL1CustomERC20Gateway(); + initializeL1ERC1155Gateway(); + initializeL1ERC721Gateway(); initializeL1ETHGateway(); - // initializeL1StandardERC20Gateway(); + initializeL1StandardERC20Gateway(); initializeL1WETHGateway(); initializeL1Whitelist(); - // alternative gas token contracts - initializeL1GasTokenGateway(); - // lock tokens on L1 to ensure bridge parity, - // we lock ETH in L1ScrollMessenger or GAS_TOKEN in L1GasTokenGateway + // we lock ETH in L1ScrollMessenger // note: this can only be done before transferring ownership lockTokensOnL1(); @@ -465,15 +399,16 @@ contract DeployScroll is DeterministicDeployment { initializeL1GasPriceOracle(); initializeL2DogeOsMessenger(); initializeL2GatewayRouter(); - // initializeL2CustomERC20Gateway(); - // initializeL2ERC1155Gateway(); - // initializeL2ERC721Gateway(); + initializeL2CustomERC20Gateway(); + initializeL2ERC1155Gateway(); + initializeL2ERC721Gateway(); initializeL2ETHGateway(); - //initializeL2StandardERC20Gateway(); + initializeL2StandardERC20Gateway(); initializeL2WETHGateway(); - //initializeScrollStandardERC20Factory(); + initializeScrollStandardERC20Factory(); initializeL2Whitelist(); initializeL2Moat(); + initializeL2SystemConfig(); transferL2ContractOwnership(); } @@ -481,10 +416,6 @@ contract DeployScroll is DeterministicDeployment { * L1: 1st pass deployment * ***************************/ - // function deployL1Weth() private { - // L1_WETH_ADDR = deploy("L1_WETH", type(WrappedEther).creationCode); - // } - function deployL1Wdoge() private { L1_WDOGE_ADDR = deploy("L1_WDOGE", type(WrappedDoge).creationCode); } @@ -548,12 +479,13 @@ contract DeployScroll is DeterministicDeployment { ); } - function deployL1EnforcedTxGatewayImpl() private { - bytes memory constructorArgs = abi.encode(notnull(L1_MESSAGE_QUEUE_V2_PROXY_ADDR), notnull(L1_FEE_VAULT_ADDR)); + function deployL1EnforcedTxGateway() private { + bytes memory args = abi.encode(notnull(L1_MESSAGE_QUEUE_V2_PROXY_ADDR), notnull(L1_FEE_VAULT_ADDR)); + L1_ENFORCED_TX_GATEWAY_IMPLEMENTATION_ADDR = deploy( "L1_ENFORCED_TX_GATEWAY_IMPLEMENTATION", type(EnforcedTxGateway).creationCode, - constructorArgs + args ); upgrade(L1_PROXY_ADMIN_ADDR, L1_ENFORCED_TX_GATEWAY_PROXY_ADDR, L1_ENFORCED_TX_GATEWAY_IMPLEMENTATION_ADDR); @@ -564,27 +496,20 @@ contract DeployScroll is DeterministicDeployment { } function deployL1ZkEvmVerifier() private { - bytes memory constructorArgs = abi.encode( - notnull(L1_PLONK_VERIFIER_ADDR), - VERIFIER_DIGEST_1, - VERIFIER_DIGEST_2 - ); - L1_ZKEVM_VERIFIER_V2_ADDR = deploy( - "L1_ZKEVM_VERIFIER_V2", - type(ZkEvmVerifierPostEuclid).creationCode, - constructorArgs - ); + bytes memory args = abi.encode(notnull(L1_PLONK_VERIFIER_ADDR), VERIFIER_DIGEST); + L1_ZKEVM_VERIFIER_V2_ADDR = deploy("L1_ZKEVM_VERIFIER_V2", type(ZkEvmVerifierV2).creationCode, args); } function deployL1MultipleVersionRollupVerifier() private { uint256[] memory _versions = new uint256[](1); address[] memory _verifiers = new address[](1); - // register V4 verifier: DarwinV2 upgrade, plonk verifier v0.13.1 - // version 6 comes from 'scripts/foundry/DeployL1BridgeContracts.s.sol:deployMultipleVersionRollupVerifier' - _versions[0] = 6; + // register V8 verifier: feynman upgrade + _versions[0] = 8; _verifiers[0] = notnull(L1_ZKEVM_VERIFIER_V2_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, @@ -592,28 +517,25 @@ contract DeployScroll is DeterministicDeployment { ); } - function deployL1MessageQueueProxy() private { + function deploySystemConfig() private { + SYSTEM_CONFIG_IMPLEMENTATION_ADDR = deploy("L1_SYSTEM_CONFIG_IMPLEMENTATION", type(SystemConfig).creationCode); + bytes memory args = abi.encode( - notnull(L1_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR), + notnull(SYSTEM_CONFIG_IMPLEMENTATION_ADDR), notnull(L1_PROXY_ADMIN_ADDR), new bytes(0) ); - L1_MESSAGE_QUEUE_V1_PROXY_ADDR = deploy( - "L1_MESSAGE_QUEUE_V1_PROXY", - type(TransparentUpgradeableProxy).creationCode, - args - ); - - L1_MESSAGE_QUEUE_V2_PROXY_ADDR = deploy( - "L1_MESSAGE_QUEUE_V2_PROXY", + SYSTEM_CONFIG_PROXY_ADDR = deploy( + "L1_SYSTEM_CONFIG_PROXY", type(TransparentUpgradeableProxy).creationCode, args ); } function deployL1MessageQueue() private { - bytes memory args_v1 = abi.encode( + // deploy L1MessageQueueV1 + bytes memory args = abi.encode( notnull(L1_SCROLL_MESSENGER_PROXY_ADDR), notnull(L1_SCROLL_CHAIN_PROXY_ADDR), notnull(L1_ENFORCED_TX_GATEWAY_PROXY_ADDR) @@ -622,27 +544,47 @@ contract DeployScroll is DeterministicDeployment { L1_MESSAGE_QUEUE_V1_IMPLEMENTATION_ADDR = deploy( "L1_MESSAGE_QUEUE_V1_IMPLEMENTATION", type(L1MessageQueueV1WithGasPriceOracle).creationCode, - args_v1 + args ); - upgrade(L1_PROXY_ADMIN_ADDR, L1_MESSAGE_QUEUE_V1_PROXY_ADDR, L1_MESSAGE_QUEUE_V1_IMPLEMENTATION_ADDR); + bytes memory args2 = abi.encode( + notnull(L1_MESSAGE_QUEUE_V1_IMPLEMENTATION_ADDR), + notnull(L1_PROXY_ADMIN_ADDR), + new bytes(0) + ); - ///// - bytes memory args_v2 = abi.encode( + L1_MESSAGE_QUEUE_V1_PROXY_ADDR = deploy( + "L1_MESSAGE_QUEUE_V1_PROXY", + type(TransparentUpgradeableProxy).creationCode, + args2 + ); + + // deploy L1MessageQueueV2 + bytes memory args3 = abi.encode( notnull(L1_SCROLL_MESSENGER_PROXY_ADDR), notnull(L1_SCROLL_CHAIN_PROXY_ADDR), notnull(L1_ENFORCED_TX_GATEWAY_PROXY_ADDR), notnull(L1_MESSAGE_QUEUE_V1_PROXY_ADDR), - notnull(L1_SYSTEM_CONFIG_PROXY_ADDR) + notnull(SYSTEM_CONFIG_PROXY_ADDR) ); L1_MESSAGE_QUEUE_V2_IMPLEMENTATION_ADDR = deploy( "L1_MESSAGE_QUEUE_V2_IMPLEMENTATION", type(L1MessageQueueV2).creationCode, - args_v2 + args3 ); - upgrade(L1_PROXY_ADMIN_ADDR, L1_MESSAGE_QUEUE_V2_PROXY_ADDR, L1_MESSAGE_QUEUE_V2_IMPLEMENTATION_ADDR); + bytes memory args4 = abi.encode( + notnull(L1_MESSAGE_QUEUE_V2_IMPLEMENTATION_ADDR), + notnull(L1_PROXY_ADMIN_ADDR), + new bytes(0) + ); + + L1_MESSAGE_QUEUE_V2_PROXY_ADDR = deploy( + "L1_MESSAGE_QUEUE_V2_PROXY", + type(TransparentUpgradeableProxy).creationCode, + args4 + ); } function deployL1ScrollChain() private { @@ -651,7 +593,7 @@ contract DeployScroll is DeterministicDeployment { notnull(L1_MESSAGE_QUEUE_V1_PROXY_ADDR), notnull(L1_MESSAGE_QUEUE_V2_PROXY_ADDR), notnull(L1_MULTIPLE_VERSION_ROLLUP_VERIFIER_ADDR), - notnull(L1_SYSTEM_CONFIG_PROXY_ADDR) + notnull(SYSTEM_CONFIG_PROXY_ADDR) ); bytes memory creationCode = type(ScrollChain).creationCode; @@ -684,7 +626,7 @@ contract DeployScroll is DeterministicDeployment { ); } - function deployL1ETHGatewayProxy() private gasToken(false) { + function deployL1ETHGatewayProxy() private { bytes memory args = abi.encode( notnull(L1_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR), notnull(L1_PROXY_ADMIN_ADDR), @@ -698,7 +640,7 @@ contract DeployScroll is DeterministicDeployment { ); } - function deployL1WETHGatewayProxy() private gasToken(false) { + function deployL1WETHGatewayProxy() private { bytes memory args = abi.encode( notnull(L1_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR), notnull(L1_PROXY_ADMIN_ADDR), @@ -768,40 +710,6 @@ contract DeployScroll is DeterministicDeployment { ); } - function deployGasToken() private gasToken(true) { - uint8 decimal = 18; - string memory key = ".gas-token.EXAMPLE_GAS_TOKEN_DECIMAL"; - if (vm.keyExistsToml(cfg, key)) { - decimal = uint8(cfg.readUint(key)); - } - - bytes memory args = abi.encode( - "ScrollGasToken", // _name - "GasToken", // _symbol - decimal, // _decimals - DEPLOYER_ADDR, // _recipient - 10**28 // _amount - ); - - // deploy gas token contract on L1, - // note: if an override address is configured, then we will use that instead - L1_GAS_TOKEN_ADDR = deploy("L1_GAS_TOKEN", type(GasTokenExample).creationCode, args); - } - - function deployL1GasTokenGatewayProxy() private gasToken(true) { - bytes memory args = abi.encode( - notnull(L1_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR), - notnull(L1_PROXY_ADMIN_ADDR), - new bytes(0) - ); - - L1_GAS_TOKEN_GATEWAY_PROXY_ADDR = deploy( - "L1_GAS_TOKEN_GATEWAY_PROXY", - type(TransparentUpgradeableProxy).creationCode, - args - ); - } - /*************************** * L2: 1st pass deployment * ***************************/ @@ -816,56 +724,18 @@ contract DeployScroll is DeterministicDeployment { L1_GAS_PRICE_ORACLE_ADDR = deploy("L1_GAS_PRICE_ORACLE", type(L1GasPriceOracle).creationCode, args); } - function deployL1SystemConfigProxy() private { - bytes memory args = abi.encode( - notnull(L1_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR), - notnull(L1_PROXY_ADMIN_ADDR), - new bytes(0) - ); - - L1_SYSTEM_CONFIG_PROXY_ADDR = deploy( - "L1_SYSTEM_CONFIG_PROXY", - type(TransparentUpgradeableProxy).creationCode, - args - ); - } - - function deployL1SystemConfig() private { - L1_SYSTEM_CONFIG_IMPLEMENTATION_ADDR = deploy( - "L1_SYSTEM_CONFIG_IMPLEMENTATION", - type(SystemConfig).creationCode, - new bytes(0) - ); - - upgrade(L1_PROXY_ADMIN_ADDR, L1_SYSTEM_CONFIG_PROXY_ADDR, L1_SYSTEM_CONFIG_IMPLEMENTATION_ADDR); - } - 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 deployL2Wdoge() private { L2_WDOGE_ADDR = deploy("L2_WDOGE", type(WrappedDoge).creationCode); } function deployTxFeeVault() private { - if (!ALTERNATIVE_GAS_TOKEN_ENABLED) { - 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); - } else { - bytes memory args = abi.encode( - L2_ETH_GATEWAY_PROXY_ADDR, - DEPLOYER_ADDR, - L1_FEE_VAULT_ADDR, - FEE_VAULT_MIN_WITHDRAW_AMOUNT - ); - L2_TX_FEE_VAULT_ADDR = deploy("L2_TX_FEE_VAULT", type(L2TxFeeVaultWithGasToken).creationCode, args); - } + 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 { @@ -922,7 +792,7 @@ contract DeployScroll is DeterministicDeployment { ); } - function deployL2WETHGatewayProxy() private gasToken(false) { + function deployL2WETHGatewayProxy() private { bytes memory args = abi.encode( notnull(L2_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR), notnull(L2_PROXY_ADMIN_ADDR), @@ -1004,39 +874,24 @@ contract DeployScroll is DeterministicDeployment { ***************************/ function deployL1ScrollMessenger() private { - if (ALTERNATIVE_GAS_TOKEN_ENABLED) { - bytes memory args = abi.encode( - notnull(L1_GAS_TOKEN_GATEWAY_PROXY_ADDR), - notnull(L2_DOGEOS_MESSENGER_PROXY_ADDR), - notnull(L1_SCROLL_CHAIN_PROXY_ADDR), - notnull(L1_MESSAGE_QUEUE_V1_PROXY_ADDR), - notnull(L1_MESSAGE_QUEUE_V2_PROXY_ADDR) - ); - - L1_SCROLL_MESSENGER_IMPLEMENTATION_ADDR = deploy( - "L1_SCROLL_MESSENGER_IMPLEMENTATION", - type(L1ScrollMessengerNonETH).creationCode, - args - ); - } else { - bytes memory args = abi.encode( - notnull(L2_DOGEOS_MESSENGER_PROXY_ADDR), - notnull(L1_SCROLL_CHAIN_PROXY_ADDR), - notnull(L1_MESSAGE_QUEUE_V1_PROXY_ADDR), - notnull(L1_MESSAGE_QUEUE_V2_PROXY_ADDR) - ); + bytes memory args = abi.encode( + notnull(L2_DOGEOS_MESSENGER_PROXY_ADDR), + notnull(L1_SCROLL_CHAIN_PROXY_ADDR), + notnull(L1_MESSAGE_QUEUE_V1_PROXY_ADDR), + notnull(L1_MESSAGE_QUEUE_V2_PROXY_ADDR), + notnull(L1_ENFORCED_TX_GATEWAY_PROXY_ADDR) + ); - L1_SCROLL_MESSENGER_IMPLEMENTATION_ADDR = deploy( - "L1_SCROLL_MESSENGER_IMPLEMENTATION", - type(L1ScrollMessenger).creationCode, - args - ); - } + 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 gasToken(false) { + function deployL1ETHGateway() private { bytes memory args = abi.encode( notnull(L2_ETH_GATEWAY_PROXY_ADDR), notnull(L1_GATEWAY_ROUTER_PROXY_ADDR), @@ -1052,10 +907,8 @@ contract DeployScroll is DeterministicDeployment { upgrade(L1_PROXY_ADMIN_ADDR, L1_ETH_GATEWAY_PROXY_ADDR, L1_ETH_GATEWAY_IMPLEMENTATION_ADDR); } - function deployL1WETHGateway() private gasToken(false) { + function deployL1WETHGateway() private { bytes memory args = abi.encode( - // notnull(L1_WETH_ADDR), - // notnull(L2_WETH_ADDR), notnull(L1_WDOGE_ADDR), notnull(L2_WDOGE_ADDR), notnull(L2_WETH_GATEWAY_PROXY_ADDR), @@ -1134,36 +987,6 @@ contract DeployScroll is DeterministicDeployment { upgrade(L1_PROXY_ADMIN_ADDR, L1_ERC1155_GATEWAY_PROXY_ADDR, L1_ERC1155_GATEWAY_IMPLEMENTATION_ADDR); } - // Only run this block during simulation (for predicting the contract address) - // and during deployment on L1. Running it on L2 would fail, as this contract - // calls `gasToken.decimals()` in its constructor. - function deployL1GasTokenGateway() private gasToken(true) skip(Layer.L2) { - bytes memory args = abi.encode( - notnull(L1_GAS_TOKEN_ADDR), - notnull(L2_ETH_GATEWAY_PROXY_ADDR), - notnull(L1_GATEWAY_ROUTER_PROXY_ADDR), - notnull(L1_SCROLL_MESSENGER_PROXY_ADDR) - ); - - L1_GAS_TOKEN_GATEWAY_IMPLEMENTATION_ADDR = deploy( - "L1_GAS_TOKEN_GATEWAY_IMPLEMENTATION", - type(L1GasTokenGateway).creationCode, - args - ); - - upgrade(L1_PROXY_ADMIN_ADDR, L1_GAS_TOKEN_GATEWAY_PROXY_ADDR, L1_GAS_TOKEN_GATEWAY_IMPLEMENTATION_ADDR); - } - - function deployL1WrappedTokenGateway() private gasToken(true) { - bytes memory args = abi.encode(notnull(L1_WDOGE_ADDR), notnull(L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR)); - - L1_WRAPPED_TOKEN_GATEWAY_ADDR = deploy( - "L1_WRAPPED_TOKEN_GATEWAY", - type(L1WrappedTokenGateway).creationCode, - args - ); - } - /*************************** * L2: 2nd pass deployment * ***************************/ @@ -1202,7 +1025,6 @@ contract DeployScroll is DeterministicDeployment { type(TransparentUpgradeableProxy).creationCode, args ); - upgrade(L2_PROXY_ADMIN_ADDR, L2_GATEWAY_ROUTER_PROXY_ADDR, L2_GATEWAY_ROUTER_IMPLEMENTATION_ADDR); } function deployL2StandardERC20Gateway() private { @@ -1227,12 +1049,7 @@ contract DeployScroll is DeterministicDeployment { } function deployL2ETHGateway() private { - address COUNTERPART; - if (ALTERNATIVE_GAS_TOKEN_ENABLED) { - COUNTERPART = L1_GAS_TOKEN_GATEWAY_PROXY_ADDR; - } else { - COUNTERPART = L1_ETH_GATEWAY_PROXY_ADDR; - } + address COUNTERPART = L1_ETH_GATEWAY_PROXY_ADDR; bytes memory args = abi.encode( notnull(COUNTERPART), notnull(L2_GATEWAY_ROUTER_PROXY_ADDR), @@ -1248,10 +1065,8 @@ contract DeployScroll is DeterministicDeployment { upgrade(L2_PROXY_ADMIN_ADDR, L2_ETH_GATEWAY_PROXY_ADDR, L2_ETH_GATEWAY_IMPLEMENTATION_ADDR); } - function deployL2WETHGateway() private gasToken(false) { + function deployL2WETHGateway() private { bytes memory args = abi.encode( - // notnull(L2_WETH_ADDR), - // notnull(L1_WETH_ADDR), notnull(L2_WDOGE_ADDR), notnull(L1_WDOGE_ADDR), notnull(L1_WETH_GATEWAY_PROXY_ADDR), @@ -1319,6 +1134,27 @@ contract DeployScroll is DeterministicDeployment { L2_BASCULE_MOCK_VERIFIER_ADDR = deploy("L2_BASCULE_MOCK_VERIFIER", type(BasculeMockVerifier).creationCode); } + function deployL2SystemConfig() private { + L2_SYSTEM_CONFIG_IMPLEMENTATION_ADDR = deploy( + "L2_SYSTEM_CONFIG_IMPLEMENTATION", + type(L2SystemConfig).creationCode + ); + + bytes memory args = abi.encode( + notnull(L2_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR), + notnull(L2_PROXY_ADMIN_ADDR), + new bytes(0) + ); + + L2_SYSTEM_CONFIG_PROXY_ADDR = deploy( + "L2_SYSTEM_CONFIG_PROXY", + type(TransparentUpgradeableProxy).creationCode, + args + ); + + upgrade(L2_PROXY_ADMIN_ADDR, L2_SYSTEM_CONFIG_PROXY_ADDR, L2_SYSTEM_CONFIG_IMPLEMENTATION_ADDR); + } + /********************** * L1: initialization * **********************/ @@ -1326,7 +1162,7 @@ contract DeployScroll is DeterministicDeployment { function initializeScrollChain() private { if (getInitializeCount(L1_SCROLL_CHAIN_PROXY_ADDR) == 0) { ScrollChain(L1_SCROLL_CHAIN_PROXY_ADDR).initialize( - notnull(L1_MESSAGE_QUEUE_V1_PROXY_ADDR), + notnull(L1_MESSAGE_QUEUE_V2_PROXY_ADDR), notnull(L1_MULTIPLE_VERSION_ROLLUP_VERIFIER_ADDR), MAX_TX_IN_CHUNK ); @@ -1341,37 +1177,33 @@ contract DeployScroll is DeterministicDeployment { } } - function initializeL1MessageQueue() private { - if (getInitializeCount(L1_MESSAGE_QUEUE_V1_PROXY_ADDR) == 0) { - L1MessageQueueV1WithGasPriceOracle(L1_MESSAGE_QUEUE_V1_PROXY_ADDR).initialize( - notnull(L1_SCROLL_MESSENGER_PROXY_ADDR), - notnull(L1_SCROLL_CHAIN_PROXY_ADDR), - notnull(L1_ENFORCED_TX_GATEWAY_PROXY_ADDR), - // note: this should be the address of L2GasPriceOracle, - // but since we are using L1MessageQueueV1WithGasPriceOracle, so we set an all zero address here - address(0), - MAX_L1_MESSAGE_GAS_LIMIT - ); - } - - // note: since we are using L1MessageQueueV1WithGasPriceOracle, - // and we don't have a L2GasPriceOracle deploy, so we skip the initializeV2. - // instead, we updateWhitelistChecker - // if (getInitializeCount(L1_MESSAGE_QUEUE_V1_PROXY_ADDR) < 2) { - // L1MessageQueueV1WithGasPriceOracle(L1_MESSAGE_QUEUE_V1_PROXY_ADDR).initializeV2(); - // } - if ( - L1MessageQueueV1WithGasPriceOracle(L1_MESSAGE_QUEUE_V1_PROXY_ADDR).whitelistChecker() != L1_WHITELIST_ADDR - ) { - L1MessageQueueV1WithGasPriceOracle(L1_MESSAGE_QUEUE_V1_PROXY_ADDR).updateWhitelistChecker( - notnull(L1_WHITELIST_ADDR) + function initializeSystemConfig() private { + address owner = L1_PROXY_ADMIN_ADDR; + address signer = L2GETH_SIGNER_ADDRESS; + SystemConfig.MessageQueueParameters memory messageQueueParameters = SystemConfig.MessageQueueParameters({ + maxGasLimit: uint32(MAX_L1_MESSAGE_GAS_LIMIT), + baseFeeOverhead: 1000000000, + baseFeeScalar: 1000000000 + }); + SystemConfig.EnforcedBatchParameters memory enforcedBatchParameters = SystemConfig.EnforcedBatchParameters({ + maxDelayEnterEnforcedMode: uint24(FINALIZE_BATCH_DEADLINE_SEC), + maxDelayMessageQueue: uint24(RELAY_MESSAGE_DEADLINE_SEC) + }); + + if (getInitializeCount(SYSTEM_CONFIG_PROXY_ADDR) == 0) { + SystemConfig(SYSTEM_CONFIG_PROXY_ADDR).initialize( + owner, + signer, + messageQueueParameters, + enforcedBatchParameters ); } + } - if (getInitializeCount(L1_MESSAGE_QUEUE_V1_PROXY_ADDR) < 3) { - L1MessageQueueV1WithGasPriceOracle(L1_MESSAGE_QUEUE_V1_PROXY_ADDR).initializeV3(); + function initializeL1MessageQueue() private { + if (getInitializeCount(L1_MESSAGE_QUEUE_V2_PROXY_ADDR) == 0) { + L1MessageQueueV2(L1_MESSAGE_QUEUE_V2_PROXY_ADDR).initialize(); } - L1MessageQueueV2(L1_MESSAGE_QUEUE_V2_PROXY_ADDR).initialize(); } function initializeL1ScrollMessenger() private { @@ -1396,18 +1228,11 @@ contract DeployScroll is DeterministicDeployment { } function initializeL1GatewayRouter() private { - address L2_ETH_GATEWAY_COUNTERPART; - - if (ALTERNATIVE_GAS_TOKEN_ENABLED) { - L2_ETH_GATEWAY_COUNTERPART = L1_GAS_TOKEN_GATEWAY_PROXY_ADDR; - } else { - L2_ETH_GATEWAY_COUNTERPART = L1_ETH_GATEWAY_PROXY_ADDR; - } - + address L2_ETH_GATEWAY_COUNTERPART = L1_ETH_GATEWAY_PROXY_ADDR; if (getInitializeCount(L1_GATEWAY_ROUTER_PROXY_ADDR) == 0) { L1GatewayRouter(L1_GATEWAY_ROUTER_PROXY_ADDR).initialize( notnull(L2_ETH_GATEWAY_COUNTERPART), - L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR + notnull(L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR) ); } } @@ -1440,7 +1265,7 @@ contract DeployScroll is DeterministicDeployment { } } - function initializeL1ETHGateway() private gasToken(false) { + function initializeL1ETHGateway() private { if (getInitializeCount(L1_ETH_GATEWAY_PROXY_ADDR) == 0) { L1ETHGateway(L1_ETH_GATEWAY_PROXY_ADDR).initialize( notnull(L2_ETH_GATEWAY_PROXY_ADDR), @@ -1462,7 +1287,7 @@ contract DeployScroll is DeterministicDeployment { } } - function initializeL1WETHGateway() private gasToken(false) { + function initializeL1WETHGateway() private { if (getInitializeCount(L1_WETH_GATEWAY_PROXY_ADDR) == 0) { L1WETHGateway(payable(L1_WETH_GATEWAY_PROXY_ADDR)).initialize( notnull(L2_WETH_GATEWAY_PROXY_ADDR), @@ -1491,62 +1316,32 @@ contract DeployScroll is DeterministicDeployment { } } - function initializeL1GasTokenGateway() private gasToken(true) { - if (getInitializeCount(L1_GAS_TOKEN_GATEWAY_PROXY_ADDR) == 0) { - L1GasTokenGateway(payable(L1_GAS_TOKEN_GATEWAY_PROXY_ADDR)).initialize(); - } - } - function lockTokensOnL1() private { - if (!ALTERNATIVE_GAS_TOKEN_ENABLED) { - uint256 l1MessengerBalance = address(L1_SCROLL_MESSENGER_PROXY_ADDR).balance; - uint256 amountToLock = L2_DEPLOYER_INITIAL_BALANCE; + uint256 l1MessengerBalance = address(L1_SCROLL_MESSENGER_PROXY_ADDR).balance; + uint256 amountToLock = L2_DEPLOYER_INITIAL_BALANCE; - if (l1MessengerBalance < amountToLock) { - uint256 amountToSend = amountToLock - l1MessengerBalance; - (bool sent, bytes memory data) = payable(L1_SCROLL_MESSENGER_PROXY_ADDR).call{value: amountToSend}(""); - require(sent, "[ERROR] failed to lock tokens on layer 1"); - } - } else { - uint256 l1GasTokenGatewayBalance = IERC20Metadata(L1_GAS_TOKEN_ADDR).balanceOf( - L1_GAS_TOKEN_GATEWAY_PROXY_ADDR - ); - - uint256 scale = 10**(18 - IERC20Metadata(L1_GAS_TOKEN_ADDR).decimals()); - uint256 amountToLock = L2_DEPLOYER_INITIAL_BALANCE / scale; - if (L2_DEPLOYER_INITIAL_BALANCE % scale != 0) { - amountToLock += 1; - } - - if (l1GasTokenGatewayBalance < amountToLock) { - uint256 amountTosend = amountToLock - l1GasTokenGatewayBalance; - IERC20Metadata(L1_GAS_TOKEN_ADDR).transfer(L1_GAS_TOKEN_GATEWAY_PROXY_ADDR, amountTosend); - } + if (l1MessengerBalance < amountToLock) { + uint256 amountToSend = amountToLock - l1MessengerBalance; + (bool sent, bytes memory data) = payable(L1_SCROLL_MESSENGER_PROXY_ADDR).call{value: amountToSend}(""); + require(sent, "[ERROR] failed to lock tokens on layer 1"); } } function transferL1ContractOwnership() private { transferOwnership(L1_ENFORCED_TX_GATEWAY_PROXY_ADDR, OWNER_ADDR); - transferOwnership(L1_SYSTEM_CONFIG_PROXY_ADDR, OWNER_ADDR); - // transferOwnership(L1_CUSTOM_ERC20_GATEWAY_PROXY_ADDR, OWNER_ADDR); - // transferOwnership(L1_ERC1155_GATEWAY_PROXY_ADDR, OWNER_ADDR); - // transferOwnership(L1_ERC721_GATEWAY_PROXY_ADDR, OWNER_ADDR); + transferOwnership(L1_CUSTOM_ERC20_GATEWAY_PROXY_ADDR, OWNER_ADDR); + transferOwnership(L1_ERC1155_GATEWAY_PROXY_ADDR, OWNER_ADDR); + transferOwnership(L1_ERC721_GATEWAY_PROXY_ADDR, OWNER_ADDR); transferOwnership(L1_GATEWAY_ROUTER_PROXY_ADDR, OWNER_ADDR); - transferOwnership(L1_MESSAGE_QUEUE_V1_PROXY_ADDR, OWNER_ADDR); transferOwnership(L1_MESSAGE_QUEUE_V2_PROXY_ADDR, OWNER_ADDR); transferOwnership(L1_SCROLL_MESSENGER_PROXY_ADDR, OWNER_ADDR); - // transferOwnership(L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR, OWNER_ADDR); + transferOwnership(L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR, OWNER_ADDR); transferOwnership(L1_MULTIPLE_VERSION_ROLLUP_VERIFIER_ADDR, OWNER_ADDR); transferOwnership(L1_PROXY_ADMIN_ADDR, OWNER_ADDR); transferOwnership(L1_SCROLL_CHAIN_PROXY_ADDR, OWNER_ADDR); transferOwnership(L1_WHITELIST_ADDR, OWNER_ADDR); - - if (!ALTERNATIVE_GAS_TOKEN_ENABLED) { - transferOwnership(L1_ETH_GATEWAY_PROXY_ADDR, OWNER_ADDR); - transferOwnership(L1_WETH_GATEWAY_PROXY_ADDR, OWNER_ADDR); - } else { - transferOwnership(L1_GAS_TOKEN_GATEWAY_PROXY_ADDR, OWNER_ADDR); - } + transferOwnership(L1_ETH_GATEWAY_PROXY_ADDR, OWNER_ADDR); + transferOwnership(L1_WETH_GATEWAY_PROXY_ADDR, OWNER_ADDR); } /********************** @@ -1560,10 +1355,7 @@ contract DeployScroll is DeterministicDeployment { } function initializeL2TxFeeVault() private { - if ( - !ALTERNATIVE_GAS_TOKEN_ENABLED && - L2TxFeeVault(payable(L2_TX_FEE_VAULT_ADDR)).messenger() != notnull(L2_DOGEOS_MESSENGER_PROXY_ADDR) - ) { + if (L2TxFeeVault(payable(L2_TX_FEE_VAULT_ADDR)).messenger() != notnull(L2_DOGEOS_MESSENGER_PROXY_ADDR)) { L2TxFeeVault(payable(L2_TX_FEE_VAULT_ADDR)).updateMessenger(L2_DOGEOS_MESSENGER_PROXY_ADDR); } } @@ -1586,7 +1378,7 @@ contract DeployScroll is DeterministicDeployment { if (getInitializeCount(L2_GATEWAY_ROUTER_PROXY_ADDR) == 0) { L2GatewayRouter(L2_GATEWAY_ROUTER_PROXY_ADDR).initialize( notnull(L2_ETH_GATEWAY_PROXY_ADDR), - L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR + notnull(L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR) ); } } @@ -1620,12 +1412,7 @@ contract DeployScroll is DeterministicDeployment { } function initializeL2ETHGateway() private { - address COUNTERPART; - if (ALTERNATIVE_GAS_TOKEN_ENABLED) { - COUNTERPART = L1_GAS_TOKEN_GATEWAY_PROXY_ADDR; - } else { - COUNTERPART = L1_ETH_GATEWAY_PROXY_ADDR; - } + address COUNTERPART = L1_ETH_GATEWAY_PROXY_ADDR; if (getInitializeCount(L2_ETH_GATEWAY_PROXY_ADDR) == 0) { L2ETHGateway(L2_ETH_GATEWAY_PROXY_ADDR).initialize( notnull(COUNTERPART), @@ -1646,7 +1433,7 @@ contract DeployScroll is DeterministicDeployment { } } - function initializeL2WETHGateway() private gasToken(false) { + function initializeL2WETHGateway() private { if (getInitializeCount(L2_WETH_GATEWAY_PROXY_ADDR) == 0) { L2WETHGateway(payable(L2_WETH_GATEWAY_PROXY_ADDR)).initialize( notnull(L1_WETH_GATEWAY_PROXY_ADDR), @@ -1684,57 +1471,48 @@ contract DeployScroll is DeterministicDeployment { function initializeL2Moat() private { Moat moat = Moat(L2_MOAT_PROXY_ADDR); - //must initialize first, the following calls will revert if owner is not current caller - moat.initialize(DEPLOYER_ADDR); - moat.updateMessenger(L2_DOGEOS_MESSENGER_PROXY_ADDR); - moat.setFee(0.1 ether); - moat.setMinWithdrawal(1 ether); - if (L2_BRIDGE_FEE_RECIPIENT_ADDR != address(0)) { - moat.setFeeRecipient(L2_BRIDGE_FEE_RECIPIENT_ADDR); - } else { - moat.setFeeRecipient(L2_TX_FEE_VAULT_ADDR); + + if (moat.owner() == address(0)) { + //must initialize first, the following calls will revert if owner is not current caller + moat.initialize(DEPLOYER_ADDR); + + moat.updateMessenger(L2_DOGEOS_MESSENGER_PROXY_ADDR); + moat.setFee(0.1 ether); + moat.setMinWithdrawal(1 ether); + if (L2_BRIDGE_FEE_RECIPIENT_ADDR != address(0)) { + moat.setFeeRecipient(L2_BRIDGE_FEE_RECIPIENT_ADDR); + } else { + moat.setFeeRecipient(L2_TX_FEE_VAULT_ADDR); + } + moat.setBascule(L2_BASCULE_MOCK_VERIFIER_ADDR); + } + } + + function initializeL2SystemConfig() private { + if (getInitializeCount(L2_SYSTEM_CONFIG_PROXY_ADDR) == 0) { + L2SystemConfig(L2_SYSTEM_CONFIG_PROXY_ADDR).initialize(DEPLOYER_ADDR); } - moat.setBascule(L2_BASCULE_MOCK_VERIFIER_ADDR); } function transferL2ContractOwnership() private { transferOwnership(L1_GAS_PRICE_ORACLE_ADDR, OWNER_ADDR); - // transferOwnership(L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR, OWNER_ADDR); - // transferOwnership(L2_ERC1155_GATEWAY_PROXY_ADDR, OWNER_ADDR); - // transferOwnership(L2_ERC721_GATEWAY_PROXY_ADDR, OWNER_ADDR); + transferOwnership(L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR, OWNER_ADDR); + transferOwnership(L2_ERC1155_GATEWAY_PROXY_ADDR, OWNER_ADDR); + transferOwnership(L2_ERC721_GATEWAY_PROXY_ADDR, OWNER_ADDR); transferOwnership(L2_ETH_GATEWAY_PROXY_ADDR, OWNER_ADDR); transferOwnership(L2_GATEWAY_ROUTER_PROXY_ADDR, OWNER_ADDR); transferOwnership(L2_MESSAGE_QUEUE_ADDR, OWNER_ADDR); transferOwnership(L2_DOGEOS_MESSENGER_PROXY_ADDR, OWNER_ADDR); - // transferOwnership(L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR, OWNER_ADDR); + transferOwnership(L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR, OWNER_ADDR); transferOwnership(L2_TX_FEE_VAULT_ADDR, OWNER_ADDR); transferOwnership(L2_PROXY_ADMIN_ADDR, OWNER_ADDR); transferOwnership(L2_WHITELIST_ADDR, OWNER_ADDR); + transferOwnership(L2_SYSTEM_CONFIG_PROXY_ADDR, OWNER_ADDR); - if (!ALTERNATIVE_GAS_TOKEN_ENABLED) { - transferOwnership(L2_WETH_GATEWAY_PROXY_ADDR, OWNER_ADDR); - } + transferOwnership(L2_WETH_GATEWAY_PROXY_ADDR, OWNER_ADDR); //TODO: Maybe Moat should inherit from OwnableUpgradeable like other contracts Moat moat = Moat(L2_MOAT_PROXY_ADDR); moat.transferOwnership(OWNER_ADDR); } - - function initializeSystemConfig() private { - if (getInitializeCount(L1_SYSTEM_CONFIG_PROXY_ADDR) == 0) { - SystemConfig(L1_SYSTEM_CONFIG_PROXY_ADDR).initialize( - vm.addr(DEPLOYER_PRIVATE_KEY), - L2GETH_SIGNER_ADDRESS, - SystemConfig.MessageQueueParameters({ - maxGasLimit: uint32(MAX_L1_MESSAGE_GAS_LIMIT), - baseFeeOverhead: 1000000000, - baseFeeScalar: 1000000000 - }), - SystemConfig.EnforcedBatchParameters({ - maxDelayEnterEnforcedMode: uint24(FINALIZE_BATCH_DEADLINE_SEC), - maxDelayMessageQueue: uint24(RELAY_MESSAGE_DEADLINE_SEC) - }) - ); - } - } } diff --git a/scripts/deterministic/GenerateConfigs.s.sol b/scripts/deterministic/GenerateConfigs.s.sol index 900b0962..f98ea489 100644 --- a/scripts/deterministic/GenerateConfigs.s.sol +++ b/scripts/deterministic/GenerateConfigs.s.sol @@ -3,7 +3,7 @@ pragma solidity =0.8.24; import {stdToml} from "forge-std/StdToml.sol"; -import {ADMIN_SYSTEM_BACKEND_CONFIG_PATH, ADMIN_SYSTEM_BACKEND_CONFIG_TEMPLATE_PATH, BALANCE_CHECKER_CONFIG_PATH, BALANCE_CHECKER_CONFIG_TEMPLATE_PATH, BRIDGE_HISTORY_CONFIG_PATH, BRIDGE_HISTORY_CONFIG_TEMPLATE_PATH, CHAIN_MONITOR_CONFIG_PATH, CHAIN_MONITOR_CONFIG_TEMPLATE_PATH, CONFIG_PATH, COORDINATOR_CONFIG_PATH, COORDINATOR_CONFIG_TEMPLATE_PATH, FRONTEND_ENV_PATH, ROLLUP_CONFIG_PATH, ROLLUP_CONFIG_TEMPLATE_PATH, ROLLUP_EXPLORER_BACKEND_CONFIG_PATH, ROLLUP_EXPLORER_BACKEND_CONFIG_TEMPLATE_PATH} from "./Constants.sol"; +import {ADMIN_SYSTEM_BACKEND_CONFIG_PATH, BALANCE_CHECKER_CONFIG_PATH, BALANCE_CHECKER_CONFIG_TEMPLATE_PATH, BRIDGE_HISTORY_API_CONFIG_PATH, BRIDGE_HISTORY_CONFIG_TEMPLATE_PATH, BRIDGE_HISTORY_FETCHER_CONFIG_PATH, CHAIN_MONITOR_CONFIG_PATH, CHAIN_MONITOR_CONFIG_TEMPLATE_PATH, COORDINATOR_API_CONFIG_PATH, COORDINATOR_CONFIG_TEMPLATE_PATH, COORDINATOR_CRON_CONFIG_PATH, GAS_ORACLE_CONFIG_PATH, GENESIS_ALLOC_JSON_PATH, GENESIS_JSON_PATH, ROLLUP_CONFIG_PATH, ROLLUP_CONFIG_TEMPLATE_PATH, ROLLUP_EXPLORER_BACKEND_CONFIG_PATH, FRONTEND_ENV_PATH, ROLLUP_EXPLORER_BACKEND_CONFIG_TEMPLATE_PATH, ADMIN_SYSTEM_BACKEND_CONFIG_TEMPLATE_PATH} from "./Constants.sol"; import {DeployScroll} from "./DeployScroll.s.sol"; import {DeterministicDeployment} from "./DeterministicDeployment.sol"; @@ -18,7 +18,8 @@ contract GenerateRollupConfig is DeployScroll { DeterministicDeployment.initialize(ScriptMode.VerifyConfig); predictAllContracts(); - generateRollupConfig(); + generateRollupConfig(ROLLUP_CONFIG_PATH); + generateRollupConfig(GAS_ORACLE_CONFIG_PATH); } /********************* @@ -26,69 +27,35 @@ contract GenerateRollupConfig is DeployScroll { *********************/ // prettier-ignore - function generateRollupConfig() private { + function generateRollupConfig(string memory PATH) private { // initialize template file - if (vm.exists(ROLLUP_CONFIG_PATH)) { - vm.removeFile(ROLLUP_CONFIG_PATH); + if (vm.exists(PATH)) { + vm.removeFile(PATH); } string memory template = vm.readFile(ROLLUP_CONFIG_TEMPLATE_PATH); - vm.writeFile(ROLLUP_CONFIG_PATH, template); + vm.writeFile(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"); + vm.writeJson(L1_RPC_ENDPOINT, PATH, ".l1_config.endpoint"); + vm.writeJson(L2_RPC_ENDPOINT, PATH, ".l1_config.relayer_config.sender_config.endpoint"); + vm.writeJson(L2_RPC_ENDPOINT, PATH, ".l2_config.endpoint"); + vm.writeJson(L1_RPC_ENDPOINT, PATH, ".l2_config.relayer_config.sender_config.endpoint"); // contracts - 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(L1_MESSAGE_QUEUE_V1_PROXY_ADDR), ROLLUP_CONFIG_PATH, ".l2_config.relayer_config.gas_price_oracle_contract_address"); - + vm.writeJson(vm.toString(L1_GAS_PRICE_ORACLE_ADDR), PATH, ".l1_config.relayer_config.gas_price_oracle_contract_address"); + vm.writeJson(vm.toString(L2_MESSAGE_QUEUE_ADDR), PATH, ".l2_config.l2_message_queue_address"); + vm.writeJson(vm.toString(L1_SCROLL_CHAIN_PROXY_ADDR), PATH, ".l2_config.relayer_config.rollup_contract_address"); + vm.writeJson(vm.toString(L1_MESSAGE_QUEUE_V2_PROXY_ADDR), PATH, ".l2_config.relayer_config.gas_price_oracle_contract_address"); // 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(TEST_ENV_MOCK_FINALIZE_TIMEOUT_SEC), ROLLUP_CONFIG_PATH, ".l2_config.relayer_config.finalize_bundle_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(vm.toString(MAX_BATCH_IN_BUNDLE), ROLLUP_CONFIG_PATH, ".l2_config.bundle_proposer_config.max_batch_num_per_bundle"); - - // alternative gas token configuration for gas oracle - if (ALTERNATIVE_GAS_TOKEN_ENABLED) { - bool GAS_ORACLE_INCORPORATE_TOKEN_EXCHANGE_RATE_ENANBLED; - if (vm.keyExistsToml(cfg, ".gas-token.GAS_ORACLE_INCORPORATE_TOKEN_EXCHANGE_RATE_ENANBLED")) { - GAS_ORACLE_INCORPORATE_TOKEN_EXCHANGE_RATE_ENANBLED = cfg.readBool(".gas-token.GAS_ORACLE_INCORPORATE_TOKEN_EXCHANGE_RATE_ENANBLED"); - vm.writeJson(vm.toString(GAS_ORACLE_INCORPORATE_TOKEN_EXCHANGE_RATE_ENANBLED), ROLLUP_CONFIG_PATH, ".l1_config.relayer_config.gas_oracle_config.alternative_gas_token_config.enabled"); - vm.writeJson(vm.toString(GAS_ORACLE_INCORPORATE_TOKEN_EXCHANGE_RATE_ENANBLED), ROLLUP_CONFIG_PATH, ".l2_config.relayer_config.gas_oracle_config.alternative_gas_token_config.enabled"); - } - if (GAS_ORACLE_INCORPORATE_TOKEN_EXCHANGE_RATE_ENANBLED) { - string memory EXCHANGE_RATE_UPDATE_MODE = cfg.readString(".gas-token.EXCHANGE_RATE_UPDATE_MODE"); - vm.writeJson(EXCHANGE_RATE_UPDATE_MODE, ROLLUP_CONFIG_PATH, ".l1_config.relayer_config.gas_oracle_config.alternative_gas_token_config.mode"); - vm.writeJson(EXCHANGE_RATE_UPDATE_MODE, ROLLUP_CONFIG_PATH, ".l2_config.relayer_config.gas_oracle_config.alternative_gas_token_config.mode"); - if (keccak256(abi.encodePacked(EXCHANGE_RATE_UPDATE_MODE)) == keccak256("Fixed")) { - string memory FIXED_EXCHANGE_RATE = cfg.readString(".gas-token.FIXED_EXCHANGE_RATE"); - vm.writeJson(FIXED_EXCHANGE_RATE, ROLLUP_CONFIG_PATH, ".l1_config.relayer_config.gas_oracle_config.alternative_gas_token_config.fixed_exchange_rate"); - vm.writeJson(FIXED_EXCHANGE_RATE, ROLLUP_CONFIG_PATH, ".l2_config.relayer_config.gas_oracle_config.alternative_gas_token_config.fixed_exchange_rate"); - } else if (keccak256(abi.encodePacked(EXCHANGE_RATE_UPDATE_MODE)) == keccak256("BinanceApi")) { - string memory TOKEN_SYMBOL_PAIR = cfg.readString(".gas-token.TOKEN_SYMBOL_PAIR"); - vm.writeJson(TOKEN_SYMBOL_PAIR, ROLLUP_CONFIG_PATH, ".l1_config.relayer_config.gas_oracle_config.alternative_gas_token_config.token_symbol_pair"); - vm.writeJson(TOKEN_SYMBOL_PAIR, ROLLUP_CONFIG_PATH, ".l2_config.relayer_config.gas_oracle_config.alternative_gas_token_config.token_symbol_pair"); - } else { - revert( - string( - abi.encodePacked( - "[ERROR] unsupported exchange rate update mode for gas oracle, mode: ", - EXCHANGE_RATE_UPDATE_MODE - ) - ) - ); - } - } - } + vm.writeJson(vm.toString(TEST_ENV_MOCK_FINALIZE_ENABLED), PATH, ".l2_config.relayer_config.enable_test_env_bypass_features"); + vm.writeJson(vm.toString(TEST_ENV_MOCK_FINALIZE_TIMEOUT_SEC), PATH, ".l2_config.relayer_config.finalize_batch_without_proof_timeout_sec"); + vm.writeJson(vm.toString(TEST_ENV_MOCK_FINALIZE_TIMEOUT_SEC), PATH, ".l2_config.relayer_config.finalize_bundle_without_proof_timeout_sec"); + + vm.writeJson(vm.toString(MAX_BLOCK_IN_CHUNK), PATH, ".l2_config.chunk_proposer_config.max_block_num_per_chunk"); + vm.writeJson(vm.toString(MAX_TX_IN_CHUNK), PATH, ".l2_config.chunk_proposer_config.max_tx_num_per_chunk"); + vm.writeJson(vm.toString(MAX_BATCH_IN_BUNDLE), PATH, ".l2_config.bundle_proposer_config.max_batch_num_per_bundle"); } } @@ -101,28 +68,36 @@ contract GenerateCoordinatorConfig is DeployScroll { DeterministicDeployment.initialize(ScriptMode.VerifyConfig); predictAllContracts(); - generateCoordinatorConfig(); + generateCoordinatorConfig(COORDINATOR_API_CONFIG_PATH); + generateCoordinatorConfig(COORDINATOR_CRON_CONFIG_PATH); } /********************* * Private functions * *********************/ - function generateCoordinatorConfig() private { + function generateCoordinatorConfig(string memory PATH) private { // initialize template file - if (vm.exists(COORDINATOR_CONFIG_PATH)) { - vm.removeFile(COORDINATOR_CONFIG_PATH); + if (vm.exists(PATH)) { + vm.removeFile(PATH); } string memory template = vm.readFile(COORDINATOR_CONFIG_TEMPLATE_PATH); - vm.writeFile(COORDINATOR_CONFIG_PATH, template); - - vm.writeJson(CHUNK_COLLECTION_TIME_SEC, COORDINATOR_CONFIG_PATH, ".prover_manager.chunk_collection_time_sec"); - vm.writeJson(BATCH_COLLECTION_TIME_SEC, COORDINATOR_CONFIG_PATH, ".prover_manager.batch_collection_time_sec"); - vm.writeJson(BUNDLE_COLLECTION_TIME_SEC, COORDINATOR_CONFIG_PATH, ".prover_manager.bundle_collection_time_sec"); - - vm.writeJson(vm.toString(CHAIN_ID_L2), COORDINATOR_CONFIG_PATH, ".l2.chain_id"); - vm.writeJson(COORDINATOR_JWT_SECRET_KEY, COORDINATOR_CONFIG_PATH, ".auth.secret"); + vm.writeFile(PATH, template); + + // coordinator api + vm.writeJson(CHUNK_COLLECTION_TIME_SEC, PATH, ".prover_manager.chunk_collection_time_sec"); + vm.writeJson(BATCH_COLLECTION_TIME_SEC, PATH, ".prover_manager.batch_collection_time_sec"); + vm.writeJson(BUNDLE_COLLECTION_TIME_SEC, PATH, ".prover_manager.bundle_collection_time_sec"); + vm.writeJson(vm.toString(CHAIN_ID_L2), PATH, ".l2.chain_id"); + vm.writeJson(COORDINATOR_JWT_SECRET_KEY, PATH, ".auth.secret"); + + // coordinator cron + vm.writeJson(CHUNK_COLLECTION_TIME_SEC, PATH, ".prover_manager.chunk_collection_time_sec"); + vm.writeJson(BATCH_COLLECTION_TIME_SEC, PATH, ".prover_manager.batch_collection_time_sec"); + vm.writeJson(BUNDLE_COLLECTION_TIME_SEC, PATH, ".prover_manager.bundle_collection_time_sec"); + vm.writeJson(vm.toString(CHAIN_ID_L2), PATH, ".l2.chain_id"); + vm.writeJson(COORDINATOR_JWT_SECRET_KEY, PATH, ".auth.secret"); } } @@ -135,7 +110,7 @@ contract GenerateChainMonitorConfig is DeployScroll { DeterministicDeployment.initialize(ScriptMode.VerifyConfig); predictAllContracts(); - generateChainMonitorConfig(); + generateChainMonitorConfig(CHAIN_MONITOR_CONFIG_PATH); } /********************* @@ -143,41 +118,39 @@ contract GenerateChainMonitorConfig is DeployScroll { *********************/ // prettier-ignore - function generateChainMonitorConfig() private { + function generateChainMonitorConfig(string memory PATH) private { // initialize template file - if (vm.exists(CHAIN_MONITOR_CONFIG_PATH)) { - vm.removeFile(CHAIN_MONITOR_CONFIG_PATH); + if (vm.exists(PATH)) { + vm.removeFile(PATH); } string memory template = vm.readFile(CHAIN_MONITOR_CONFIG_TEMPLATE_PATH); - vm.writeFile(CHAIN_MONITOR_CONFIG_PATH, template); + vm.writeFile(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_GAS_TOKEN_GATEWAY_PROXY_ADDR), CHAIN_MONITOR_CONFIG_PATH, ".l1_config.l1_contracts.l1_gateways.gas_token_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_V2_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(L1_GAS_TOKEN_ADDR), CHAIN_MONITOR_CONFIG_PATH, ".l1_config.l1_contracts.gas_token"); - vm.writeJson(vm.toString(L2_DEPLOYER_INITIAL_BALANCE), CHAIN_MONITOR_CONFIG_PATH, ".l1_config.start_messenger_balance"); + vm.writeJson(L1_RPC_ENDPOINT, PATH, ".l1_config.l1_url"); + vm.writeJson(vm.toString(L1_CONTRACT_DEPLOYMENT_BLOCK), PATH, ".l1_config.start_number"); + vm.writeJson(vm.toString(L1_ETH_GATEWAY_PROXY_ADDR), PATH, ".l1_config.l1_contracts.l1_gateways.eth_gateway"); + vm.writeJson(vm.toString(L1_WETH_GATEWAY_PROXY_ADDR), PATH, ".l1_config.l1_contracts.l1_gateways.weth_gateway"); + vm.writeJson(vm.toString(L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR), PATH, ".l1_config.l1_contracts.l1_gateways.standard_erc20_gateway"); + vm.writeJson(vm.toString(L1_CUSTOM_ERC20_GATEWAY_PROXY_ADDR), PATH, ".l1_config.l1_contracts.l1_gateways.custom_erc20_gateway"); + vm.writeJson(vm.toString(L1_ERC721_GATEWAY_PROXY_ADDR), PATH, ".l1_config.l1_contracts.l1_gateways.erc721_gateway"); + vm.writeJson(vm.toString(L1_ERC1155_GATEWAY_PROXY_ADDR), PATH, ".l1_config.l1_contracts.l1_gateways.erc1155_gateway"); + vm.writeJson(vm.toString(L1_SCROLL_MESSENGER_PROXY_ADDR), PATH, ".l1_config.l1_contracts.scroll_messenger"); + vm.writeJson(vm.toString(L1_MESSAGE_QUEUE_V2_PROXY_ADDR), PATH, ".l1_config.l1_contracts.message_queue"); + vm.writeJson(vm.toString(L1_SCROLL_CHAIN_PROXY_ADDR), PATH, ".l1_config.l1_contracts.scroll_chain"); + vm.writeJson(vm.toString(L2_DEPLOYER_INITIAL_BALANCE), 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_DOGEOS_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"); + vm.writeJson(L2_RPC_ENDPOINT, PATH, ".l2_config.l2_url"); + vm.writeJson(vm.toString(L2_ETH_GATEWAY_PROXY_ADDR), PATH, ".l2_config.l2_contracts.l2_gateways.eth_gateway"); + vm.writeJson(vm.toString(L2_WETH_GATEWAY_PROXY_ADDR), PATH, ".l2_config.l2_contracts.l2_gateways.weth_gateway"); + vm.writeJson(vm.toString(L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR), PATH, ".l2_config.l2_contracts.l2_gateways.standard_erc20_gateway"); + vm.writeJson(vm.toString(L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR), PATH, ".l2_config.l2_contracts.l2_gateways.custom_erc20_gateway"); + vm.writeJson(vm.toString(L2_ERC721_GATEWAY_PROXY_ADDR), PATH, ".l2_config.l2_contracts.l2_gateways.erc721_gateway"); + vm.writeJson(vm.toString(L2_ERC1155_GATEWAY_PROXY_ADDR), PATH, ".l2_config.l2_contracts.l2_gateways.erc1155_gateway"); + vm.writeJson(vm.toString(L2_DOGEOS_MESSENGER_PROXY_ADDR), PATH, ".l2_config.l2_contracts.scroll_messenger"); + vm.writeJson(vm.toString(L2_MESSAGE_QUEUE_ADDR), PATH, ".l2_config.l2_contracts.message_queue"); } } @@ -190,7 +163,8 @@ contract GenerateBridgeHistoryConfig is DeployScroll { DeterministicDeployment.initialize(ScriptMode.VerifyConfig); predictAllContracts(); - generateBridgeHistoryConfig(); + generateBridgeHistoryConfig(BRIDGE_HISTORY_API_CONFIG_PATH); + generateBridgeHistoryConfig(BRIDGE_HISTORY_FETCHER_CONFIG_PATH); } /********************* @@ -198,46 +172,41 @@ contract GenerateBridgeHistoryConfig is DeployScroll { *********************/ // prettier-ignore - function generateBridgeHistoryConfig() private { + function generateBridgeHistoryConfig(string memory PATH) private { // initialize template file - if (vm.exists(BRIDGE_HISTORY_CONFIG_PATH)) { - vm.removeFile(BRIDGE_HISTORY_CONFIG_PATH); + if (vm.exists(PATH)) { + vm.removeFile(PATH); } string memory template = vm.readFile(BRIDGE_HISTORY_CONFIG_TEMPLATE_PATH); - vm.writeFile(BRIDGE_HISTORY_CONFIG_PATH, template); + vm.writeFile(PATH, template); // L1 contracts - vm.writeJson(vm.toString(L1_MESSAGE_QUEUE_V2_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"); - vm.writeJson(vm.toString(L1_GAS_TOKEN_GATEWAY_PROXY_ADDR), BRIDGE_HISTORY_CONFIG_PATH, ".L1.GasTokenGatewayAddr"); - vm.writeJson(vm.toString(L1_WRAPPED_TOKEN_GATEWAY_ADDR), BRIDGE_HISTORY_CONFIG_PATH, ".L1.WrappedTokenGatewayAddr"); + vm.writeJson(vm.toString(L1_MESSAGE_QUEUE_V2_PROXY_ADDR), PATH, ".L1.MessageQueueAddr"); + vm.writeJson(vm.toString(L1_SCROLL_MESSENGER_PROXY_ADDR), PATH, ".L1.MessengerAddr"); + vm.writeJson(vm.toString(L1_SCROLL_CHAIN_PROXY_ADDR), PATH, ".L1.ScrollChainAddr"); + vm.writeJson(vm.toString(L1_GATEWAY_ROUTER_PROXY_ADDR), PATH, ".L1.GatewayRouterAddr"); + vm.writeJson(vm.toString(L1_ETH_GATEWAY_PROXY_ADDR), PATH, ".L1.ETHGatewayAddr"); + vm.writeJson(vm.toString(L1_WETH_GATEWAY_PROXY_ADDR), PATH, ".L1.WETHGatewayAddr"); + vm.writeJson(vm.toString(L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR), PATH, ".L1.StandardERC20GatewayAddr"); + vm.writeJson(vm.toString(L1_CUSTOM_ERC20_GATEWAY_PROXY_ADDR), PATH, ".L1.CustomERC20GatewayAddr"); + vm.writeJson(vm.toString(L1_ERC721_GATEWAY_PROXY_ADDR), PATH, ".L1.ERC721GatewayAddr"); + vm.writeJson(vm.toString(L1_ERC1155_GATEWAY_PROXY_ADDR), PATH, ".L1.ERC1155GatewayAddr"); + vm.writeJson(vm.toString(L1_WRAPPED_TOKEN_GATEWAY_ADDR), PATH, ".L1.WrappedTokenGatewayAddr"); // L2 contracts - vm.writeJson(vm.toString(L2_MESSAGE_QUEUE_ADDR), BRIDGE_HISTORY_CONFIG_PATH, ".L2.MessageQueueAddr"); - vm.writeJson(vm.toString(L2_DOGEOS_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"); + vm.writeJson(vm.toString(L2_MESSAGE_QUEUE_ADDR), PATH, ".L2.MessageQueueAddr"); + vm.writeJson(vm.toString(L2_DOGEOS_MESSENGER_PROXY_ADDR), PATH, ".L2.MessengerAddr"); + vm.writeJson(vm.toString(L2_GATEWAY_ROUTER_PROXY_ADDR), PATH, ".L2.GatewayRouterAddr"); + vm.writeJson(vm.toString(L2_ETH_GATEWAY_PROXY_ADDR), PATH, ".L2.ETHGatewayAddr"); + vm.writeJson(vm.toString(L2_WETH_GATEWAY_PROXY_ADDR), PATH, ".L2.WETHGatewayAddr"); + vm.writeJson(vm.toString(L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR), PATH, ".L2.StandardERC20GatewayAddr"); + vm.writeJson(vm.toString(L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR), PATH, ".L2.CustomERC20GatewayAddr"); + vm.writeJson(vm.toString(L2_ERC721_GATEWAY_PROXY_ADDR), PATH, ".L2.ERC721GatewayAddr"); + vm.writeJson(vm.toString(L2_ERC1155_GATEWAY_PROXY_ADDR), PATH, ".L2.ERC1155GatewayAddr"); // others - vm.writeJson(vm.toString(L1_CONTRACT_DEPLOYMENT_BLOCK), BRIDGE_HISTORY_CONFIG_PATH, ".L1.startHeight"); + vm.writeJson(vm.toString(L1_CONTRACT_DEPLOYMENT_BLOCK), PATH, ".L1.startHeight"); } } @@ -250,42 +219,28 @@ contract GenerateBalanceCheckerConfig is DeployScroll { DeterministicDeployment.initialize(ScriptMode.VerifyConfig); predictAllContracts(); - generateBalanceCheckerConfig(); + generateBalanceCheckerConfig(BALANCE_CHECKER_CONFIG_PATH); } /********************* * Private functions * *********************/ - function generateBalanceCheckerConfig() private { + function generateBalanceCheckerConfig(string memory PATH) private { // initialize template file - if (vm.exists(BALANCE_CHECKER_CONFIG_PATH)) { - vm.removeFile(BALANCE_CHECKER_CONFIG_PATH); + if (vm.exists(PATH)) { + vm.removeFile(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"); - - vm.writeJson(L2_RPC_ENDPOINT, BALANCE_CHECKER_CONFIG_PATH, ".addresses[6].rpc_url"); - vm.writeJson(vm.toString(L2_BRIDGE_FEE_RECIPIENT_ADDR), BALANCE_CHECKER_CONFIG_PATH, ".addresses[6].address"); + vm.writeFile(PATH, template); + + vm.writeJson(vm.toString(L1_COMMIT_SENDER_ADDR), PATH, ".addresses[0].address"); + vm.writeJson(vm.toString(L1_FINALIZE_SENDER_ADDR), PATH, ".addresses[1].address"); + vm.writeJson(vm.toString(L1_GAS_ORACLE_SENDER_ADDR), PATH, ".addresses[2].address"); + vm.writeJson(vm.toString(L1_FEE_VAULT_ADDR), PATH, ".addresses[3].address"); + vm.writeJson(vm.toString(L2_GAS_ORACLE_SENDER_ADDR), PATH, ".addresses[4].address"); + vm.writeJson(vm.toString(L2_TX_FEE_VAULT_ADDR), PATH, ".addresses[5].address"); } } @@ -338,9 +293,9 @@ contract GenerateFrontendConfig is DeployScroll { // 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), "\"")); - vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_L1_GAS_TOKEN_GATEWAY = \"", vm.toString(L1_GAS_TOKEN_GATEWAY_PROXY_ADDR), "\"")); + //vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_L1_GAS_TOKEN_GATEWAY = \"", vm.toString(L1_GAS_TOKEN_GATEWAY_PROXY_ADDR), "\"")); vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_L1_WRAPPED_TOKEN_GATEWAY = \"", vm.toString(L1_WRAPPED_TOKEN_GATEWAY_ADDR), "\"")); - vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_L1_GAS_TOKEN_ADDR = \"", vm.toString(L1_GAS_TOKEN_ADDR), "\"")); + //vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_L1_GAS_TOKEN_ADDR = \"", vm.toString(L1_GAS_TOKEN_ADDR), "\"")); // vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_L1_WETH_ADDR = \"", vm.toString(L1_WETH_ADDR), "\"")); vm.writeLine(FRONTEND_ENV_PATH, string.concat("REACT_APP_L1_WDOGE_ADDR = \"", vm.toString(L1_WDOGE_ADDR), "\"")); @@ -381,7 +336,7 @@ contract GenerateRollupExplorerBackendConfig is DeployScroll { DeterministicDeployment.initialize(ScriptMode.VerifyConfig); predictAllContracts(); - generateRollupExplorerBackendConfig(); + generateRollupExplorerBackendConfig(ROLLUP_EXPLORER_BACKEND_CONFIG_PATH); } /********************* @@ -389,16 +344,16 @@ contract GenerateRollupExplorerBackendConfig is DeployScroll { *********************/ // prettier-ignore - function generateRollupExplorerBackendConfig() private { + function generateRollupExplorerBackendConfig(string memory PATH) private { // initialize template file - if (vm.exists(ROLLUP_EXPLORER_BACKEND_CONFIG_PATH)) { - vm.removeFile(ROLLUP_EXPLORER_BACKEND_CONFIG_PATH); + if (vm.exists(PATH)) { + vm.removeFile(PATH); } string memory template = vm.readFile(ROLLUP_EXPLORER_BACKEND_CONFIG_TEMPLATE_PATH); - vm.writeFile(ROLLUP_EXPLORER_BACKEND_CONFIG_PATH, template); + vm.writeFile(PATH, template); - vm.writeJson(ROLLUP_EXPLORER_BACKEND_DB_CONNECTION_STRING, ROLLUP_EXPLORER_BACKEND_CONFIG_PATH, ".db_url"); + vm.writeJson(ROLLUP_EXPLORER_BACKEND_DB_CONNECTION_STRING, PATH, ".db_url"); } } diff --git a/scripts/deterministic/GenerateGenesis.s.sol b/scripts/deterministic/GenerateGenesis.s.sol index fc62f69f..ca7d7449 100644 --- a/scripts/deterministic/GenerateGenesis.s.sol +++ b/scripts/deterministic/GenerateGenesis.s.sol @@ -4,9 +4,7 @@ pragma solidity =0.8.24; 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 {L2TxFeeVaultWithGasToken} from "../../src/alternative-gas-token/L2TxFeeVaultWithGasToken.sol"; import {Whitelist} from "../../src/L2/predeploys/Whitelist.sol"; -// import {WrappedEther} from "../../src/L2/predeploys/WrappedEther.sol"; import {WrappedDoge} from "../../src/dogeos/WrappedDoge.sol"; import {DETERMINISTIC_DEPLOYMENT_PROXY_ADDR, FEE_VAULT_MIN_WITHDRAW_AMOUNT, GENESIS_ALLOC_JSON_PATH, GENESIS_JSON_PATH, GENESIS_JSON_TEMPLATE_PATH} from "./Constants.sol"; @@ -131,7 +129,6 @@ contract GenerateGenesis is DeployScroll { } // set code - // WrappedEther _weth = new WrappedEther(); WrappedDoge _wdoge = new WrappedDoge(); vm.etch(predeployAddr, address(_wdoge).code); @@ -157,20 +154,10 @@ contract GenerateGenesis is DeployScroll { // set code address _vaultAddr; vm.prank(DEPLOYER_ADDR); - if (!ALTERNATIVE_GAS_TOKEN_ENABLED) { - L2TxFeeVault _vault = new L2TxFeeVault(DEPLOYER_ADDR, L1_FEE_VAULT_ADDR, FEE_VAULT_MIN_WITHDRAW_AMOUNT); - vm.prank(DEPLOYER_ADDR); - _vault.updateMessenger(L2_DOGEOS_MESSENGER_PROXY_ADDR); - _vaultAddr = address(_vault); - } else { - L2TxFeeVaultWithGasToken _vault = new L2TxFeeVaultWithGasToken( - L2_ETH_GATEWAY_PROXY_ADDR, - DEPLOYER_ADDR, - L1_FEE_VAULT_ADDR, - FEE_VAULT_MIN_WITHDRAW_AMOUNT - ); - _vaultAddr = address(_vault); - } + L2TxFeeVault _vault = new L2TxFeeVault(DEPLOYER_ADDR, L1_FEE_VAULT_ADDR, FEE_VAULT_MIN_WITHDRAW_AMOUNT); + vm.prank(DEPLOYER_ADDR); + _vault.updateMessenger(L2_DOGEOS_MESSENGER_PROXY_ADDR); + _vaultAddr = address(_vault); vm.etch(predeployAddr, _vaultAddr.code); @@ -243,15 +230,15 @@ contract GenerateGenesis is DeployScroll { vm.writeJson(l1ChainId, GENESIS_JSON_PATH, ".config.scroll.l1Config.l1ChainId"); vm.writeJson( - vm.toString(L1_MESSAGE_QUEUE_V1_PROXY_ADDR), + vm.toString(SYSTEM_CONFIG_PROXY_ADDR), GENESIS_JSON_PATH, - ".config.scroll.l1Config.l1MessageQueueAddress" + ".config.systemContract.system_contract_address" ); vm.writeJson( - vm.toString(L1_SCROLL_CHAIN_PROXY_ADDR), + vm.toString(L1_MESSAGE_QUEUE_V1_PROXY_ADDR), GENESIS_JSON_PATH, - ".config.scroll.l1Config.scrollChainAddress" + ".config.scroll.l1Config.l1MessageQueueAddress" ); vm.writeJson( @@ -261,16 +248,19 @@ contract GenerateGenesis is DeployScroll { ); vm.writeJson( - vm.toString(L1_SYSTEM_CONFIG_PROXY_ADDR), + vm.toString(L1_SCROLL_CHAIN_PROXY_ADDR), GENESIS_JSON_PATH, - ".config.systemContract.system_contract_address" + ".config.scroll.l1Config.scrollChainAddress" ); - vm.writeJson(vm.toString(bytes32(BASE_FEE_PER_GAS)), GENESIS_JSON_PATH, ".baseFeePerGas"); - // predeploys and prefunded accounts string memory alloc = vm.readFile(GENESIS_ALLOC_JSON_PATH); vm.writeJson(alloc, GENESIS_JSON_PATH, ".alloc"); + vm.writeJson( + vm.toString(L2_SYSTEM_CONFIG_PROXY_ADDR), + GENESIS_JSON_PATH, + ".config.scroll.l1Config.l2SystemConfigAddress" + ); } /// @notice Sorts the allocs by address diff --git a/scripts/deterministic/config/config-contracts.toml b/scripts/deterministic/config/config-contracts.toml new file mode 100644 index 00000000..af10cce4 --- /dev/null +++ b/scripts/deterministic/config/config-contracts.toml @@ -0,0 +1,68 @@ +L1_WETH_ADDR = "" +L1_PROXY_ADMIN_ADDR = "0xF073a49bC8Ec2Ee218968be66cf29f924697e2bd" +L1_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR = "0x62c3Ced16eE242E4D45416bC0B365F820b71739e" +L1_WHITELIST_ADDR = "0x55764a23F118A4CA66Ea97ffcCa8348AAC6A047E" +L1_SCROLL_CHAIN_PROXY_ADDR = "0x5c64ad84B7DC6E45BdEBbE5193787Ab893514FC7" +L1_SCROLL_MESSENGER_PROXY_ADDR = "0xB8BD4E103F461a613E220E83932a350F73085bDA" +L1_MESSAGE_QUEUE_V1_IMPLEMENTATION_ADDR = "0x4393B9195158566e9f7fAB4519BDb4e496cE5989" +L1_MESSAGE_QUEUE_V1_PROXY_ADDR = "0xedeE3b3009Bb66948A6152aFA340c9D385D29f3E" +L1_MESSAGE_QUEUE_V2_IMPLEMENTATION_ADDR = "0x86ec91688Fc357e5188409b4e3e98054b47c8ea2" +L1_MESSAGE_QUEUE_V2_PROXY_ADDR = "0x41544cE3Bb903c8b21FFaE3B8A2c6722787063F2" +L1_ENFORCED_TX_GATEWAY_IMPLEMENTATION_ADDR = "0x4b69F2A12FE165939eaF55dA1f3a8F9af7Bc0d31" +L1_ENFORCED_TX_GATEWAY_PROXY_ADDR = "0x09753595Fc91aC441E3D49048EA780843a4c72ca" +L1_PLONK_VERIFIER_ADDR = "0xB00B09CE14812932afAE4e97D03c48b18FB878e9" +L1_ZKEVM_VERIFIER_V2_ADDR = "0x825176A2F2566a68bF23DA8929a74887015fDA99" +L1_MULTIPLE_VERSION_ROLLUP_VERIFIER_ADDR = "0x13C03cba4C25807e23e56C1895C23B00df093610" +L1_SCROLL_CHAIN_IMPLEMENTATION_ADDR = "0x05833514D05d4f343020f1ed5Eed5b080A4cfBFC" +L1_GATEWAY_ROUTER_IMPLEMENTATION_ADDR = "0xA1b6B28914bD608b85792fDE16d1A914729a62Eb" +L1_GATEWAY_ROUTER_PROXY_ADDR = "0xa5Aab9141f248c19Bd5Db23bd30372d47C361223" +L1_ETH_GATEWAY_PROXY_ADDR = "0xB871794762fd294B0a84Dd75e03c6E3F39Fe337b" +L1_WETH_GATEWAY_PROXY_ADDR = "0xA199F6628dE78849D1Bb119D2a5764e78e2E9c07" +L1_STANDARD_ERC20_GATEWAY_PROXY_ADDR = "0x8b075C23c6C45489D8Db9B3B726B6f0f2ba7fEb7" +L1_CUSTOM_ERC20_GATEWAY_PROXY_ADDR = "0xfeBb0bd88ADdA433FE4BC191BC3794cca4F9B5FE" +L1_ERC721_GATEWAY_PROXY_ADDR = "0xbE5aB8CfB2B60279c30bD74Aa3a28fB063abF0DF" +L1_ERC1155_GATEWAY_PROXY_ADDR = "0xc2699AC5d3Df3E00C3EBd1a290a6490b822C1549" +L2_MESSAGE_QUEUE_ADDR = "0x5300000000000000000000000000000000000000" +L1_GAS_PRICE_ORACLE_ADDR = "0x5300000000000000000000000000000000000002" +L2_WHITELIST_ADDR = "0x5300000000000000000000000000000000000003" +L2_WETH_ADDR = "" +L2_TX_FEE_VAULT_ADDR = "0x5300000000000000000000000000000000000005" +L2_PROXY_ADMIN_ADDR = "0xbAE1c838C812705A60B0450A22961C6b1221Bb21" +L2_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR = "0x5eE73D06023e985d91F5EaA1b9b34566357e6843" +L2_SCROLL_MESSENGER_PROXY_ADDR = "" +L2_ETH_GATEWAY_PROXY_ADDR = "0x861f25a798583b5c9ee1d57B6d1e3a904D10fc11" +L2_WETH_GATEWAY_PROXY_ADDR = "0xDEBD44BaFC4564c2281617dbAdc75665c225d3e0" +L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR = "0x880Ca608F916E34ec2036fBB7f3a76365C4192B5" +L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR = "0xEb98B9bdb54f8Eb10ae7b62f88d8d26BbF16ae06" +L2_ERC721_GATEWAY_PROXY_ADDR = "0xB16b498a86F157F39EB44eb4B26794aA49bd46d1" +L2_ERC1155_GATEWAY_PROXY_ADDR = "0x768e33cC8520392156FB09f2Ec3fD8d3c46d733F" +L2_SCROLL_STANDARD_ERC20_ADDR = "0x14a437236E7a85b344DdDe73bDB4fE743422F576" +L2_SCROLL_STANDARD_ERC20_FACTORY_ADDR = "0x541F00A99eF14227A344c9223E9BE4F3ED263187" +L1_SCROLL_MESSENGER_IMPLEMENTATION_ADDR = "0xd47D5cD21Dc57304D16FA7D2cbFf9BAc379c3F97" +L1_STANDARD_ERC20_GATEWAY_IMPLEMENTATION_ADDR = "0x41b239d74d6e59fA4604fD60c2a9162C1fA2f5C3" +L1_ETH_GATEWAY_IMPLEMENTATION_ADDR = "0x4daa18e4ff71ebcbC7Dfb251917E1263972b2a36" +L1_WETH_GATEWAY_IMPLEMENTATION_ADDR = "0x8160aA244013fCaE7DbcC6D6dB12Db6F1e53A92e" +L1_CUSTOM_ERC20_GATEWAY_IMPLEMENTATION_ADDR = "0xdAb297E5B5A7320f906d3088708Df548a1033e25" +L1_ERC721_GATEWAY_IMPLEMENTATION_ADDR = "0x7F39Ae375a131A415A05FBD8d46e5Bd6F5f3578d" +L1_ERC1155_GATEWAY_IMPLEMENTATION_ADDR = "0x26f58942946d3e2ec638C64a3a25Ee69319c76AF" +L2_SCROLL_MESSENGER_IMPLEMENTATION_ADDR = "" +L2_GATEWAY_ROUTER_IMPLEMENTATION_ADDR = "0x0e15A5839EE68cEEa1Ae35F63F5F415c55FAB731" +L2_GATEWAY_ROUTER_PROXY_ADDR = "0x07C9821495f7d28526331971652D669A2E361618" +L2_STANDARD_ERC20_GATEWAY_IMPLEMENTATION_ADDR = "0xfc629eb4BB98Bf354E33C4652271f24eEeedEF3d" +L2_ETH_GATEWAY_IMPLEMENTATION_ADDR = "0xE9E69ac9CC6b928059d6FA89CE46112d566664f0" +L2_WETH_GATEWAY_IMPLEMENTATION_ADDR = "0x90294Ac83b79bde23CA9144532B2840fDFc24C6D" +L2_CUSTOM_ERC20_GATEWAY_IMPLEMENTATION_ADDR = "0x72d364a93E5C1f7d0944f6bfD145355e7A3150f5" +L2_ERC721_GATEWAY_IMPLEMENTATION_ADDR = "0xE77037fC7ed332857723c97529a4d457a348a1E9" +L2_ERC1155_GATEWAY_IMPLEMENTATION_ADDR = "0x6F8264Cf313F20DA2721fF1A86c01F7754c6e432" +L1_SYSTEM_CONFIG_IMPLEMENTATION_ADDR = "0x18B99289fa325c5dEe67a151f699de30963721dD" +L1_SYSTEM_CONFIG_PROXY_ADDR = "0x2aB9e1aaEe8926eD659A740B27f976994d04AeD9" +L1_WDOGE_ADDR = "0xF853cEA14b2fE81b840a3CF9456dfeCa9be9a273" +L1_WRAPPED_TOKEN_GATEWAY_ADDR = "" +L2_BASCULE_MOCK_VERIFIER_ADDR = "0xe68F95f05a9493cc98b9908b4cdC757a4B343796" +L2_DOGEOS_MESSENGER_IMPLEMENTATION_ADDR = "0x1578E17fbe36CB7717DCf67f18A5E9F41756826F" +L2_DOGEOS_MESSENGER_PROXY_ADDR = "0xA0e17A64E4c5885bbA2EE189652A73f414C0f814" +L2_MOAT_IMPLEMENTATION_ADDR = "0x368e0550Fc0A06dAED24C34606fb1D2Ee2494484" +L2_MOAT_PROXY_ADDR = "0x85070ad802E5f296AFb4d83340127Dbe4aD9c25C" +L2_WDOGE_ADDR = "0x628bEb8C034C873bbC5cEB62c3e2F1657e62d78d" +L2_WDOGE_GATEWAY_IMPLEMENTATION_ADDR = "" +L2_WDOGE_GATEWAY_PROXY_ADDR = "" diff --git a/scripts/deterministic/config/config-contracts.toml.template b/scripts/deterministic/config/config-contracts.toml.template new file mode 100644 index 00000000..b1256692 --- /dev/null +++ b/scripts/deterministic/config/config-contracts.toml.template @@ -0,0 +1,68 @@ +L1_WETH_ADDR = "" +L1_PROXY_ADMIN_ADDR = "" +L1_PROXY_IMPLEMENTATION_PLACEHOLDER_ADDR = "" +L1_WHITELIST_ADDR = "" +L1_SCROLL_CHAIN_PROXY_ADDR = "" +L1_SCROLL_MESSENGER_PROXY_ADDR = "" +L1_MESSAGE_QUEUE_V1_IMPLEMENTATION_ADDR = "" +L1_MESSAGE_QUEUE_V1_PROXY_ADDR = "" +L1_MESSAGE_QUEUE_V2_IMPLEMENTATION_ADDR = "" +L1_MESSAGE_QUEUE_V2_PROXY_ADDR = "" +L1_ENFORCED_TX_GATEWAY_IMPLEMENTATION_ADDR = "" +L1_ENFORCED_TX_GATEWAY_PROXY_ADDR = "" +L1_PLONK_VERIFIER_ADDR = "" +L1_ZKEVM_VERIFIER_V2_ADDR = "" +L1_MULTIPLE_VERSION_ROLLUP_VERIFIER_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 = "" +L1_SYSTEM_CONFIG_IMPLEMENTATION_ADDR = "" +L1_SYSTEM_CONFIG_PROXY_ADDR = "" +L1_WDOGE_ADDR = "" +L1_WRAPPED_TOKEN_GATEWAY_ADDR = "" +L2_BASCULE_MOCK_VERIFIER_ADDR = "" +L2_DOGEOS_MESSENGER_IMPLEMENTATION_ADDR = "" +L2_DOGEOS_MESSENGER_PROXY_ADDR = "" +L2_MOAT_IMPLEMENTATION_ADDR = "" +L2_MOAT_PROXY_ADDR = "" +L2_WDOGE_ADDR = "" +L2_WDOGE_GATEWAY_IMPLEMENTATION_ADDR = "" +L2_WDOGE_GATEWAY_PROXY_ADDR = "" \ No newline at end of file diff --git a/scripts/deterministic/config/config.toml b/scripts/deterministic/config/config.toml new file mode 100644 index 00000000..eff158c9 --- /dev/null +++ b/scripts/deterministic/config/config.toml @@ -0,0 +1,149 @@ +[general] +L1_RPC_ENDPOINT = "http://l1-devnet:8545" +L1_RPC_ENDPOINT_WEBSOCKET = "ws://l1-devnet:8546" +L2_RPC_ENDPOINT = "http://l2-rpc:8545" +CHAIN_NAME_L1 = "DOGE" +CHAIN_NAME_L2 = "DogeOsShuDe" +CHAIN_ID_L1 = "5555555" +CHAIN_ID_L2 = 221_122 +L1_CONTRACT_DEPLOYMENT_BLOCK = "0" +VERIFIER_DIGEST_1 = "0x0000000000000000000000000000000000000000000000000000000000000001" +VERIFIER_DIGEST_2 = "0x0000000000000000000000000000000000000000000000000000000000000002" + +[accounts] +DEPLOYER_PRIVATE_KEY = "0x35c587b4621c2bea0ab9151cfa6f0eda2afff689dd98f4291b054807401727df" +L1_COMMIT_SENDER_PRIVATE_KEY = "0x59c889a661a316b10f5adaf3885194e9df0dff463765979c29af3d75c76559dc" +L1_FINALIZE_SENDER_PRIVATE_KEY = "0xf3276e689f249fa0804db68ff4598d6dcb35d30073ed389dc77a54a80e3eac07" +L1_GAS_ORACLE_SENDER_PRIVATE_KEY = "0x05a39b208996c4b687c32983f7a25bd1c6f8c7f266cc3723b44043ccebca63c7" +L2_GAS_ORACLE_SENDER_PRIVATE_KEY = "0x4eb8a1a5df38504c1a5e3448ffe0f5706ab6c93a5327fe8226ba8ecada0faa39" +DEPLOYER_ADDR = "0x8542719E3B915dC4FF39B0BB90E714407498FE2B" +OWNER_ADDR = "0x7B015Ee06ea974692030352f4358AECF337d188e" +L1_COMMIT_SENDER_ADDR = "0xC37e823D5bee422c6DAAE588E3967DC532191d6D" +L1_FINALIZE_SENDER_ADDR = "0xD1FA0c06e935871Bfd6Ad8a70224f90622D18448" +L1_GAS_ORACLE_SENDER_ADDR = "0xE2b3337F6403b2BdB7bF95F993783D7A41096cB6" +L2_GAS_ORACLE_SENDER_ADDR = "0xA125D100c378FBab2Ab503C2D96701D1335E5e99" +L2_TESTNET_ACTIVITY_HELPER_PRIVATE_KEY = "0x97da3dd40651ac1a8069b59c5d1c1f3c502c38b77f8362f84dae0faef7e400f9" +L2_TESTNET_ACTIVITY_HELPER_ADDR = "0x74c975cC73d4F5f24CDfDF9bD16ce16e6E28e965" + +[db] +ADMIN_SYSTEM_DB_CONNECTION_STRING = "" +BLOCKSCOUT_DB_CONNECTION_STRING = "postgres://blockscout:0.mqikaef97w@scroll-sdk-db.cfc6im0u6fq6.us-west-2.rds.amazonaws.com:5432/scroll_blockscout?sslmode=require" +BRIDGE_HISTORY_DB_CONNECTION_STRING = "postgres://bridge_history:.xvy9bhyn50l@scroll-sdk-db.cfc6im0u6fq6.us-west-2.rds.amazonaws.com:5432/scroll_bridge_history?sslmode=require" +CHAIN_MONITOR_DB_CONNECTION_STRING = "postgres://chain_monitor:.zl3p57gis7o@scroll-sdk-db.cfc6im0u6fq6.us-west-2.rds.amazonaws.com:5432/scroll_chain_monitor?sslmode=require" +GAS_ORACLE_DB_CONNECTION_STRING = "postgres://rollup_node:0.eauyn1tnuk@scroll-sdk-db.cfc6im0u6fq6.us-west-2.rds.amazonaws.com:5432/scroll_rollup?sslmode=require" +COORDINATOR_DB_CONNECTION_STRING = "postgres://rollup_node:0.eauyn1tnuk@scroll-sdk-db.cfc6im0u6fq6.us-west-2.rds.amazonaws.com:5432/scroll_rollup?sslmode=require" +L1_EXPLORER_DB_CONNECTION_STRING = "" +ROLLUP_NODE_DB_CONNECTION_STRING = "postgres://rollup_node:0.eauyn1tnuk@scroll-sdk-db.cfc6im0u6fq6.us-west-2.rds.amazonaws.com:5432/scroll_rollup?sslmode=require" +ROLLUP_EXPLORER_DB_CONNECTION_STRING = "postgres://rollup_node:0.eauyn1tnuk@scroll-sdk-db.cfc6im0u6fq6.us-west-2.rds.amazonaws.com:5432/scroll_rollup?sslmode=require" +SCROLL_DB_CONNECTION_STRING = "postgres://rollup_node:0.eauyn1tnuk@scroll-sdk-db.cfc6im0u6fq6.us-west-2.rds.amazonaws.com:5432/scroll_rollup?sslmode=require" +ADMIN_SYSTEM_BACKEND_DB_CONNECTION_STRING = "postgres://rollup_node:0.eauyn1tnuk@scroll-sdk-db.cfc6im0u6fq6.us-west-2.rds.amazonaws.com:5432/scroll_rollup?sslmode=require" + +[gas-token] +ALTERNATIVE_GAS_TOKEN_ENABLED = false +GAS_ORACLE_INCORPORATE_TOKEN_EXCHANGE_RATE_ENANBLED = false +EXCHANGE_RATE_UPDATE_MODE = "Fixed" +FIXED_EXCHANGE_RATE = "0.01" +TOKEN_SYMBOL_PAIR = "UNIETH" + +[sequencer] +L2GETH_SIGNER_ADDRESS = "0xE0A6D8c37ee09cd8EAE8b218F6668927c561CAf5" +L2GETH_KEYSTORE = "{\"address\":\"e0a6d8c37ee09cd8eae8b218f6668927c561caf5\",\"id\":\"44d3382b-506c-462c-9320-9f72743c64b7\",\"version\":3,\"Crypto\":{\"cipher\":\"aes-128-ctr\",\"cipherparams\":{\"iv\":\"c0be55c63a72e31cfbab17a31235c369\"},\"ciphertext\":\"62e1b8e161727d4942a3a49af55ce65f72706d72f753b2552b0df23265106de2\",\"kdf\":\"scrypt\",\"kdfparams\":{\"salt\":\"12a1b4599e850f1d21e93899b554657c7587ad5a133099254bd37b08220d56cc\",\"n\":131072,\"dklen\":32,\"p\":1,\"r\":8},\"mac\":\"8197782b7627d7e50731e80fe9dd59dc878e80b0e5ec266f918bfdc46d7e1da6\"},\"x-ethers\":{\"client\":\"ethers/6.13.2\",\"gethFilename\":\"UTC--2025-07-17T06-39-33.0Z--e0a6d8c37ee09cd8eae8b218f6668927c561caf5\",\"path\":\"m/44'/60'/0'/0/0\",\"locale\":\"en\",\"mnemonicCounter\":\"669343a5f675adafd72c542ec71dbf55\",\"mnemonicCiphertext\":\"8a4add77e00c2c212987fcb49f52c918\",\"version\":\"0.1\"}}" +L2GETH_PASSWORD = "hjk" +L2GETH_NODEKEY = "e9262343552db1c2412b76907f3be21773238a35284417b94f79cdd19c4593c6" +L2_GETH_STATIC_PEERS = [ + "enode://cfa1526e8f752249cd19ec972064858e196c78d56ce2ae7f9cd71198c31cbf1e495a71b4c27c1f86026a1b86e84009ae32c3b4db3bbb6f525c57954ea858d7ee@l2-sequencer-0:30303", + "enode://0bedf8b183f7e72a343f2e0b8a9f71504d2098361d16af682255c65439592377accf9729fe38f06681c9bfdd27a30fedc7b71244dc9613869c4e16ae52ddd6e3@l2-sequencer-1:30303" +] + + [sequencer.sequencer-1] + L2GETH_SIGNER_ADDRESS = "0x258DBec03bDCaDC01D853767da7065BeA080Ee8d" + L2GETH_KEYSTORE = "{\"address\":\"258dbec03bdcadc01d853767da7065bea080ee8d\",\"id\":\"2ceb96ae-85da-492e-85e0-334b410608d2\",\"version\":3,\"Crypto\":{\"cipher\":\"aes-128-ctr\",\"cipherparams\":{\"iv\":\"60a26ca8b0a78ea4cd599043b10fe201\"},\"ciphertext\":\"71f880e74096eba9db6cfa8c35df238a5adf190e04f16c4b8f1ca8e2d34a86ce\",\"kdf\":\"scrypt\",\"kdfparams\":{\"salt\":\"172fcb47cefed0cfe06f602e1a19562eba46079a82152137e8c16f3731f7ef16\",\"n\":131072,\"dklen\":32,\"p\":1,\"r\":8},\"mac\":\"e0849e67b9e6c5a43892b6a0ea4e85e1cf21c7142d7a769b74a89f9e8467df28\"},\"x-ethers\":{\"client\":\"ethers/6.13.2\",\"gethFilename\":\"UTC--2025-07-17T06-39-35.0Z--258dbec03bdcadc01d853767da7065bea080ee8d\",\"path\":\"m/44'/60'/0'/0/0\",\"locale\":\"en\",\"mnemonicCounter\":\"183662f2debc27d905be52f7fc5416f8\",\"mnemonicCiphertext\":\"3d57496ff263fac34136aacbf2c4e791\",\"version\":\"0.1\"}}" + L2GETH_PASSWORD = "asj" + L2GETH_NODEKEY = "23455d6334399e55252a63c408431dd598d091cdbf47d19da5e362e77e63d90f" + +[bootnode] +L2_GETH_PUBLIC_PEERS = [ + "enode://7ae722528620c63a8dc92800039a52f80a6ce4be9b2cea020fd465df95c1c88173355b42671ba6974e2de3155f6feac4a0a957ce4fdff226f2897ec53a7c77ca@l2-bootnode-0:30303", + "enode://0ea49090086058ec6b5b8b9e8ab759bb302750ba31093e7226464eacf14b031885fab5a49790920c46a988980a60e2f0e2c5128f0f08679f99f7ad6c95a8fbc6@l2-bootnode-1:30303" +] + + [bootnode.bootnode-0] + L2GETH_NODEKEY = "e66a5836a96140c3c60ccca507c66ba9cf5278020bc1086bf756500c5073623c" + + [bootnode.bootnode-1] + L2GETH_NODEKEY = "d09611100c08fa049a2320a3deee1782c7f2f8c5a22444f754c0b9c59439dd88" + +[rollup] +MAX_TX_IN_CHUNK = 10_000 +MAX_BLOCK_IN_CHUNK = 100 +MAX_BATCH_IN_BUNDLE = 30 +MAX_L1_MESSAGE_GAS_LIMIT = 10_000_000 +TEST_ENV_MOCK_FINALIZE_ENABLED = true +TEST_ENV_MOCK_FINALIZE_TIMEOUT_SEC = 60 +FINALIZE_BATCH_DEADLINE_SEC = 113 +RELAY_MESSAGE_DEADLINE_SEC = 60 + +[frontend] +EXTERNAL_RPC_URI_L1 = "https://l1-devnet.shude.unifra.xyz" +EXTERNAL_RPC_URI_L2 = "https://rpc.shude.unifra.xyz" +BRIDGE_API_URI = "https://bridge-history-api.shude.unifra.xyz/api" +ROLLUPSCAN_API_URI = "https://rollup-explorer-backend.shude.unifra.xyz/api" +EXTERNAL_EXPLORER_URI_L1 = "https://l1-explorer.shude.unifra.xyz" +EXTERNAL_EXPLORER_URI_L2 = "https://blockscout.shude.unifra.xyz" +ADMIN_SYSTEM_DASHBOARD_URI = "https://admin-system-dashboard.shude.unifra.xyz" +GRAFANA_URI = "https://grafana.shude.unifra.xyz" +ETH_SYMBOL = "DOGE" +BASE_CHAIN = "DOGE" +CONNECT_WALLET_PROJECT_ID = "14efbaafcf5232a47d93a68229b71028" +DOGE_EXTERNAL_RPC_URI_L1 = "https://sochain.com/DOGETEST" +DOGE_EXTERNAL_EXPLORER_URI_L1 = "https://sochain.com/DOGETEST" + +[genesis] +L2_MAX_ETH_SUPPLY = "226156424291633194186662080095093570025917938800079226639565593765455331328" +L2_DEPLOYER_INITIAL_BALANCE = "1000000000000000000" +BASE_FEE_PER_GAS = "10" + +[contracts] +DEPLOYMENT_SALT = "ihfahbgyeuw-wv54vk" +L1_FEE_VAULT_ADDR = "0x7B015Ee06ea974692030352f4358AECF337d188e" +L2_BRIDGE_FEE_RECIPIENT_ADDR = "0x0000000000000000000000000000000000000000" + + [contracts.overrides] + L2_MESSAGE_QUEUE = "0x5300000000000000000000000000000000000000" + L1_GAS_PRICE_ORACLE = "0x5300000000000000000000000000000000000002" + L2_WHITELIST = "0x5300000000000000000000000000000000000003" + L2_WETH = "0x5300000000000000000000000000000000000004" + L2_TX_FEE_VAULT = "0x5300000000000000000000000000000000000005" + + [contracts.verification] + VERIFIER_TYPE_L1 = "blockscout" + VERIFIER_TYPE_L2 = "blockscout" + EXPLORER_URI_L1 = "https://l1-explorer.shude.unifra.xyz" + EXPLORER_URI_L2 = "https://blockscout.shude.unifra.xyz" + RPC_URI_L1 = "https://l1-devnet.shude.unifra.xyz" + RPC_URI_L2 = "https://rpc.shude.unifra.xyz" + EXPLORER_API_KEY_L1 = "" + EXPLORER_API_KEY_L2 = "" + +[coordinator] +CHUNK_COLLECTION_TIME_SEC = 3_600 +BATCH_COLLECTION_TIME_SEC = 1_800 +BUNDLE_COLLECTION_TIME_SEC = 600 +COORDINATOR_JWT_SECRET_KEY = "f4ef9e7ddfb0639caf7922a0b26f54e9045d966a6841f0137d5d472c361c5231" + +[ingress] +FRONTEND_HOST = "portal.shude.unifra.xyz" +BRIDGE_HISTORY_API_HOST = "bridge-history-api.shude.unifra.xyz" +ROLLUP_EXPLORER_API_HOST = "rollup-explorer-backend.shude.unifra.xyz" +COORDINATOR_API_HOST = "coordinator-api.shude.unifra.xyz" +RPC_GATEWAY_HOST = "rpc.shude.unifra.xyz" +BLOCKSCOUT_HOST = "blockscout.shude.unifra.xyz" +BLOCKSCOUT_BACKEND_HOST = "blockscout-backend.shude.unifra.xyz" +ADMIN_SYSTEM_DASHBOARD_HOST = "admin-system-dashboard.shude.unifra.xyz" +L1_DEVNET_HOST = "l1-devnet.shude.unifra.xyz" +L1_EXPLORER_HOST = "l1-explorer.shude.unifra.xyz" +GRAFANA_HOST = "grafana.shude.unifra.xyz" +TSO_HOST = "tso.shude.unifra.xyz" +CELESTIA_HOST = "celestia.shude.unifra.xyz" +DOGECOIN_HOST = "dogecoin.shude.unifra.xyz" +RPC_GATEWAY_WS_HOST = "ws.rpc.shude.unifra.xyz" diff --git a/scripts/deterministic/shell/deploy.sh b/scripts/deterministic/shell/deploy.sh new file mode 100755 index 00000000..e7319a48 --- /dev/null +++ b/scripts/deterministic/shell/deploy.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +export FOUNDRY_EVM_VERSION="cancun" +export FOUNDRY_BYTECODE_HASH="none" + +if [ "${L1_RPC_ENDPOINT}" = "" ]; then + echo "L1_RPC_ENDPOINT is not set" + exit +fi + +if [ "$L2_RPC_ENDPOINT" = "" ]; then + echo "L2_RPC_ENDPOINT is not set" + exit +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/deterministic/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/deterministic/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/deterministic/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/deterministic/DeployScroll.s.sol:DeployScroll --rpc-url "$L2_RPC_ENDPOINT" --batch-size "$BATCH_SIZE" --sig "run(string,string)" "L2" "verify-config" --broadcast --legacy || exit 1 \ No newline at end of file diff --git a/scripts/deterministic/shell/update-configs.sh b/scripts/deterministic/shell/update-configs.sh new file mode 100755 index 00000000..07ed9c9c --- /dev/null +++ b/scripts/deterministic/shell/update-configs.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +echo "" +echo "generating config-contracts.toml" +forge script scripts/deterministic/DeployScroll.s.sol:DeployScroll --sig "run(string,string)" "none" "write-config" || exit 1 + +echo "" +echo "updating genesis.yaml" +forge script scripts/deterministic/GenerateGenesis.s.sol:GenerateGenesis || exit 1 + +echo "" +echo "updating rollup-config.yaml" +forge script scripts/deterministic/GenerateConfigs.s.sol:GenerateRollupConfig || exit 1 + +echo "" +echo "updating coordinator-config.yaml" +forge script scripts/deterministic/GenerateConfigs.s.sol:GenerateCoordinatorConfig || exit 1 + +# echo "" +# echo "updating chain-monitor-config.yaml" +# forge script scripts/deterministic/GenerateConfigs.s.sol:GenerateChainMonitorConfig || exit 1 + +# echo "" +# echo "updating bridge-history-config.yaml" +# forge script scripts/deterministic/GenerateConfigs.s.sol:GenerateBridgeHistoryConfig || exit 1 + +# echo "" +# echo "updating balance-checker-config.yaml" +# forge script scripts/deterministic/GenerateConfigs.s.sol:GenerateBalanceCheckerConfig || exit 1 \ No newline at end of file diff --git a/scripts/foundry/DeployL1BridgeContracts.s.sol b/scripts/foundry/DeployL1BridgeContracts.s.sol index 3eeb3a83..afefa2e1 100644 --- a/scripts/foundry/DeployL1BridgeContracts.s.sol +++ b/scripts/foundry/DeployL1BridgeContracts.s.sol @@ -159,7 +159,8 @@ contract DeployL1BridgeContracts is Script { L2_DOGEOS_MESSENGER_PROXY_ADDR, L1_SCROLL_CHAIN_PROXY_ADDR, L1_MESSAGE_QUEUE_V1_PROXY_ADDR, - L1_MESSAGE_QUEUE_V2_PROXY_ADDR + L1_MESSAGE_QUEUE_V2_PROXY_ADDR, + address(enforcedTxGateway) ); logAddress("L1_SCROLL_MESSENGER_IMPLEMENTATION_ADDR", address(impl)); diff --git a/scripts/foundry/InitializeL1ScrollOwner.s.sol b/scripts/foundry/InitializeL1ScrollOwner.s.sol index 4e07eb8d..0055df38 100644 --- a/scripts/foundry/InitializeL1ScrollOwner.s.sol +++ b/scripts/foundry/InitializeL1ScrollOwner.s.sol @@ -180,11 +180,6 @@ contract InitializeL1ScrollOwner is Script { _selectors[0] = ScrollChain.addSequencer.selector; _selectors[1] = ScrollChain.addProver.selector; owner.updateAccess(L1_SCROLL_CHAIN_PROXY_ADDR, _selectors, TIMELOCK_1DAY_DELAY_ROLE, true); - - // delay 7 day, scroll multisig - _selectors = new bytes4[](1); - _selectors[0] = ScrollChain.updateMaxNumTxInChunk.selector; - owner.updateAccess(L1_SCROLL_CHAIN_PROXY_ADDR, _selectors, TIMELOCK_7DAY_DELAY_ROLE, true); } function configL1MessageQueue() internal { @@ -207,11 +202,6 @@ contract InitializeL1ScrollOwner is Script { _selectors[0] = ScrollMessengerBase.setPause.selector; owner.updateAccess(L1_SCROLL_MESSENGER_PROXY_ADDR, _selectors, SCROLL_MULTISIG_NO_DELAY_ROLE, true); owner.updateAccess(L1_SCROLL_MESSENGER_PROXY_ADDR, _selectors, EMERGENCY_MULTISIG_NO_DELAY_ROLE, true); - - // delay 1 day, scroll multisig - _selectors = new bytes4[](1); - _selectors[0] = L1ScrollMessenger.updateMaxReplayTimes.selector; - owner.updateAccess(L1_SCROLL_MESSENGER_PROXY_ADDR, _selectors, TIMELOCK_1DAY_DELAY_ROLE, true); } function configL2GasPriceOracle() internal { diff --git a/src/L1/IL1ScrollMessenger.sol b/src/L1/IL1ScrollMessenger.sol index 4a8f2b9f..0af44027 100644 --- a/src/L1/IL1ScrollMessenger.sol +++ b/src/L1/IL1ScrollMessenger.sol @@ -5,15 +5,6 @@ pragma solidity ^0.8.24; import {IScrollMessenger} from "../libraries/IScrollMessenger.sol"; interface IL1ScrollMessenger is IScrollMessenger { - /********** - * Events * - **********/ - - /// @notice Emitted when the maximum number of times each message can be replayed is updated. - /// @param oldMaxReplayTimes The old maximum number of times each message can be replayed. - /// @param newMaxReplayTimes The new maximum number of times each message can be replayed. - event UpdateMaxReplayTimes(uint256 oldMaxReplayTimes, uint256 newMaxReplayTimes); - /*********** * Structs * ***********/ @@ -62,18 +53,4 @@ interface IL1ScrollMessenger is IScrollMessenger { uint32 newGasLimit, address refundAddress ) external payable; - - /// @notice Drop a skipped message. - /// @param from The address of the sender of the message. - /// @param to The address of the recipient of the message. - /// @param value The msg.value passed to the message call. - /// @param messageNonce The nonce for the message to drop. - /// @param message The content of the message. - function dropMessage( - address from, - address to, - uint256 value, - uint256 messageNonce, - bytes memory message - ) external; } diff --git a/src/L1/L1ScrollMessenger.sol b/src/L1/L1ScrollMessenger.sol index 8f2141d7..9f98a9da 100644 --- a/src/L1/L1ScrollMessenger.sol +++ b/src/L1/L1ScrollMessenger.sol @@ -10,8 +10,6 @@ import {IScrollMessenger} from "../libraries/IScrollMessenger.sol"; import {ScrollMessengerBase} from "../libraries/ScrollMessengerBase.sol"; import {WithdrawTrieVerifier} from "../libraries/verifier/WithdrawTrieVerifier.sol"; -import {IMessageDropCallback} from "../libraries/callbacks/IMessageDropCallback.sol"; - // solhint-disable avoid-low-level-calls // solhint-disable not-rely-on-time // solhint-disable reason-string @@ -53,6 +51,9 @@ contract L1ScrollMessenger is ScrollMessengerBase, IL1ScrollMessenger { /// @notice The address of L1MessageQueueV2 contract. address public immutable messageQueueV2; + /// @notice The address of `EnforcedTxGateway`. + address public immutable enforcedTxGateway; + /*********** * Structs * ***********/ @@ -75,16 +76,21 @@ contract L1ScrollMessenger is ScrollMessengerBase, IL1ScrollMessenger { mapping(bytes32 => bool) public isL2MessageExecuted; /// @notice Mapping from L1 message hash to drop status. - mapping(bytes32 => bool) public isL1MessageDropped; + /// @custom:deprecated This is no longer used. + // slither-disable-next-line uninitialized-state + mapping(bytes32 => bool) private __isL1MessageDropped; /// @dev The storage slot used as Rollup contract, which is deprecated now. + /// @custom:deprecated This is no longer used. address private __rollup; /// @dev The storage slot used as L1MessageQueue contract, which is deprecated now. + /// @custom:deprecated This is no longer used. address private __messageQueue; - /// @notice The maximum number of times each L1 message can be replayed. - uint256 public maxReplayTimes; + /// @dev The maximum number of times each L1 message can be replayed. + /// @custom:deprecated This is no longer used. + uint256 private __maxReplayTimes; /// @notice Mapping from L1 message hash to replay state. mapping(bytes32 => ReplayState) public replayStates; @@ -110,17 +116,15 @@ contract L1ScrollMessenger is ScrollMessengerBase, IL1ScrollMessenger { address _counterpart, address _rollup, address _messageQueueV1, - address _messageQueueV2 + address _messageQueueV2, + address _enforcedTxGateway ) ScrollMessengerBase(_counterpart) { - if (_rollup == address(0) || _messageQueueV1 == address(0) || _messageQueueV2 == address(0)) { - revert ErrorZeroAddress(); - } - _disableInitializers(); rollup = _rollup; messageQueueV1 = _messageQueueV1; messageQueueV2 = _messageQueueV2; + enforcedTxGateway = _enforcedTxGateway; } /// @notice Initialize the storage of L1ScrollMessenger. @@ -141,9 +145,6 @@ contract L1ScrollMessenger is ScrollMessengerBase, IL1ScrollMessenger { __rollup = _rollup; __messageQueue = _messageQueue; - - maxReplayTimes = 3; - emit UpdateMaxReplayTimes(0, 3); } /***************************** @@ -193,7 +194,7 @@ contract L1ScrollMessenger is ScrollMessengerBase, IL1ScrollMessenger { } // @note check more `_to` address to avoid attack in the future when we add more gateways. - if (_to == messageQueueV1 || _to == messageQueueV2) { + if (_to == messageQueueV1 || _to == messageQueueV2 || _to == enforcedTxGateway) { revert ErrorForbidToCallMessageQueue(); } _validateTargetAddress(_to); @@ -202,6 +203,8 @@ contract L1ScrollMessenger is ScrollMessengerBase, IL1ScrollMessenger { require(_from != xDomainMessageSender, "Invalid message sender"); xDomainMessageSender = _from; + // xDomainMessageSender serves as reentrancy guard (notInExecution modifier). + // slither-disable-next-line reentrancy-eth (bool success, ) = _to.call{value: _value}(_message); // reset value to refund gas. xDomainMessageSender = ScrollConstants.DEFAULT_XDOMAIN_MESSAGE_SENDER; @@ -233,7 +236,7 @@ contract L1ScrollMessenger is ScrollMessengerBase, IL1ScrollMessenger { require(messageSendTimestamp[_xDomainCalldataHash] > 0, "Provided message has not been enqueued"); // cannot replay dropped message - require(!isL1MessageDropped[_xDomainCalldataHash], "Message already dropped"); + require(!__isL1MessageDropped[_xDomainCalldataHash], "Message already dropped"); // compute and deduct the messaging fee to fee vault. uint256 _fee = IL1MessageQueueV2(messageQueueV2).estimateCrossDomainMessageFee(_newGasLimit); @@ -261,8 +264,6 @@ contract L1ScrollMessenger is ScrollMessengerBase, IL1ScrollMessenger { } _replayState.lastIndex = uint128(_nextQueueIndex); - // update replay times - require(_replayState.times < maxReplayTimes, "Exceed maximum replay times"); unchecked { _replayState.times += 1; } @@ -278,74 +279,6 @@ contract L1ScrollMessenger is ScrollMessengerBase, IL1ScrollMessenger { } } - /// @inheritdoc IL1ScrollMessenger - /// @dev Since we don't skip any messages in `L1MessageQueueV2`, only messages from `L1MessageQueueV1` can be dropped. - function dropMessage( - address _from, - address _to, - uint256 _value, - uint256 _messageNonce, - bytes memory _message - ) external override whenNotPaused notInExecution { - // The criteria for dropping a message: - // 1. The message is a L1 message. - // 2. The message has not been dropped before. - // 3. the message and all of its replacement are finalized in L1. - // 4. the message and all of its replacement are skipped. - // - // Possible denial of service attack: - // + replayMessage is called every time someone want to drop the message. - // + replayMessage is called so many times for a skipped message, thus results a long list. - // - // We limit the number of `replayMessage` calls of each message, which may solve the above problem. - - // check message exists - bytes memory _xDomainCalldata = _encodeXDomainCalldata(_from, _to, _value, _messageNonce, _message); - bytes32 _xDomainCalldataHash = keccak256(_xDomainCalldata); - require(messageSendTimestamp[_xDomainCalldataHash] > 0, "Provided message has not been enqueued"); - - // check message not dropped - require(!isL1MessageDropped[_xDomainCalldataHash], "Message already dropped"); - - // check message is finalized - uint256 _lastIndex = replayStates[_xDomainCalldataHash].lastIndex; - if (_lastIndex == 0) _lastIndex = _messageNonce; - - // check message is skipped and drop it. - // @note If the list is very long, the message may never be dropped. - while (true) { - // If the `_lastIndex` is from `messageQueueV2`, it will revert in `messageQueueV1.dropCrossDomainMessage`. - IL1MessageQueueV1(messageQueueV1).dropCrossDomainMessage(_lastIndex); - _lastIndex = prevReplayIndex[_lastIndex]; - if (_lastIndex == 0) break; - unchecked { - _lastIndex = _lastIndex - 1; - } - } - - isL1MessageDropped[_xDomainCalldataHash] = true; - - // set execution context - xDomainMessageSender = ScrollConstants.DROP_XDOMAIN_MESSAGE_SENDER; - IMessageDropCallback(_from).onDropMessage{value: _value}(_message); - // clear execution context - xDomainMessageSender = ScrollConstants.DEFAULT_XDOMAIN_MESSAGE_SENDER; - } - - /************************ - * Restricted Functions * - ************************/ - - /// @notice Update max replay times. - /// @dev This function can only called by contract owner. - /// @param _newMaxReplayTimes The new max replay times. - function updateMaxReplayTimes(uint256 _newMaxReplayTimes) external onlyOwner { - uint256 _oldMaxReplayTimes = maxReplayTimes; - maxReplayTimes = _newMaxReplayTimes; - - emit UpdateMaxReplayTimes(_oldMaxReplayTimes, _newMaxReplayTimes); - } - /********************** * Internal Functions * **********************/ diff --git a/src/L1/gateways/L1ERC1155Gateway.sol b/src/L1/gateways/L1ERC1155Gateway.sol index 794d43ce..9b0b4162 100644 --- a/src/L1/gateways/L1ERC1155Gateway.sol +++ b/src/L1/gateways/L1ERC1155Gateway.sol @@ -9,7 +9,6 @@ import {IL2ERC1155Gateway} from "../../L2/gateways/IL2ERC1155Gateway.sol"; import {IL1ScrollMessenger} from "../IL1ScrollMessenger.sol"; import {IL1ERC1155Gateway} from "./IL1ERC1155Gateway.sol"; -import {IMessageDropCallback} from "../../libraries/callbacks/IMessageDropCallback.sol"; import {ScrollGatewayBase} from "../../libraries/gateway/ScrollGatewayBase.sol"; /// @title L1ERC1155Gateway @@ -19,7 +18,7 @@ import {ScrollGatewayBase} from "../../libraries/gateway/ScrollGatewayBase.sol"; /// NFT will be transfer to the recipient directly. /// /// This will be changed if we have more specific scenarios. -contract L1ERC1155Gateway is ERC1155HolderUpgradeable, ScrollGatewayBase, IL1ERC1155Gateway, IMessageDropCallback { +contract L1ERC1155Gateway is ERC1155HolderUpgradeable, ScrollGatewayBase, IL1ERC1155Gateway { /********** * Events * **********/ @@ -139,31 +138,6 @@ contract L1ERC1155Gateway is ERC1155HolderUpgradeable, ScrollGatewayBase, IL1ERC emit FinalizeBatchWithdrawERC1155(_l1Token, _l2Token, _from, _to, _tokenIds, _amounts); } - /// @inheritdoc IMessageDropCallback - function onDropMessage(bytes calldata _message) external payable virtual onlyInDropContext nonReentrant { - require(msg.value == 0, "nonzero msg.value"); - - if (bytes4(_message[0:4]) == IL2ERC1155Gateway.finalizeDepositERC1155.selector) { - (address _token, , address _sender, , uint256 _tokenId, uint256 _amount) = abi.decode( - _message[4:], - (address, address, address, address, uint256, uint256) - ); - IERC1155Upgradeable(_token).safeTransferFrom(address(this), _sender, _tokenId, _amount, ""); - - emit RefundERC1155(_token, _sender, _tokenId, _amount); - } else if (bytes4(_message[0:4]) == IL2ERC1155Gateway.finalizeBatchDepositERC1155.selector) { - (address _token, , address _sender, , uint256[] memory _tokenIds, uint256[] memory _amounts) = abi.decode( - _message[4:], - (address, address, address, address, uint256[], uint256[]) - ); - IERC1155Upgradeable(_token).safeBatchTransferFrom(address(this), _sender, _tokenIds, _amounts, ""); - - emit BatchRefundERC1155(_token, _sender, _tokenIds, _amounts); - } else { - revert("invalid selector"); - } - } - /************************ * Restricted Functions * ************************/ diff --git a/src/L1/gateways/L1ERC20Gateway.sol b/src/L1/gateways/L1ERC20Gateway.sol index 42c51144..c9c5820a 100644 --- a/src/L1/gateways/L1ERC20Gateway.sol +++ b/src/L1/gateways/L1ERC20Gateway.sol @@ -10,12 +10,11 @@ import {IL1GatewayRouter} from "./IL1GatewayRouter.sol"; import {IL2ERC20Gateway} from "../../L2/gateways/IL2ERC20Gateway.sol"; import {ScrollGatewayBase} from "../../libraries/gateway/ScrollGatewayBase.sol"; -import {IMessageDropCallback} from "../../libraries/callbacks/IMessageDropCallback.sol"; /// @title L1ERC20Gateway /// @notice The `L1ERC20Gateway` as a base contract for ERC20 gateways in L1. /// It has implementation of common used functions for ERC20 gateways. -abstract contract L1ERC20Gateway is IL1ERC20Gateway, IMessageDropCallback, ScrollGatewayBase { +abstract contract L1ERC20Gateway is IL1ERC20Gateway, ScrollGatewayBase { using SafeERC20Upgradeable for IERC20Upgradeable; /************* @@ -79,25 +78,6 @@ abstract contract L1ERC20Gateway is IL1ERC20Gateway, IMessageDropCallback, Scrol emit FinalizeWithdrawERC20(_l1Token, _l2Token, _from, _to, _amount, _data); } - /// @inheritdoc IMessageDropCallback - function onDropMessage(bytes calldata _message) external payable virtual onlyInDropContext nonReentrant { - // _message should start with 0x8431f5c1 => finalizeDepositERC20(address,address,address,address,uint256,bytes) - require(bytes4(_message[0:4]) == IL2ERC20Gateway.finalizeDepositERC20.selector, "invalid selector"); - - // decode (token, receiver, amount) - (address _token, , address _receiver, , uint256 _amount, ) = abi.decode( - _message[4:], - (address, address, address, address, uint256, bytes) - ); - - // do dome check for each custom gateway - _beforeDropMessage(_token, _receiver, _amount); - - IERC20Upgradeable(_token).safeTransfer(_receiver, _amount); - - emit RefundERC20(_token, _receiver, _amount); - } - /********************** * Internal Functions * **********************/ diff --git a/src/L1/gateways/L1ERC721Gateway.sol b/src/L1/gateways/L1ERC721Gateway.sol index f8ee6113..9055512b 100644 --- a/src/L1/gateways/L1ERC721Gateway.sol +++ b/src/L1/gateways/L1ERC721Gateway.sol @@ -9,7 +9,6 @@ import {IL2ERC721Gateway} from "../../L2/gateways/IL2ERC721Gateway.sol"; import {IL1ScrollMessenger} from "../IL1ScrollMessenger.sol"; import {IL1ERC721Gateway} from "./IL1ERC721Gateway.sol"; -import {IMessageDropCallback} from "../../libraries/callbacks/IMessageDropCallback.sol"; import {ScrollGatewayBase} from "../../libraries/gateway/ScrollGatewayBase.sol"; /// @title L1ERC721Gateway @@ -19,7 +18,7 @@ import {ScrollGatewayBase} from "../../libraries/gateway/ScrollGatewayBase.sol"; /// NFT will be transfer to the recipient directly. /// /// This will be changed if we have more specific scenarios. -contract L1ERC721Gateway is ERC721HolderUpgradeable, ScrollGatewayBase, IL1ERC721Gateway, IMessageDropCallback { +contract L1ERC721Gateway is ERC721HolderUpgradeable, ScrollGatewayBase, IL1ERC721Gateway { /********** * Events * **********/ @@ -137,32 +136,6 @@ contract L1ERC721Gateway is ERC721HolderUpgradeable, ScrollGatewayBase, IL1ERC72 emit FinalizeBatchWithdrawERC721(_l1Token, _l2Token, _from, _to, _tokenIds); } - /// @inheritdoc IMessageDropCallback - function onDropMessage(bytes calldata _message) external payable virtual onlyInDropContext nonReentrant { - require(msg.value == 0, "nonzero msg.value"); - - if (bytes4(_message[0:4]) == IL2ERC721Gateway.finalizeDepositERC721.selector) { - (address _token, , address _receiver, , uint256 _tokenId) = abi.decode( - _message[4:], - (address, address, address, address, uint256) - ); - IERC721Upgradeable(_token).safeTransferFrom(address(this), _receiver, _tokenId); - - emit RefundERC721(_token, _receiver, _tokenId); - } else if (bytes4(_message[0:4]) == IL2ERC721Gateway.finalizeBatchDepositERC721.selector) { - (address _token, , address _receiver, , uint256[] memory _tokenIds) = abi.decode( - _message[4:], - (address, address, address, address, uint256[]) - ); - for (uint256 i = 0; i < _tokenIds.length; i++) { - IERC721Upgradeable(_token).safeTransferFrom(address(this), _receiver, _tokenIds[i]); - } - emit BatchRefundERC721(_token, _receiver, _tokenIds); - } else { - revert("invalid selector"); - } - } - /************************ * Restricted Functions * ************************/ diff --git a/src/L1/gateways/L1ETHGateway.sol b/src/L1/gateways/L1ETHGateway.sol index c268a1ba..e6e4a5a4 100644 --- a/src/L1/gateways/L1ETHGateway.sol +++ b/src/L1/gateways/L1ETHGateway.sol @@ -6,7 +6,6 @@ import {IL2ETHGateway} from "../../L2/gateways/IL2ETHGateway.sol"; import {IL1ScrollMessenger} from "../IL1ScrollMessenger.sol"; import {IL1ETHGateway} from "./IL1ETHGateway.sol"; -import {IMessageDropCallback} from "../../libraries/callbacks/IMessageDropCallback.sol"; import {ScrollGatewayBase} from "../../libraries/gateway/ScrollGatewayBase.sol"; // solhint-disable avoid-low-level-calls @@ -16,7 +15,7 @@ import {ScrollGatewayBase} from "../../libraries/gateway/ScrollGatewayBase.sol"; /// finalize withdraw ETH from layer 2. /// @dev The deposited ETH tokens are held in this gateway. On finalizing withdraw, the corresponding /// ETH will be transfer to the recipient directly. -contract L1ETHGateway is ScrollGatewayBase, IL1ETHGateway, IMessageDropCallback { +contract L1ETHGateway is ScrollGatewayBase, IL1ETHGateway { /*************** * Constructor * ***************/ @@ -90,6 +89,8 @@ contract L1ETHGateway is ScrollGatewayBase, IL1ETHGateway, IMessageDropCallback // @note can possible trigger reentrant call to messenger, // but it seems not a big problem. + // no reentrancy risk (nonReentrant modifier). + // slither-disable-next-line arbitrary-send-eth (bool _success, ) = _to.call{value: _amount}(""); require(_success, "ETH transfer failed"); @@ -98,22 +99,6 @@ contract L1ETHGateway is ScrollGatewayBase, IL1ETHGateway, IMessageDropCallback emit FinalizeWithdrawETH(_from, _to, _amount, _data); } - /// @inheritdoc IMessageDropCallback - function onDropMessage(bytes calldata _message) external payable virtual onlyInDropContext nonReentrant { - // _message should start with 0x232e8748 => finalizeDepositETH(address,address,uint256,bytes) - require(bytes4(_message[0:4]) == IL2ETHGateway.finalizeDepositETH.selector, "invalid selector"); - - // decode (receiver, amount) - (address _receiver, , uint256 _amount, ) = abi.decode(_message[4:], (address, address, uint256, bytes)); - - require(_amount == msg.value, "msg.value mismatch"); - - (bool _success, ) = _receiver.call{value: _amount}(""); - require(_success, "ETH transfer failed"); - - emit RefundETH(_receiver, _amount); - } - /********************** * Internal Functions * **********************/ diff --git a/src/L1/gateways/L1GatewayRouter.sol b/src/L1/gateways/L1GatewayRouter.sol index 25bf1aa7..5e9f30d4 100644 --- a/src/L1/gateways/L1GatewayRouter.sol +++ b/src/L1/gateways/L1GatewayRouter.sol @@ -104,7 +104,7 @@ contract L1GatewayRouter is OwnableUpgradeable, IL1GatewayRouter { *****************************/ /// @inheritdoc IL1GatewayRouter - /// @dev All the gateways should have reentrancy guard to prevent potential attack though this function. + /// @dev All the gateways should have reentrancy guard to prevent potential attack through this function. function requestERC20( address _sender, address _token, @@ -112,7 +112,11 @@ contract L1GatewayRouter is OwnableUpgradeable, IL1GatewayRouter { ) external onlyInContext returns (uint256) { address _caller = _msgSender(); uint256 _balance = IERC20Upgradeable(_token).balanceOf(_caller); + + // only whitelisted caller allowed (onlyInContext modifier). + // slither-disable-next-line arbitrary-send-erc20 IERC20Upgradeable(_token).safeTransferFrom(_sender, _caller, _amount); + _amount = IERC20Upgradeable(_token).balanceOf(_caller) - _balance; return _amount; } @@ -157,6 +161,8 @@ contract L1GatewayRouter is OwnableUpgradeable, IL1GatewayRouter { // encode msg.sender with _data bytes memory _routerData = abi.encode(_msgSender(), _data); + // gatewayInContext serves as reentrancy guard (onlyNotInContext modifier). + // slither-disable-next-line reentrancy-eth IL1ERC20Gateway(_gateway).depositERC20AndCall{value: msg.value}(_token, _to, _amount, _routerData, _gasLimit); // leave deposit context diff --git a/src/L1/gateways/L1WETHGateway.sol b/src/L1/gateways/L1WETHGateway.sol index 9adfd7af..8c0f4b22 100644 --- a/src/L1/gateways/L1WETHGateway.sol +++ b/src/L1/gateways/L1WETHGateway.sol @@ -102,6 +102,8 @@ contract L1WETHGateway is L1ERC20Gateway { require(_l2Token == l2WETH, "l2 token not WETH"); require(_amount == msg.value, "msg.value mismatch"); + // deposit to WETH token is safe. + // slither-disable-next-line arbitrary-send-eth IWETH(_l1Token).deposit{value: _amount}(); } @@ -114,6 +116,8 @@ contract L1WETHGateway is L1ERC20Gateway { require(_token == WETH, "token not WETH"); require(_amount == msg.value, "msg.value mismatch"); + // deposit to WETH token is safe. + // slither-disable-next-line arbitrary-send-eth IWETH(_token).deposit{value: _amount}(); } diff --git a/src/L1/rollup/IL1MessageQueueV1.sol b/src/L1/rollup/IL1MessageQueueV1.sol index e90b60c7..41c2de8e 100644 --- a/src/L1/rollup/IL1MessageQueueV1.sol +++ b/src/L1/rollup/IL1MessageQueueV1.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.24; +/// @custom:deprecated This contract is no longer used in production. interface IL1MessageQueueV1 { /********** * Events * @@ -63,26 +64,32 @@ interface IL1MessageQueueV1 { *************************/ /// @notice The start index of all pending inclusion messages. + /// @custom:deprecated Please use `IL1MessageQueueV2.pendingQueueIndex` instead. function pendingQueueIndex() external view returns (uint256); /// @notice The start index of all unfinalized messages. /// @dev All messages from `nextUnfinalizedQueueIndex` to `pendingQueueIndex-1` are committed but not finalized. + /// @custom:deprecated Please use `IL1MessageQueueV2.nextUnfinalizedQueueIndex` instead. function nextUnfinalizedQueueIndex() external view returns (uint256); /// @notice Return the index of next appended message. /// @dev Also the total number of appended messages. + /// @custom:deprecated Please use `IL1MessageQueueV2.nextCrossDomainMessageIndex` instead. function nextCrossDomainMessageIndex() external view returns (uint256); /// @notice Return the message of in `queueIndex`. /// @param queueIndex The index to query. + /// @custom:deprecated Please use `IL1MessageQueueV2.getCrossDomainMessage` instead. function getCrossDomainMessage(uint256 queueIndex) external view returns (bytes32); /// @notice Return the amount of ETH should pay for cross domain message. /// @param gasLimit Gas limit required to complete the message relay on L2. + /// @custom:deprecated Please use `IL1MessageQueueV2.estimateCrossDomainMessageFee` instead. function estimateCrossDomainMessageFee(uint256 gasLimit) external view returns (uint256); /// @notice Return the amount of intrinsic gas fee should pay for cross domain message. /// @param _calldata The calldata of L1-initiated transaction. + /// @custom:deprecated Please use `IL1MessageQueueV2.calculateIntrinsicGasFee` instead. function calculateIntrinsicGasFee(bytes calldata _calldata) external view returns (uint256); /// @notice Return the hash of a L1 message. @@ -92,6 +99,7 @@ interface IL1MessageQueueV1 { /// @param target The address of target. /// @param gasLimit The gas limit provided. /// @param data The calldata passed to target address. + /// @custom:deprecated Please use `IL1MessageQueueV2.computeTransactionHash` instead. function computeTransactionHash( address sender, uint256 queueIndex, @@ -103,10 +111,12 @@ interface IL1MessageQueueV1 { /// @notice Return whether the message is skipped. /// @param queueIndex The queue index of the message to check. + /// @custom:deprecated function isMessageSkipped(uint256 queueIndex) external view returns (bool); /// @notice Return whether the message is dropped. /// @param queueIndex The queue index of the message to check. + /// @custom:deprecated function isMessageDropped(uint256 queueIndex) external view returns (bool); /***************************** @@ -117,6 +127,7 @@ interface IL1MessageQueueV1 { /// @param target The address of target contract to call in L2. /// @param gasLimit The maximum gas should be used for relay this message in L2. /// @param data The calldata passed to target contract. + /// @custom:deprecated Please use `IL1MessageQueueV2.appendCrossDomainMessage` instead. function appendCrossDomainMessage( address target, uint256 gasLimit, @@ -130,6 +141,7 @@ interface IL1MessageQueueV1 { /// @param value The value passed /// @param gasLimit The maximum gas should be used for this transaction in L2. /// @param data The calldata passed to target contract. + /// @custom:deprecated Please use `IL1MessageQueueV2.appendEnforcedTransaction` instead. function appendEnforcedTransaction( address sender, address target, @@ -146,6 +158,7 @@ interface IL1MessageQueueV1 { /// @param startIndex The start index to pop. /// @param count The number of messages to pop. /// @param skippedBitmap A bitmap indicates whether a message is skipped. + /// @custom:deprecated function popCrossDomainMessage( uint256 startIndex, uint256 count, @@ -157,12 +170,15 @@ interface IL1MessageQueueV1 { /// @dev We can only reset unfinalized popped messages. /// /// @param startIndex The start index to reset. + /// @custom:deprecated function resetPoppedCrossDomainMessage(uint256 startIndex) external; /// @notice Finalize status of popped messages. /// @param newFinalizedQueueIndexPlusOne The index of message to finalize plus one. + /// @custom:deprecated function finalizePoppedCrossDomainMessage(uint256 newFinalizedQueueIndexPlusOne) external; /// @notice Drop a skipped message from the queue. + /// @custom:deprecated function dropCrossDomainMessage(uint256 index) external; } diff --git a/src/L1/rollup/IL1MessageQueueWithGasPriceOracle.sol b/src/L1/rollup/IL1MessageQueueWithGasPriceOracle.sol index c4f697d9..88937ffd 100644 --- a/src/L1/rollup/IL1MessageQueueWithGasPriceOracle.sol +++ b/src/L1/rollup/IL1MessageQueueWithGasPriceOracle.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.24; import {IL1MessageQueueV1} from "./IL1MessageQueueV1.sol"; +/// @custom:deprecated This contract is no longer used in production. interface IL1MessageQueueWithGasPriceOracle is IL1MessageQueueV1 { /********** * Events * diff --git a/src/L1/rollup/IL2GasPriceOracle.sol b/src/L1/rollup/IL2GasPriceOracle.sol index 773c1c95..e7da38ec 100644 --- a/src/L1/rollup/IL2GasPriceOracle.sol +++ b/src/L1/rollup/IL2GasPriceOracle.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.24; +/// @custom:deprecated This contract is no longer used in production. interface IL2GasPriceOracle { /// @notice Return the latest known l2 base fee. function l2BaseFee() external view returns (uint256); diff --git a/src/L1/rollup/IScrollChain.sol b/src/L1/rollup/IScrollChain.sol index 453dd169..b22a061b 100644 --- a/src/L1/rollup/IScrollChain.sol +++ b/src/L1/rollup/IScrollChain.sol @@ -41,11 +41,6 @@ interface IScrollChain { /// @param status The status of the account updated. event UpdateProver(address indexed account, bool status); - /// @notice Emitted when the value of `maxNumTxInChunk` is updated. - /// @param oldMaxNumTxInChunk The old value of `maxNumTxInChunk`. - /// @param newMaxNumTxInChunk The new value of `maxNumTxInChunk`. - event UpdateMaxNumTxInChunk(uint256 oldMaxNumTxInChunk, uint256 newMaxNumTxInChunk); - /// @notice Emitted when we enter or exit enforced batch mode. /// @param enabled True if we are entering enforced batch mode, false otherwise. /// @param lastCommittedBatchIndex The index of the last committed batch. @@ -78,26 +73,6 @@ interface IScrollChain { * Public Mutating Functions * *****************************/ - /// @notice Commit a batch of transactions on layer 1 with blob data proof. - /// - /// @dev Memory layout of `blobDataProof`: - /// | z | y | kzg_commitment | kzg_proof | - /// |---------|---------|----------------|-----------| - /// | bytes32 | bytes32 | bytes48 | bytes48 | - /// - /// @param version The version of current batch. - /// @param parentBatchHeader The header of parent batch. - /// @param chunks The list of encoded chunks, see the comments of `ChunkCodec`. - /// @param skippedL1MessageBitmap The bitmap indicates whether each L1 message is skipped or not. - /// @param blobDataProof The proof for blob data. - function commitBatchWithBlobProof( - uint8 version, - bytes calldata parentBatchHeader, - bytes[] memory chunks, - bytes calldata skippedL1MessageBitmap, - bytes calldata blobDataProof - ) external; - /// @notice Commit one or more batches after the EuclidV2 upgrade. /// @param version The version of the committed batches. /// @param parentBatchHash The hash of parent batch. @@ -114,18 +89,6 @@ interface IScrollChain { /// @param batchHeader The header of the last batch we want to keep. function revertBatch(bytes calldata batchHeader) external; - /// @notice Finalize a list of committed batches (i.e. bundle) on layer 1. - /// @param batchHeader The header of last batch in current bundle, see the encoding in comments of `commitBatch. - /// @param postStateRoot The state root after current bundle. - /// @param withdrawRoot The withdraw trie root after current batch. - /// @param aggrProof The aggregation proof for current bundle. - function finalizeBundleWithProof( - bytes calldata batchHeader, - bytes32 postStateRoot, - bytes32 withdrawRoot, - bytes calldata aggrProof - ) external; - /// @notice Finalize a list of committed batches (i.e. bundle) on layer 1 after the EuclidV2 upgrade. /// @param batchHeader The header of the last batch in this bundle. /// @param totalL1MessagesPoppedOverall The number of messages processed after this bundle. diff --git a/src/L1/rollup/L1MessageQueueV1.sol b/src/L1/rollup/L1MessageQueueV1.sol index bc24be86..dc5d8ffb 100644 --- a/src/L1/rollup/L1MessageQueueV1.sol +++ b/src/L1/rollup/L1MessageQueueV1.sol @@ -17,6 +17,7 @@ import {AddressAliasHelper} from "../../libraries/common/AddressAliasHelper.sol" /// @title L1MessageQueue /// @notice This contract will hold all L1 to L2 messages. /// Each appended message is assigned with a unique and increasing `uint256` index. +/// @custom:deprecated This contract is no longer used in production. contract L1MessageQueueV1 is OwnableUpgradeable, IL1MessageQueueV1 { using BitMapsUpgradeable for BitMapsUpgradeable.BitMap; diff --git a/src/L1/rollup/L1MessageQueueV1WithGasPriceOracle.sol b/src/L1/rollup/L1MessageQueueV1WithGasPriceOracle.sol index 40873d94..b84f2bd0 100644 --- a/src/L1/rollup/L1MessageQueueV1WithGasPriceOracle.sol +++ b/src/L1/rollup/L1MessageQueueV1WithGasPriceOracle.sol @@ -9,6 +9,7 @@ import {IL2GasPriceOracle} from "./IL2GasPriceOracle.sol"; import {L1MessageQueueV1} from "./L1MessageQueueV1.sol"; +/// @custom:deprecated This contract is no longer used in production. contract L1MessageQueueV1WithGasPriceOracle is L1MessageQueueV1, IL1MessageQueueWithGasPriceOracle { /************* * Constants * diff --git a/src/L1/rollup/L1MessageQueueV2.sol b/src/L1/rollup/L1MessageQueueV2.sol index bd2d2ff5..0b7961ec 100644 --- a/src/L1/rollup/L1MessageQueueV2.sol +++ b/src/L1/rollup/L1MessageQueueV2.sol @@ -53,7 +53,8 @@ contract L1MessageQueueV2 is OwnableUpgradeable, IL1MessageQueueV2 { uint256 private constant INTRINSIC_GAS_TX = 21000; /// @notice The appropriate intrinsic gas for each byte. - uint256 private constant APPROPRIATE_INTRINSIC_GAS_PER_BYTE = 16; + // @dev This accounts for both intrinsic gas and EIP-7623 floor gas. + uint256 private constant APPROPRIATE_INTRINSIC_GAS_PER_BYTE = 40; uint256 private constant PRECISION = 1e18; diff --git a/src/L1/rollup/L2GasPriceOracle.sol b/src/L1/rollup/L2GasPriceOracle.sol index 4585621a..7fe65029 100644 --- a/src/L1/rollup/L2GasPriceOracle.sol +++ b/src/L1/rollup/L2GasPriceOracle.sol @@ -10,6 +10,7 @@ import {IL2GasPriceOracle} from "./IL2GasPriceOracle.sol"; // solhint-disable reason-string +/// @custom:deprecated This contract is no longer used in production. contract L2GasPriceOracle is OwnableUpgradeable, IL2GasPriceOracle { /********** * Events * diff --git a/src/L1/rollup/ScrollChain.sol b/src/L1/rollup/ScrollChain.sol index 30f1be30..e8e0954b 100644 --- a/src/L1/rollup/ScrollChain.sol +++ b/src/L1/rollup/ScrollChain.sol @@ -12,8 +12,6 @@ import {BatchHeaderV0Codec} from "../../libraries/codec/BatchHeaderV0Codec.sol"; import {BatchHeaderV1Codec} from "../../libraries/codec/BatchHeaderV1Codec.sol"; import {BatchHeaderV3Codec} from "../../libraries/codec/BatchHeaderV3Codec.sol"; import {BatchHeaderV7Codec} from "../../libraries/codec/BatchHeaderV7Codec.sol"; -import {ChunkCodecV0} from "../../libraries/codec/ChunkCodecV0.sol"; -import {ChunkCodecV1} from "../../libraries/codec/ChunkCodecV1.sol"; import {IRollupVerifier} from "../../libraries/verifier/IRollupVerifier.sol"; import {SystemConfig} from "../system-contract/SystemConfig.sol"; @@ -31,27 +29,18 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { /// @dev Thrown when the given account is not EOA account. error ErrorAccountIsNotEOA(); - /// @dev Thrown when committing a committed batch. - error ErrorBatchIsAlreadyCommitted(); - /// @dev Thrown when finalizing a verified batch. error ErrorBatchIsAlreadyVerified(); /// @dev Thrown when committing empty batch (batch without chunks) error ErrorBatchIsEmpty(); - /// @dev Thrown when call precompile failed. - error ErrorCallPointEvaluationPrecompileFailed(); - /// @dev Thrown when the caller is not prover. error ErrorCallerIsNotProver(); /// @dev Thrown when the caller is not sequencer. error ErrorCallerIsNotSequencer(); - /// @dev Thrown when the transaction has multiple blobs. - error ErrorFoundMultipleBlobs(); - /// @dev Thrown when some fields are not zero in genesis batch. error ErrorGenesisBatchHasNonZeroField(); @@ -70,57 +59,21 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { /// @dev Thrown when the batch version is incorrect. error ErrorIncorrectBatchVersion(); - /// @dev Thrown when the bitmap length is incorrect. - error ErrorIncorrectBitmapLength(); - - /// @dev Thrown when the last message is skipped. - error ErrorLastL1MessageSkipped(); - - /// @dev Thrown when no blob found in the transaction. - error ErrorNoBlobFound(); - - /// @dev Thrown when the number of transactions is less than number of L1 message in one block. - error ErrorNumTxsLessThanNumL1Msgs(); - /// @dev Thrown when reverting a finalized batch. error ErrorRevertFinalizedBatch(); /// @dev Thrown when the given state root is zero. error ErrorStateRootIsZero(); - /// @dev Thrown when a chunk contains too many transactions. - error ErrorTooManyTxsInOneChunk(); - - /// @dev Thrown when the precompile output is incorrect. - error ErrorUnexpectedPointEvaluationPrecompileOutput(); - /// @dev Thrown when the given address is `address(0)`. error ErrorZeroAddress(); - /// @dev Thrown when commit batch with lower version. - error ErrorCannotDowngradeVersion(); - /// @dev Thrown when we try to commit or finalize normal batch in enforced batch mode. error ErrorInEnforcedBatchMode(); /// @dev Thrown when we try to commit enforced batch while not in enforced batch mode. error ErrorNotInEnforcedBatchMode(); - /// @dev Thrown when commit old batch after Euclid fork is enabled. - error ErrorEuclidForkEnabled(); - - /// @dev Thrown when the committed v5 batch doesn't contain only one chunk. - error ErrorV5BatchNotContainsOnlyOneChunk(); - - /// @dev Thrown when the committed v5 batch doesn't contain only one block. - error ErrorV5BatchNotContainsOnlyOneBlock(); - - /// @dev Thrown when the committed v5 batch contains some transactions (L1 or L2). - error ErrorV5BatchContainsTransactions(); - - /// @dev Thrown when finalize v4/v5, v5/v6, v4/v5/v6 batches in the same bundle. - error ErrorFinalizePreAndPostEuclidBatchInOneBundle(); - /// @dev Thrown when finalize v7 batches while some v1 messages still unfinalized. error ErrorNotAllV1MessagesAreFinalized(); @@ -130,18 +83,14 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { /// @dev Thrown when given batch is not committed before. error ErrorBatchNotCommitted(); + /// @dev Thrown when the function call is not the top-level call in the transaction. + /// @dev This is checked so that indexers that need to decode calldata continue to work. + error ErrorTopLevelCallRequired(); + /************* * Constants * *************/ - /// @dev Address of the point evaluation precompile used for EIP-4844 blob verification. - 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 internal constant BLS_MODULUS = - 52435875175126190479447740508185965837690552500527637822603658699938581184513; - /// @dev offsets in miscData.flags uint256 private constant V1_MESSAGES_FINALIZED_OFFSET = 0; uint256 private constant ENFORCED_MODE_OFFSET = 1; @@ -184,13 +133,16 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { * Variables * *************/ - /// @notice The maximum number of transactions allowed in each chunk. - uint256 public maxNumTxInChunk; + /// @dev The maximum number of transactions allowed in each chunk. + /// @custom:deprecated This is no longer used. + uint256 private __maxNumTxInChunk; /// @dev The storage slot used as L1MessageQueue contract, which is deprecated now. + /// @custom:deprecated This is no longer used. address private __messageQueue; /// @dev The storage slot used as RollupVerifier contract, which is deprecated now. + /// @custom:deprecated This is no longer used. address private __verifier; /// @notice Whether an account is a sequencer. @@ -200,6 +152,7 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { mapping(address => bool) public isProver; /// @dev The storage slot used as `lastFinalizedBatchIndex`, which is deprecated now. + /// @custom:deprecated This is no longer used. uint256 private __lastFinalizedBatchIndex; /// @inheritdoc IScrollChain @@ -220,6 +173,7 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { /// @notice The index of first Euclid batch. uint256 public initialEuclidBatchIndex; + /// @notice The misc data of ScrollChain. ScrollChainMiscData public miscData; /********************** @@ -242,6 +196,14 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { _; } + modifier OnlyTopLevelCall() { + // disallow contract accounts and delegated EOAs + if (msg.sender != tx.origin || msg.sender.code.length != 0) { + revert ErrorTopLevelCallRequired(); + } + _; + } + /*************** * Constructor * ***************/ @@ -292,11 +254,9 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { ) external initializer { OwnableUpgradeable.__Ownable_init(); - maxNumTxInChunk = _maxNumTxInChunk; + __maxNumTxInChunk = _maxNumTxInChunk; __verifier = _verifier; __messageQueue = _messageQueue; - - emit UpdateMaxNumTxInChunk(0, _maxNumTxInChunk); } function initializeV2() external reinitializer(2) { @@ -378,53 +338,6 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { emit FinalizeBatch(0, _batchHash, _stateRoot, bytes32(0)); } - /// @inheritdoc IScrollChain - /// - /// @dev This function will revert unless all V0/V1/V2 batches are finalized. This is because we start to - /// pop L1 messages in `commitBatchWithBlobProof` but not in `commitBatch`. We also introduce `finalizedQueueIndex` - /// in `L1MessageQueue`. If one of V0/V1/V2 batches not finalized, `L1MessageQueue.pendingQueueIndex` will not - /// match `parentBatchHeader.totalL1MessagePopped` and thus revert. - /// - /// @dev This function now only accept batches with 4 <= version <= 6. And for `_version=5`, we should make sure this - /// batch contains only one empty block, since it is the Euclid initial batch for zkt/mpt transition. - function commitBatchWithBlobProof( - uint8 _version, - bytes calldata _parentBatchHeader, - bytes[] memory _chunks, - bytes calldata _skippedL1MessageBitmap, - bytes calldata _blobDataProof - ) external override OnlySequencer whenNotPaused whenEnforcedBatchNotEnabled { - // only accept 4 <= version <= 6 - if (_version < 4) { - revert ErrorIncorrectBatchVersion(); - } else if (_version == 5) { - // only commit once for Euclid initial batch - if (initialEuclidBatchIndex != 0) revert ErrorBatchIsAlreadyCommitted(); - } else if (_version > 6) { - revert ErrorIncorrectBatchVersion(); - } - // @note We suppose to check v6 batches cannot be committed without initial Euclid Batch. - // However it will introduce extra sload (2000 gas), we let the sequencer to do this check offchain. - // Even if the sequencer commits v6 batches without v5 batch, the security council can still revert it. - - uint256 batchIndex = _commitBatchFromV2ToV6( - _version, - _parentBatchHeader, - _chunks, - _skippedL1MessageBitmap, - _blobDataProof - ); - // Don't allow to commit version 4 after Euclid upgrade. - // This check is to avoid sequencer committing wrong batch due to human error. - // And This check won't introduce much gas overhead (likely less than 100). - if (_version == 4) { - uint256 euclidForkBatchIndex = initialEuclidBatchIndex; - if (euclidForkBatchIndex > 0 && batchIndex > euclidForkBatchIndex) revert ErrorEuclidForkEnabled(); - } else if (_version == 5) { - initialEuclidBatchIndex = batchIndex; - } - } - /// @inheritdoc IScrollChain function commitBatches( uint8 version, @@ -457,48 +370,6 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { miscData.lastCommittedBatchIndex = uint64(startBatchIndex); } - /// @inheritdoc IScrollChain - /// @dev All batches in the given bundle should have the same version and version <= 4 or version >= 6. - function finalizeBundleWithProof( - bytes calldata batchHeader, - bytes32 postStateRoot, - bytes32 withdrawRoot, - bytes calldata aggrProof - ) external override OnlyProver whenNotPaused whenEnforcedBatchNotEnabled { - // actions before verification - ( - uint256 version, - bytes32 batchHash, - uint256 batchIndex, - uint256 totalL1MessagesPoppedOverall, - uint256 prevBatchIndex - ) = _beforeFinalizeBatch(batchHeader, postStateRoot); - - uint256 euclidForkBatchIndex = initialEuclidBatchIndex; - // Make sure we don't finalize v4, v5 and v6 batches in the same bundle, that - // means `batchIndex < euclidForkBatchIndex` or `prevBatchIndex >= euclidForkBatchIndex`. - if (prevBatchIndex < euclidForkBatchIndex && euclidForkBatchIndex <= batchIndex) { - revert ErrorFinalizePreAndPostEuclidBatchInOneBundle(); - } - - bytes memory publicInputs = abi.encodePacked( - layer2ChainId, - uint32(batchIndex - prevBatchIndex), // numBatches - finalizedStateRoots[prevBatchIndex], // _prevStateRoot - committedBatches[prevBatchIndex], // _prevBatchHash - postStateRoot, - batchHash, - withdrawRoot - ); - - // verify bundle, choose the correct verifier based on the last batch - // our off-chain service will make sure all unfinalized batches have the same batch version. - IRollupVerifier(verifier).verifyBundleProof(version, batchIndex, aggrProof, publicInputs); - - // actions after verification - _afterFinalizeBatch(batchIndex, batchHash, totalL1MessagesPoppedOverall, postStateRoot, withdrawRoot, true); - } - /// @inheritdoc IScrollChain function finalizeBundlePostEuclidV2( bytes calldata batchHeader, @@ -528,7 +399,7 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { uint8 version, bytes32 parentBatchHash, FinalizeStruct calldata finalizeStruct - ) external { + ) external OnlyTopLevelCall { ScrollChainMiscData memory cachedMiscData = miscData; if (!isEnforcedModeEnabled()) { (uint256 maxDelayEnterEnforcedMode, uint256 maxDelayMessageQueue) = SystemConfig(systemConfig) @@ -621,15 +492,6 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { emit UpdateProver(_account, false); } - /// @notice Update the value of `maxNumTxInChunk`. - /// @param _maxNumTxInChunk The new value of `maxNumTxInChunk`. - function updateMaxNumTxInChunk(uint256 _maxNumTxInChunk) external onlyOwner { - uint256 _oldMaxNumTxInChunk = maxNumTxInChunk; - maxNumTxInChunk = _maxNumTxInChunk; - - emit UpdateMaxNumTxInChunk(_oldMaxNumTxInChunk, _maxNumTxInChunk); - } - /// @notice Pause the contract /// @param _status The pause status to update. function setPause(bool _status) external onlyOwner { @@ -668,53 +530,6 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { return flag; } - /// @dev Internal function to do common checks before actual batch committing. - /// @param _version The version of the batch to commit. - /// @param _parentBatchHeader The parent batch header in calldata. - /// @param _chunks The list of chunks in memory. - /// @param _lastCommittedBatchIndex The index of the last committed batch. - /// @return _parentBatchHash The batch hash of parent batch header. - /// @return _batchIndex The index of current batch. - /// @return _totalL1MessagesPoppedOverall The total number of L1 messages popped before current batch. - function _beforeCommitBatch( - uint8 _version, - bytes calldata _parentBatchHeader, - bytes[] memory _chunks, - uint256 _lastCommittedBatchIndex - ) - private - view - returns ( - bytes32 _parentBatchHash, - uint256 _batchIndex, - uint256 _totalL1MessagesPoppedOverall - ) - { - // check whether the batch is empty - if (_chunks.length == 0) revert ErrorBatchIsEmpty(); - uint256 batchPtr; - (batchPtr, _parentBatchHash, _batchIndex, _totalL1MessagesPoppedOverall) = _loadBatchHeader( - _parentBatchHeader, - _lastCommittedBatchIndex - ); - // version should non-decreasing - if (BatchHeaderV0Codec.getVersion(batchPtr) > _version) revert ErrorCannotDowngradeVersion(); - - if (_batchIndex != _lastCommittedBatchIndex) revert ErrorBatchIsAlreadyCommitted(); - unchecked { - _batchIndex += 1; - } - } - - /// @dev Internal function to do common actions after actual batch committing. - /// @param _batchIndex The index of current batch. - /// @param _batchHash The hash of current batch. - function _afterCommitBatch(uint256 _batchIndex, bytes32 _batchHash) private { - miscData.lastCommittedBatchIndex = uint64(_batchIndex); - committedBatches[_batchIndex] = _batchHash; - emit CommitBatch(_batchIndex, _batchHash); - } - /// @dev Internal function to do common actions before actual batch finalization. function _beforeFinalizeBatch(bytes calldata batchHeader, bytes32 postStateRoot) internal @@ -767,65 +582,6 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { emit FinalizeBatch(batchIndex, batchHash, postStateRoot, withdrawRoot); } - /// @dev Internal function to check the `SkippedL1MessageBitmap`. - /// @param _totalL1MessagesPoppedOverall The total number of L1 messages popped after current batch. - /// @param _totalL1MessagesPoppedInBatch The total number of L1 messages popped in current batch. - /// @param _skippedL1MessageBitmap The skipped L1 message bitmap in calldata. - /// @param _doPopMessage Whether we actually pop the messages from message queue. - function _checkSkippedL1MessageBitmap( - uint256 _totalL1MessagesPoppedOverall, - uint256 _totalL1MessagesPoppedInBatch, - bytes calldata _skippedL1MessageBitmap, - bool _doPopMessage - ) private { - // check the length of bitmap - unchecked { - if (((_totalL1MessagesPoppedInBatch + 255) / 256) * 32 != _skippedL1MessageBitmap.length) { - revert ErrorIncorrectBitmapLength(); - } - } - if (_doPopMessage) { - _popL1MessagesCalldata( - _skippedL1MessageBitmap, - _totalL1MessagesPoppedOverall, - _totalL1MessagesPoppedInBatch - ); - } - } - - /// @dev Internal function to get and check the blob versioned hash. - /// @param _blobDataProof The blob data proof passing to point evaluation precompile. - /// @return _blobVersionedHash The retrieved blob versioned hash. - function _getAndCheckBlobVersionedHash(bytes calldata _blobDataProof) - internal - returns (bytes32 _blobVersionedHash) - { - _blobVersionedHash = _getBlobVersionedHash(); - - // 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(); - } - - /// @dev Internal function to get the blob versioned hash. - /// @return _blobVersionedHash The retrieved blob versioned hash. - function _getBlobVersionedHash() internal virtual returns (bytes32 _blobVersionedHash) { - bytes32 _secondBlob; - // Get blob's versioned hash - assembly { - _blobVersionedHash := blobhash(0) - _secondBlob := blobhash(1) - } - if (_blobVersionedHash == bytes32(0)) revert ErrorNoBlobFound(); - if (_secondBlob != bytes32(0)) revert ErrorFoundMultipleBlobs(); - } - /// @dev Internal function to get the blob versioned hash. /// @return _blobVersionedHash The retrieved blob versioned hash. function _getBlobVersionedHash(uint256 index) internal virtual returns (bytes32 _blobVersionedHash) { @@ -835,23 +591,6 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { } } - /// @dev We make sure v5 batch only contains one empty block here. - function _validateV5Batch(bytes[] memory chunks) internal pure { - if (chunks.length != 1) revert ErrorV5BatchNotContainsOnlyOneChunk(); - bytes memory chunk = chunks[0]; - uint256 chunkPtr; - uint256 blockPtr; - assembly { - chunkPtr := add(chunk, 0x20) // skip chunkLength - blockPtr := add(chunkPtr, 1) - } - - uint256 numBlocks = ChunkCodecV1.validateChunkLength(chunkPtr, chunk.length); - if (numBlocks != 1) revert ErrorV5BatchNotContainsOnlyOneBlock(); - uint256 numTransactions = ChunkCodecV1.getNumTransactions(blockPtr); - if (numTransactions != 0) revert ErrorV5BatchContainsTransactions(); - } - /// @dev Internal function to commit one ore more batches after the EuclidV2 upgrade. /// @param version The version of the batches (version >= 7). /// @param parentBatchHash The hash of parent batch. @@ -950,126 +689,6 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { _afterFinalizeBatch(batchIndex, batchHash, totalL1MessagesPoppedOverall, postStateRoot, withdrawRoot, false); } - /// @dev Internal function to commit batches from V2 to V6 (except V5, since it is Euclid initial batch) - function _commitBatchFromV2ToV6( - uint8 _version, - bytes calldata _parentBatchHeader, - bytes[] memory _chunks, - bytes calldata _skippedL1MessageBitmap, - bytes calldata _blobDataProof - ) internal returns (uint256) { - // do extra checks for batch v5. - if (_version == 5) { - _validateV5Batch(_chunks); - } - - // allocate memory of batch header and store entries if necessary, the order matters - // @note why store entries if necessary, to avoid stack overflow problem. - // The codes for `version`, `batchIndex`, `l1MessagePopped`, `totalL1MessagePopped` and `dataHash` - // are the same as `BatchHeaderV0Codec`. - // The codes for `blobVersionedHash`, and `parentBatchHash` are the same as `BatchHeaderV1Codec`. - uint256 batchPtr = BatchHeaderV3Codec.allocate(); - BatchHeaderV0Codec.storeVersion(batchPtr, _version); - - (bytes32 _parentBatchHash, uint256 _batchIndex, uint256 _totalL1MessagesPoppedOverall) = _beforeCommitBatch( - _version, - _parentBatchHeader, - _chunks, - miscData.lastCommittedBatchIndex - ); - BatchHeaderV0Codec.storeBatchIndex(batchPtr, _batchIndex); - - // versions 2 to 6 both use ChunkCodecV1 - (bytes32 _dataHash, uint256 _totalL1MessagesPoppedInBatch) = _commitChunksV1( - _totalL1MessagesPoppedOverall, - _chunks, - _skippedL1MessageBitmap - ); - unchecked { - _totalL1MessagesPoppedOverall += _totalL1MessagesPoppedInBatch; - } - - // verify skippedL1MessageBitmap - _checkSkippedL1MessageBitmap( - _totalL1MessagesPoppedOverall, - _totalL1MessagesPoppedInBatch, - _skippedL1MessageBitmap, - true - ); - BatchHeaderV0Codec.storeL1MessagePopped(batchPtr, _totalL1MessagesPoppedInBatch); - BatchHeaderV0Codec.storeTotalL1MessagePopped(batchPtr, _totalL1MessagesPoppedOverall); - BatchHeaderV0Codec.storeDataHash(batchPtr, _dataHash); - - // verify blob versioned hash - BatchHeaderV1Codec.storeBlobVersionedHash(batchPtr, _getAndCheckBlobVersionedHash(_blobDataProof)); - BatchHeaderV1Codec.storeParentBatchHash(batchPtr, _parentBatchHash); - - uint256 lastBlockTimestamp; - { - bytes memory lastChunk = _chunks[_chunks.length - 1]; - lastBlockTimestamp = ChunkCodecV1.getLastBlockTimestamp(lastChunk); - } - BatchHeaderV3Codec.storeLastBlockTimestamp(batchPtr, lastBlockTimestamp); - BatchHeaderV3Codec.storeBlobDataProof(batchPtr, _blobDataProof); - - // compute batch hash, V2~V6 has same code as V0 - bytes32 _batchHash = BatchHeaderV0Codec.computeBatchHash( - batchPtr, - BatchHeaderV3Codec.BATCH_HEADER_FIXED_LENGTH - ); - - _afterCommitBatch(_batchIndex, _batchHash); - - return _batchIndex; - } - - /// @dev Internal function to commit chunks with version 1 - /// @param _totalL1MessagesPoppedOverall The number of L1 messages popped before the list of chunks. - /// @param _chunks The list of chunks to commit. - /// @param _skippedL1MessageBitmap The bitmap indicates whether each L1 message is skipped or not. - /// @return _batchDataHash The computed data hash for the list of chunks. - /// @return _totalL1MessagesPoppedInBatch The total number of L1 messages popped in this batch, including skipped one. - function _commitChunksV1( - uint256 _totalL1MessagesPoppedOverall, - bytes[] memory _chunks, - bytes calldata _skippedL1MessageBitmap - ) internal view returns (bytes32 _batchDataHash, uint256 _totalL1MessagesPoppedInBatch) { - uint256 _chunksLength = _chunks.length; - - // load `batchDataHashPtr` and reserve the memory region for chunk data hashes - uint256 batchDataHashPtr; - assembly { - batchDataHashPtr := mload(0x40) - mstore(0x40, add(batchDataHashPtr, mul(_chunksLength, 32))) - } - - // compute the data hash for each chunk - for (uint256 i = 0; i < _chunksLength; i++) { - uint256 _totalNumL1MessagesInChunk; - bytes32 _chunkDataHash; - (_chunkDataHash, _totalNumL1MessagesInChunk) = _commitChunkV1( - _chunks[i], - _totalL1MessagesPoppedInBatch, - _totalL1MessagesPoppedOverall, - _skippedL1MessageBitmap - ); - unchecked { - _totalL1MessagesPoppedInBatch += _totalNumL1MessagesInChunk; - _totalL1MessagesPoppedOverall += _totalNumL1MessagesInChunk; - } - assembly { - mstore(batchDataHashPtr, _chunkDataHash) - batchDataHashPtr := add(batchDataHashPtr, 0x20) - } - } - - // compute the data hash for current batch - assembly { - let dataLen := mul(_chunksLength, 0x20) - _batchDataHash := keccak256(sub(batchDataHashPtr, dataLen), dataLen) - } - } - /// @dev Internal function to load batch header from calldata to memory. /// @param _batchHeader The batch header in calldata. /// @param _lastCommittedBatchIndex The index of the last committed batch. @@ -1123,133 +742,6 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { } } - /// @dev Internal function to commit a chunk with version 1. - /// @param _chunk The encoded chunk to commit. - /// @param _totalL1MessagesPoppedInBatch The total number of L1 messages popped in current batch. - /// @param _totalL1MessagesPoppedOverall The total number of L1 messages popped in all batches including current batch. - /// @param _skippedL1MessageBitmap The bitmap indicates whether each L1 message is skipped or not. - /// @return _dataHash The computed data hash for this chunk. - /// @return _totalNumL1MessagesInChunk The total number of L1 message popped in current chunk - function _commitChunkV1( - bytes memory _chunk, - uint256 _totalL1MessagesPoppedInBatch, - uint256 _totalL1MessagesPoppedOverall, - bytes calldata _skippedL1MessageBitmap - ) internal view returns (bytes32 _dataHash, uint256 _totalNumL1MessagesInChunk) { - uint256 chunkPtr; - uint256 startDataPtr; - uint256 dataPtr; - - assembly { - dataPtr := mload(0x40) - startDataPtr := dataPtr - chunkPtr := add(_chunk, 0x20) // skip chunkLength - } - - uint256 _numBlocks = ChunkCodecV1.validateChunkLength(chunkPtr, _chunk.length); - // concatenate block contexts, use scope to avoid stack too deep - for (uint256 i = 0; i < _numBlocks; i++) { - dataPtr = ChunkCodecV1.copyBlockContext(chunkPtr, dataPtr, i); - uint256 blockPtr = chunkPtr + 1 + i * ChunkCodecV1.BLOCK_CONTEXT_LENGTH; - uint256 _numL1MessagesInBlock = ChunkCodecV1.getNumL1Messages(blockPtr); - unchecked { - _totalNumL1MessagesInChunk += _numL1MessagesInBlock; - } - } - assembly { - mstore(0x40, add(dataPtr, mul(_totalNumL1MessagesInChunk, 0x20))) // reserve memory for l1 message hashes - chunkPtr := add(chunkPtr, 1) - } - - // the number of actual transactions in one chunk: non-skipped l1 messages + l2 txs - uint256 _totalTransactionsInChunk; - // concatenate tx hashes - while (_numBlocks > 0) { - // concatenate l1 message hashes - uint256 _numL1MessagesInBlock = ChunkCodecV1.getNumL1Messages(chunkPtr); - uint256 startPtr = dataPtr; - dataPtr = _loadL1MessageHashes( - dataPtr, - _numL1MessagesInBlock, - _totalL1MessagesPoppedInBatch, - _totalL1MessagesPoppedOverall, - _skippedL1MessageBitmap - ); - uint256 _numTransactionsInBlock = ChunkCodecV1.getNumTransactions(chunkPtr); - if (_numTransactionsInBlock < _numL1MessagesInBlock) revert ErrorNumTxsLessThanNumL1Msgs(); - unchecked { - _totalTransactionsInChunk += (dataPtr - startPtr) / 32; // number of non-skipped l1 messages - _totalTransactionsInChunk += _numTransactionsInBlock - _numL1MessagesInBlock; // number of l2 txs - _totalL1MessagesPoppedInBatch += _numL1MessagesInBlock; - _totalL1MessagesPoppedOverall += _numL1MessagesInBlock; - - _numBlocks -= 1; - chunkPtr += ChunkCodecV1.BLOCK_CONTEXT_LENGTH; - } - } - - // check the actual number of transactions in the chunk - if (_totalTransactionsInChunk > maxNumTxInChunk) { - revert ErrorTooManyTxsInOneChunk(); - } - - // compute data hash and store to memory - assembly { - _dataHash := keccak256(startDataPtr, sub(dataPtr, startDataPtr)) - } - } - - /// @dev Internal function to load L1 message hashes from the message queue. - /// @param _ptr The memory offset to store the transaction hash. - /// @param _numL1Messages The number of L1 messages to load. - /// @param _totalL1MessagesPoppedInBatch The total number of L1 messages popped in current batch. - /// @param _totalL1MessagesPoppedOverall The total number of L1 messages popped in all batches including current batch. - /// @param _skippedL1MessageBitmap The bitmap indicates whether each L1 message is skipped or not. - /// @return uint256 The new memory offset after loading. - function _loadL1MessageHashes( - uint256 _ptr, - uint256 _numL1Messages, - uint256 _totalL1MessagesPoppedInBatch, - uint256 _totalL1MessagesPoppedOverall, - bytes calldata _skippedL1MessageBitmap - ) internal view returns (uint256) { - if (_numL1Messages == 0) return _ptr; - IL1MessageQueueV1 _messageQueue = IL1MessageQueueV1(messageQueueV1); - - unchecked { - uint256 _bitmap; - uint256 rem; - for (uint256 i = 0; i < _numL1Messages; i++) { - uint256 quo = _totalL1MessagesPoppedInBatch >> 8; - rem = _totalL1MessagesPoppedInBatch & 0xff; - - // load bitmap every 256 bits - if (i == 0 || rem == 0) { - assembly { - _bitmap := calldataload(add(_skippedL1MessageBitmap.offset, mul(0x20, quo))) - } - } - if (((_bitmap >> rem) & 1) == 0) { - // message not skipped - bytes32 _hash = _messageQueue.getCrossDomainMessage(_totalL1MessagesPoppedOverall); - assembly { - mstore(_ptr, _hash) - _ptr := add(_ptr, 0x20) - } - } - - _totalL1MessagesPoppedInBatch += 1; - _totalL1MessagesPoppedOverall += 1; - } - - // check last L1 message is not skipped, _totalL1MessagesPoppedInBatch must > 0 - rem = (_totalL1MessagesPoppedInBatch - 1) & 0xff; - if (((_bitmap >> rem) & 1) > 0) revert ErrorLastL1MessageSkipped(); - } - - return _ptr; - } - /// @param totalL1MessagesPoppedOverall The total number of L1 messages popped in all batches including current batch. function _finalizePoppedL1Messages(uint256 totalL1MessagesPoppedOverall, bool isV1) internal { if (totalL1MessagesPoppedOverall > 0) { @@ -1260,59 +752,4 @@ contract ScrollChain is OwnableUpgradeable, PausableUpgradeable, IScrollChain { } } } - - /// @dev Internal function to pop l1 messages from `skippedL1MessageBitmap` in calldata. - /// @param skippedL1MessageBitmap The `skippedL1MessageBitmap` in calldata. - /// @param totalL1MessagesPoppedOverall The total number of L1 messages popped in all batches including current batch. - /// @param totalL1MessagesPoppedInBatch The number of L1 messages popped in current batch. - function _popL1MessagesCalldata( - bytes calldata skippedL1MessageBitmap, - uint256 totalL1MessagesPoppedOverall, - uint256 totalL1MessagesPoppedInBatch - ) internal { - if (totalL1MessagesPoppedInBatch == 0) return; - uint256 bitmapPtr; - assembly { - bitmapPtr := skippedL1MessageBitmap.offset - } - _popL1Messages(true, bitmapPtr, totalL1MessagesPoppedOverall, totalL1MessagesPoppedInBatch); - } - - /// @dev Internal function to pop l1 messages from `skippedL1MessageBitmap` in calldata or memory. - /// @param isCalldata Whether the `skippedL1MessageBitmap` is in calldata or memory. - /// @param bitmapPtr The offset of `skippedL1MessageBitmap` in calldata or memory. - /// @param totalL1MessagesPoppedOverall The total number of L1 messages popped in all batches including current batch. - /// @param totalL1MessagesPoppedInBatch The number of L1 messages popped in current batch. - function _popL1Messages( - bool isCalldata, - uint256 bitmapPtr, - uint256 totalL1MessagesPoppedOverall, - uint256 totalL1MessagesPoppedInBatch - ) internal { - if (totalL1MessagesPoppedInBatch == 0) return; - - unchecked { - uint256 startIndex = totalL1MessagesPoppedOverall - totalL1MessagesPoppedInBatch; - uint256 bitmap; - - for (uint256 i = 0; i < totalL1MessagesPoppedInBatch; i += 256) { - uint256 _count = 256; - if (totalL1MessagesPoppedInBatch - i < _count) { - _count = totalL1MessagesPoppedInBatch - i; - } - assembly { - switch isCalldata - case 1 { - bitmap := calldataload(bitmapPtr) - } - default { - bitmap := mload(bitmapPtr) - } - bitmapPtr := add(bitmapPtr, 0x20) - } - IL1MessageQueueV1(messageQueueV1).popCrossDomainMessage(startIndex, _count, bitmap); - startIndex += 256; - } - } - } } diff --git a/src/L1/rollup/ScrollChainCommitmentVerifier.sol b/src/L1/rollup/ScrollChainCommitmentVerifier.sol index 3d5674f5..ba94ecca 100644 --- a/src/L1/rollup/ScrollChainCommitmentVerifier.sol +++ b/src/L1/rollup/ScrollChainCommitmentVerifier.sol @@ -2,8 +2,9 @@ pragma solidity =0.8.24; -import {IScrollChain} from "./IScrollChain.sol"; +import {ScrollChain} from "./ScrollChain.sol"; import {ZkTrieVerifier} from "../../libraries/verifier/ZkTrieVerifier.sol"; +import {PatriciaMerkleTrieVerifier} from "../../libraries/verifier/PatriciaMerkleTrieVerifier.sol"; contract ScrollChainCommitmentVerifier { /// @notice The address of poseidon hash contract @@ -17,7 +18,7 @@ contract ScrollChainCommitmentVerifier { rollup = _rollup; } - /// @notice Validates a proof from eth_getProof in l2geth. + /// @notice Validates a proof from eth_getProof in l2geth zktrie node. /// @param account The address of the contract. /// @param storageKey The storage slot to verify. /// @param proof The rlp encoding result of eth_getProof. @@ -37,6 +38,26 @@ contract ScrollChainCommitmentVerifier { return ZkTrieVerifier.verifyZkTrieProof(poseidon, account, storageKey, proof); } + /// @notice Validates a proof from eth_getProof in l2geth zktrie node. + /// @param account The address of the contract. + /// @param storageKey The storage slot to verify. + /// @param proof The rlp encoding result of eth_getProof. + /// @return stateRoot The computed state root. Must be checked by the caller. + /// @return storageValue The value of `storageKey`. + /// + /// The encoding order of `proof` is + /// ```text + /// | 1 byte | ... | 1 byte | ... | + /// | account proof length | account proof | storage proof length | storage proof | + /// ``` + function verifyPatriciaMerkleTrieProof( + address account, + bytes32 storageKey, + bytes calldata proof + ) public pure returns (bytes32 stateRoot, bytes32 storageValue) { + return PatriciaMerkleTrieVerifier.verifyPatriciaProof(account, storageKey, proof); + } + /// @notice Verifies a batch inclusion proof. /// @param batchIndex The index of the batch. /// @param account The address of the contract in L2. @@ -49,11 +70,16 @@ contract ScrollChainCommitmentVerifier { bytes32 storageKey, bytes calldata proof ) external view returns (bytes32 storageValue) { - require(IScrollChain(rollup).isBatchFinalized(batchIndex), "Batch not finalized"); + require(ScrollChain(rollup).isBatchFinalized(batchIndex), "Batch not finalized"); bytes32 computedStateRoot; - (computedStateRoot, storageValue) = verifyZkTrieProof(account, storageKey, proof); - bytes32 expectedStateRoot = IScrollChain(rollup).finalizedStateRoots(batchIndex); + uint256 initialEuclidBatchIndex = ScrollChain(rollup).initialEuclidBatchIndex(); + if (batchIndex < initialEuclidBatchIndex) { + (computedStateRoot, storageValue) = verifyZkTrieProof(account, storageKey, proof); + } else { + (computedStateRoot, storageValue) = verifyPatriciaMerkleTrieProof(account, storageKey, proof); + } + bytes32 expectedStateRoot = ScrollChain(rollup).finalizedStateRoots(batchIndex); require(computedStateRoot == expectedStateRoot, "Invalid inclusion proof"); } } diff --git a/src/L1/system-contract/SystemConfig.sol b/src/L1/system-contract/SystemConfig.sol index 6dba3ffa..2c8ce0b0 100644 --- a/src/L1/system-contract/SystemConfig.sol +++ b/src/L1/system-contract/SystemConfig.sol @@ -4,6 +4,25 @@ pragma solidity =0.8.24; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; contract SystemConfig is OwnableUpgradeable { + /********** + * Events * + **********/ + + /// @notice Emitted when the message queue parameters are updated. + /// @param oldParams The old parameters. + /// @param newParams The new parameters. + event MessageQueueParametersUpdated(MessageQueueParameters oldParams, MessageQueueParameters newParams); + + /// @notice Emitted when the enforced batch parameters are updated. + /// @param oldParams The old parameters. + /// @param newParams The new parameters. + event EnforcedBatchParametersUpdated(EnforcedBatchParameters oldParams, EnforcedBatchParameters newParams); + + /// @notice Emitted when the signer is updated. + /// @param oldSigner The old signer. + /// @param newSigner The new signer. + event SignerUpdated(address oldSigner, address newSigner); + /*********** * Structs * ***********/ @@ -83,20 +102,26 @@ contract SystemConfig is OwnableUpgradeable { /// @param _params The new message queue parameters. /// @dev Only the owner can call this function. function updateMessageQueueParameters(MessageQueueParameters memory _params) external onlyOwner { + MessageQueueParameters memory oldParams = messageQueueParameters; messageQueueParameters = _params; + emit MessageQueueParametersUpdated(oldParams, _params); } /// @notice Update the enforced batch parameters. /// @param _params The new enforced batch parameters. /// @dev Only the owner can call this function. function updateEnforcedBatchParameters(EnforcedBatchParameters memory _params) external onlyOwner { + EnforcedBatchParameters memory oldParams = enforcedBatchParameters; enforcedBatchParameters = _params; + emit EnforcedBatchParametersUpdated(oldParams, _params); } /// @notice Update the current signer. /// @param _newSigner The address of the new authorized signer. /// @dev Only the owner can call this function. function updateSigner(address _newSigner) external onlyOwner { + address oldSigner = currentSigner; currentSigner = _newSigner; + emit SignerUpdated(oldSigner, _newSigner); } } diff --git a/src/L2/L2ScrollMessenger.sol b/src/L2/L2ScrollMessenger.sol index b15e1461..ebe43708 100644 --- a/src/L2/L2ScrollMessenger.sol +++ b/src/L2/L2ScrollMessenger.sol @@ -156,6 +156,8 @@ contract L2ScrollMessenger is ScrollMessengerBase, IL2ScrollMessenger { xDomainMessageSender = _from; // solhint-disable-next-line avoid-low-level-calls + // no reentrancy risk, only alias(l1ScrollMessenger) can call relayMessage. + // slither-disable-next-line reentrancy-eth (bool success, ) = _to.call{value: _value}(_message); // reset value to refund gas. xDomainMessageSender = ScrollConstants.DEFAULT_XDOMAIN_MESSAGE_SENDER; diff --git a/src/L2/L2SystemConfig.sol b/src/L2/L2SystemConfig.sol new file mode 100644 index 00000000..c79c9b2b --- /dev/null +++ b/src/L2/L2SystemConfig.sol @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: MIT + +pragma solidity =0.8.24; + +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract L2SystemConfig is OwnableUpgradeable { + /********** + * Events * + **********/ + + /// @notice Emitted when the base fee overhead is updated. + /// @param oldBaseFeeOverhead The old base fee overhead. + /// @param newBaseFeeOverhead The new base fee overhead. + event BaseFeeOverheadUpdated(uint256 oldBaseFeeOverhead, uint256 newBaseFeeOverhead); + + /// @notice Emitted when the base fee scalar is updated. + /// @param oldBaseFeeScalar The old base fee scalar. + /// @param newBaseFeeScalar The new base fee scalar. + event BaseFeeScalarUpdated(uint256 oldBaseFeeScalar, uint256 newBaseFeeScalar); + + /************* + * Constants * + *************/ + + uint256 private constant PRECISION = 1e18; + + /********************* + * Storage Variables * + *********************/ + + /// @notice The base fee overhead. This is part of the L2 base fee calculation. + uint256 public baseFeeOverhead; + + /// @notice The base fee scalar. This is part of the L2 base fee calculation. + uint256 public baseFeeScalar; + + /*************** + * Constructor * + ***************/ + + constructor() { + _disableInitializers(); + } + + function initialize(address _owner) external initializer { + __Ownable_init(); + transferOwnership(_owner); + } + + /************************* + * Public View Functions * + *************************/ + + /// @notice Calculates the L2 base fee based on the L1 base fee. + /// @param l1BaseFee The L1 base fee. + /// @return l2BaseFee The L2 base fee. + function getL2BaseFee(uint256 l1BaseFee) public view returns (uint256 l2BaseFee) { + l2BaseFee = (l1BaseFee * baseFeeScalar) / PRECISION + baseFeeOverhead; + } + + /************************ + * Restricted Functions * + ************************/ + + /// @notice Updates the base fee overhead. + /// @param _baseFeeOverhead The new base fee overhead. + function updateBaseFeeOverhead(uint256 _baseFeeOverhead) external onlyOwner { + uint256 oldBaseFeeOverhead = baseFeeOverhead; + baseFeeOverhead = _baseFeeOverhead; + emit BaseFeeOverheadUpdated(oldBaseFeeOverhead, _baseFeeOverhead); + } + + /// @notice Updates the base fee scalar. + /// @param _baseFeeScalar The new base fee scalar. + function updateBaseFeeScalar(uint256 _baseFeeScalar) external onlyOwner { + uint256 oldBaseFeeScalar = baseFeeScalar; + baseFeeScalar = _baseFeeScalar; + emit BaseFeeScalarUpdated(oldBaseFeeScalar, _baseFeeScalar); + } +} diff --git a/src/L2/gateways/L2ETHGateway.sol b/src/L2/gateways/L2ETHGateway.sol index ae9c42ff..695f1485 100644 --- a/src/L2/gateways/L2ETHGateway.sol +++ b/src/L2/gateways/L2ETHGateway.sol @@ -86,6 +86,8 @@ contract L2ETHGateway is ScrollGatewayBase, IL2ETHGateway { require(msg.value == _amount, "msg.value mismatch"); // solhint-disable-next-line avoid-low-level-calls + // no reentrancy risk. + // slither-disable-next-line arbitrary-send-eth (bool _success, ) = _to.call{value: _amount}(""); require(_success, "ETH transfer failed"); diff --git a/src/L2/gateways/L2WETHGateway.sol b/src/L2/gateways/L2WETHGateway.sol index a50a7b35..593c4cae 100644 --- a/src/L2/gateways/L2WETHGateway.sol +++ b/src/L2/gateways/L2WETHGateway.sol @@ -110,6 +110,8 @@ contract L2WETHGateway is L2ERC20Gateway { require(_l2Token == WETH, "l2 token not WETH"); require(_amount == msg.value, "msg.value mismatch"); + // deposit to WETH token is safe. + // slither-disable-next-line arbitrary-send-eth IWETH(_l2Token).deposit{value: _amount}(); IERC20Upgradeable(_l2Token).safeTransfer(_to, _amount); diff --git a/src/L2/predeploys/IL1GasPriceOracle.sol b/src/L2/predeploys/IL1GasPriceOracle.sol index 850a178a..114903e0 100644 --- a/src/L2/predeploys/IL1GasPriceOracle.sol +++ b/src/L2/predeploys/IL1GasPriceOracle.sol @@ -23,6 +23,14 @@ interface IL1GasPriceOracle { /// @param scalar The current blob fee scalar updated. event BlobScalarUpdated(uint256 scalar); + /// @notice Emitted when current compression penalty threshold is updated. + /// @param threshold The new compression penalty threshold. + event PenaltyThresholdUpdated(uint256 threshold); + + /// @notice Emitted when current compression penalty factor is updated. + /// @param factor The new compression penalty factor. + event PenaltyFactorUpdated(uint256 factor); + /// @notice Emitted when current l1 base fee is updated. /// @param l1BaseFee The current l1 base fee updated. event L1BaseFeeUpdated(uint256 l1BaseFee); @@ -47,6 +55,12 @@ interface IL1GasPriceOracle { /// @notice Return the current l1 blob fee scalar. function blobScalar() external view returns (uint256); + /// @notice Return the current compression penalty threshold. + function penaltyThreshold() external view returns (uint256); + + /// @notice Return the current compression penalty factor. + function penaltyFactor() external view returns (uint256); + /// @notice Return the latest known l1 base fee. function l1BaseFee() external view returns (uint256); diff --git a/src/L2/predeploys/L1GasPriceOracle.sol b/src/L2/predeploys/L1GasPriceOracle.sol index 6d535328..28a03792 100644 --- a/src/L2/predeploys/L1GasPriceOracle.sol +++ b/src/L2/predeploys/L1GasPriceOracle.sol @@ -39,6 +39,17 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle { /// @dev Thrown when we enable Curie fork after Curie fork. error ErrAlreadyInCurieFork(); + /// @dev Thrown when the compression penalty threshold exceeds `MAX_PENALTY_THRESHOLD`, + /// or is less than 1 * PRECISION. + error ErrInvalidPenaltyThreshold(); + + /// @dev Thrown when the compression penalty factor exceeds `MAX_PENALTY_FACTOR`, + /// or is less than 1 * PRECISION. + error ErrInvalidPenaltyFactor(); + + /// @dev Thrown when we enable Feynman fork after Feynman fork. + error ErrAlreadyInFeynmanFork(); + /************* * Constants * *************/ @@ -70,6 +81,14 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle { /// So, the value should not exceed 10^9 * 1e9 normally. uint256 private constant MAX_BLOB_SCALAR = 10**9 * PRECISION; + /// @dev The maximum possible compression penalty threshold after Feynman. + /// The value should not exceed 10^9 * 1e9 normally. + uint256 private constant MAX_PENALTY_THRESHOLD = 10**9 * PRECISION; + + /// @dev The maximum possible compression penalty factor after Feynman. + /// The value should not exceed 10^9 * 1e9 normally. + uint256 private constant MAX_PENALTY_FACTOR = 10**9 * PRECISION; + /************* * Variables * *************/ @@ -98,6 +117,15 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle { /// @notice Indicates whether the network has gone through the Curie upgrade. bool public isCurie; + /// @inheritdoc IL1GasPriceOracle + uint256 public override penaltyThreshold; + + /// @inheritdoc IL1GasPriceOracle + uint256 public override penaltyFactor; + + /// @notice Indicates whether the network has gone through the Feynman upgrade. + bool public isFeynman; + /************* * Modifiers * *************/ @@ -121,7 +149,9 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle { /// @inheritdoc IL1GasPriceOracle function getL1Fee(bytes memory _data) external view override returns (uint256) { - if (isCurie) { + if (isFeynman) { + return _getL1FeeFeynman(_data); + } else if (isCurie) { return _getL1FeeCurie(_data); } else { return _getL1FeeBeforeCurie(_data); @@ -130,7 +160,7 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle { /// @inheritdoc IL1GasPriceOracle function getL1GasUsed(bytes memory _data) public view override returns (uint256) { - if (isCurie) { + if (isFeynman || isCurie) { // It is near zero since we put all transactions to blob. return 0; } else { @@ -202,6 +232,24 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle { emit BlobScalarUpdated(_scalar); } + /// Allows the owner to modify the penaltyThreshold. + /// @param _threshold New threshold + function setPenaltyThreshold(uint256 _threshold) external onlyOwner { + if (_threshold < PRECISION || _threshold > MAX_PENALTY_THRESHOLD) revert ErrInvalidPenaltyThreshold(); + + penaltyThreshold = _threshold; + emit PenaltyThresholdUpdated(_threshold); + } + + /// Allows the owner to modify the penaltyFactor. + /// @param _factor New factor + function setPenaltyFactor(uint256 _factor) external onlyOwner { + if (_factor < PRECISION || _factor > MAX_PENALTY_FACTOR) revert ErrInvalidPenaltyFactor(); + + penaltyFactor = _factor; + emit PenaltyFactorUpdated(_factor); + } + /// @notice Update whitelist contract. /// @dev This function can only called by contract owner. /// @param _newWhitelist The address of new whitelist contract. @@ -222,6 +270,16 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle { isCurie = true; } + /// @notice Enable the Feynman fork (callable by contract owner). + /// + /// @dev Since this is a predeploy contract, we will directly set the slot while hard fork + /// to avoid external owner operations. + /// The reason that we keep this function is for easy unit testing. + function enableFeynman() external onlyOwner { + if (isFeynman) revert ErrAlreadyInFeynmanFork(); + isFeynman = true; + } + /********************** * Internal Functions * **********************/ @@ -264,4 +322,16 @@ contract L1GasPriceOracle is OwnableBase, IL1GasPriceOracle { // We have bounded the value of `commitScalar` and `blobScalar`, the whole expression won't overflow. return (commitScalar * l1BaseFee + blobScalar * _data.length * l1BlobBaseFee) / PRECISION; } + + /// @dev Internal function to compute the L1 portion of the fee based on the size of the rlp encoded input + /// transaction, the current L1 base fee, and the various dynamic parameters, after the Feynman fork. + /// @param _data Signed fully RLP-encoded transaction to get the L1 fee for. + /// @return L1 fee that should be paid for the tx + function _getL1FeeFeynman(bytes memory _data) private view returns (uint256) { + // We have bounded the value of `commitScalar`, `blobScalar`, and `penalty`, the whole expression won't overflow. + return + ((commitScalar * l1BaseFee + blobScalar * l1BlobBaseFee) * _data.length * penaltyFactor) / + PRECISION / + PRECISION; + } } diff --git a/src/L2/predeploys/L2TxFeeVault.sol b/src/L2/predeploys/L2TxFeeVault.sol index 20248eb7..08cedf6d 100644 --- a/src/L2/predeploys/L2TxFeeVault.sol +++ b/src/L2/predeploys/L2TxFeeVault.sol @@ -119,7 +119,9 @@ contract L2TxFeeVault is OwnableBase { emit Withdrawal(_value, recipient, msg.sender); - // no fee provided + // @note no fee provided + // transfer to messenger is safe. + // slither-disable-next-line arbitrary-send-eth IL2ScrollMessenger(messenger).sendMessage{value: _value}( recipient, _value, diff --git a/src/L2/predeploys/WrappedEther.sol b/src/L2/predeploys/WrappedEther.sol index 64aa269f..fa5e5c72 100644 --- a/src/L2/predeploys/WrappedEther.sol +++ b/src/L2/predeploys/WrappedEther.sol @@ -39,6 +39,8 @@ contract WrappedEther is ERC20Permit { _burn(_sender, wad); + // no reentrancy risk (checks-effects-interactions). + // slither-disable-next-line arbitrary-send-eth (bool success, ) = _sender.call{value: wad}(""); require(success, "withdraw ETH failed"); diff --git a/src/LICENSE b/src/LICENSE index c7d61110..3e38430c 100644 --- a/src/LICENSE +++ b/src/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Scroll +Copyright (c) 2025 Scroll Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/alternative-gas-token/GasTokenExample.sol b/src/alternative-gas-token/GasTokenExample.sol deleted file mode 100644 index dff43afa..00000000 --- a/src/alternative-gas-token/GasTokenExample.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity =0.8.24; - -import "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol"; - -contract GasTokenExample is ERC20 { - uint8 private decimals_; - - constructor( - string memory _name, - string memory _symbol, - uint8 _decimals, - address _recipient, - uint256 _amount - ) ERC20(_name, _symbol) { - decimals_ = _decimals; - _mint(_recipient, _amount); - } - - function decimals() public view virtual override returns (uint8) { - return decimals_; - } -} diff --git a/src/alternative-gas-token/L1GasTokenGateway.sol b/src/alternative-gas-token/L1GasTokenGateway.sol deleted file mode 100644 index c571ee31..00000000 --- a/src/alternative-gas-token/L1GasTokenGateway.sol +++ /dev/null @@ -1,186 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity =0.8.24; - -import {IERC20MetadataUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol"; -import {SafeERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; -import {IERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; - -import {IL1ETHGateway} from "../L1/gateways/IL1ETHGateway.sol"; -import {IL1ScrollMessenger} from "../L1/IL1ScrollMessenger.sol"; -import {IL2ETHGateway} from "../L2/gateways/IL2ETHGateway.sol"; - -import {IMessageDropCallback} from "../libraries/callbacks/IMessageDropCallback.sol"; -import {ScrollGatewayBase} from "../libraries/gateway/ScrollGatewayBase.sol"; - -// solhint-disable avoid-low-level-calls - -/// @title L1GasTokenGateway -/// @notice The `L1GasTokenGateway` is used to deposit gas token on layer 1 and -/// finalize withdraw gas token from layer 2. -/// @dev The deposited gas tokens are held in this gateway. On finalizing withdraw, the corresponding -/// gas token will be transfer to the recipient directly. -contract L1GasTokenGateway is ScrollGatewayBase, IL1ETHGateway, IMessageDropCallback { - using SafeERC20Upgradeable for IERC20Upgradeable; - - /********** - * Errors * - **********/ - - /// @dev Thrown when `msg.value` is not zero. - error ErrorNonZeroMsgValue(); - - /// @dev Thrown when the selector is invalid during `onDropMessage`. - error ErrorInvalidSelector(); - - /// @dev Thrown when the deposit amount is zero. - error ErrorDepositZeroGasToken(); - - /************* - * Constants * - *************/ - - /// @dev The address of gas token. - address public immutable gasToken; - - /// @dev The scalar to scale the gas token decimals to 18. - uint256 public immutable scale; - - /*************** - * Constructor * - ***************/ - - /// @notice Constructor for `L1GasTokenGateway` implementation contract. - /// - /// @param _gasToken The address of gas token in L1. - /// @param _counterpart The address of `L2ETHGateway` contract in L2. - /// @param _router The address of `L1GatewayRouter` contract in L1. - /// @param _messenger The address of `L1ScrollMessenger` contract in L1. - constructor( - address _gasToken, - address _counterpart, - address _router, - address _messenger - ) ScrollGatewayBase(_counterpart, _router, _messenger) { - if (_gasToken == address(0) || _router == address(0)) revert ErrorZeroAddress(); - - _disableInitializers(); - - gasToken = _gasToken; - scale = 10**(18 - IERC20MetadataUpgradeable(_gasToken).decimals()); - } - - /// @notice Initialize the storage of L1GasTokenGateway. - function initialize() external initializer { - ScrollGatewayBase._initialize(address(0), address(0), address(0)); - } - - /***************************** - * Public Mutating Functions * - *****************************/ - - /// @inheritdoc IL1ETHGateway - function depositETH(uint256 _amount, uint256 _gasLimit) external payable override { - _deposit(_msgSender(), _amount, new bytes(0), _gasLimit); - } - - /// @inheritdoc IL1ETHGateway - function depositETH( - address _to, - uint256 _amount, - uint256 _gasLimit - ) external payable override { - _deposit(_to, _amount, new bytes(0), _gasLimit); - } - - /// @inheritdoc IL1ETHGateway - function depositETHAndCall( - address _to, - uint256 _amount, - bytes calldata _data, - uint256 _gasLimit - ) external payable override { - _deposit(_to, _amount, _data, _gasLimit); - } - - /// @inheritdoc IL1ETHGateway - function finalizeWithdrawETH( - address _from, - address _to, - uint256 _amount, - bytes calldata _data - ) external payable override onlyCallByCounterpart nonReentrant { - if (msg.value > 0) { - revert ErrorNonZeroMsgValue(); - } - - uint256 downScaledAmount = _amount / scale; - IERC20Upgradeable(gasToken).safeTransfer(_to, downScaledAmount); - _doCallback(_to, _data); - - emit FinalizeWithdrawETH(_from, _to, downScaledAmount, _data); - } - - /// @inheritdoc IMessageDropCallback - function onDropMessage(bytes calldata _message) external payable virtual onlyInDropContext nonReentrant { - // _message should start with 0x232e8748 => finalizeDepositETH(address,address,uint256,bytes) - if (bytes4(_message[0:4]) != IL2ETHGateway.finalizeDepositETH.selector) { - revert ErrorInvalidSelector(); - } - - // decode (receiver, amount) - (address _receiver, , uint256 _amount, ) = abi.decode(_message[4:], (address, address, uint256, bytes)); - uint256 downScaledAmount = _amount / scale; - - IERC20Upgradeable(gasToken).safeTransfer(_receiver, downScaledAmount); - - emit RefundETH(_receiver, downScaledAmount); - } - - /********************** - * Internal Functions * - **********************/ - - /// @dev The internal ETH deposit implementation. - /// @param _to The address of recipient's account on L2. - /// @param _amount The amount of ETH to be deposited. - /// @param _data Optional data to forward to recipient's account. - /// @param _gasLimit Gas limit required to complete the deposit on L2. - function _deposit( - address _to, - uint256 _amount, - bytes memory _data, - uint256 _gasLimit - ) internal virtual nonReentrant { - // 1. Extract real sender if this call is from L1GatewayRouter. - address _from = _msgSender(); - - if (router == _from) { - (_from, _data) = abi.decode(_data, (address, bytes)); - } - - // 2. transfer gas token from caller - uint256 _before = IERC20Upgradeable(gasToken).balanceOf(address(this)); - IERC20Upgradeable(gasToken).safeTransferFrom(_from, address(this), _amount); - uint256 _after = IERC20Upgradeable(gasToken).balanceOf(address(this)); - _amount = _after - _before; - if (_amount == 0) { - revert ErrorDepositZeroGasToken(); - } - - uint256 upScaledAmount = _amount * scale; - - // 3. Generate message passed to L1ScrollMessenger. - bytes memory _message = abi.encodeCall(IL2ETHGateway.finalizeDepositETH, (_from, _to, upScaledAmount, _data)); - - IL1ScrollMessenger(messenger).sendMessage{value: msg.value}( - counterpart, - upScaledAmount, - _message, - _gasLimit, - _from - ); - - emit DepositETH(_from, _to, _amount, _data); - } -} diff --git a/src/alternative-gas-token/L1ScrollMessengerNonETH.sol b/src/alternative-gas-token/L1ScrollMessengerNonETH.sol deleted file mode 100644 index a6aa8b09..00000000 --- a/src/alternative-gas-token/L1ScrollMessengerNonETH.sol +++ /dev/null @@ -1,246 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity =0.8.24; - -import {AddressUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; - -import {IL1MessageQueueV1} from "../L1/rollup/IL1MessageQueueV1.sol"; -import {IScrollChain} from "../L1/rollup/IScrollChain.sol"; -import {L1ScrollMessenger} from "../L1/L1ScrollMessenger.sol"; -import {IMessageDropCallback} from "../libraries/callbacks/IMessageDropCallback.sol"; -import {ScrollConstants} from "../libraries/constants/ScrollConstants.sol"; -import {WithdrawTrieVerifier} from "../libraries/verifier/WithdrawTrieVerifier.sol"; - -contract L1ScrollMessengerNonETH is L1ScrollMessenger { - /********** - * Errors * - **********/ - - /// @dev Thrown when the message is duplicated. - error ErrorDuplicatedMessage(); - - /// @dev Thrown when caller pass non-zero value in `sendMessage`. - error ErrorNonZeroValueFromCaller(); - - /// @dev Thrown when caller pass non-zero value in `relayMessageWithProof`. - error ErrorNonZeroValueFromCrossDomainCaller(); - - /// @dev Thrown when the `msg.value` cannot cover cross domain fee. - error ErrorInsufficientMsgValue(); - - /// @dev Thrown when the message is executed before. - error ErrorMessageExecuted(); - - /// @dev Thrown when the message has not enqueued before. - error ErrorMessageNotEnqueued(); - - /// @dev Thrown when the message is dropped before. - error ErrorMessageDropped(); - - /// @dev Thrown when relay a message belonging to an unfinalized batch. - error ErrorBatchNotFinalized(); - - /// @dev Thrown when the provided merkle proof is invalid. - error ErrorInvalidMerkleProof(); - - /* - //shu@unifra.io: this error had been decllared by L1ScrollMessenger - - /// @dev Thrown when call to message queue. - error ErrorForbidToCallMessageQueue(); - */ - - /// @dev Thrown when the message sender is invalid. - error ErrorInvalidMessageSender(); - - /************* - * Constants * - *************/ - - /// @notice The address of `L1NativeTokenGateway` contract. - address public immutable nativeTokenGateway; - - /*************** - * Constructor * - ***************/ - - constructor( - address _nativeTokenGateway, - address _counterpart, - address _rollup, - address _messageQueue, - address _messageQueueV2 - ) L1ScrollMessenger(_counterpart, _rollup, _messageQueue, _messageQueueV2) { - nativeTokenGateway = _nativeTokenGateway; - } - - /********************** - * Internal Functions * - **********************/ - - /// @inheritdoc L1ScrollMessenger - function _sendMessage( - address _to, - uint256 _l2GasTokenValue, - bytes memory _message, - uint256 _gasLimit, - address _refundAddress - ) internal override { - // if we want to pass value to L2, must call from `L1NativeTokenGateway`. - if (_l2GasTokenValue > 0 && _msgSender() != nativeTokenGateway) { - revert ErrorNonZeroValueFromCaller(); - } - - // compute the actual cross domain message calldata. - uint256 _messageNonce = IL1MessageQueueV1(messageQueueV1).nextCrossDomainMessageIndex(); - bytes memory _xDomainCalldata = _encodeXDomainCalldata( - _msgSender(), - _to, - _l2GasTokenValue, - _messageNonce, - _message - ); - - // compute and deduct the messaging fee to fee vault. - uint256 _fee = IL1MessageQueueV1(messageQueueV1).estimateCrossDomainMessageFee(_gasLimit); - if (msg.value < _fee) { - revert ErrorInsufficientMsgValue(); - } - if (_fee > 0) { - AddressUpgradeable.sendValue(payable(feeVault), _fee); - } - - // append message to L1MessageQueue - IL1MessageQueueV1(messageQueueV1).appendCrossDomainMessage(counterpart, _gasLimit, _xDomainCalldata); - - // record the message hash for future use. - bytes32 _xDomainCalldataHash = keccak256(_xDomainCalldata); - - // normally this won't happen, since each message has different nonce, but just in case. - if (messageSendTimestamp[_xDomainCalldataHash] != 0) { - revert ErrorDuplicatedMessage(); - } - messageSendTimestamp[_xDomainCalldataHash] = block.timestamp; - - emit SentMessage(_msgSender(), _to, _l2GasTokenValue, _messageNonce, _gasLimit, _message); - - // refund fee to `_refundAddress` - unchecked { - uint256 _refund = msg.value - _fee; - if (_refund > 0) { - AddressUpgradeable.sendValue(payable(_refundAddress), _refund); - } - } - } - - function _relayMessageWithProof( - address _from, - address _to, - uint256 _l2GasTokenValue, - uint256 _nonce, - bytes memory _message, - L2MessageProof memory _proof - ) internal virtual { - // if we want to pass value to L1, must call to `L1NativeTokenGateway`. - if (_l2GasTokenValue > 0 && _to != nativeTokenGateway) { - revert ErrorNonZeroValueFromCrossDomainCaller(); - } - - bytes32 _xDomainCalldataHash = keccak256( - _encodeXDomainCalldata(_from, _to, _l2GasTokenValue, _nonce, _message) - ); - if (isL2MessageExecuted[_xDomainCalldataHash]) { - revert ErrorMessageExecuted(); - } - - { - if (!IScrollChain(rollup).isBatchFinalized(_proof.batchIndex)) { - revert ErrorBatchNotFinalized(); - } - bytes32 _messageRoot = IScrollChain(rollup).withdrawRoots(_proof.batchIndex); - if ( - !WithdrawTrieVerifier.verifyMerkleProof(_messageRoot, _xDomainCalldataHash, _nonce, _proof.merkleProof) - ) { - revert ErrorInvalidMerkleProof(); - } - } - - // @note check more `_to` address to avoid attack in the future when we add more gateways. - if (_to == messageQueueV1) { - revert ErrorForbidToCallMessageQueue(); - } - _validateTargetAddress(_to); - - // @note This usually will never happen, just in case. - if (_from == xDomainMessageSender) { - revert ErrorInvalidMessageSender(); - } - - xDomainMessageSender = _from; - (bool success, ) = _to.call(_message); - // reset value to refund gas. - xDomainMessageSender = ScrollConstants.DEFAULT_XDOMAIN_MESSAGE_SENDER; - - if (success) { - isL2MessageExecuted[_xDomainCalldataHash] = true; - emit RelayedMessage(_xDomainCalldataHash); - } else { - emit FailedRelayedMessage(_xDomainCalldataHash); - } - } - - function _dropMessage( - address _from, - address _to, - uint256 _l2GasTokenValue, - uint256 _messageNonce, - bytes memory _message - ) internal virtual { - // The criteria for dropping a message: - // 1. The message is a L1 message. - // 2. The message has not been dropped before. - // 3. the message and all of its replacement are finalized in L1. - // 4. the message and all of its replacement are skipped. - // - // Possible denial of service attack: - // + replayMessage is called every time someone want to drop the message. - // + replayMessage is called so many times for a skipped message, thus results a long list. - // - // We limit the number of `replayMessage` calls of each message, which may solve the above problem. - - // check message exists - bytes memory _xDomainCalldata = _encodeXDomainCalldata(_from, _to, _l2GasTokenValue, _messageNonce, _message); - bytes32 _xDomainCalldataHash = keccak256(_xDomainCalldata); - if (messageSendTimestamp[_xDomainCalldataHash] == 0) { - revert ErrorMessageNotEnqueued(); - } - - // check message not dropped - if (isL1MessageDropped[_xDomainCalldataHash]) { - revert ErrorMessageDropped(); - } - - // check message is finalized - uint256 _lastIndex = replayStates[_xDomainCalldataHash].lastIndex; - if (_lastIndex == 0) _lastIndex = _messageNonce; - - // check message is skipped and drop it. - // @note If the list is very long, the message may never be dropped. - while (true) { - IL1MessageQueueV1(messageQueueV1).dropCrossDomainMessage(_lastIndex); - _lastIndex = prevReplayIndex[_lastIndex]; - if (_lastIndex == 0) break; - unchecked { - _lastIndex = _lastIndex - 1; - } - } - - isL1MessageDropped[_xDomainCalldataHash] = true; - - // set execution context - xDomainMessageSender = ScrollConstants.DROP_XDOMAIN_MESSAGE_SENDER; - IMessageDropCallback(_from).onDropMessage(_message); - // clear execution context - xDomainMessageSender = ScrollConstants.DEFAULT_XDOMAIN_MESSAGE_SENDER; - } -} diff --git a/src/alternative-gas-token/L1WrappedTokenGateway.sol b/src/alternative-gas-token/L1WrappedTokenGateway.sol deleted file mode 100644 index 065a00f2..00000000 --- a/src/alternative-gas-token/L1WrappedTokenGateway.sol +++ /dev/null @@ -1,100 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity =0.8.24; - -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import {Address} from "@openzeppelin/contracts/utils/Address.sol"; - -import {IL1ERC20Gateway} from "../L1/gateways/IL1ERC20Gateway.sol"; -import {IWETH} from "../interfaces/IWETH.sol"; - -contract L1WrappedTokenGateway { - using SafeERC20 for IERC20; - - /********** - * Events * - **********/ - - /// @notice Emitted when someone wrap ETH to WETH and then deposit WETH from L1 to L2. - /// @param from The address of sender in L1. - /// @param to The address of recipient in L2. - /// @param amount The amount of ETH will be deposited from L1 to L2. - event DepositWrappedToken(address indexed from, address indexed to, uint256 amount); - - /********* - * Error * - *********/ - - /// @dev Thrown when someone try to send ETH to this contract. - error ErrorCallNotFromFeeRefund(); - - /************* - * Constants * - *************/ - - /// @dev The safe gas limit used to bridge WETH to L2. - uint256 private constant SAFE_GAS_LIMIT = 450000; - - /// @dev The default value of `sender`. - address private constant DEFAULT_SENDER = address(1); - - /// @notice The address of Wrapped Ether. - address public immutable WETH; - - /// @notice The address of ERC20 gateway used to bridge WETH. - address public immutable gateway; - - /************* - * Variables * - *************/ - - /// @notice The address of caller who called `deposit`. - /// @dev This will be reset after call `gateway.depositERC20`, which is used to - /// prevent malicious user sending ETH to this contract. - address public sender; - - /*************** - * Constructor * - ***************/ - - constructor(address _weth, address _gateway) { - WETH = _weth; - gateway = _gateway; - - sender = DEFAULT_SENDER; - } - - /***************************** - * Public Mutating Functions * - *****************************/ - - /// @dev Only receive cross domain fee refund - receive() external payable { - if (sender == DEFAULT_SENDER) { - revert ErrorCallNotFromFeeRefund(); - } - } - - /// @notice Deposit ETH. - /// @dev This will wrap ETH to WETH first and then deposit as WETH. - /// @param _to The address of recipient in L2. - /// @param _amount The amount of ETH to deposit. - function deposit(address _to, uint256 _amount) external payable { - IWETH(WETH).deposit{value: _amount}(); - - IERC20(WETH).safeApprove(gateway, 0); - IERC20(WETH).safeApprove(gateway, _amount); - sender = msg.sender; - IL1ERC20Gateway(gateway).depositERC20{value: msg.value - _amount}(WETH, _to, _amount, SAFE_GAS_LIMIT); - sender = DEFAULT_SENDER; - - emit DepositWrappedToken(msg.sender, _to, _amount); - - // refund exceed fee - uint256 balance = address(this).balance; - if (balance > 0) { - Address.sendValue(payable(msg.sender), balance); - } - } -} diff --git a/src/alternative-gas-token/L2TxFeeVaultWithGasToken.sol b/src/alternative-gas-token/L2TxFeeVaultWithGasToken.sol deleted file mode 100644 index 767259c8..00000000 --- a/src/alternative-gas-token/L2TxFeeVaultWithGasToken.sol +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity =0.8.24; - -import {L2TxFeeVault} from "../L2/predeploys/L2TxFeeVault.sol"; -import {IL2ETHGateway} from "../L2/gateways/IL2ETHGateway.sol"; - -contract L2TxFeeVaultWithGasToken is L2TxFeeVault { - /************* - * Constants * - *************/ - - /// @notice The address of `L2ETHGateway` contract. - address public ETHGateway; - - /*************** - * Constructor * - ***************/ - - constructor( - address _ETHGateway, - address _owner, - address _recipient, - uint256 _minWithdrawalAmount - ) L2TxFeeVault(_owner, _recipient, _minWithdrawalAmount) { - ETHGateway = _ETHGateway; - } - - /************************ - * Restricted Functions * - ************************/ - - /// @notice Update the address of ETHGateway. - /// @param _newETHGateway The address of ETHGateway to update. - function updateNativeTokenGateway(address _newETHGateway) external onlyOwner { - address _oldETHGateway = ETHGateway; - ETHGateway = _newETHGateway; - - emit UpdateMessenger(_oldETHGateway, _newETHGateway); - } - - /********************** - * Internal Functions * - **********************/ - - function sendWithdrawMessage(address _recipient, uint256 _value) internal { - // no fee provided - IL2ETHGateway(ETHGateway).withdrawETH{value: _value}( - _recipient, - _value, - 0 // _gasLimit can be zero for fee vault. - ); - } -} diff --git a/src/batch-bridge/L1BatchBridgeGateway.sol b/src/batch-bridge/L1BatchBridgeGateway.sol index 8033213b..75b46b2a 100644 --- a/src/batch-bridge/L1BatchBridgeGateway.sol +++ b/src/batch-bridge/L1BatchBridgeGateway.sol @@ -16,6 +16,7 @@ import {BatchBridgeCodec} from "./BatchBridgeCodec.sol"; import {L2BatchBridgeGateway} from "./L2BatchBridgeGateway.sol"; /// @title L1BatchBridgeGateway +/// @custom:deprecated This contract is no longer used in production. contract L1BatchBridgeGateway is AccessControlEnumerableUpgradeable, ReentrancyGuardUpgradeable { using SafeERC20Upgradeable for IERC20Upgradeable; @@ -220,9 +221,13 @@ contract L1BatchBridgeGateway is AccessControlEnumerableUpgradeable, ReentrancyG function depositERC20(address token, uint96 amount) external nonReentrant { if (token == address(0)) revert ErrorIncorrectMethodForETHDeposit(); - // common practice to handle fee on transfer token. uint256 beforeBalance = IERC20Upgradeable(token).balanceOf(address(this)); + + // no reentrancy risk (nonReentrant modifier). + // slither-disable-next-line reentrancy-no-eth IERC20Upgradeable(token).safeTransferFrom(_msgSender(), address(this), amount); + + // common practice to handle fee on transfer token. amount = uint96(IERC20Upgradeable(token).balanceOf(address(this)) - beforeBalance); _deposit(token, _msgSender(), amount); @@ -280,6 +285,8 @@ contract L1BatchBridgeGateway is AccessControlEnumerableUpgradeable, ReentrancyG accumulatedFee = IERC20Upgradeable(token).balanceOf(address(this)) - cachedTokenState.pending; } if (accumulatedFee > 0) { + // no reentrancy risk (onlyRole modifier). + // slither-disable-next-line reentrancy-eth, reentrancy-no-eth _transferToken(token, feeVault, accumulatedFee); } @@ -287,6 +294,8 @@ contract L1BatchBridgeGateway is AccessControlEnumerableUpgradeable, ReentrancyG BatchState memory cachedBatchState = batches[token][cachedTokenState.pendingBatchIndex]; address l2Token; if (token == address(0)) { + // transfer to messenger is safe. + // slither-disable-next-line arbitrary-send-eth IL1ScrollMessenger(messenger).sendMessage{value: cachedBatchState.amount + depositFee}( counterpart, cachedBatchState.amount, @@ -298,6 +307,9 @@ contract L1BatchBridgeGateway is AccessControlEnumerableUpgradeable, ReentrancyG l2Token = IL1ERC20Gateway(gateway).getL2ERC20Address(token); IERC20Upgradeable(token).safeApprove(gateway, 0); IERC20Upgradeable(token).safeApprove(gateway, cachedBatchState.amount); + + // transfer to whitelisted gateway is safe. + // slither-disable-next-line arbitrary-send-eth IL1ERC20Gateway(gateway).depositERC20{value: depositFee}( token, counterpart, @@ -329,6 +341,8 @@ contract L1BatchBridgeGateway is AccessControlEnumerableUpgradeable, ReentrancyG // refund keeper fee unchecked { if (msg.value > depositFee + batchBridgeFee) { + // no reentrancy risk (onlyRole modifier). + // slither-disable-next-line reentrancy-eth, reentrancy-no-eth _transferToken(address(0), _msgSender(), msg.value - depositFee - batchBridgeFee); } } diff --git a/src/batch-bridge/L2BatchBridgeGateway.sol b/src/batch-bridge/L2BatchBridgeGateway.sol index 84b95273..4e01e68f 100644 --- a/src/batch-bridge/L2BatchBridgeGateway.sol +++ b/src/batch-bridge/L2BatchBridgeGateway.sol @@ -9,6 +9,7 @@ import {IL2ScrollMessenger} from "../L2/IL2ScrollMessenger.sol"; import {BatchBridgeCodec} from "./BatchBridgeCodec.sol"; /// @title L2BatchBridgeGateway +/// @custom:deprecated This contract is no longer used in production. contract L2BatchBridgeGateway is AccessControlEnumerableUpgradeable { /********** * Events * @@ -232,6 +233,7 @@ contract L2BatchBridgeGateway is AccessControlEnumerableUpgradeable { ) private returns (bool success) { if (token == address(0)) { // We add gas limit here to avoid DDOS from malicious receiver. + // slither-disable-next-line arbitrary-send-eth (success, ) = receiver.call{value: amount, gas: SAFE_ETH_TRANSFER_GAS_LIMIT}(""); } else { // We perform a low level call here, to bypass Solidity's return data size checking mechanism. diff --git a/src/gas-swap/GasSwap.sol b/src/gas-swap/GasSwap.sol deleted file mode 100644 index 61bea33c..00000000 --- a/src/gas-swap/GasSwap.sol +++ /dev/null @@ -1,201 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity =0.8.24; - -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol"; -import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol"; -import {Context} from "@openzeppelin/contracts/utils/Context.sol"; - -// solhint-disable no-empty-blocks - -contract GasSwap is ERC2771Context, Ownable, ReentrancyGuard { - using SafeERC20 for IERC20; - using SafeERC20 for IERC20Permit; - - /********** - * Events * - **********/ - - /// @notice Emitted when the fee ratio is updated. - /// @param feeRatio The new fee ratio, multiplied by 1e18. - event UpdateFeeRatio(uint256 feeRatio); - - /// @notice Emitted when the status of target is updated. - /// @param target The address of target contract. - /// @param status The status updated. - event UpdateApprovedTarget(address target, bool status); - - /************* - * Constants * - *************/ - - /// @dev The fee precision. - uint256 private constant PRECISION = 1e18; - - /*********** - * Structs * - ***********/ - - struct PermitData { - // The address of token to spend. - address token; - // The amount of token to spend. - uint256 value; - // The deadline of the permit. - uint256 deadline; - // Below three are signatures. - uint8 v; - bytes32 r; - bytes32 s; - } - - struct SwapData { - // The address of target contract to call. - address target; - // The calldata passed to target contract. - bytes data; - // The minimum amount of Ether should receive. - uint256 minOutput; - } - - /************* - * Variables * - *************/ - - /// @notice Keep track whether an address is approved. - mapping(address => bool) public approvedTargets; - - /// @notice The fee ratio charged for each swap, multiplied by 1e18. - uint256 public feeRatio; - - /*************** - * Constructor * - ***************/ - - constructor(address trustedForwarder) ERC2771Context(trustedForwarder) {} - - /***************************** - * Public Mutating Functions * - *****************************/ - - receive() external payable {} - - /// @notice Swap some token for ether. - /// @param _permit The permit data, see comments from `PermitData`. - /// @param _swap The swap data, see comments from `SwapData`. - function swap(PermitData memory _permit, SwapData memory _swap) external nonReentrant { - require(approvedTargets[_swap.target], "target not approved"); - address _sender = _msgSender(); - - // do permit - IERC20Permit(_permit.token).safePermit( - _sender, - address(this), - _permit.value, - _permit.deadline, - _permit.v, - _permit.r, - _permit.s - ); - - // record token balance in this contract - uint256 _balance = IERC20(_permit.token).balanceOf(address(this)); - - // transfer token - IERC20(_permit.token).safeTransferFrom(_sender, address(this), _permit.value); - - // approve - IERC20(_permit.token).safeApprove(_swap.target, 0); - IERC20(_permit.token).safeApprove(_swap.target, _permit.value); - - // do swap - uint256 _outputTokenAmount = address(this).balance; - // solhint-disable-next-line avoid-low-level-calls - (bool _success, bytes memory _res) = _swap.target.call(_swap.data); - require(_success, string(concat(bytes("swap failed: "), bytes(getRevertMsg(_res))))); - _outputTokenAmount = address(this).balance - _outputTokenAmount; - - // take fee - uint256 _fee = (_outputTokenAmount * feeRatio) / PRECISION; - _outputTokenAmount = _outputTokenAmount - _fee; - require(_outputTokenAmount >= _swap.minOutput, "insufficient output amount"); - - // transfer ETH to sender - (_success, ) = _sender.call{value: _outputTokenAmount}(""); - require(_success, "transfer ETH failed"); - - // refund rest token - uint256 _dust = IERC20(_permit.token).balanceOf(address(this)) - _balance; - if (_dust > 0) { - IERC20(_permit.token).safeTransfer(_sender, _dust); - } - } - - /************************ - * Restricted Functions * - ************************/ - - /// @notice Withdraw stucked tokens. - /// @param _token The address of token to withdraw. Use `address(0)` if you want to withdraw Ether. - /// @param _amount The amount of token to withdraw. - function withdraw(address _token, uint256 _amount) external onlyOwner { - if (_token == address(0)) { - (bool success, ) = _msgSender().call{value: _amount}(""); - require(success, "ETH transfer failed"); - } else { - IERC20(_token).safeTransfer(_msgSender(), _amount); - } - } - - /// @notice Update the fee ratio. - /// @param _feeRatio The new fee ratio. - function updateFeeRatio(uint256 _feeRatio) external onlyOwner { - feeRatio = _feeRatio; - - emit UpdateFeeRatio(_feeRatio); - } - - /// @notice Update the status of a target address. - /// @param _target The address of target to update. - /// @param _status The new status. - function updateApprovedTarget(address _target, bool _status) external onlyOwner { - approvedTargets[_target] = _status; - - emit UpdateApprovedTarget(_target, _status); - } - - /********************** - * Internal Functions * - **********************/ - - /// @inheritdoc Context - function _msgData() internal view virtual override(Context, ERC2771Context) returns (bytes calldata) { - return ERC2771Context._msgData(); - } - - /// @inheritdoc Context - function _msgSender() internal view virtual override(Context, ERC2771Context) returns (address) { - return ERC2771Context._msgSender(); - } - - /// @dev Internal function to concat two bytes array. - function concat(bytes memory a, bytes memory b) internal pure returns (bytes memory) { - return abi.encodePacked(a, b); - } - - /// @dev Internal function decode revert message from return data. - function getRevertMsg(bytes memory _returnData) internal pure returns (string memory) { - if (_returnData.length < 68) return "Transaction reverted silently"; - - // solhint-disable-next-line no-inline-assembly - assembly { - _returnData := add(_returnData, 0x04) - } - - return abi.decode(_returnData, (string)); - } -} diff --git a/src/libraries/callbacks/IMessageDropCallback.sol b/src/libraries/callbacks/IMessageDropCallback.sol deleted file mode 100644 index e23f3cda..00000000 --- a/src/libraries/callbacks/IMessageDropCallback.sol +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.24; - -interface IMessageDropCallback { - function onDropMessage(bytes memory message) external payable; -} diff --git a/src/libraries/gateway/ScrollGatewayBase.sol b/src/libraries/gateway/ScrollGatewayBase.sol index 4bc49ebc..59aa65d1 100644 --- a/src/libraries/gateway/ScrollGatewayBase.sol +++ b/src/libraries/gateway/ScrollGatewayBase.sol @@ -9,7 +9,6 @@ import {IScrollGateway} from "./IScrollGateway.sol"; import {IScrollMessenger} from "../IScrollMessenger.sol"; import {IScrollGatewayCallback} from "../callbacks/IScrollGatewayCallback.sol"; import {ScrollConstants} from "../constants/ScrollConstants.sol"; -import {ITokenRateLimiter} from "../../rate-limiter/ITokenRateLimiter.sol"; /// @title ScrollGatewayBase /// @notice The `ScrollGatewayBase` is a base contract for gateway contracts used in both in L1 and L2. diff --git a/src/libraries/verifier/ZkEvmVerifierPostFeynman.sol b/src/libraries/verifier/ZkEvmVerifierPostFeynman.sol new file mode 100644 index 00000000..bb511356 --- /dev/null +++ b/src/libraries/verifier/ZkEvmVerifierPostFeynman.sol @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: MIT + +pragma solidity =0.8.24; + +import {IZkEvmVerifierV2} from "./IZkEvmVerifier.sol"; + +// solhint-disable no-inline-assembly + +contract ZkEvmVerifierPostFeynman is IZkEvmVerifierV2 { + /********** + * Errors * + **********/ + + /// @dev Thrown when bundle recursion zk proof verification is failed. + error VerificationFailed(); + + /************* + * Constants * + *************/ + + /// @notice The address of highly optimized plonk verifier contract. + address public immutable plonkVerifier; + + /// @notice A predetermined digest for the `plonkVerifier`. + bytes32 public immutable verifierDigest1; + + /// @notice A predetermined digest for the `plonkVerifier`. + bytes32 public immutable verifierDigest2; + + /// @notice The version of the protocol. + uint256 public immutable protocolVersion; + + /*************** + * Constructor * + ***************/ + + constructor( + address _verifier, + bytes32 _verifierDigest1, + bytes32 _verifierDigest2, + uint256 _protocolVersion + ) { + plonkVerifier = _verifier; + verifierDigest1 = _verifierDigest1; + verifierDigest2 = _verifierDigest2; + protocolVersion = _protocolVersion; + } + + /************************* + * Public View Functions * + *************************/ + + /// @inheritdoc IZkEvmVerifierV2 + /// + /// @dev Encoding for `publicInput`. And this is exactly the same as `ZkEvmVerifierV2`. + /// ```text + /// | layer2ChainId | numBatches | prevStateRoot | prevBatchHash | postStateRoot | batchHash | withdrawRoot | + /// | 8 bytes | 4 bytes | 32 bytes | 32 bytes | 32 bytes | 32 bytes | 32 bytes | + /// ``` + function verify(bytes calldata bundleProof, bytes calldata publicInput) external view override { + address _verifier = plonkVerifier; + bytes32 _verifierDigest1 = verifierDigest1; + bytes32 _verifierDigest2 = verifierDigest2; + bytes32 publicInputHash = keccak256(abi.encodePacked(protocolVersion, publicInput)); + bool success; + + // 1. the first 12 * 32 (0x180) bytes of `bundleProof` is `accumulator` + // 2. the rest bytes of `bundleProof` is the actual `bundle_proof` + // 3. Inserted between `accumulator` and `bundle_proof` are + // 32 * 34 (0x440) bytes, such that: + // | start | end | field | + // |---------------|---------------|-------------------------| + // | 0x00 | 0x180 | bundleProof[0x00:0x180] | + // | 0x180 | 0x180 + 0x20 | verifierDigest1 | + // | 0x180 + 0x20 | 0x180 + 0x40 | verifierDigest2 | + // | 0x180 + 0x40 | 0x180 + 0x60 | publicInputHash[0] | + // | 0x180 + 0x60 | 0x180 + 0x80 | publicInputHash[1] | + // ... + // | 0x180 + 0x420 | 0x180 + 0x440 | publicInputHash[31] | + // | 0x180 + 0x440 | dynamic | bundleProof[0x180:] | + assembly { + let p := mload(0x40) + // 1. copy the accumulator's 0x180 bytes + calldatacopy(p, bundleProof.offset, 0x180) + // 2. insert the public input's 0x440 bytes + mstore(add(p, 0x180), _verifierDigest1) // verifierDigest1 + mstore(add(p, 0x1a0), _verifierDigest2) // verifierDigest2 + for { + let i := 0 + } lt(i, 0x400) { + i := add(i, 0x20) + } { + mstore(add(p, sub(0x5a0, i)), and(publicInputHash, 0xff)) + publicInputHash := shr(8, publicInputHash) + } + // 3. copy all remaining bytes from bundleProof + calldatacopy(add(p, 0x5c0), add(bundleProof.offset, 0x180), sub(bundleProof.length, 0x180)) + // 4. call plonk verifier + success := staticcall(gas(), _verifier, p, add(bundleProof.length, 0x440), 0x00, 0x00) + } + if (!success) { + revert VerificationFailed(); + } + } +} diff --git a/src/misc/ERC2771Forwarder.sol b/src/misc/ERC2771Forwarder.sol deleted file mode 100644 index 8e1b4d69..00000000 --- a/src/misc/ERC2771Forwarder.sol +++ /dev/null @@ -1,391 +0,0 @@ -// SPDX-License-Identifier: MIT - -// @note This file is directly copied from OpenZeppelin's master branch: -// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/metatx/ERC2771Forwarder.sol -// Modifications are made to make it compatible with solidity 0.8.16. - -pragma solidity =0.8.24; - -import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol"; -import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; -import {Nonces} from "./Nonces.sol"; -import {Address} from "@openzeppelin/contracts/utils/Address.sol"; - -/** - * @dev A forwarder compatible with ERC2771 contracts. See {ERC2771Context}. - * - * This forwarder operates on forward requests that include: - * - * * `from`: An address to operate on behalf of. It is required to be equal to the request signer. - * * `to`: The address that should be called. - * * `value`: The amount of native token to attach with the requested call. - * * `gas`: The amount of gas limit that will be forwarded with the requested call. - * * `nonce`: A unique transaction ordering identifier to avoid replayability and request invalidation. - * * `deadline`: A timestamp after which the request is not executable anymore. - * * `data`: Encoded `msg.data` to send with the requested call. - * - * Relayers are able to submit batches if they are processing a high volume of requests. With high - * throughput, relayers may run into limitations of the chain such as limits on the number of - * transactions in the mempool. In these cases the recommendation is to distribute the load among - * multiple accounts. - * - * NOTE: Batching requests includes an optional refund for unused `msg.value` that is achieved by - * performing a call with empty calldata. While this is within the bounds of ERC-2771 compliance, - * if the refund receiver happens to consider the forwarder a trusted forwarder, it MUST properly - * handle `msg.data.length == 0`. `ERC2771Context` in OpenZeppelin Contracts versions prior to 4.9.3 - * do not handle this properly. - * - * ==== Security Considerations - * - * If a relayer submits a forward request, it should be willing to pay up to 100% of the gas amount - * specified in the request. This contract does not implement any kind of retribution for this gas, - * and it is assumed that there is an out of band incentive for relayers to pay for execution on - * behalf of signers. Often, the relayer is operated by a project that will consider it a user - * acquisition cost. - * - * By offering to pay for gas, relayers are at risk of having that gas used by an attacker toward - * some other purpose that is not aligned with the expected out of band incentives. If you operate a - * relayer, consider whitelisting target contracts and function selectors. When relaying ERC-721 or - * ERC-1155 transfers specifically, consider rejecting the use of the `data` field, since it can be - * used to execute arbitrary code. - */ -contract ERC2771Forwarder is EIP712, Nonces { - using ECDSA for bytes32; - - struct ForwardRequestData { - address from; - address to; - uint256 value; - uint256 gas; - uint48 deadline; - bytes data; - bytes signature; - } - - bytes32 internal constant _FORWARD_REQUEST_TYPEHASH = - keccak256( - "ForwardRequest(address from,address to,uint256 value,uint256 gas,uint256 nonce,uint48 deadline,bytes data)" - ); - - /** - * @dev Emitted when a `ForwardRequest` is executed. - * - * NOTE: An unsuccessful forward request could be due to an invalid signature, an expired deadline, - * or simply a revert in the requested call. The contract guarantees that the relayer is not able to force - * the requested call to run out of gas. - */ - event ExecutedForwardRequest(address indexed signer, uint256 nonce, bool success); - - /** - * @dev The request `from` doesn't match with the recovered `signer`. - */ - error ERC2771ForwarderInvalidSigner(address signer, address from); - - /** - * @dev The `requestedValue` doesn't match with the available `msgValue`. - */ - error ERC2771ForwarderMismatchedValue(uint256 requestedValue, uint256 msgValue); - - /** - * @dev The request `deadline` has expired. - */ - error ERC2771ForwarderExpiredRequest(uint48 deadline); - - /** - * @dev The request target doesn't trust the `forwarder`. - */ - error ERC2771UntrustfulTarget(address target, address forwarder); - - /** - * @dev A call to an address target failed. The target may have reverted. - */ - error FailedInnerCall(); - - /** - * @dev See {EIP712-constructor}. - */ - constructor(string memory name) EIP712(name, "1") {} - - /** - * @dev Returns `true` if a request is valid for a provided `signature` at the current block timestamp. - * - * A transaction is considered valid when the target trusts this forwarder, the request hasn't expired - * (deadline is not met), and the signer matches the `from` parameter of the signed request. - * - * NOTE: A request may return false here but it won't cause {executeBatch} to revert if a refund - * receiver is provided. - */ - function verify(ForwardRequestData calldata request) public view virtual returns (bool) { - (bool isTrustedForwarder, bool active, bool signerMatch, ) = _validate(request); - return isTrustedForwarder && active && signerMatch; - } - - /** - * @dev Executes a `request` on behalf of `signature`'s signer using the ERC-2771 protocol. The gas - * provided to the requested call may not be exactly the amount requested, but the call will not run - * out of gas. Will revert if the request is invalid or the call reverts, in this case the nonce is not consumed. - * - * Requirements: - * - * - The request value should be equal to the provided `msg.value`. - * - The request should be valid according to {verify}. - */ - function execute(ForwardRequestData calldata request) public payable virtual { - // We make sure that msg.value and request.value match exactly. - // If the request is invalid or the call reverts, this whole function - // will revert, ensuring value isn't stuck. - if (msg.value != request.value) { - revert ERC2771ForwarderMismatchedValue(request.value, msg.value); - } - - if (!_execute(request, true)) { - revert FailedInnerCall(); - } - } - - /** - * @dev Batch version of {execute} with optional refunding and atomic execution. - * - * In case a batch contains at least one invalid request (see {verify}), the - * request will be skipped and the `refundReceiver` parameter will receive back the - * unused requested value at the end of the execution. This is done to prevent reverting - * the entire batch when a request is invalid or has already been submitted. - * - * If the `refundReceiver` is the `address(0)`, this function will revert when at least - * one of the requests was not valid instead of skipping it. This could be useful if - * a batch is required to get executed atomically (at least at the top-level). For example, - * refunding (and thus atomicity) can be opt-out if the relayer is using a service that avoids - * including reverted transactions. - * - * Requirements: - * - * - The sum of the requests' values should be equal to the provided `msg.value`. - * - All of the requests should be valid (see {verify}) when `refundReceiver` is the zero address. - * - * NOTE: Setting a zero `refundReceiver` guarantees an all-or-nothing requests execution only for - * the first-level forwarded calls. In case a forwarded request calls to a contract with another - * subcall, the second-level call may revert without the top-level call reverting. - */ - function executeBatch(ForwardRequestData[] calldata requests, address payable refundReceiver) - public - payable - virtual - { - bool atomic = refundReceiver == address(0); - - uint256 requestsValue; - uint256 refundValue; - - for (uint256 i; i < requests.length; ++i) { - requestsValue += requests[i].value; - bool success = _execute(requests[i], atomic); - if (!success) { - refundValue += requests[i].value; - } - } - - // The batch should revert if there's a mismatched msg.value provided - // to avoid request value tampering - if (requestsValue != msg.value) { - revert ERC2771ForwarderMismatchedValue(requestsValue, msg.value); - } - - // Some requests with value were invalid (possibly due to frontrunning). - // To avoid leaving ETH in the contract this value is refunded. - if (refundValue != 0) { - // We know refundReceiver != address(0) && requestsValue == msg.value - // meaning we can ensure refundValue is not taken from the original contract's balance - // and refundReceiver is a known account. - Address.sendValue(refundReceiver, refundValue); - } - } - - /** - * @dev Validates if the provided request can be executed at current block timestamp with - * the given `request.signature` on behalf of `request.signer`. - */ - function _validate(ForwardRequestData calldata request) - internal - view - virtual - returns ( - bool isTrustedForwarder, - bool active, - bool signerMatch, - address signer - ) - { - (bool isValid, address recovered) = _recoverForwardRequestSigner(request); - - return ( - _isTrustedByTarget(request.to), - request.deadline >= block.timestamp, - isValid && recovered == request.from, - recovered - ); - } - - /** - * @dev Returns a tuple with the recovered the signer of an EIP712 forward request message hash - * and a boolean indicating if the signature is valid. - * - * NOTE: The signature is considered valid if {ECDSA-tryRecover} indicates no recover error for it. - */ - function _recoverForwardRequestSigner(ForwardRequestData calldata request) - internal - view - virtual - returns (bool, address) - { - (address recovered, ECDSA.RecoverError err) = _hashTypedDataV4( - keccak256( - abi.encode( - _FORWARD_REQUEST_TYPEHASH, - request.from, - request.to, - request.value, - request.gas, - nonces(request.from), - request.deadline, - keccak256(request.data) - ) - ) - ).tryRecover(request.signature); - - return (err == ECDSA.RecoverError.NoError, recovered); - } - - /** - * @dev Validates and executes a signed request returning the request call `success` value. - * - * Internal function without msg.value validation. - * - * Requirements: - * - * - The caller must have provided enough gas to forward with the call. - * - The request must be valid (see {verify}) if the `requireValidRequest` is true. - * - * Emits an {ExecutedForwardRequest} event. - * - * IMPORTANT: Using this function doesn't check that all the `msg.value` was sent, potentially - * leaving value stuck in the contract. - */ - function _execute(ForwardRequestData calldata request, bool requireValidRequest) - internal - virtual - returns (bool success) - { - (bool isTrustedForwarder, bool active, bool signerMatch, address signer) = _validate(request); - - // Need to explicitly specify if a revert is required since non-reverting is default for - // batches and reversion is opt-in since it could be useful in some scenarios - if (requireValidRequest) { - if (!isTrustedForwarder) { - revert ERC2771UntrustfulTarget(request.to, address(this)); - } - - if (!active) { - revert ERC2771ForwarderExpiredRequest(request.deadline); - } - - if (!signerMatch) { - revert ERC2771ForwarderInvalidSigner(signer, request.from); - } - } - - // Ignore an invalid request because requireValidRequest = false - if (isTrustedForwarder && signerMatch && active) { - // Nonce should be used before the call to prevent reusing by reentrancy - uint256 currentNonce = _useNonce(signer); - - uint256 reqGas = request.gas; - address to = request.to; - uint256 value = request.value; - bytes memory data = abi.encodePacked(request.data, request.from); - - uint256 gasLeft; - - assembly { - success := call(reqGas, to, value, add(data, 0x20), mload(data), 0, 0) - gasLeft := gas() - } - - _checkForwardedGas(gasLeft, request); - - emit ExecutedForwardRequest(signer, currentNonce, success); - } - } - - /** - * @dev Returns whether the target trusts this forwarder. - * - * This function performs a static call to the target contract calling the - * {ERC2771Context-isTrustedForwarder} function. - */ - function _isTrustedByTarget(address target) private view returns (bool) { - bytes memory encodedParams = abi.encodeCall(ERC2771Context.isTrustedForwarder, (address(this))); - - bool success; - uint256 returnSize; - uint256 returnValue; - /// @solidity memory-safe-assembly - assembly { - // Perform the staticcal and save the result in the scratch space. - // | Location | Content | Content (Hex) | - // |-----------|----------|--------------------------------------------------------------------| - // | | | result ↓ | - // | 0x00:0x1F | selector | 0x0000000000000000000000000000000000000000000000000000000000000001 | - success := staticcall(gas(), target, add(encodedParams, 0x20), mload(encodedParams), 0, 0x20) - returnSize := returndatasize() - returnValue := mload(0) - } - - return success && returnSize >= 0x20 && returnValue > 0; - } - - /** - * @dev Checks if the requested gas was correctly forwarded to the callee. - * - * As a consequence of https://eips.ethereum.org/EIPS/eip-150[EIP-150]: - * - At most `gasleft() - floor(gasleft() / 64)` is forwarded to the callee. - * - At least `floor(gasleft() / 64)` is kept in the caller. - * - * It reverts consuming all the available gas if the forwarded gas is not the requested gas. - * - * IMPORTANT: The `gasLeft` parameter should be measured exactly at the end of the forwarded call. - * Any gas consumed in between will make room for bypassing this check. - */ - function _checkForwardedGas(uint256 gasLeft, ForwardRequestData calldata request) private pure { - // To avoid insufficient gas griefing attacks, as referenced in https://ronan.eth.limo/blog/ethereum-gas-dangers/ - // - // A malicious relayer can attempt to shrink the gas forwarded so that the underlying call reverts out-of-gas - // but the forwarding itself still succeeds. In order to make sure that the subcall received sufficient gas, - // we will inspect gasleft() after the forwarding. - // - // Let X be the gas available before the subcall, such that the subcall gets at most X * 63 / 64. - // We can't know X after CALL dynamic costs, but we want it to be such that X * 63 / 64 >= req.gas. - // Let Y be the gas used in the subcall. gasleft() measured immediately after the subcall will be gasleft() = X - Y. - // If the subcall ran out of gas, then Y = X * 63 / 64 and gasleft() = X - Y = X / 64. - // Under this assumption req.gas / 63 > gasleft() is true is true if and only if - // req.gas / 63 > X / 64, or equivalently req.gas > X * 63 / 64. - // This means that if the subcall runs out of gas we are able to detect that insufficient gas was passed. - // - // We will now also see that req.gas / 63 > gasleft() implies that req.gas >= X * 63 / 64. - // The contract guarantees Y <= req.gas, thus gasleft() = X - Y >= X - req.gas. - // - req.gas / 63 > gasleft() - // - req.gas / 63 >= X - req.gas - // - req.gas >= X * 63 / 64 - // In other words if req.gas < X * 63 / 64 then req.gas / 63 <= gasleft(), thus if the relayer behaves honestly - // the forwarding does not revert. - if (gasLeft < request.gas / 63) { - // We explicitly trigger invalid opcode to consume all gas and bubble-up the effects, since - // neither revert or assert consume all gas since Solidity 0.8.20 - // https://docs.soliditylang.org/en/v0.8.20/control-structures.html#panic-via-assert-and-error-via-require - /// @solidity memory-safe-assembly - assembly { - invalid() - } - } - } -} diff --git a/src/misc/IPausable.sol b/src/misc/IPausable.sol new file mode 100644 index 00000000..3e8a644c --- /dev/null +++ b/src/misc/IPausable.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +interface IPausable { + /// @notice Returns true if the contract is paused, and false otherwise. + function paused() external view returns (bool); + + /// @notice Pause or unpause this contract. + /// @param _status Pause this contract if it is true, otherwise unpause this contract. + function setPause(bool _status) external; +} diff --git a/src/misc/Nonces.sol b/src/misc/Nonces.sol deleted file mode 100644 index 7c761498..00000000 --- a/src/misc/Nonces.sol +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-License-Identifier: MIT - -// @note This file is directly copied from OpenZeppelin's master branch: -// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Nonces.sol -// Modifications are made to make it compatible with solidity 0.8.16. - -pragma solidity ^0.8.24; - -/** - * @dev Provides tracking nonces for addresses. Nonces will only increment. - */ -abstract contract Nonces { - /** - * @dev The nonce used for an `account` is not the expected current nonce. - */ - error InvalidAccountNonce(address account, uint256 currentNonce); - - mapping(address => uint256) private _nonces; - - /** - * @dev Returns an the next unused nonce for an address. - */ - function nonces(address owner) public view virtual returns (uint256) { - return _nonces[owner]; - } - - /** - * @dev Consumes a nonce. - * - * Returns the current value and increments nonce. - */ - function _useNonce(address owner) internal virtual returns (uint256) { - // For each account, the nonce has an initial value of 0, can only be incremented by one, and cannot be - // decremented or reset. This guarantees that the nonce never overflows. - unchecked { - // It is important to do x++ and not ++x here. - return _nonces[owner]++; - } - } - - /** - * @dev Same as {_useNonce} but checking that `nonce` is the next valid for `owner`. - */ - function _useCheckedNonce(address owner, uint256 nonce) internal virtual returns (uint256) { - uint256 current = _useNonce(owner); - if (nonce != current) { - revert InvalidAccountNonce(owner, current); - } - return current; - } -} diff --git a/src/misc/PauseController.sol b/src/misc/PauseController.sol new file mode 100644 index 00000000..54192918 --- /dev/null +++ b/src/misc/PauseController.sol @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +import {IPausable} from "./IPausable.sol"; + +import {ScrollOwner} from "./ScrollOwner.sol"; + +/// @title PauseController +/// @notice This contract is used to pause and unpause components in Scroll. +/// @dev The owner of this contract should be `ScrollOwner` contract to allow fine-grained control over the pause and unpause of components. +contract PauseController is OwnableUpgradeable { + /********** + * Events * + **********/ + + /// @notice Emitted when a component is paused. + /// @param component The component that is paused. + event Pause(address indexed component); + + /// @notice Emitted when a component is unpaused. + /// @param component The component that is unpaused. + event Unpause(address indexed component); + + /// @notice Emitted when the pause cooldown period of a component is reset. + /// @param component The component that has its pause cooldown period reset. + event ResetPauseCooldownPeriod(address indexed component); + + /// @notice Emitted when the pause cooldown period is updated. + /// @param oldPauseCooldownPeriod The old pause cooldown period. + /// @param newPauseCooldownPeriod The new pause cooldown period. + event UpdatePauseCooldownPeriod(uint256 oldPauseCooldownPeriod, uint256 newPauseCooldownPeriod); + + /********** + * Errors * + **********/ + + /// @dev Thrown when the cooldown period is not passed. + error ErrorCooldownPeriodNotPassed(); + + /// @dev Thrown when the component is already paused. + error ErrorComponentAlreadyPaused(); + + /// @dev Thrown when the component is not paused. + error ErrorComponentNotPaused(); + + /// @dev Thrown when the execution of `ScrollOwner` contract fails. + error ErrorExecutePauseFailed(); + + /// @dev Thrown when the execution of `ScrollOwner` contract fails. + error ErrorExecuteUnpauseFailed(); + + /************* + * Constants * + *************/ + + /// @notice The role for pause controller in `ScrollOwner` contract. + bytes32 public constant PAUSE_CONTROLLER_ROLE = keccak256("PAUSE_CONTROLLER_ROLE"); + + /*********************** + * Immutable Variables * + ***********************/ + + /// @notice The address of the ScrollOwner contract. + address public immutable SCROLL_OWNER; + + /********************* + * Storage Variables * + *********************/ + + /// @notice The pause cooldown period. That is the minimum time between two consecutive pauses. + uint256 public pauseCooldownPeriod; + + /// @notice The last unpause time of each component. + mapping(address => uint256) private lastUnpauseTime; + + /*************** + * Constructor * + ***************/ + + constructor(address _scrollOwner) { + SCROLL_OWNER = _scrollOwner; + + _disableInitializers(); + } + + function initialize(uint256 _pauseCooldownPeriod) external initializer { + __Ownable_init(); + + _updatePauseCooldownPeriod(_pauseCooldownPeriod); + } + + /************************* + * Public View Functions * + *************************/ + + /// @notice Get the last unpause timestamp of a component. + /// @param component The component to get the last unpause timestamp. + /// @return The last unpause timestamp of the component. + function getLastUnpauseTime(IPausable component) external view returns (uint256) { + return lastUnpauseTime[address(component)]; + } + + /************************ + * Restricted Functions * + ************************/ + + /// @notice Pause a component. + /// @param component The component to pause. + function pause(IPausable component) external onlyOwner { + if (component.paused()) { + revert ErrorComponentAlreadyPaused(); + } + + if (lastUnpauseTime[address(component)] + pauseCooldownPeriod >= block.timestamp) { + revert ErrorCooldownPeriodNotPassed(); + } + + ScrollOwner(payable(SCROLL_OWNER)).execute( + address(component), + 0, + abi.encodeWithSelector(IPausable.setPause.selector, true), + PAUSE_CONTROLLER_ROLE + ); + + if (!component.paused()) { + revert ErrorExecutePauseFailed(); + } + + emit Pause(address(component)); + } + + /// @notice Unpause a component. + /// @param component The component to unpause. + function unpause(IPausable component) external onlyOwner { + if (!component.paused()) { + revert ErrorComponentNotPaused(); + } + + ScrollOwner(payable(SCROLL_OWNER)).execute( + address(component), + 0, + abi.encodeWithSelector(IPausable.setPause.selector, false), + PAUSE_CONTROLLER_ROLE + ); + + lastUnpauseTime[address(component)] = block.timestamp; + + if (component.paused()) { + revert ErrorExecuteUnpauseFailed(); + } + + emit Unpause(address(component)); + } + + /// @notice Reset the pause cooldown period of a component. + /// @param component The component to reset the pause cooldown period. + function resetPauseCooldownPeriod(IPausable component) external onlyOwner { + lastUnpauseTime[address(component)] = 0; + + emit ResetPauseCooldownPeriod(address(component)); + } + + /// @notice Set the pause cooldown period. + /// @param newPauseCooldownPeriod The new pause cooldown period. + function updatePauseCooldownPeriod(uint256 newPauseCooldownPeriod) external onlyOwner { + _updatePauseCooldownPeriod(newPauseCooldownPeriod); + } + + /********************** + * Internal Functions * + **********************/ + + /// @dev Internal function to set the pause cooldown period. + /// @param newPauseCooldownPeriod The new pause cooldown period. + function _updatePauseCooldownPeriod(uint256 newPauseCooldownPeriod) internal { + uint256 oldPauseCooldownPeriod = pauseCooldownPeriod; + pauseCooldownPeriod = newPauseCooldownPeriod; + + emit UpdatePauseCooldownPeriod(oldPauseCooldownPeriod, newPauseCooldownPeriod); + } +} diff --git a/src/misc/ScrollOwner.sol b/src/misc/ScrollOwner.sol index bb3bace7..bf7f3fbe 100644 --- a/src/misc/ScrollOwner.sol +++ b/src/misc/ScrollOwner.sol @@ -136,6 +136,8 @@ contract ScrollOwner is AccessControlEnumerable { bytes calldata _data ) private { // solhint-disable-next-line avoid-low-level-calls + // no reentrancy risk. + // slither-disable-next-line arbitrary-send-eth (bool success, ) = _target.call{value: _value}(_data); if (!success) { // solhint-disable-next-line no-inline-assembly diff --git a/src/mocks/ScrollChainMockBlob.sol b/src/mocks/ScrollChainMockBlob.sol index 3490f06a..d52c2f34 100644 --- a/src/mocks/ScrollChainMockBlob.sol +++ b/src/mocks/ScrollChainMockBlob.sol @@ -48,10 +48,6 @@ contract ScrollChainMockBlob is ScrollChain { overrideBatchHashCheck = status; } - function _getBlobVersionedHash() internal virtual override returns (bytes32 _blobVersionedHash) { - _blobVersionedHash = blobhashes[0]; - } - function _getBlobVersionedHash(uint256 index) internal virtual override returns (bytes32 _blobVersionedHash) { _blobVersionedHash = blobhashes[index]; } diff --git a/src/package.json b/src/package.json index 555c1a1e..65a8b108 100644 --- a/src/package.json +++ b/src/package.json @@ -1,35 +1,18 @@ { "name": "@scroll-tech/contracts", "description": "A library for interacting with Scroll contracts.", - "version": "0.0.4", + "version": "2.0.1", "repository": { "type": "git", "url": "https://github.com/scroll-tech/scroll.git" }, "files": [ - "L1/gateways", - "L1/rollup/IL1MessageQueueV1.sol", - "L1/rollup/IL1MessageQueueV2.sol", - "L1/rollup/IL2GasPriceOracle.sol", - "L1/rollup/IScrollChain.sol", - "L1/IL1ScrollMessenger.sol", - "L2/gateways", - "L2/predeploys/IL1BlockContainer.sol", - "L2/predeploys/IL1GasPriceOracle.sol", - "L2/IL2ScrollMessenger.sol", + "L1", + "L2", "interfaces", - "libraries/callbacks", - "libraries/gateway", - "libraries/oracle/IGasOracle.sol", - "libraries/token/IScrollERC20.sol", - "libraries/token/IScrollERC20Extension.sol", - "libraries/token/IScrollERC1155.sol", - "libraries/token/IScrollERC1155Extension.sol", - "libraries/token/IScrollERC721.sol", - "libraries/token/IScrollERC721Extension.sol", - "libraries/token/IScrollStandardERC20.sol", - "libraries/token/IScrollStandardERC20Factory.sol", - "libraries/IScrollMessenger.sol" + "libraries", + "misc", + "mocks" ], "keywords": [ "solidity", diff --git a/src/rate-limiter/ETHRateLimiter.sol b/src/rate-limiter/ETHRateLimiter.sol deleted file mode 100644 index 31ba904c..00000000 --- a/src/rate-limiter/ETHRateLimiter.sol +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity =0.8.24; - -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; -import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; - -import {IETHRateLimiter} from "./IETHRateLimiter.sol"; - -// solhint-disable func-name-mixedcase -// solhint-disable not-rely-on-time - -contract ETHRateLimiter is Ownable, IETHRateLimiter { - /*********** - * Structs * - ***********/ - - struct ETHAmount { - // The timestamp when the amount is updated. - uint48 lastUpdateTs; - // The ETH limit in wei. - uint104 limit; - // The amount of ETH in current period. - uint104 amount; - } - - /************* - * Constants * - *************/ - - /// @notice The period length in seconds. - /// @dev The time frame for the `k`-th period is `[periodDuration * k, periodDuration * (k + 1))`. - uint256 public immutable periodDuration; - - /// @notice The address of ETH spender. - address public immutable spender; - - /************* - * Variables * - *************/ - - /// @notice The ETH amount used in current period. - ETHAmount public currentPeriod; - - /*************** - * Constructor * - ***************/ - - constructor( - uint256 _periodDuration, - address _spender, - uint104 _totalLimit - ) { - if (_periodDuration == 0) { - revert PeriodIsZero(); - } - - periodDuration = _periodDuration; - spender = _spender; - - _updateTotalLimit(_totalLimit); - } - - /***************************** - * Public Mutating Functions * - *****************************/ - - /// @inheritdoc IETHRateLimiter - function addUsedAmount(uint256 _amount) external override { - if (_msgSender() != spender) { - revert CallerNotSpender(); - } - if (_amount == 0) return; - - uint256 _currentPeriodStart = (block.timestamp / periodDuration) * periodDuration; - - // check total limit - uint256 _currentTotalAmount; - ETHAmount memory _currentPeriod = currentPeriod; - - if (uint256(_currentPeriod.lastUpdateTs) < _currentPeriodStart) { - _currentTotalAmount = _amount; - } else { - _currentTotalAmount = _currentPeriod.amount + _amount; - } - if (_currentTotalAmount > _currentPeriod.limit) { - revert ExceedTotalLimit(); - } - - _currentPeriod.lastUpdateTs = uint48(block.timestamp); - _currentPeriod.amount = SafeCast.toUint104(_currentTotalAmount); - - currentPeriod = _currentPeriod; - } - - /************************ - * Restricted Functions * - ************************/ - - /// @notice Update the total ETH amount limit. - /// @param _newTotalLimit The new total limit. - function updateTotalLimit(uint104 _newTotalLimit) external onlyOwner { - _updateTotalLimit(_newTotalLimit); - } - - /********************** - * Internal Functions * - **********************/ - - /// @dev Internal function to update the total token amount limit. - /// @param _newTotalLimit The new total limit. - function _updateTotalLimit(uint104 _newTotalLimit) private { - if (_newTotalLimit == 0) { - revert TotalLimitIsZero(); - } - - uint256 _oldTotalLimit = currentPeriod.limit; - currentPeriod.limit = _newTotalLimit; - - emit UpdateTotalLimit(_oldTotalLimit, _newTotalLimit); - } -} diff --git a/src/rate-limiter/IETHRateLimiter.sol b/src/rate-limiter/IETHRateLimiter.sol deleted file mode 100644 index 43db682e..00000000 --- a/src/rate-limiter/IETHRateLimiter.sol +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.24; - -interface IETHRateLimiter { - /********** - * Events * - **********/ - - /// @notice Emitted when the total limit is updated. - /// @param oldTotalLimit The previous value of total limit before updating. - /// @param newTotalLimit The current value of total limit after updating. - event UpdateTotalLimit(uint256 oldTotalLimit, uint256 newTotalLimit); - - /********** - * Errors * - **********/ - - /// @dev Thrown when the `periodDuration` is initialized to zero. - error PeriodIsZero(); - - /// @dev Thrown when the `totalAmount` is initialized to zero. - error TotalLimitIsZero(); - - /// @dev Thrown when an amount breaches the total limit in the period. - error ExceedTotalLimit(); - - /// @dev Thrown when the call is not spender. - error CallerNotSpender(); - - /***************************** - * Public Mutating Functions * - *****************************/ - - /// @notice Request some ETH usage for `sender`. - /// @param _amount The amount of ETH to use. - function addUsedAmount(uint256 _amount) external; -} diff --git a/src/rate-limiter/ITokenRateLimiter.sol b/src/rate-limiter/ITokenRateLimiter.sol deleted file mode 100644 index 050680ef..00000000 --- a/src/rate-limiter/ITokenRateLimiter.sol +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.24; - -interface ITokenRateLimiter { - /********** - * Events * - **********/ - - /// @notice Emitted when the total limit is updated. - /// @param oldTotalLimit The previous value of total limit before updating. - /// @param newTotalLimit The current value of total limit after updating. - event UpdateTotalLimit(address indexed token, uint256 oldTotalLimit, uint256 newTotalLimit); - - /********** - * Errors * - **********/ - - /// @dev Thrown when the `periodDuration` is initialized to zero. - error PeriodIsZero(); - - /// @dev Thrown when the `totalAmount` is initialized to zero. - /// @param token The address of the token. - error TotalLimitIsZero(address token); - - /// @dev Thrown when an amount breaches the total limit in the period. - /// @param token The address of the token. - error ExceedTotalLimit(address token); - - /***************************** - * Public Mutating Functions * - *****************************/ - - /// @notice Request some token usage for `sender`. - /// @param token The address of the token. - /// @param amount The amount of token to use. - function addUsedAmount(address token, uint256 amount) external; -} diff --git a/src/rate-limiter/TokenRateLimiter.sol b/src/rate-limiter/TokenRateLimiter.sol deleted file mode 100644 index dab08e8a..00000000 --- a/src/rate-limiter/TokenRateLimiter.sol +++ /dev/null @@ -1,106 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity =0.8.24; - -import {AccessControlEnumerable} from "@openzeppelin/contracts/access/AccessControlEnumerable.sol"; -import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; - -import {ITokenRateLimiter} from "./ITokenRateLimiter.sol"; - -// solhint-disable func-name-mixedcase -// solhint-disable not-rely-on-time - -contract TokenRateLimiter is AccessControlEnumerable, ITokenRateLimiter { - /*********** - * Structs * - ***********/ - - struct TokenAmount { - // The timestamp when the amount is updated. - uint48 lastUpdateTs; - // The token limit. - uint104 limit; - // The amount of token in current period. - uint104 amount; - } - - /************* - * Constants * - *************/ - - /// @notice The role for token spender. - bytes32 public constant TOKEN_SPENDER_ROLE = keccak256("TOKEN_SPENDER_ROLE"); - - /// @notice The period length in seconds. - /// @dev The time frame for the `k`-th period is `[periodDuration * k, periodDuration * (k + 1))`. - uint256 public immutable periodDuration; - - /************* - * Variables * - *************/ - - /// @notice Mapping from token address to the total amounts used in current period and total token amount limit. - mapping(address => TokenAmount) public currentPeriod; - - /// @dev The storage slots for future usage. - uint256[49] private __gap; - - /*************** - * Constructor * - ***************/ - - constructor(uint256 _periodDuration) { - if (_periodDuration == 0) { - revert PeriodIsZero(); - } - - _grantRole(DEFAULT_ADMIN_ROLE, _msgSender()); - - periodDuration = _periodDuration; - } - - /***************************** - * Public Mutating Functions * - *****************************/ - - /// @inheritdoc ITokenRateLimiter - function addUsedAmount(address _token, uint256 _amount) external override onlyRole(TOKEN_SPENDER_ROLE) { - if (_amount == 0) return; - - uint256 _currentPeriodStart = (block.timestamp / periodDuration) * periodDuration; - - // check total limit, `0` means no limit at all. - uint256 _currentTotalAmount; - TokenAmount memory _currentPeriod = currentPeriod[_token]; - if (uint256(_currentPeriod.lastUpdateTs) < _currentPeriodStart) { - _currentTotalAmount = _amount; - } else { - _currentTotalAmount = _currentPeriod.amount + _amount; - } - if (_currentPeriod.limit != 0 && _currentTotalAmount > _currentPeriod.limit) { - revert ExceedTotalLimit(_token); - } - - _currentPeriod.lastUpdateTs = uint48(block.timestamp); - _currentPeriod.amount = SafeCast.toUint104(_currentTotalAmount); - - currentPeriod[_token] = _currentPeriod; - } - - /************************ - * Restricted Functions * - ************************/ - - /// @notice Update the total token amount limit. - /// @param _newTotalLimit The new total limit. - function updateTotalLimit(address _token, uint104 _newTotalLimit) external onlyRole(DEFAULT_ADMIN_ROLE) { - if (_newTotalLimit == 0) { - revert TotalLimitIsZero(_token); - } - - uint256 _oldTotalLimit = currentPeriod[_token].limit; - currentPeriod[_token].limit = _newTotalLimit; - - emit UpdateTotalLimit(_token, _oldTotalLimit, _newTotalLimit); - } -} diff --git a/src/test/ETHRateLimiter.t.sol b/src/test/ETHRateLimiter.t.sol deleted file mode 100644 index 31c53725..00000000 --- a/src/test/ETHRateLimiter.t.sol +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity =0.8.24; - -import {DSTestPlus} from "solmate/test/utils/DSTestPlus.sol"; - -import {ETHRateLimiter} from "../rate-limiter/ETHRateLimiter.sol"; -import {IETHRateLimiter} from "../rate-limiter/IETHRateLimiter.sol"; - -contract ETHRateLimiterTest is DSTestPlus { - event UpdateTotalLimit(uint256 oldTotalLimit, uint256 newTotalLimit); - - ETHRateLimiter private limiter; - - function setUp() public { - hevm.warp(86400); - limiter = new ETHRateLimiter(86400, address(this), 100 ether); - } - - function testUpdateTotalLimit(uint104 _newTotalLimit) external { - hevm.assume(_newTotalLimit > 0); - - // not owner, revert - hevm.startPrank(address(1)); - hevm.expectRevert("Ownable: caller is not the owner"); - limiter.updateTotalLimit(_newTotalLimit); - hevm.stopPrank(); - - // zero revert - hevm.expectRevert(IETHRateLimiter.TotalLimitIsZero.selector); - limiter.updateTotalLimit(0); - - // success - hevm.expectEmit(false, false, false, true); - emit UpdateTotalLimit(100 ether, _newTotalLimit); - limiter.updateTotalLimit(_newTotalLimit); - (, uint104 _totalLimit, ) = limiter.currentPeriod(); - assertEq(_totalLimit, _newTotalLimit); - } - - function testAddUsedAmount() external { - // non-spender, revert - hevm.startPrank(address(1)); - hevm.expectRevert(IETHRateLimiter.CallerNotSpender.selector); - limiter.addUsedAmount(0); - hevm.stopPrank(); - - // exceed total limit on first call - hevm.expectRevert(IETHRateLimiter.ExceedTotalLimit.selector); - limiter.addUsedAmount(100 ether + 1); - _checkTotalCurrentPeriodAmountAmount(0); - - // exceed total limit on second call - limiter.addUsedAmount(50 ether); - _checkTotalCurrentPeriodAmountAmount(50 ether); - hevm.expectRevert(IETHRateLimiter.ExceedTotalLimit.selector); - limiter.addUsedAmount(50 ether + 1); - _checkTotalCurrentPeriodAmountAmount(50 ether); - - // one period passed - hevm.warp(86400 * 2); - limiter.addUsedAmount(1 ether); - _checkTotalCurrentPeriodAmountAmount(1 ether); - - // exceed - hevm.expectRevert(IETHRateLimiter.ExceedTotalLimit.selector); - limiter.addUsedAmount(99 ether + 1); - _checkTotalCurrentPeriodAmountAmount(1 ether); - } - - function _checkTotalCurrentPeriodAmountAmount(uint256 expected) internal { - (, , uint256 totalAmount) = limiter.currentPeriod(); - assertEq(totalAmount, expected); - } -} diff --git a/src/test/L1CustomERC20Gateway.t.sol b/src/test/L1CustomERC20Gateway.t.sol index 98e0a75b..1443061d 100644 --- a/src/test/L1CustomERC20Gateway.t.sol +++ b/src/test/L1CustomERC20Gateway.t.sol @@ -130,94 +130,6 @@ contract L1CustomERC20GatewayTest is L1GatewayTestBase { _depositERC20WithRecipientAndCalldata(false, amount, recipient, dataToCall, gasLimit, feePerGas); } - function testDropMessageMocking() public { - MockScrollMessenger mockMessenger = new MockScrollMessenger(); - gateway = _deployGateway(address(mockMessenger)); - gateway.initialize(address(counterpartGateway), address(router), address(mockMessenger)); - - // only messenger can call, revert - hevm.expectRevert(ErrorCallerIsNotMessenger.selector); - gateway.onDropMessage(new bytes(0)); - - // only called in drop context, revert - hevm.expectRevert(ErrorNotInDropMessageContext.selector); - mockMessenger.callTarget( - address(gateway), - abi.encodeWithSelector(gateway.onDropMessage.selector, new bytes(0)) - ); - - mockMessenger.setXDomainMessageSender(ScrollConstants.DROP_XDOMAIN_MESSAGE_SENDER); - - // invalid selector, revert - hevm.expectRevert("invalid selector"); - mockMessenger.callTarget( - address(gateway), - abi.encodeWithSelector(gateway.onDropMessage.selector, new bytes(4)) - ); - - bytes memory message = abi.encodeWithSelector( - IL2ERC20Gateway.finalizeDepositERC20.selector, - address(l1Token), - address(l2Token), - address(this), - address(this), - 100, - new bytes(0) - ); - - // nonzero msg.value, revert - hevm.expectRevert("nonzero msg.value"); - mockMessenger.callTarget{value: 1}( - address(gateway), - abi.encodeWithSelector(gateway.onDropMessage.selector, message) - ); - } - - function testDropMessage( - uint256 amount, - address recipient, - bytes memory dataToCall - ) public { - // message 0 is append here - gateway.updateTokenMapping{value: 1 ether}(address(l1Token), address(l2Token)); - - // finalize message 0 - hevm.startPrank(address(rollup)); - messageQueueV1.popCrossDomainMessage(0, 1, 0); - messageQueueV1.finalizePoppedCrossDomainMessage(1); - hevm.stopPrank(); - assertEq(messageQueueV1.pendingQueueIndex(), 1); - assertEq(messageQueueV1.nextUnfinalizedQueueIndex(), 1); - - amount = bound(amount, 1, l1Token.balanceOf(address(this))); - bytes memory message = abi.encodeWithSelector( - IL2ERC20Gateway.finalizeDepositERC20.selector, - address(l1Token), - address(l2Token), - address(this), - recipient, - amount, - dataToCall - ); - gateway.depositERC20AndCall(address(l1Token), recipient, amount, dataToCall, defaultGasLimit); - - // skip message 1 - hevm.startPrank(address(rollup)); - messageQueueV1.popCrossDomainMessage(1, 1, 0x1); - messageQueueV1.finalizePoppedCrossDomainMessage(2); - assertEq(messageQueueV1.pendingQueueIndex(), 2); - assertEq(messageQueueV1.nextUnfinalizedQueueIndex(), 2); - hevm.stopPrank(); - - // drop message 1 - hevm.expectEmit(true, true, false, true); - emit RefundERC20(address(l1Token), address(this), amount); - - uint256 balance = l1Token.balanceOf(address(this)); - l1Messenger.dropMessage(address(gateway), address(counterpartGateway), 0, 1, message); - assertEq(balance + amount, l1Token.balanceOf(address(this))); - } - function testFinalizeWithdrawERC20FailedMocking( address sender, address recipient, diff --git a/src/test/L1ERC1155Gateway.t.sol b/src/test/L1ERC1155Gateway.t.sol index ef707693..ad4c9589 100644 --- a/src/test/L1ERC1155Gateway.t.sol +++ b/src/test/L1ERC1155Gateway.t.sol @@ -160,127 +160,6 @@ contract L1ERC1155GatewayTest is L1GatewayTestBase, ERC1155TokenReceiver { _testBatchDepositERC1155WithRecipient(tokenCount, amount, recipient, gasLimit, feePerGas); } - function testDropMessageMocking() public { - MockScrollMessenger mockMessenger = new MockScrollMessenger(); - gateway = _deployGateway(address(mockMessenger)); - gateway.initialize(address(counterpartGateway), address(mockMessenger)); - - // only messenger can call, revert - hevm.expectRevert(ErrorCallerIsNotMessenger.selector); - gateway.onDropMessage(new bytes(0)); - - // only called in drop context, revert - hevm.expectRevert(ErrorNotInDropMessageContext.selector); - mockMessenger.callTarget( - address(gateway), - abi.encodeWithSelector(gateway.onDropMessage.selector, new bytes(0)) - ); - - mockMessenger.setXDomainMessageSender(ScrollConstants.DROP_XDOMAIN_MESSAGE_SENDER); - - // invalid selector, revert - hevm.expectRevert("invalid selector"); - mockMessenger.callTarget( - address(gateway), - abi.encodeWithSelector(gateway.onDropMessage.selector, new bytes(4)) - ); - - bytes memory message = abi.encodeWithSelector( - IL2ERC1155Gateway.finalizeDepositERC1155.selector, - address(l1Token), - address(l2Token), - address(this), - address(this), - 0, - 0 - ); - - // nonzero msg.value, revert - hevm.expectRevert("nonzero msg.value"); - mockMessenger.callTarget{value: 1}( - address(gateway), - abi.encodeWithSelector(gateway.onDropMessage.selector, message) - ); - } - - function testDropMessage(uint256 tokenId, uint256 amount) public { - gateway.updateTokenMapping(address(l1Token), address(l2Token)); - - tokenId = bound(tokenId, 0, TOKEN_COUNT - 1); - amount = bound(amount, 1, MAX_TOKEN_BALANCE); - bytes memory message = abi.encodeWithSelector( - IL2ERC1155Gateway.finalizeDepositERC1155.selector, - address(l1Token), - address(l2Token), - address(this), - address(this), - tokenId, - amount - ); - gateway.depositERC1155(address(l1Token), tokenId, amount, defaultGasLimit); - - // skip message 0 - hevm.startPrank(address(rollup)); - messageQueueV1.popCrossDomainMessage(0, 1, 0x1); - messageQueueV1.finalizePoppedCrossDomainMessage(1); - assertEq(messageQueueV1.nextUnfinalizedQueueIndex(), 1); - assertEq(messageQueueV1.pendingQueueIndex(), 1); - hevm.stopPrank(); - - // drop message 0 - hevm.expectEmit(true, true, false, true); - emit RefundERC1155(address(l1Token), address(this), tokenId, amount); - - uint256 balance = l1Token.balanceOf(address(this), tokenId); - l1Messenger.dropMessage(address(gateway), address(counterpartGateway), 0, 0, message); - assertEq(balance + amount, l1Token.balanceOf(address(this), tokenId)); - } - - function testDropMessageBatch(uint256 tokenCount, uint256 amount) public { - tokenCount = bound(tokenCount, 1, TOKEN_COUNT); - amount = bound(amount, 1, MAX_TOKEN_BALANCE); - gateway.updateTokenMapping(address(l1Token), address(l2Token)); - - uint256[] memory _tokenIds = new uint256[](tokenCount); - uint256[] memory _amounts = new uint256[](tokenCount); - for (uint256 i = 0; i < tokenCount; i++) { - _tokenIds[i] = i; - _amounts[i] = amount; - } - - bytes memory message = abi.encodeWithSelector( - IL2ERC1155Gateway.finalizeBatchDepositERC1155.selector, - address(l1Token), - address(l2Token), - address(this), - address(this), - _tokenIds, - _amounts - ); - gateway.batchDepositERC1155(address(l1Token), _tokenIds, _amounts, defaultGasLimit); - - // skip message 0 - hevm.startPrank(address(rollup)); - messageQueueV1.popCrossDomainMessage(0, 1, 0x1); - messageQueueV1.finalizePoppedCrossDomainMessage(1); - assertEq(messageQueueV1.nextUnfinalizedQueueIndex(), 1); - assertEq(messageQueueV1.pendingQueueIndex(), 1); - hevm.stopPrank(); - - // drop message 0 - hevm.expectEmit(true, true, false, true); - emit BatchRefundERC1155(address(l1Token), address(this), _tokenIds, _amounts); - - uint256[] memory balances = new uint256[](tokenCount); - for (uint256 i = 0; i < tokenCount; i++) { - balances[i] = l1Token.balanceOf(address(this), _tokenIds[i]); - } - l1Messenger.dropMessage(address(gateway), address(counterpartGateway), 0, 0, message); - for (uint256 i = 0; i < tokenCount; i++) { - assertEq(balances[i] + _amounts[i], l1Token.balanceOf(address(this), _tokenIds[i])); - } - } - function testFinalizeWithdrawERC1155FailedMocking( address sender, address recipient, diff --git a/src/test/L1ERC721Gateway.t.sol b/src/test/L1ERC721Gateway.t.sol index edebe9ea..9cd52de7 100644 --- a/src/test/L1ERC721Gateway.t.sol +++ b/src/test/L1ERC721Gateway.t.sol @@ -151,119 +151,6 @@ contract L1ERC721GatewayTest is L1GatewayTestBase, ERC721TokenReceiver { _testBatchDepositERC721WithRecipient(tokenCount, recipient, gasLimit, feePerGas); } - function testDropMessageMocking() public { - MockScrollMessenger mockMessenger = new MockScrollMessenger(); - gateway = _deployGateway(address(mockMessenger)); - gateway.initialize(address(counterpartGateway), address(mockMessenger)); - - // only messenger can call, revert - hevm.expectRevert(ErrorCallerIsNotMessenger.selector); - gateway.onDropMessage(new bytes(0)); - - // only called in drop context, revert - hevm.expectRevert(ErrorNotInDropMessageContext.selector); - mockMessenger.callTarget( - address(gateway), - abi.encodeWithSelector(gateway.onDropMessage.selector, new bytes(0)) - ); - - mockMessenger.setXDomainMessageSender(ScrollConstants.DROP_XDOMAIN_MESSAGE_SENDER); - - // invalid selector, revert - hevm.expectRevert("invalid selector"); - mockMessenger.callTarget( - address(gateway), - abi.encodeWithSelector(gateway.onDropMessage.selector, new bytes(4)) - ); - - bytes memory message = abi.encodeWithSelector( - IL2ERC721Gateway.finalizeDepositERC721.selector, - address(l1Token), - address(l2Token), - address(this), - address(this), - 0 - ); - - // nonzero msg.value, revert - hevm.expectRevert("nonzero msg.value"); - mockMessenger.callTarget{value: 1}( - address(gateway), - abi.encodeWithSelector(gateway.onDropMessage.selector, message) - ); - } - - function testDropMessage(uint256 tokenId) public { - gateway.updateTokenMapping(address(l1Token), address(l2Token)); - - tokenId = bound(tokenId, 0, TOKEN_COUNT - 1); - bytes memory message = abi.encodeWithSelector( - IL2ERC721Gateway.finalizeDepositERC721.selector, - address(l1Token), - address(l2Token), - address(this), - address(this), - tokenId - ); - gateway.depositERC721(address(l1Token), tokenId, defaultGasLimit); - - // skip message 0 - hevm.startPrank(address(rollup)); - messageQueueV1.popCrossDomainMessage(0, 1, 0x1); - messageQueueV1.finalizePoppedCrossDomainMessage(1); - assertEq(messageQueueV1.nextUnfinalizedQueueIndex(), 1); - assertEq(messageQueueV1.pendingQueueIndex(), 1); - hevm.stopPrank(); - - // drop message 0 - hevm.expectEmit(true, true, false, true); - emit RefundERC721(address(l1Token), address(this), tokenId); - - assertEq(l1Token.ownerOf(tokenId), address(gateway)); - l1Messenger.dropMessage(address(gateway), address(counterpartGateway), 0, 0, message); - assertEq(l1Token.ownerOf(tokenId), address(this)); - } - - function testDropMessageBatch(uint256 tokenCount) public { - tokenCount = bound(tokenCount, 1, TOKEN_COUNT); - gateway.updateTokenMapping(address(l1Token), address(l2Token)); - - uint256[] memory _tokenIds = new uint256[](tokenCount); - for (uint256 i = 0; i < tokenCount; i++) { - _tokenIds[i] = i; - } - - bytes memory message = abi.encodeWithSelector( - IL2ERC721Gateway.finalizeBatchDepositERC721.selector, - address(l1Token), - address(l2Token), - address(this), - address(this), - _tokenIds - ); - gateway.batchDepositERC721(address(l1Token), _tokenIds, defaultGasLimit); - - // skip message 0 - hevm.startPrank(address(rollup)); - messageQueueV1.popCrossDomainMessage(0, 1, 0x1); - messageQueueV1.finalizePoppedCrossDomainMessage(1); - assertEq(messageQueueV1.nextUnfinalizedQueueIndex(), 1); - assertEq(messageQueueV1.pendingQueueIndex(), 1); - hevm.stopPrank(); - - // drop message 0 - hevm.expectEmit(true, true, false, true); - emit BatchRefundERC721(address(l1Token), address(this), _tokenIds); - for (uint256 i = 0; i < tokenCount; i++) { - assertEq(l1Token.ownerOf(_tokenIds[i]), address(gateway)); - } - - l1Messenger.dropMessage(address(gateway), address(counterpartGateway), 0, 0, message); - for (uint256 i = 0; i < tokenCount; i++) { - assertEq(l1Token.ownerOf(_tokenIds[i]), address(this)); - } - } - function testFinalizeWithdrawERC721FailedMocking( address sender, address recipient, diff --git a/src/test/L1ETHGateway.t.sol b/src/test/L1ETHGateway.t.sol index 4b5ebe83..8c5460c6 100644 --- a/src/test/L1ETHGateway.t.sol +++ b/src/test/L1ETHGateway.t.sol @@ -104,85 +104,6 @@ contract L1ETHGatewayTest is L1GatewayTestBase { _depositETHWithRecipientAndCalldata(true, amount, recipient, dataToCall, gasLimit, feePerGas); } - function testDropMessageMocking() public { - MockScrollMessenger mockMessenger = new MockScrollMessenger(); - gateway = _deployGateway(address(mockMessenger)); - gateway.initialize(address(counterpartGateway), address(router), address(mockMessenger)); - - // only messenger can call, revert - hevm.expectRevert(ErrorCallerIsNotMessenger.selector); - gateway.onDropMessage(new bytes(0)); - - // only called in drop context, revert - hevm.expectRevert(ErrorNotInDropMessageContext.selector); - mockMessenger.callTarget( - address(gateway), - abi.encodeWithSelector(gateway.onDropMessage.selector, new bytes(0)) - ); - - mockMessenger.setXDomainMessageSender(ScrollConstants.DROP_XDOMAIN_MESSAGE_SENDER); - - // invalid selector, revert - hevm.expectRevert("invalid selector"); - mockMessenger.callTarget( - address(gateway), - abi.encodeWithSelector(gateway.onDropMessage.selector, new bytes(4)) - ); - - bytes memory message = abi.encodeWithSelector( - IL2ETHGateway.finalizeDepositETH.selector, - address(this), - address(this), - 100, - new bytes(0) - ); - - // msg.value mismatch, revert - hevm.expectRevert("msg.value mismatch"); - mockMessenger.callTarget{value: 99}( - address(gateway), - abi.encodeWithSelector(gateway.onDropMessage.selector, message) - ); - } - - function testDropMessage( - uint256 amount, - address recipient, - bytes memory dataToCall - ) public { - amount = bound(amount, 1, address(this).balance); - bytes memory message = abi.encodeWithSelector( - IL2ETHGateway.finalizeDepositETH.selector, - address(this), - recipient, - amount, - dataToCall - ); - gateway.depositETHAndCall{value: amount}(recipient, amount, dataToCall, defaultGasLimit); - - // skip message 0 - hevm.startPrank(address(rollup)); - messageQueueV1.popCrossDomainMessage(0, 1, 0x1); - messageQueueV1.finalizePoppedCrossDomainMessage(1); - assertEq(messageQueueV1.nextUnfinalizedQueueIndex(), 1); - assertEq(messageQueueV1.pendingQueueIndex(), 1); - hevm.stopPrank(); - - // ETH transfer failed, revert - revertOnReceive = true; - hevm.expectRevert("ETH transfer failed"); - l1Messenger.dropMessage(address(gateway), address(counterpartGateway), amount, 0, message); - - // drop message 0 - hevm.expectEmit(true, true, false, true); - emit RefundETH(address(this), amount); - - revertOnReceive = false; - uint256 balance = address(this).balance; - l1Messenger.dropMessage(address(gateway), address(counterpartGateway), amount, 0, message); - assertEq(balance + amount, address(this).balance); - } - function testFinalizeWithdrawETHFailedMocking( address sender, address recipient, diff --git a/src/test/L1GasPriceOracle.t.sol b/src/test/L1GasPriceOracle.t.sol index 3d845ad9..7dff1cfb 100644 --- a/src/test/L1GasPriceOracle.t.sol +++ b/src/test/L1GasPriceOracle.t.sol @@ -135,6 +135,23 @@ contract L1GasPriceOracleTest is DSTestPlus { oracle.enableCurie(); } + function testEnableFeynman() external { + // call by non-owner, should revert + hevm.startPrank(address(1)); + hevm.expectRevert("caller is not the owner"); + oracle.enableFeynman(); + hevm.stopPrank(); + + // call by owner, should succeed + assertBoolEq(oracle.isFeynman(), false); + oracle.enableFeynman(); + assertBoolEq(oracle.isFeynman(), true); + + // enable twice, should revert + hevm.expectRevert(L1GasPriceOracle.ErrAlreadyInFeynmanFork.selector); + oracle.enableFeynman(); + } + function testSetL1BaseFee(uint256 _baseFee) external { _baseFee = bound(_baseFee, 0, 1e9 * 20000); // max 20k gwei @@ -232,4 +249,40 @@ contract L1GasPriceOracleTest is DSTestPlus { (_commitScalar * _baseFee + _blobScalar * _blobBaseFee * _data.length) / PRECISION ); } + + function testGetL1GasUsedFeynman(bytes memory _data) external { + oracle.enableFeynman(); + assertEq(oracle.getL1GasUsed(_data), 0); + } + + function testGetL1FeeFeynman( + uint256 _baseFee, + uint256 _blobBaseFee, + uint256 _commitScalar, + uint256 _blobScalar, + uint256 _penaltyThreshold, + uint256 _penaltyFactor, + bytes memory _data + ) external { + _baseFee = bound(_baseFee, 0, 1e9 * 20000); // max 20k gwei + _blobBaseFee = bound(_blobBaseFee, 0, 1e9 * 20000); // max 20k gwei + _commitScalar = bound(_commitScalar, 0, MAX_COMMIT_SCALAR); + _blobScalar = bound(_blobScalar, 0, MAX_BLOB_SCALAR); + _penaltyThreshold = bound(_penaltyThreshold, 1e9, 1e9 * 5); + _penaltyFactor = bound(_penaltyFactor, 1e9, 1e9 * 10); // min 1x, max 10x penalty + + oracle.enableFeynman(); + oracle.setCommitScalar(_commitScalar); + oracle.setBlobScalar(_blobScalar); + oracle.setL1BaseFeeAndBlobBaseFee(_baseFee, _blobBaseFee); + oracle.setPenaltyThreshold(_penaltyThreshold); + oracle.setPenaltyFactor(_penaltyFactor); + + assertEq( + oracle.getL1Fee(_data), + ((_commitScalar * _baseFee + _blobScalar * _blobBaseFee) * _data.length * _penaltyFactor) / + PRECISION / + PRECISION + ); + } } diff --git a/src/test/L1GatewayTestBase.t.sol b/src/test/L1GatewayTestBase.t.sol index fd43715a..2d04fdec 100644 --- a/src/test/L1GatewayTestBase.t.sol +++ b/src/test/L1GatewayTestBase.t.sol @@ -121,7 +121,8 @@ abstract contract L1GatewayTestBase is ScrollTestBase { address(l2Messenger), address(rollup), address(messageQueueV1), - address(messageQueueV2) + address(messageQueueV2), + address(enforcedTxGateway) ) ) ); @@ -203,33 +204,26 @@ abstract contract L1GatewayTestBase is ScrollTestBase { memory blobDataProof = hex"2c9d777660f14ad49803a6442935c0d24a0d83551de5995890bf70a17d24e68753ab0fe6807c7081f0885fe7da741554d658a03730b1fa006f8319f8b993bcb0a5a0c9e8a145c5ef6e415c245690effa2914ec9393f58a7251d30c0657da1453d9ad906eae8b97dd60c9a216f81b4df7af34d01e214e1ec5865f0133ecc16d7459e49dab66087340677751e82097fbdd20551d66076f425775d1758a9dfd186b"; rollup.setBlobVersionedHash(0, blobVersionedHash); + bytes memory batchHeader1 = new bytes(73); + assembly { + mstore8(add(batchHeader1, 0x20), 7) // version + mstore(add(batchHeader1, add(0x20, 1)), shl(192, 1)) // batchIndex + mstore(add(batchHeader1, add(0x20, 9)), 0x013590dc3544d56629ba81bb14d4d31248f825001653aa575eb8e3a719046757) // blobVersionedHash + mstore(add(batchHeader1, add(0x20, 41)), batchHash0) // parentBatchHash + } + // commit one batch bytes[] memory chunks = new bytes[](1); bytes memory chunk0 = new bytes(1 + 60); chunk0[0] = bytes1(uint8(1)); // one block in this chunk chunks[0] = chunk0; hevm.startPrank(address(0)); - rollup.commitBatchWithBlobProof(4, batchHeader0, chunks, new bytes(0), blobDataProof); + rollup.commitBatches(7, batchHash0, keccak256(batchHeader1)); hevm.stopPrank(); - bytes memory batchHeader1 = new bytes(193); - assembly { - mstore8(add(batchHeader1, 0x20), 4) // version - mstore(add(batchHeader1, add(0x20, 1)), shl(192, 1)) // batchIndex - mstore(add(batchHeader1, add(0x20, 9)), shl(192, 0)) // l1MessagePopped - mstore(add(batchHeader1, add(0x20, 17)), shl(192, 0)) // totalL1MessagePopped - mstore(add(batchHeader1, add(0x20, 25)), 0x246394445f4fe64ed5598554d55d1682d6fb3fe04bf58eb54ef81d1189fafb51) // dataHash - mstore(add(batchHeader1, add(0x20, 57)), 0x013590dc3544d56629ba81bb14d4d31248f825001653aa575eb8e3a719046757) // blobVersionedHash - mstore(add(batchHeader1, add(0x20, 89)), batchHash0) // parentBatchHash - mstore(add(batchHeader1, add(0x20, 121)), 0) // lastBlockTimestamp - mcopy(add(batchHeader1, add(0x20, 129)), add(blobDataProof, 0x20), 64) // blobDataProof - } - hevm.startPrank(address(0)); - rollup.finalizeBundleWithProof(batchHeader1, bytes32(uint256(2)), messageHash, new bytes(0)); + rollup.finalizeBundlePostEuclidV2(batchHeader1, 0, bytes32(uint256(2)), messageHash, new bytes(0)); hevm.stopPrank(); - - rollup.lastFinalizedBatchIndex(); } function setL2BaseFee(uint256 feePerGas) internal { diff --git a/src/test/L1MessageQueueV2.t.sol b/src/test/L1MessageQueueV2.t.sol index 982fcd37..6cff224a 100644 --- a/src/test/L1MessageQueueV2.t.sol +++ b/src/test/L1MessageQueueV2.t.sol @@ -135,7 +135,7 @@ contract L1MessageQueueV2Test is ScrollTestBase { } function testCalculateIntrinsicGasFee(bytes calldata data) external { - assertEq(queueV2.calculateIntrinsicGasFee(data), 21000 + data.length * 16); + assertEq(queueV2.calculateIntrinsicGasFee(data), 21000 + data.length * 40); } function testAppendCrossDomainMessage( @@ -143,7 +143,7 @@ contract L1MessageQueueV2Test is ScrollTestBase { bytes memory data, uint256 timestamp ) external { - gasLimit = bound(gasLimit, 21000 + data.length * 16, 10000000); + gasLimit = bound(gasLimit, 21000 + data.length * 40, 10000000); timestamp = bound(timestamp, 1, 2**31 - 1); // should revert, when non-messenger call @@ -197,7 +197,7 @@ contract L1MessageQueueV2Test is ScrollTestBase { bytes memory data, uint256 timestamp ) external { - gasLimit = bound(gasLimit, 21000 + data.length * 16, 10000000); + gasLimit = bound(gasLimit, 21000 + data.length * 40, 10000000); timestamp = bound(timestamp, 1, 2**31 - 1); // should revert, when non-gateway call diff --git a/src/test/L1ReverseCustomERC20Gateway.t.sol b/src/test/L1ReverseCustomERC20Gateway.t.sol index 0886a1f5..d4972191 100644 --- a/src/test/L1ReverseCustomERC20Gateway.t.sol +++ b/src/test/L1ReverseCustomERC20Gateway.t.sol @@ -147,53 +147,6 @@ contract L1ReverseCustomERC20GatewayTest is L1GatewayTestBase { _depositERC20(true, 2, amount, recipient, dataToCall, gasLimit, feePerGas); } - function testDropMessage( - uint256 amount, - address recipient, - bytes memory dataToCall - ) public { - // message 0 is append here - gateway.updateTokenMapping{value: 1 ether}(address(l1Token), address(l2Token)); - - // finalize message 0 - hevm.startPrank(address(rollup)); - messageQueueV1.popCrossDomainMessage(0, 1, 0); - messageQueueV1.finalizePoppedCrossDomainMessage(1); - hevm.stopPrank(); - assertEq(messageQueueV1.pendingQueueIndex(), 1); - assertEq(messageQueueV1.nextUnfinalizedQueueIndex(), 1); - - amount = bound(amount, 1, l1Token.balanceOf(address(this))); - bytes memory message = abi.encodeWithSelector( - IL2ERC20Gateway.finalizeDepositERC20.selector, - address(l1Token), - address(l2Token), - address(this), - recipient, - amount, - dataToCall - ); - gateway.depositERC20AndCall(address(l1Token), recipient, amount, dataToCall, defaultGasLimit); - - // skip message 1 - hevm.startPrank(address(rollup)); - messageQueueV1.popCrossDomainMessage(1, 1, 0x1); - messageQueueV1.finalizePoppedCrossDomainMessage(2); - assertEq(messageQueueV1.pendingQueueIndex(), 2); - assertEq(messageQueueV1.nextUnfinalizedQueueIndex(), 2); - hevm.stopPrank(); - - // drop message 1 - hevm.expectEmit(true, true, false, true); - emit RefundERC20(address(l1Token), address(this), amount); - - uint256 balance = l1Token.balanceOf(address(this)); - uint256 gatewayBalance = l1Token.balanceOf(address(gateway)); - l1Messenger.dropMessage(address(gateway), address(counterpartGateway), 0, 1, message); - assertEq(balance + amount, l1Token.balanceOf(address(this))); - assertEq(gatewayBalance, l1Token.balanceOf(address(gateway))); - } - function testFinalizeWithdrawERC20( address sender, uint256 amount, diff --git a/src/test/L1ScrollMessengerTest.t.sol b/src/test/L1ScrollMessengerTest.t.sol index 50544681..306804c0 100644 --- a/src/test/L1ScrollMessengerTest.t.sol +++ b/src/test/L1ScrollMessengerTest.t.sol @@ -62,6 +62,26 @@ contract L1ScrollMessengerTest is L1GatewayTestBase { l1Messenger.relayMessageWithProof(address(this), address(messageQueueV2), 0, 0, new bytes(0), proof); } + function testForbidCallEnforcedGatewayFromL2() external { + bytes32 _xDomainCalldataHash = keccak256( + abi.encodeWithSignature( + "relayMessage(address,address,uint256,uint256,bytes)", + address(this), + address(enforcedTxGateway), + 0, + 0, + new bytes(0) + ) + ); + prepareL2MessageRoot(_xDomainCalldataHash); + + IL1ScrollMessenger.L2MessageProof memory proof; + proof.batchIndex = rollup.lastFinalizedBatchIndex(); + + hevm.expectRevert(L1ScrollMessenger.ErrorForbidToCallMessageQueue.selector); + l1Messenger.relayMessageWithProof(address(this), address(enforcedTxGateway), 0, 0, new bytes(0), proof); + } + function testForbidCallSelfFromL2() external { bytes32 _xDomainCalldataHash = keccak256( abi.encodeWithSignature( @@ -106,8 +126,6 @@ contract L1ScrollMessengerTest is L1GatewayTestBase { exceedValue = bound(exceedValue, 1, address(this).balance / 2); - l1Messenger.updateMaxReplayTimes(0); - // append a message l1Messenger.sendMessage{value: 100}(address(0), 100, new bytes(0), defaultGasLimit, refundAddress); @@ -122,20 +140,6 @@ contract L1ScrollMessengerTest is L1GatewayTestBase { uint256 _fee = messageQueueV2.estimateL2BaseFee() * defaultGasLimit; - // Exceed maximum replay times - hevm.expectRevert("Exceed maximum replay times"); - l1Messenger.replayMessage{value: _fee}( - address(this), - address(0), - 100, - 0, - new bytes(0), - defaultGasLimit, - refundAddress - ); - - l1Messenger.updateMaxReplayTimes(1); - // refund exceed fee uint256 balanceBefore = refundAddress.balance; uint256 feeVaultBefore = feeVault.balance; @@ -155,7 +159,6 @@ contract L1ScrollMessengerTest is L1GatewayTestBase { // 1. send a message with nonce 2 // 2. replay 3 times setL2BaseFee(0); - l1Messenger.updateMaxReplayTimes(100); l1Messenger.sendMessage{value: 100}(address(0), 100, new bytes(0), defaultGasLimit, refundAddress); bytes32 hash = keccak256( abi.encodeWithSignature( @@ -182,21 +185,6 @@ contract L1ScrollMessengerTest is L1GatewayTestBase { } } - function testUpdateMaxReplayTimes(uint256 _maxReplayTimes) external { - // not owner, revert - hevm.startPrank(address(1)); - hevm.expectRevert("Ownable: caller is not the owner"); - l1Messenger.updateMaxReplayTimes(_maxReplayTimes); - hevm.stopPrank(); - - hevm.expectEmit(false, false, false, true); - emit UpdateMaxReplayTimes(3, _maxReplayTimes); - - assertEq(l1Messenger.maxReplayTimes(), 3); - l1Messenger.updateMaxReplayTimes(_maxReplayTimes); - assertEq(l1Messenger.maxReplayTimes(), _maxReplayTimes); - } - function testSetPause() external { // not owner, revert hevm.startPrank(address(1)); @@ -217,8 +205,6 @@ contract L1ScrollMessengerTest is L1GatewayTestBase { l1Messenger.relayMessageWithProof(address(0), address(0), 0, 0, new bytes(0), _proof); hevm.expectRevert("Pausable: paused"); l1Messenger.replayMessage(address(0), address(0), 0, 0, new bytes(0), 0, address(0)); - hevm.expectRevert("Pausable: paused"); - l1Messenger.dropMessage(address(0), address(0), 0, 0, new bytes(0)); // unpause l1Messenger.setPause(false); @@ -236,13 +222,13 @@ contract L1ScrollMessengerTest is L1GatewayTestBase { // 32B value // 32B nonce // message byte array (32B offset + 32B length + bytes (padding to multiple of 32)) - // So the intrinsic gas must be greater than 21000 + 16 * 228 = 24648 - uint256 _fee = messageQueueV2.estimateL2BaseFee() * 24648; - l1Messenger.sendMessage{value: _fee + value}(address(0), value, hex"0011220033", 24648); + // So the intrinsic gas must be greater than 21000 + 40 * 228 = 30120 + uint256 _fee = messageQueueV2.estimateL2BaseFee() * 30120; + l1Messenger.sendMessage{value: _fee + value}(address(0), value, hex"0011220033", 30120); // insufficient intrinsic gas hevm.expectRevert(L1MessageQueueV2.ErrorGasLimitBelowIntrinsicGas.selector); - l1Messenger.sendMessage{value: _fee + value}(address(0), 1, hex"0011220033", 24647); + l1Messenger.sendMessage{value: _fee + value}(address(0), 1, hex"0011220033", 30119); // gas limit exceeds the max value uint256 gasLimit = 100000000; @@ -255,111 +241,4 @@ contract L1ScrollMessengerTest is L1GatewayTestBase { _fee = messageQueueV2.estimateL2BaseFee() * gasLimit; l1Messenger.sendMessage{value: _fee + value}(address(0), value, hex"0011220033", gasLimit); } - - /* comments out, it is tested in `src/test/MessageQueueSwitch.t.sol`. - function testDropMessage() external { - // Provided message has not been enqueued, revert - hevm.expectRevert("Provided message has not been enqueued"); - l1Messenger.dropMessage(address(0), address(0), 0, 0, new bytes(0)); - - // send one message with nonce 0 - l1Messenger.sendMessage(address(0), 0, new bytes(0), defaultGasLimit); - assertEq(messageQueueV1.nextCrossDomainMessageIndex(), 1); - - // drop pending message, revert - hevm.expectRevert("cannot drop pending message"); - l1Messenger.dropMessage(address(this), address(0), 0, 0, new bytes(0)); - - l1Messenger.updateMaxReplayTimes(10); - - // replay 1 time - l1Messenger.replayMessage(address(this), address(0), 0, 0, new bytes(0), defaultGasLimit, address(0)); - assertEq(messageQueueV1.nextCrossDomainMessageIndex(), 2); - - // skip all 2 messages - hevm.startPrank(address(rollup)); - messageQueueV1.popCrossDomainMessage(0, 2, 0x3); - messageQueueV1.finalizePoppedCrossDomainMessage(2); - assertEq(messageQueueV1.nextUnfinalizedQueueIndex(), 2); - assertEq(messageQueueV1.pendingQueueIndex(), 2); - hevm.stopPrank(); - for (uint256 i = 0; i < 2; ++i) { - assertBoolEq(messageQueueV1.isMessageSkipped(i), true); - assertBoolEq(messageQueueV1.isMessageDropped(i), false); - } - hevm.expectEmit(false, false, false, true); - emit OnDropMessageCalled(new bytes(0)); - l1Messenger.dropMessage(address(this), address(0), 0, 0, new bytes(0)); - for (uint256 i = 0; i < 2; ++i) { - assertBoolEq(messageQueueV1.isMessageSkipped(i), true); - assertBoolEq(messageQueueV1.isMessageDropped(i), true); - } - - // send one message with nonce 2 and replay 3 times - l1Messenger.sendMessage(address(0), 0, new bytes(0), defaultGasLimit); - assertEq(messageQueueV1.nextCrossDomainMessageIndex(), 3); - for (uint256 i = 0; i < 3; i++) { - l1Messenger.replayMessage(address(this), address(0), 0, 2, new bytes(0), defaultGasLimit, address(0)); - } - assertEq(messageQueueV1.nextCrossDomainMessageIndex(), 6); - - // only first 3 are skipped - hevm.startPrank(address(rollup)); - messageQueueV1.popCrossDomainMessage(2, 4, 0x7); - messageQueueV1.finalizePoppedCrossDomainMessage(6); - assertEq(messageQueueV1.nextUnfinalizedQueueIndex(), 6); - assertEq(messageQueueV1.pendingQueueIndex(), 6); - hevm.stopPrank(); - for (uint256 i = 2; i < 6; i++) { - assertBoolEq(messageQueueV1.isMessageSkipped(i), i < 5); - assertBoolEq(messageQueueV1.isMessageDropped(i), false); - } - - // drop non-skipped message, revert - hevm.expectRevert("drop non-skipped message"); - l1Messenger.dropMessage(address(this), address(0), 0, 2, new bytes(0)); - - // send one message with nonce 6 and replay 4 times - l1Messenger.sendMessage(address(0), 0, new bytes(0), defaultGasLimit); - for (uint256 i = 0; i < 4; i++) { - l1Messenger.replayMessage(address(this), address(0), 0, 6, new bytes(0), defaultGasLimit, address(0)); - } - assertEq(messageQueueV1.nextCrossDomainMessageIndex(), 11); - - // skip all 5 messages - hevm.startPrank(address(rollup)); - messageQueueV1.popCrossDomainMessage(6, 5, 0x1f); - messageQueueV1.finalizePoppedCrossDomainMessage(11); - assertEq(messageQueueV1.nextUnfinalizedQueueIndex(), 11); - assertEq(messageQueueV1.pendingQueueIndex(), 11); - hevm.stopPrank(); - for (uint256 i = 6; i < 11; ++i) { - assertBoolEq(messageQueueV1.isMessageSkipped(i), true); - assertBoolEq(messageQueueV1.isMessageDropped(i), false); - } - hevm.expectEmit(false, false, false, true); - emit OnDropMessageCalled(new bytes(0)); - l1Messenger.dropMessage(address(this), address(0), 0, 6, new bytes(0)); - for (uint256 i = 6; i < 11; ++i) { - assertBoolEq(messageQueueV1.isMessageSkipped(i), true); - assertBoolEq(messageQueueV1.isMessageDropped(i), true); - } - - // Message already dropped, revert - hevm.expectRevert("Message already dropped"); - l1Messenger.dropMessage(address(this), address(0), 0, 0, new bytes(0)); - hevm.expectRevert("Message already dropped"); - l1Messenger.dropMessage(address(this), address(0), 0, 6, new bytes(0)); - - // replay dropped message, revert - hevm.expectRevert("Message already dropped"); - l1Messenger.replayMessage(address(this), address(0), 0, 0, new bytes(0), defaultGasLimit, address(0)); - hevm.expectRevert("Message already dropped"); - l1Messenger.replayMessage(address(this), address(0), 0, 6, new bytes(0), defaultGasLimit, address(0)); - } - */ - - function onDropMessage(bytes memory message) external payable { - emit OnDropMessageCalled(message); - } } diff --git a/src/test/L1StandardERC20Gateway.t.sol b/src/test/L1StandardERC20Gateway.t.sol index 909b1f3f..08043b2e 100644 --- a/src/test/L1StandardERC20Gateway.t.sol +++ b/src/test/L1StandardERC20Gateway.t.sol @@ -218,90 +218,6 @@ contract L1StandardERC20GatewayTest is L1GatewayTestBase { assertEq(balanceBefore + amount - fee, balanceAfter); } - function testDropMessageMocking() public { - MockScrollMessenger mockMessenger = new MockScrollMessenger(); - gateway = _deployGateway(address(mockMessenger)); - gateway.initialize( - address(counterpartGateway), - address(router), - address(mockMessenger), - address(template), - address(factory) - ); - - // only messenger can call, revert - hevm.expectRevert(ErrorCallerIsNotMessenger.selector); - gateway.onDropMessage(new bytes(0)); - - // only called in drop context, revert - hevm.expectRevert(ErrorNotInDropMessageContext.selector); - mockMessenger.callTarget( - address(gateway), - abi.encodeWithSelector(gateway.onDropMessage.selector, new bytes(0)) - ); - - mockMessenger.setXDomainMessageSender(ScrollConstants.DROP_XDOMAIN_MESSAGE_SENDER); - - // invalid selector, revert - hevm.expectRevert("invalid selector"); - mockMessenger.callTarget( - address(gateway), - abi.encodeWithSelector(gateway.onDropMessage.selector, new bytes(4)) - ); - - bytes memory message = abi.encodeWithSelector( - IL2ERC20Gateway.finalizeDepositERC20.selector, - address(l1Token), - address(l2Token), - address(this), - address(this), - 100, - new bytes(0) - ); - - // nonzero msg.value, revert - hevm.expectRevert("nonzero msg.value"); - mockMessenger.callTarget{value: 1}( - address(gateway), - abi.encodeWithSelector(gateway.onDropMessage.selector, message) - ); - } - - function testDropMessage( - uint256 amount, - address recipient, - bytes memory dataToCall - ) public { - amount = bound(amount, 1, l1Token.balanceOf(address(this)) / 2); - bytes memory message = abi.encodeWithSelector( - IL2ERC20Gateway.finalizeDepositERC20.selector, - address(l1Token), - address(l2Token), - address(this), - recipient, - amount, - abi.encode(true, abi.encode(dataToCall, abi.encode(l1Token.symbol(), l1Token.name(), l1Token.decimals()))) - ); - gateway.depositERC20AndCall(address(l1Token), recipient, amount, dataToCall, defaultGasLimit); - gateway.depositERC20AndCall(address(l1Token), recipient, amount, dataToCall, defaultGasLimit); - - // skip message 0 and 1 - hevm.startPrank(address(rollup)); - messageQueueV1.popCrossDomainMessage(0, 2, 0x3); - messageQueueV1.finalizePoppedCrossDomainMessage(2); - assertEq(messageQueueV1.nextUnfinalizedQueueIndex(), 2); - assertEq(messageQueueV1.pendingQueueIndex(), 2); - hevm.stopPrank(); - - // drop message 1 - hevm.expectEmit(true, true, false, true); - emit RefundERC20(address(l1Token), address(this), amount); - - uint256 balance = l1Token.balanceOf(address(this)); - l1Messenger.dropMessage(address(gateway), address(counterpartGateway), 0, 1, message); - assertEq(balance + amount, l1Token.balanceOf(address(this))); - } - function testFinalizeWithdrawERC20FailedMocking( address sender, address recipient, diff --git a/src/test/L1WETHGateway.t.sol b/src/test/L1WETHGateway.t.sol index d93ab583..a4f3fb6f 100644 --- a/src/test/L1WETHGateway.t.sol +++ b/src/test/L1WETHGateway.t.sol @@ -143,101 +143,6 @@ contract L1WETHGatewayTest is L1GatewayTestBase { _depositERC20WithRecipientAndCalldata(true, amount, recipient, dataToCall, gasLimit, feePerGas); } - function testDropMessageMocking() public { - MockScrollMessenger mockMessenger = new MockScrollMessenger(); - gateway = _deployGateway(address(mockMessenger)); - gateway.initialize(address(counterpartGateway), address(router), address(mockMessenger)); - - // only messenger can call, revert - hevm.expectRevert(ErrorCallerIsNotMessenger.selector); - gateway.onDropMessage(new bytes(0)); - - // only called in drop context, revert - hevm.expectRevert(ErrorNotInDropMessageContext.selector); - mockMessenger.callTarget( - address(gateway), - abi.encodeWithSelector(gateway.onDropMessage.selector, new bytes(0)) - ); - - mockMessenger.setXDomainMessageSender(ScrollConstants.DROP_XDOMAIN_MESSAGE_SENDER); - - // invalid selector, revert - hevm.expectRevert("invalid selector"); - mockMessenger.callTarget( - address(gateway), - abi.encodeWithSelector(gateway.onDropMessage.selector, new bytes(4)) - ); - - bytes memory message = abi.encodeWithSelector( - IL2ERC20Gateway.finalizeDepositERC20.selector, - address(l1weth), - address(l2weth), - address(this), - address(this), - 100, - new bytes(0) - ); - - // token not WETH, revert - hevm.expectRevert("token not WETH"); - mockMessenger.callTarget( - address(gateway), - abi.encodeWithSelector( - gateway.onDropMessage.selector, - abi.encodeWithSelector( - IL2ERC20Gateway.finalizeDepositERC20.selector, - address(l2weth), - address(l2weth), - address(this), - address(this), - 100, - new bytes(0) - ) - ) - ); - - // msg.value mismatch, revert - hevm.expectRevert("msg.value mismatch"); - mockMessenger.callTarget{value: 99}( - address(gateway), - abi.encodeWithSelector(gateway.onDropMessage.selector, message) - ); - } - - function testDropMessage( - uint256 amount, - address recipient, - bytes memory dataToCall - ) public { - amount = bound(amount, 1, l1weth.balanceOf(address(this))); - bytes memory message = abi.encodeWithSelector( - IL2ERC20Gateway.finalizeDepositERC20.selector, - address(l1weth), - address(l2weth), - address(this), - recipient, - amount, - dataToCall - ); - gateway.depositERC20AndCall(address(l1weth), recipient, amount, dataToCall, defaultGasLimit); - - // skip message 0 - hevm.startPrank(address(rollup)); - messageQueueV1.popCrossDomainMessage(0, 1, 0x1); - messageQueueV1.finalizePoppedCrossDomainMessage(1); - assertEq(messageQueueV1.nextUnfinalizedQueueIndex(), 1); - assertEq(messageQueueV1.pendingQueueIndex(), 1); - hevm.stopPrank(); - - // drop message 0 - hevm.expectEmit(true, true, false, true); - emit RefundERC20(address(l1weth), address(this), amount); - - uint256 balance = l1weth.balanceOf(address(this)); - l1Messenger.dropMessage(address(gateway), address(counterpartGateway), amount, 0, message); - assertEq(balance + amount, l1weth.balanceOf(address(this))); - } - function testFinalizeWithdrawERC20FailedMocking( address sender, address recipient, diff --git a/src/test/L2ScrollMessenger.t.sol b/src/test/L2ScrollMessenger.t.sol index b0c8c2f9..2bdb6d5d 100644 --- a/src/test/L2ScrollMessenger.t.sol +++ b/src/test/L2ScrollMessenger.t.sol @@ -28,7 +28,7 @@ contract L2ScrollMessengerTest is DSTestPlus { function setUp() public { // Deploy L1 contracts - l1Messenger = new L1ScrollMessenger(address(1), address(1), address(1), address(1)); + l1Messenger = new L1ScrollMessenger(address(1), address(1), address(1), address(1), address(1)); // Deploy L2 contracts whitelist = new Whitelist(address(this)); diff --git a/src/test/L2SystemConfig.t.sol b/src/test/L2SystemConfig.t.sol new file mode 100644 index 00000000..465ea37d --- /dev/null +++ b/src/test/L2SystemConfig.t.sol @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: MIT + +pragma solidity =0.8.24; + +import {Test} from "forge-std/Test.sol"; + +import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; + +import {L2SystemConfig} from "../L2/L2SystemConfig.sol"; + +contract L2SystemConfigTest is Test { + address public admin; + address public owner; + address public nonOwner; + + L2SystemConfig public l2SystemConfig; + + event BaseFeeOverheadUpdated(uint256 oldBaseFeeOverhead, uint256 newBaseFeeOverhead); + event BaseFeeScalarUpdated(uint256 oldBaseFeeScalar, uint256 newBaseFeeScalar); + + function setUp() public { + admin = makeAddr("admin"); + owner = makeAddr("owner"); + nonOwner = makeAddr("nonOwner"); + + L2SystemConfig implementation = new L2SystemConfig(); + address proxy = address(new TransparentUpgradeableProxy(address(implementation), admin, "")); + l2SystemConfig = L2SystemConfig(proxy); + + l2SystemConfig.initialize(owner); + } + + function test_Initialize() public { + // Test initialization + assertEq(l2SystemConfig.owner(), owner); + + // revert when initialize again + vm.expectRevert("Initializable: contract is already initialized"); + l2SystemConfig.initialize(owner); + } + + function test_UpdateBaseFeeOverhead(uint256 newBaseFeeOverhead) public { + // Test that only owner can update base fee overhead + vm.prank(nonOwner); + vm.expectRevert("Ownable: caller is not the owner"); + l2SystemConfig.updateBaseFeeOverhead(newBaseFeeOverhead); + + // Test owner can update base fee overhead + assertEq(l2SystemConfig.baseFeeOverhead(), 0); + vm.prank(owner); + vm.expectEmit(true, true, true, true); + emit BaseFeeOverheadUpdated(0, newBaseFeeOverhead); + l2SystemConfig.updateBaseFeeOverhead(newBaseFeeOverhead); + assertEq(l2SystemConfig.baseFeeOverhead(), newBaseFeeOverhead); + } + + function test_UpdateBaseFeeScalar(uint256 newBaseFeeScalar) public { + // Test that only owner can update base fee scalar + vm.prank(nonOwner); + vm.expectRevert("Ownable: caller is not the owner"); + l2SystemConfig.updateBaseFeeScalar(newBaseFeeScalar); + + // Test owner can update base fee scalar + assertEq(l2SystemConfig.baseFeeScalar(), 0); + vm.prank(owner); + vm.expectEmit(true, true, true, true); + emit BaseFeeScalarUpdated(0, newBaseFeeScalar); + l2SystemConfig.updateBaseFeeScalar(newBaseFeeScalar); + assertEq(l2SystemConfig.baseFeeScalar(), newBaseFeeScalar); + } + + function test_GetL2BaseFee( + uint256 l1BaseFee, + uint256 baseFeeScalar, + uint256 baseFeeOverhead + ) public { + l1BaseFee = bound(l1BaseFee, 0, type(uint64).max); + baseFeeScalar = bound(baseFeeScalar, 0, type(uint128).max); + baseFeeOverhead = bound(baseFeeOverhead, 0, type(uint128).max); + + // Set up the contract state + vm.prank(owner); + l2SystemConfig.updateBaseFeeScalar(baseFeeScalar); + vm.prank(owner); + l2SystemConfig.updateBaseFeeOverhead(baseFeeOverhead); + + // Calculate expected L2 base fee + uint256 expectedL2BaseFee = (l1BaseFee * baseFeeScalar) / 1e18 + baseFeeOverhead; + + // Test getL2BaseFee function + uint256 actualL2BaseFee = l2SystemConfig.getL2BaseFee(l1BaseFee); + assertEq(actualL2BaseFee, expectedL2BaseFee); + } + + function test_GetL2BaseFeeWithZeroL1BaseFee() public { + uint256 l1BaseFee = 0; + uint256 baseFeeScalar = 2000; + uint256 baseFeeOverhead = 1000; + + // Set up the contract state + vm.prank(owner); + l2SystemConfig.updateBaseFeeScalar(baseFeeScalar); + vm.prank(owner); + l2SystemConfig.updateBaseFeeOverhead(baseFeeOverhead); + + // Calculate expected L2 base fee + uint256 expectedL2BaseFee = baseFeeOverhead; // When L1 base fee is 0, only overhead is added + + // Test getL2BaseFee function + uint256 actualL2BaseFee = l2SystemConfig.getL2BaseFee(l1BaseFee); + assertEq(actualL2BaseFee, expectedL2BaseFee); + } + + function test_GetL2BaseFeeWithLargeValues() public { + uint256 l1BaseFee = 1e18; // 1 ETH + uint256 baseFeeScalar = 2e18; // 2x multiplier + uint256 baseFeeOverhead = 1e17; // 0.1 ETH + + // Set up the contract state + vm.prank(owner); + l2SystemConfig.updateBaseFeeScalar(baseFeeScalar); + vm.prank(owner); + l2SystemConfig.updateBaseFeeOverhead(baseFeeOverhead); + + // Calculate expected L2 base fee + uint256 expectedL2BaseFee = (l1BaseFee * baseFeeScalar) / 1e18 + baseFeeOverhead; + + // Test getL2BaseFee function + uint256 actualL2BaseFee = l2SystemConfig.getL2BaseFee(l1BaseFee); + assertEq(actualL2BaseFee, expectedL2BaseFee); + } +} diff --git a/src/test/ScrollChain.t.sol b/src/test/ScrollChain.t.sol index 07854970..6e1deea5 100644 --- a/src/test/ScrollChain.t.sol +++ b/src/test/ScrollChain.t.sol @@ -126,7 +126,7 @@ contract ScrollChainTest is DSTestPlus { bytes memory header = _commitGenesisBatch(); for (uint256 i = 0; i < batches; ++i) { - header = _commitBatchV3Codec(6, header, 0, 0); + header = _commitBatchV7Codec(7, header); } assertEq(rollup.committedBatches(batches), keccak256(header)); @@ -140,221 +140,8 @@ contract ScrollChainTest is DSTestPlus { assertBoolEq(rollup.isEnforcedModeEnabled(), false); } - function testCommitBatchV3Codec() external { - bytes memory batchHeader0 = new bytes(89); - - // import 10 L1 messages - for (uint256 i = 0; i < 10; i++) { - messageQueueV1.appendCrossDomainMessage(address(this), 1000000, new bytes(0)); - } - // import genesis batch first - assembly { - mstore(add(batchHeader0, add(0x20, 25)), 1) - } - rollup.importGenesisBatch(batchHeader0, bytes32(uint256(1))); - assertEq(rollup.committedBatches(0), keccak256(batchHeader0)); - - // caller not sequencer, revert - hevm.expectRevert(ScrollChain.ErrorCallerIsNotSequencer.selector); - rollup.commitBatchWithBlobProof(4, batchHeader0, new bytes[](0), new bytes(0), new bytes(0)); - rollup.addSequencer(address(0)); - - // revert when ErrorIncorrectBatchVersion - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorIncorrectBatchVersion.selector); - rollup.commitBatchWithBlobProof(2, batchHeader0, new bytes[](0), new bytes(0), new bytes(0)); - hevm.expectRevert(ScrollChain.ErrorIncorrectBatchVersion.selector); - rollup.commitBatchWithBlobProof(3, batchHeader0, new bytes[](0), new bytes(0), new bytes(0)); - hevm.stopPrank(); - - // revert when ErrorBatchIsEmpty - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorBatchIsEmpty.selector); - rollup.commitBatchWithBlobProof(4, batchHeader0, new bytes[](0), new bytes(0), new bytes(0)); - hevm.stopPrank(); - - // revert when ErrorBatchHeaderV3LengthMismatch - bytes memory header = new bytes(192); - assembly { - mstore8(add(header, 0x20), 4) // version - } - hevm.startPrank(address(0)); - hevm.expectRevert(BatchHeaderV3Codec.ErrorBatchHeaderV3LengthMismatch.selector); - rollup.commitBatchWithBlobProof(4, header, new bytes[](1), new bytes(0), new bytes(0)); - hevm.stopPrank(); - - // revert when ErrorIncorrectBatchHash - assembly { - mstore(add(batchHeader0, add(0x20, 25)), 2) // change data hash for batch0 - } - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorIncorrectBatchHash.selector); - rollup.commitBatchWithBlobProof(4, batchHeader0, new bytes[](1), new bytes(0), new bytes(0)); - hevm.stopPrank(); - assembly { - mstore(add(batchHeader0, add(0x20, 25)), 1) // change back - } - - bytes[] memory chunks = new bytes[](1); - bytes memory chunk0; - - // no block in chunk, revert - chunk0 = new bytes(1); - chunks[0] = chunk0; - hevm.startPrank(address(0)); - hevm.expectRevert(ChunkCodecV1.ErrorNoBlockInChunkV1.selector); - rollup.commitBatchWithBlobProof(4, batchHeader0, chunks, new bytes(0), new bytes(0)); - hevm.stopPrank(); - - // invalid chunk length, revert - chunk0 = new bytes(1); - chunk0[0] = bytes1(uint8(1)); // one block in this chunk - chunks[0] = chunk0; - hevm.startPrank(address(0)); - hevm.expectRevert(ChunkCodecV1.ErrorIncorrectChunkLengthV1.selector); - rollup.commitBatchWithBlobProof(4, batchHeader0, chunks, new bytes(0), new bytes(0)); - hevm.stopPrank(); - - // cannot skip last L1 message, revert - chunk0 = new bytes(1 + 60); - bytes memory bitmap = new bytes(32); - chunk0[0] = bytes1(uint8(1)); // one block in this chunk - chunk0[58] = bytes1(uint8(1)); // numTransactions = 1 - chunk0[60] = bytes1(uint8(1)); // numL1Messages = 1 - bitmap[31] = bytes1(uint8(1)); - chunks[0] = chunk0; - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorLastL1MessageSkipped.selector); - rollup.commitBatchWithBlobProof(4, batchHeader0, chunks, bitmap, new bytes(0)); - hevm.stopPrank(); - - // num txs less than num L1 msgs, revert - chunk0 = new bytes(1 + 60); - bitmap = new bytes(32); - chunk0[0] = bytes1(uint8(1)); // one block in this chunk - chunk0[58] = bytes1(uint8(1)); // numTransactions = 1 - chunk0[60] = bytes1(uint8(3)); // numL1Messages = 3 - bitmap[31] = bytes1(uint8(3)); - chunks[0] = chunk0; - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorNumTxsLessThanNumL1Msgs.selector); - rollup.commitBatchWithBlobProof(4, batchHeader0, chunks, bitmap, new bytes(0)); - hevm.stopPrank(); - - // revert when ErrorNoBlobFound - chunk0 = new bytes(1 + 60); - chunk0[0] = bytes1(uint8(1)); // one block in this chunk - chunks[0] = chunk0; - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorNoBlobFound.selector); - rollup.commitBatchWithBlobProof(4, batchHeader0, chunks, new bytes(0), new bytes(0)); - hevm.stopPrank(); - - // @note we cannot check `ErrorFoundMultipleBlobs` here - - // upgrade to ScrollChainMockBlob - _upgradeToMockBlob(); - bytes - memory blobDataProof = hex"2c9d777660f14ad49803a6442935c0d24a0d83551de5995890bf70a17d24e68753ab0fe6807c7081f0885fe7da741554d658a03730b1fa006f8319f8b993bcb0a5a0c9e8a145c5ef6e415c245690effa2914ec9393f58a7251d30c0657da1453d9ad906eae8b97dd60c9a216f81b4df7af34d01e214e1ec5865f0133ecc16d7459e49dab66087340677751e82097fbdd20551d66076f425775d1758a9dfd186b"; - - chunk0 = new bytes(1 + 60); - chunk0[0] = bytes1(uint8(1)); // one block in this chunk - chunks[0] = chunk0; - // revert when ErrorCallPointEvaluationPrecompileFailed - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorCallPointEvaluationPrecompileFailed.selector); - rollup.commitBatchWithBlobProof(4, batchHeader0, chunks, new bytes(0), new bytes(0)); - hevm.stopPrank(); - - bytes32 batchHash0 = rollup.committedBatches(0); - bytes memory batchHeader1 = new bytes(193); - assembly { - mstore8(add(batchHeader1, 0x20), 4) // version - mstore(add(batchHeader1, add(0x20, 1)), shl(192, 1)) // batchIndex - mstore(add(batchHeader1, add(0x20, 9)), 0) // l1MessagePopped - mstore(add(batchHeader1, add(0x20, 17)), 0) // totalL1MessagePopped - mstore(add(batchHeader1, add(0x20, 25)), 0x246394445f4fe64ed5598554d55d1682d6fb3fe04bf58eb54ef81d1189fafb51) // dataHash - mstore(add(batchHeader1, add(0x20, 57)), blobVersionedHash) // blobVersionedHash - mstore(add(batchHeader1, add(0x20, 89)), batchHash0) // parentBatchHash - mstore(add(batchHeader1, add(0x20, 121)), 0) // lastBlockTimestamp - mcopy(add(batchHeader1, add(0x20, 129)), add(blobDataProof, 0x20), 64) // blobDataProof - } - // hash is ed32768c5f910a11edaf1c1ec0c0da847def9d24e0a24567c3c3d284061cf935 - - // succeed - hevm.startPrank(address(0)); - assertEq(rollup.committedBatches(1), bytes32(0)); - rollup.commitBatchWithBlobProof(4, batchHeader0, chunks, new bytes(0), blobDataProof); - hevm.stopPrank(); - assertEq(rollup.committedBatches(1), keccak256(batchHeader1)); - - // revert when ErrorBatchIsAlreadyCommitted - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorBatchIsAlreadyCommitted.selector); - rollup.commitBatchWithBlobProof(4, batchHeader0, chunks, new bytes(0), blobDataProof); - hevm.stopPrank(); - } - - function testCommitBatchV5() external { - bytes[] memory headers = _prepareBatchesV3Codec(4); - - // revert when ErrorV5BatchNotContainsOnlyOneChunk - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorV5BatchNotContainsOnlyOneChunk.selector); // 0 chunk - rollup.commitBatchWithBlobProof(5, headers[10], new bytes[](0), new bytes(0), new bytes(0)); - hevm.expectRevert(ScrollChain.ErrorV5BatchNotContainsOnlyOneChunk.selector); // 2 chunks - rollup.commitBatchWithBlobProof(5, headers[10], new bytes[](2), new bytes(0), new bytes(0)); - hevm.stopPrank(); - - bytes[] memory chunks = new bytes[](1); - // revert when ErrorV5BatchNotContainsOnlyOneBlock - hevm.startPrank(address(0)); - chunks[0] = new bytes(1); - hevm.expectRevert(ChunkCodecV1.ErrorNoBlockInChunkV1.selector); // 1 chunk, 0 block - rollup.commitBatchWithBlobProof(5, headers[10], chunks, new bytes(0), new bytes(0)); - for (uint256 i = 2; i < 256; ++i) { - chunks[0] = new bytes(1 + 60 * i); - chunks[0][0] = bytes1(uint8(i)); - hevm.expectRevert(ScrollChain.ErrorV5BatchNotContainsOnlyOneBlock.selector); // 1 chunk, i block - rollup.commitBatchWithBlobProof(5, headers[10], chunks, new bytes(0), new bytes(0)); - } - hevm.stopPrank(); - - // revert when ErrorV5BatchContainsTransactions - hevm.startPrank(address(0)); - for (uint256 x = 0; x < 5; ++x) { - for (uint256 y = 0; y < 5; ++y) { - if (x + y == 0) continue; - bytes memory chunk = new bytes(1 + 60); - chunk[0] = bytes1(uint8(1)); - uint256 blockPtr; - assembly { - blockPtr := add(chunk, 0x21) - mstore(add(blockPtr, 56), shl(240, add(x, y))) - mstore(add(blockPtr, 58), shl(240, y)) - } - assertEq(x + y, ChunkCodecV1.getNumTransactions(blockPtr)); - assertEq(y, ChunkCodecV1.getNumL1Messages(blockPtr)); - chunks[0] = chunk; - hevm.expectRevert(ScrollChain.ErrorV5BatchContainsTransactions.selector); // 1 chunk, 1 nonempty block - rollup.commitBatchWithBlobProof(5, headers[10], chunks, new bytes(0), new bytes(0)); - } - } - hevm.stopPrank(); - - assertEq(rollup.initialEuclidBatchIndex(), 0); - bytes memory v5Header = _commitBatchV3Codec(5, headers[10], 0, 0); - assertEq(rollup.initialEuclidBatchIndex(), 11); - - // revert when commit again - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorBatchIsAlreadyCommitted.selector); - rollup.commitBatchWithBlobProof(5, v5Header, new bytes[](0), new bytes(0), new bytes(0)); - hevm.stopPrank(); - } - function testCommitBatchV7Codec() external { - bytes[] memory headers = _prepareBatchesV3Codec(6); + bytes[] memory headers = _prepareBatchesV7Codec(7); (, bytes memory h11) = _constructBatchStructCodecV7(7, headers[10]); (, bytes memory h12) = _constructBatchStructCodecV7(7, h11); (, bytes memory h13) = _constructBatchStructCodecV7(7, h12); @@ -401,113 +188,23 @@ contract ScrollChainTest is DSTestPlus { assertGt(uint256(rollup.committedBatches(13)), 0); } - function testFinalizeBundleWithProof() external { - // caller not prover, revert - hevm.expectRevert(ScrollChain.ErrorCallerIsNotProver.selector); - rollup.finalizeBundleWithProof(new bytes(0), bytes32(0), bytes32(0), new bytes(0)); - - rollup.addProver(address(0)); - rollup.addSequencer(address(0)); - - // import genesis batch - bytes memory batchHeader0 = new bytes(89); - assembly { - mstore(add(batchHeader0, add(0x20, 25)), 1) - } - rollup.importGenesisBatch(batchHeader0, bytes32(uint256(1))); - - // upgrade to ScrollChainMockBlob - _upgradeToMockBlob(); - bytes - memory blobDataProof = hex"2c9d777660f14ad49803a6442935c0d24a0d83551de5995890bf70a17d24e68753ab0fe6807c7081f0885fe7da741554d658a03730b1fa006f8319f8b993bcb0a5a0c9e8a145c5ef6e415c245690effa2914ec9393f58a7251d30c0657da1453d9ad906eae8b97dd60c9a216f81b4df7af34d01e214e1ec5865f0133ecc16d7459e49dab66087340677751e82097fbdd20551d66076f425775d1758a9dfd186b"; - - bytes[] memory chunks = new bytes[](1); - bytes memory chunk0; - - bytes32 batchHash0 = rollup.committedBatches(0); - bytes memory batchHeader1 = new bytes(193); - assembly { - mstore8(add(batchHeader1, 0x20), 4) // version - mstore(add(batchHeader1, add(0x20, 1)), shl(192, 1)) // batchIndex - mstore(add(batchHeader1, add(0x20, 9)), 0) // l1MessagePopped - mstore(add(batchHeader1, add(0x20, 17)), 0) // totalL1MessagePopped - mstore(add(batchHeader1, add(0x20, 25)), 0x246394445f4fe64ed5598554d55d1682d6fb3fe04bf58eb54ef81d1189fafb51) // dataHash - mstore(add(batchHeader1, add(0x20, 57)), blobVersionedHash) // blobVersionedHash - mstore(add(batchHeader1, add(0x20, 89)), batchHash0) // parentBatchHash - mstore(add(batchHeader1, add(0x20, 121)), 0) // lastBlockTimestamp - mcopy(add(batchHeader1, add(0x20, 129)), add(blobDataProof, 0x20), 64) // blobDataProof - } - // hash is ed32768c5f910a11edaf1c1ec0c0da847def9d24e0a24567c3c3d284061cf935 - - // commit one batch - chunk0 = new bytes(1 + 60); - chunk0[0] = bytes1(uint8(1)); // one block in this chunk - chunks[0] = chunk0; - hevm.startPrank(address(0)); - assertEq(rollup.committedBatches(1), bytes32(0)); - rollup.commitBatchWithBlobProof(4, batchHeader0, chunks, new bytes(0), blobDataProof); - hevm.stopPrank(); - assertEq(rollup.committedBatches(1), keccak256(batchHeader1)); - - // revert when ErrorStateRootIsZero - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorStateRootIsZero.selector); - rollup.finalizeBundleWithProof(batchHeader1, bytes32(0), bytes32(0), new bytes(0)); - hevm.stopPrank(); - - // revert when ErrorBatchHeaderV3LengthMismatch - bytes memory header = new bytes(192); - assembly { - mstore8(add(header, 0x20), 4) // version - } - hevm.startPrank(address(0)); - hevm.expectRevert(BatchHeaderV3Codec.ErrorBatchHeaderV3LengthMismatch.selector); - rollup.finalizeBundleWithProof(header, bytes32(uint256(1)), bytes32(uint256(2)), new bytes(0)); - hevm.stopPrank(); - - // revert when ErrorIncorrectBatchHash - batchHeader1[10] = bytes1(uint8(1)); // change random byte - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorIncorrectBatchHash.selector); - rollup.finalizeBundleWithProof(batchHeader1, bytes32(uint256(1)), bytes32(uint256(2)), new bytes(0)); - hevm.stopPrank(); - batchHeader1[10] = bytes1(uint8(0)); // change back - - // verify success - (, , uint256 lastFinalizeTimestamp, , ) = rollup.miscData(); - assertEq(lastFinalizeTimestamp, 0); - hevm.warp(100); - assertBoolEq(rollup.isBatchFinalized(1), false); - hevm.startPrank(address(0)); - rollup.finalizeBundleWithProof(batchHeader1, bytes32(uint256(2)), bytes32(uint256(3)), new bytes(0)); - hevm.stopPrank(); - (, , lastFinalizeTimestamp, , ) = rollup.miscData(); - assertEq(lastFinalizeTimestamp, 100); - assertBoolEq(rollup.isBatchFinalized(1), true); - assertEq(rollup.finalizedStateRoots(1), bytes32(uint256(2))); - assertEq(rollup.withdrawRoots(1), bytes32(uint256(3))); - assertEq(rollup.lastFinalizedBatchIndex(), 1); - - // revert when ErrorBatchIsAlreadyVerified - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorBatchIsAlreadyVerified.selector); - rollup.finalizeBundleWithProof(batchHeader1, bytes32(uint256(2)), bytes32(uint256(3)), new bytes(0)); - hevm.stopPrank(); - } - function testFinalizeBundlePostEuclidV2() external { + messageQueueV2.initialize(); + rollup.initializeV2(); + // import 10 L1 messages into message queue V2 + for (uint256 i = 0; i < 10; i++) { + messageQueueV2.appendCrossDomainMessage(address(this), 1000000, new bytes(0)); + } // 0: genesis // 1-10: v6 // 11-20: v7 bytes[] memory headers = new bytes[](21); { - bytes[] memory headersV6 = _prepareBatchesV3Codec(6); + bytes[] memory headersV7 = _prepareBatchesV7Codec(7); for (uint256 i = 0; i <= 10; ++i) { - headers[i] = headersV6[i]; + headers[i] = headersV7[i]; } } - messageQueueV2.initialize(); - rollup.initializeV2(); for (uint256 i = 11; i < 21; ++i) { headers[i] = _commitBatchV7Codec(7, headers[i - 1]); } @@ -517,10 +214,10 @@ contract ScrollChainTest is DSTestPlus { rollup.finalizeBundlePostEuclidV2(new bytes(0), 0, bytes32(0), bytes32(0), new bytes(0)); // revert ErrorNotAllV1MessagesAreFinalized - hevm.startPrank(address(0)); - hevm.expectRevert(ScrollChain.ErrorNotAllV1MessagesAreFinalized.selector); - rollup.finalizeBundlePostEuclidV2(headers[20], 0, bytes32(0), bytes32(0), new bytes(0)); - hevm.stopPrank(); + // hevm.startPrank(address(0)); + // hevm.expectRevert(ScrollChain.ErrorNotAllV1MessagesAreFinalized.selector); + // rollup.finalizeBundlePostEuclidV2(headers[20], 0, bytes32(0), bytes32(0), new bytes(0)); + // hevm.stopPrank(); // finalize all v6 batches (, uint256 lastFinalizedBatchIndex, uint256 lastFinalizeTimestamp, uint256 flags, ) = rollup.miscData(); @@ -529,16 +226,16 @@ contract ScrollChainTest is DSTestPlus { assertEq(flags, 0); hevm.warp(100); hevm.startPrank(address(0)); - rollup.finalizeBundleWithProof(headers[10], keccak256("x10"), keccak256("y10"), new bytes(0)); + rollup.finalizeBundlePostEuclidV2(headers[10], 10, keccak256("x10"), keccak256("y10"), new bytes(0)); hevm.stopPrank(); (, lastFinalizedBatchIndex, lastFinalizeTimestamp, flags, ) = rollup.miscData(); assertEq(lastFinalizedBatchIndex, 10); assertEq(lastFinalizeTimestamp, 100); - assertEq(flags, 0); + assertEq(flags, 1); assertEq(rollup.lastFinalizedBatchIndex(), lastFinalizedBatchIndex); assertEq(rollup.finalizedStateRoots(10), keccak256("x10")); assertEq(rollup.withdrawRoots(10), keccak256("y10")); - assertEq(messageQueueV1.nextUnfinalizedQueueIndex(), 10); + // assertEq(messageQueueV1.nextUnfinalizedQueueIndex(), 10); // revert ErrorStateRootIsZero hevm.startPrank(address(0)); @@ -612,24 +309,28 @@ contract ScrollChainTest is DSTestPlus { } function testCommitAndFinalizeBatchByExpiredMessage() external { + messageQueueV2.initialize(); + rollup.initializeV2(); + // import 10 L1 messages into message queue V2 + for (uint256 i = 0; i < 10; i++) { + messageQueueV2.appendCrossDomainMessage(address(this), 1000000, new bytes(0)); + } // 0: genesis // 1-10: v6 // 11-20: v7 bytes[] memory headers = new bytes[](21); { - bytes[] memory headersV6 = _prepareBatchesV3Codec(6); + bytes[] memory headersV7 = _prepareBatchesV7Codec(7); for (uint256 i = 0; i <= 10; ++i) { - headers[i] = headersV6[i]; + headers[i] = headersV7[i]; } } - messageQueueV2.initialize(); - rollup.initializeV2(); for (uint256 i = 11; i < 21; ++i) { headers[i] = _commitBatchV7Codec(7, headers[i - 1]); } // finalize all v6 batches hevm.startPrank(address(0)); - rollup.finalizeBundleWithProof(headers[10], keccak256("x10"), keccak256("y10"), new bytes(0)); + rollup.finalizeBundlePostEuclidV2(headers[10], 10, keccak256("x10"), keccak256("y10"), new bytes(0)); hevm.stopPrank(); // finalize two v7 batches hevm.startPrank(address(0)); @@ -638,6 +339,7 @@ contract ScrollChainTest is DSTestPlus { // revert when ErrorNotInEnforcedBatchMode hevm.expectRevert(ScrollChain.ErrorNotInEnforcedBatchMode.selector); + hevm.startPrank(address(0x00a329c0648769A73afAc7F9381E08FB43dBEA72)); rollup.commitAndFinalizeBatch( 7, bytes32(0), @@ -649,6 +351,7 @@ contract ScrollChainTest is DSTestPlus { zkProof: new bytes(0) }) ); + hevm.stopPrank(); system.updateEnforcedBatchParameters( SystemConfig.EnforcedBatchParameters({ @@ -669,6 +372,7 @@ contract ScrollChainTest is DSTestPlus { // succeed to call commitAndFinalizeBatch 13 ScrollChainMockBlob(address(rollup)).setBlobVersionedHash(0, blobVersionedHash); ScrollChainMockBlob(address(rollup)).setBlobVersionedHash(1, bytes32(0)); + hevm.startPrank(address(0x00a329c0648769A73afAc7F9381E08FB43dBEA72)); rollup.commitAndFinalizeBatch( 7, keccak256(headers[12]), @@ -680,6 +384,7 @@ contract ScrollChainTest is DSTestPlus { zkProof: new bytes(0) }) ); + hevm.stopPrank(); (uint256 lastCommittedBatchIndex, uint256 lastFinalizedBatchIndex, uint256 lastFinalizeTimestamp, , ) = rollup .miscData(); assertEq(lastCommittedBatchIndex, 13); @@ -693,22 +398,19 @@ contract ScrollChainTest is DSTestPlus { // revert when do commit hevm.startPrank(address(0)); hevm.expectRevert(ScrollChain.ErrorInEnforcedBatchMode.selector); - rollup.commitBatchWithBlobProof(0, new bytes(0), new bytes[](0), new bytes(0), new bytes(0)); - hevm.expectRevert(ScrollChain.ErrorInEnforcedBatchMode.selector); rollup.commitBatches(0, bytes32(0), bytes32(0)); hevm.stopPrank(); // revert when do finalize hevm.startPrank(address(0)); hevm.expectRevert(ScrollChain.ErrorInEnforcedBatchMode.selector); - rollup.finalizeBundleWithProof(new bytes(0), bytes32(0), bytes32(0), new bytes(0)); - hevm.expectRevert(ScrollChain.ErrorInEnforcedBatchMode.selector); rollup.finalizeBundlePostEuclidV2(new bytes(0), 0, bytes32(0), bytes32(0), new bytes(0)); hevm.stopPrank(); // succeed to call commitAndFinalizeBatch 14, no need to warp time ScrollChainMockBlob(address(rollup)).setBlobVersionedHash(0, blobVersionedHash); ScrollChainMockBlob(address(rollup)).setBlobVersionedHash(1, bytes32(0)); + hevm.startPrank(address(0x00a329c0648769A73afAc7F9381E08FB43dBEA72)); rollup.commitAndFinalizeBatch( 7, keccak256(headers[13]), @@ -720,6 +422,7 @@ contract ScrollChainTest is DSTestPlus { zkProof: new bytes(0) }) ); + hevm.stopPrank(); (lastCommittedBatchIndex, lastFinalizedBatchIndex, lastFinalizeTimestamp, , ) = rollup.miscData(); assertEq(lastCommittedBatchIndex, 14); assertEq(lastFinalizedBatchIndex, 14); @@ -739,6 +442,7 @@ contract ScrollChainTest is DSTestPlus { // not in enforced mode hevm.expectRevert(ScrollChain.ErrorNotInEnforcedBatchMode.selector); + hevm.startPrank(address(0x00a329c0648769A73afAc7F9381E08FB43dBEA72)); rollup.commitAndFinalizeBatch( 7, keccak256(headers[13]), @@ -750,28 +454,33 @@ contract ScrollChainTest is DSTestPlus { zkProof: new bytes(0) }) ); + hevm.stopPrank(); } function testCommitAndFinalizeBatchByExpiredBatch() external { hevm.warp(100); + messageQueueV2.initialize(); + rollup.initializeV2(); + // import 10 L1 messages into message queue V2 + for (uint256 i = 0; i < 10; i++) { + messageQueueV2.appendCrossDomainMessage(address(this), 1000000, new bytes(0)); + } // 0: genesis // 1-10: v6 // 11-20: v7 bytes[] memory headers = new bytes[](21); { - bytes[] memory headersV6 = _prepareBatchesV3Codec(6); + bytes[] memory headersV7 = _prepareBatchesV7Codec(7); for (uint256 i = 0; i <= 10; ++i) { - headers[i] = headersV6[i]; + headers[i] = headersV7[i]; } } - messageQueueV2.initialize(); - rollup.initializeV2(); for (uint256 i = 11; i < 21; ++i) { headers[i] = _commitBatchV7Codec(7, headers[i - 1]); } // finalize all v6 batches hevm.startPrank(address(0)); - rollup.finalizeBundleWithProof(headers[10], keccak256("x10"), keccak256("y10"), new bytes(0)); + rollup.finalizeBundlePostEuclidV2(headers[10], 10, keccak256("x10"), keccak256("y10"), new bytes(0)); hevm.stopPrank(); // finalize two v7 batches hevm.startPrank(address(0)); @@ -780,6 +489,7 @@ contract ScrollChainTest is DSTestPlus { // revert when ErrorNotInEnforcedBatchMode hevm.expectRevert(ScrollChain.ErrorNotInEnforcedBatchMode.selector); + hevm.startPrank(address(0x00a329c0648769A73afAc7F9381E08FB43dBEA72)); rollup.commitAndFinalizeBatch( 7, bytes32(0), @@ -791,6 +501,7 @@ contract ScrollChainTest is DSTestPlus { zkProof: new bytes(0) }) ); + hevm.stopPrank(); system.updateEnforcedBatchParameters( SystemConfig.EnforcedBatchParameters({ @@ -803,6 +514,7 @@ contract ScrollChainTest is DSTestPlus { // succeed to call commitAndFinalizeBatch 13 ScrollChainMockBlob(address(rollup)).setBlobVersionedHash(0, blobVersionedHash); ScrollChainMockBlob(address(rollup)).setBlobVersionedHash(1, bytes32(0)); + hevm.startPrank(address(0x00a329c0648769A73afAc7F9381E08FB43dBEA72)); rollup.commitAndFinalizeBatch( 7, keccak256(headers[12]), @@ -814,6 +526,7 @@ contract ScrollChainTest is DSTestPlus { zkProof: new bytes(0) }) ); + hevm.stopPrank(); (uint256 lastCommittedBatchIndex, uint256 lastFinalizedBatchIndex, uint256 lastFinalizeTimestamp, , ) = rollup .miscData(); assertEq(lastCommittedBatchIndex, 13); @@ -826,25 +539,25 @@ contract ScrollChainTest is DSTestPlus { } function testRevertBatch() external { + messageQueueV2.initialize(); + rollup.initializeV2(); + // import 10 L1 messages into message queue V2 + for (uint256 i = 0; i < 10; i++) { + messageQueueV2.appendCrossDomainMessage(address(this), 1000000, new bytes(0)); + } // 0: genesis // 1-10: v6 // 11-20: v7 bytes[] memory headers = new bytes[](21); { - bytes[] memory headersV6 = _prepareBatchesV3Codec(6); + bytes[] memory headersV7 = _prepareBatchesV7Codec(7); for (uint256 i = 0; i <= 10; ++i) { - headers[i] = headersV6[i]; + headers[i] = headersV7[i]; } } - messageQueueV2.initialize(); - rollup.initializeV2(); for (uint256 i = 11; i < 21; ++i) { headers[i] = _commitBatchV7Codec(7, headers[i - 1]); } - // finalize all v6 batches - hevm.startPrank(address(0)); - rollup.finalizeBundleWithProof(headers[10], keccak256("x10"), keccak256("y10"), new bytes(0)); - hevm.stopPrank(); // finalize two v7 batches hevm.startPrank(address(0)); rollup.finalizeBundlePostEuclidV2(headers[12], 10, keccak256("x12"), keccak256("y12"), new bytes(0)); @@ -857,8 +570,8 @@ contract ScrollChainTest is DSTestPlus { hevm.stopPrank(); // revert ErrorIncorrectBatchVersion - hevm.expectRevert(ScrollChain.ErrorIncorrectBatchVersion.selector); - rollup.revertBatch(headers[10]); + // hevm.expectRevert(ScrollChain.ErrorIncorrectBatchVersion.selector); + // rollup.revertBatch(headers[10]); // revert ErrorRevertFinalizedBatch hevm.expectRevert(ScrollChain.ErrorRevertFinalizedBatch.selector); @@ -957,14 +670,6 @@ contract ScrollChainTest is DSTestPlus { hevm.startPrank(address(0)); hevm.expectRevert("Pausable: paused"); - rollup.commitBatchWithBlobProof(4, new bytes(0), new bytes[](0), new bytes(0), new bytes(0)); - hevm.expectRevert("Pausable: paused"); - rollup.commitBatchWithBlobProof(5, new bytes(0), new bytes[](0), new bytes(0), new bytes(0)); - hevm.expectRevert("Pausable: paused"); - rollup.commitBatchWithBlobProof(6, new bytes(0), new bytes[](0), new bytes(0), new bytes(0)); - hevm.expectRevert("Pausable: paused"); - rollup.finalizeBundleWithProof(new bytes(0), bytes32(0), bytes32(0), new bytes(0)); - hevm.expectRevert("Pausable: paused"); rollup.commitBatches(7, bytes32(0), bytes32(0)); hevm.expectRevert("Pausable: paused"); rollup.finalizeBundlePostEuclidV2(new bytes(0), 0, bytes32(0), bytes32(0), new bytes(0)); @@ -975,22 +680,6 @@ contract ScrollChainTest is DSTestPlus { assertBoolEq(false, rollup.paused()); } - function testUpdateMaxNumTxInChunk(uint256 _maxNumTxInChunk) external { - // set by non-owner, should revert - hevm.startPrank(address(1)); - hevm.expectRevert("Ownable: caller is not the owner"); - rollup.updateMaxNumTxInChunk(_maxNumTxInChunk); - hevm.stopPrank(); - - // change to random operator - hevm.expectEmit(false, false, false, true); - emit UpdateMaxNumTxInChunk(100, _maxNumTxInChunk); - - assertEq(rollup.maxNumTxInChunk(), 100); - rollup.updateMaxNumTxInChunk(_maxNumTxInChunk); - assertEq(rollup.maxNumTxInChunk(), _maxNumTxInChunk); - } - function testImportGenesisBlock() external { bytes memory batchHeader; @@ -1079,30 +768,6 @@ contract ScrollChainTest is DSTestPlus { ScrollChainMockBlob(address(rollup)).setBlobVersionedHash(0, blobVersionedHash); } - /// @dev Prepare 10 batches, each of the first 5 has 2 l1 messages, each of the second 5 has no l1 message. - function _prepareBatchesV3Codec(uint8 version) internal returns (bytes[] memory headers) { - // grant roles - rollup.addProver(address(0)); - rollup.addSequencer(address(0)); - _upgradeToMockBlob(); - - headers = new bytes[](11); - // import 10 L1 messages into message queue V1 - for (uint256 i = 0; i < 10; i++) { - messageQueueV1.appendCrossDomainMessage(address(this), 1000000, new bytes(0)); - } - // commit genesis batch - headers[0] = _commitGenesisBatch(); - // commit 5 batches, each has 2 l1 messages - for (uint256 i = 1; i <= 5; ++i) { - headers[i] = _commitBatchV3Codec(version, headers[i - 1], 2, 1); - } - // commit 5 batches, each has 0 l1 message - for (uint256 i = 6; i <= 10; ++i) { - headers[i] = _commitBatchV3Codec(version, headers[i - 1], 0, 1); - } - } - function _commitGenesisBatch() internal returns (bytes memory header) { header = new bytes(89); assembly { @@ -1112,117 +777,6 @@ contract ScrollChainTest is DSTestPlus { assertEq(rollup.committedBatches(0), keccak256(header)); } - function _constructBatchStructCodecV3( - uint8 version, - bytes memory parentHeader, - uint256 numL1Message, - uint256 numL2Transaction - ) - internal - view - returns ( - bytes memory bitmap, - bytes[] memory chunks, - bytes memory blobDataProof, - uint256 index, - uint256 totalL1MessagePopped, - bytes memory header - ) - { - uint256 batchPtr; - assembly { - batchPtr := add(parentHeader, 0x20) - } - index = BatchHeaderV0Codec.getBatchIndex(batchPtr) + 1; - totalL1MessagePopped = BatchHeaderV0Codec.getTotalL1MessagePopped(batchPtr) + numL1Message; - bytes32 parentHash = keccak256(parentHeader); - blobDataProof = hex"2c9d777660f14ad49803a6442935c0d24a0d83551de5995890bf70a17d24e68753ab0fe6807c7081f0885fe7da741554d658a03730b1fa006f8319f8b993bcb0a5a0c9e8a145c5ef6e415c245690effa2914ec9393f58a7251d30c0657da1453d9ad906eae8b97dd60c9a216f81b4df7af34d01e214e1ec5865f0133ecc16d7459e49dab66087340677751e82097fbdd20551d66076f425775d1758a9dfd186b"; - bytes32[] memory hashes = new bytes32[](numL1Message); - for (uint256 i = 0; i < numL1Message; ++i) { - hashes[i] = messageQueueV1.getCrossDomainMessage(BatchHeaderV0Codec.getTotalL1MessagePopped(batchPtr) + i); - } - // commit batch, one chunk with one block, 1 + numL1Message tx, numL1Message L1 message - // payload for data hash of chunk0 - // hex(index) // block number - // hex(index) // timestamp - // 0000000000000000000000000000000000000000000000000000000000000000 // baseFee - // 0000000000000000 // gasLimit - // hex(numL2Transaction + numL1Message) // numTransactions - // ... // l1 messages - // data hash for chunk0 - // keccak256(chunk0) - // data hash for all chunks - // keccak256(keccak256(chunk0)) - // => payload for batch header - // 03 // version - // hex(index) // batchIndex - // hex(numL1Message) // l1MessagePopped - // hex(totalL1MessagePopped) // totalL1MessagePopped - // keccak256(keccak256(chunk0)) // dataHash - // 013590dc3544d56629ba81bb14d4d31248f825001653aa575eb8e3a719046757 // blobVersionedHash - // keccak256(parentHeader) // parentBatchHash - // hex(index) // lastBlockTimestamp - // 2c9d777660f14ad49803a6442935c0d24a0d83551de5995890bf70a17d24e687 // blobDataProof - // 53ab0fe6807c7081f0885fe7da741554d658a03730b1fa006f8319f8b993bcb0 // blobDataProof - if (numL1Message > 0) bitmap = new bytes(32); - chunks = new bytes[](1); - bytes memory chunk0; - chunk0 = new bytes(1 + 60); - assembly { - mstore(add(chunk0, 0x20), shl(248, 1)) // numBlocks = 1 - mstore(add(chunk0, add(0x21, 8)), shl(192, index)) // timestamp = 0x123 - mstore(add(chunk0, add(0x21, 56)), shl(240, add(numL1Message, numL2Transaction))) // numTransactions = numL2Transaction + numL1Message - mstore(add(chunk0, add(0x21, 58)), shl(240, numL1Message)) // numL1Messages - } - chunks[0] = chunk0; - bytes memory chunkData = new bytes(58 + numL1Message * 32); - assembly { - mcopy(add(chunkData, 0x20), add(chunk0, 0x21), 58) - mcopy(add(chunkData, 0x5a), add(hashes, 0x20), mul(32, mload(hashes))) - } - bytes32 dataHash = keccak256(abi.encode(keccak256(chunkData))); - header = new bytes(193); - assembly { - mstore8(add(header, 0x20), version) // version - mstore(add(header, add(0x20, 1)), shl(192, index)) // batchIndex - mstore(add(header, add(0x20, 9)), shl(192, numL1Message)) // l1MessagePopped - mstore(add(header, add(0x20, 17)), shl(192, totalL1MessagePopped)) // totalL1MessagePopped - mstore(add(header, add(0x20, 25)), dataHash) // dataHash - mstore(add(header, add(0x20, 57)), 0x013590dc3544d56629ba81bb14d4d31248f825001653aa575eb8e3a719046757) // blobVersionedHash - mstore(add(header, add(0x20, 89)), parentHash) // parentBatchHash - mstore(add(header, add(0x20, 121)), shl(192, index)) // lastBlockTimestamp - mcopy(add(header, add(0x20, 129)), add(blobDataProof, 0x20), 64) // blobDataProof - } - } - - function _commitBatchV3Codec( - uint8 version, - bytes memory parentHeader, - uint256 numL1Message, - uint256 numL2Transaction - ) internal returns (bytes memory) { - ( - bytes memory bitmap, - bytes[] memory chunks, - bytes memory blobDataProof, - uint256 index, - uint256 totalL1MessagePopped, - bytes memory header - ) = _constructBatchStructCodecV3(version, parentHeader, numL1Message, numL2Transaction); - hevm.startPrank(address(0)); - if (numL1Message > 0) { - hevm.expectEmit(false, false, false, true); - emit DequeueTransaction(totalL1MessagePopped - numL1Message, numL1Message, 0); - } - hevm.expectEmit(true, true, false, true); - emit CommitBatch(index, keccak256(header)); - rollup.commitBatchWithBlobProof(version, parentHeader, chunks, bitmap, blobDataProof); - hevm.stopPrank(); - assertEq(rollup.committedBatches(index), keccak256(header)); - assertEq(messageQueueV1.pendingQueueIndex(), totalL1MessagePopped); - return header; - } - function _constructBatchStructCodecV7(uint8 version, bytes memory parentHeader) internal pure @@ -1253,4 +807,24 @@ contract ScrollChainTest is DSTestPlus { assertEq(rollup.committedBatches(index), keccak256(header)); return header; } + + /// @dev Prepare 10 batches, each of the first 5 has 2 l1 messages, each of the second 5 has no l1 message. + function _prepareBatchesV7Codec(uint8 version) internal returns (bytes[] memory headers) { + // grant roles + rollup.addProver(address(0)); + rollup.addSequencer(address(0)); + _upgradeToMockBlob(); + + headers = new bytes[](11); + // commit genesis batch + headers[0] = _commitGenesisBatch(); + // commit 5 batches, each has 2 l1 messages + for (uint256 i = 1; i <= 5; ++i) { + headers[i] = _commitBatchV7Codec(version, headers[i - 1]); + } + // commit 5 batches, each has 0 l1 message + for (uint256 i = 6; i <= 10; ++i) { + headers[i] = _commitBatchV7Codec(version, headers[i - 1]); + } + } } diff --git a/src/test/TokenRateLimiter.t.sol b/src/test/TokenRateLimiter.t.sol deleted file mode 100644 index 5eb44b14..00000000 --- a/src/test/TokenRateLimiter.t.sol +++ /dev/null @@ -1,82 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity =0.8.24; - -import {DSTestPlus} from "solmate/test/utils/DSTestPlus.sol"; - -import {TokenRateLimiter} from "../rate-limiter/TokenRateLimiter.sol"; -import {ITokenRateLimiter} from "../rate-limiter/ITokenRateLimiter.sol"; - -contract TokenRateLimiterTest is DSTestPlus { - event UpdateTotalLimit(address indexed token, uint256 oldTotalLimit, uint256 newTotalLimit); - - TokenRateLimiter private limiter; - - function setUp() public { - hevm.warp(86400); - limiter = new TokenRateLimiter(86400); - } - - function testUpdateTotalLimit(address _token, uint104 _newTotalLimit) external { - hevm.assume(_newTotalLimit > 0); - - // not admin, revert - hevm.startPrank(address(1)); - hevm.expectRevert( - "AccessControl: account 0x0000000000000000000000000000000000000001 is missing role 0x0000000000000000000000000000000000000000000000000000000000000000" - ); - limiter.updateTotalLimit(_token, _newTotalLimit); - hevm.stopPrank(); - - // zero revert - hevm.expectRevert(abi.encodeWithSelector(ITokenRateLimiter.TotalLimitIsZero.selector, _token)); - limiter.updateTotalLimit(_token, 0); - - // success - hevm.expectEmit(true, false, false, true); - emit UpdateTotalLimit(_token, 0 ether, _newTotalLimit); - limiter.updateTotalLimit(_token, _newTotalLimit); - (, uint104 _totalLimit, ) = limiter.currentPeriod(_token); - assertEq(_totalLimit, _newTotalLimit); - } - - function testAddUsedAmount(address _token) external { - // non-spender, revert - hevm.startPrank(address(1)); - hevm.expectRevert( - "AccessControl: account 0x0000000000000000000000000000000000000001 is missing role 0x267f05081a073059ae452e6ac77ec189636e43e41051d4c3ec760734b3d173cb" - ); - limiter.addUsedAmount(_token, 0); - hevm.stopPrank(); - - limiter.grantRole(bytes32(0x267f05081a073059ae452e6ac77ec189636e43e41051d4c3ec760734b3d173cb), address(this)); - limiter.updateTotalLimit(_token, 100 ether); - - // exceed total limit on first call - hevm.expectRevert(abi.encodeWithSelector(ITokenRateLimiter.ExceedTotalLimit.selector, _token)); - limiter.addUsedAmount(_token, 100 ether + 1); - _checkTotalCurrentPeriodAmountAmount(_token, 0); - - // exceed total limit on second call - limiter.addUsedAmount(_token, 50 ether); - _checkTotalCurrentPeriodAmountAmount(_token, 50 ether); - hevm.expectRevert(abi.encodeWithSelector(ITokenRateLimiter.ExceedTotalLimit.selector, _token)); - limiter.addUsedAmount(_token, 50 ether + 1); - _checkTotalCurrentPeriodAmountAmount(_token, 50 ether); - - // one period passed - hevm.warp(86400 * 2); - limiter.addUsedAmount(_token, 1 ether); - _checkTotalCurrentPeriodAmountAmount(_token, 1 ether); - - // exceed - hevm.expectRevert(abi.encodeWithSelector(ITokenRateLimiter.ExceedTotalLimit.selector, _token)); - limiter.addUsedAmount(_token, 99 ether + 1); - _checkTotalCurrentPeriodAmountAmount(_token, 1 ether); - } - - function _checkTotalCurrentPeriodAmountAmount(address token, uint256 expected) internal { - (, , uint256 totalAmount) = limiter.currentPeriod(token); - assertEq(totalAmount, expected); - } -} diff --git a/src/test/dogeos/L2DogeOsMessenger.t.sol b/src/test/dogeos/L2DogeOsMessenger.t.sol index 4a941228..fbd6a37d 100644 --- a/src/test/dogeos/L2DogeOsMessenger.t.sol +++ b/src/test/dogeos/L2DogeOsMessenger.t.sol @@ -41,7 +41,7 @@ contract L2DogeOsMessengerTest is Test { function setUp() public { // Deploy L1 contracts - _l1Messenger = new L1ScrollMessenger(address(1), address(1), address(1), address(1)); + _l1Messenger = new L1ScrollMessenger(address(1), address(1), address(1), address(1), address(1)); // Deploy L2 contracts _l2MessageQueue = new L2MessageQueue(address(this)); // Needs owner diff --git a/src/test/lido/L1LidoGateway.t.sol b/src/test/lido/L1LidoGateway.t.sol index 5d6b4951..b94ac352 100644 --- a/src/test/lido/L1LidoGateway.t.sol +++ b/src/test/lido/L1LidoGateway.t.sol @@ -330,86 +330,6 @@ contract L1LidoGatewayTest is L1GatewayTestBase { _depositERC20(true, 2, amount, recipient, dataToCall, gasLimit, feePerGas); } - function testDropMessage(uint256 amount, address recipient) public { - hevm.assume(recipient != address(0)); - - amount = bound(amount, 1, l1Token.balanceOf(address(this))); - bytes memory message = abi.encodeCall( - IL2ERC20Gateway.finalizeDepositERC20, - (address(l1Token), address(l2Token), address(this), recipient, amount, new bytes(0)) - ); - gateway.depositERC20AndCall(address(l1Token), recipient, amount, new bytes(0), defaultGasLimit); - - MockScrollMessenger mockMessenger = new MockScrollMessenger(); - MockL1LidoGateway mockGateway = _deployGateway(address(mockMessenger)); - mockGateway.initialize(address(counterpartGateway), address(router), address(mockMessenger)); - mockGateway.initializeV2(address(0), address(0), address(0), address(0)); - - // revert caller is not messenger - hevm.expectRevert(ErrorCallerIsNotMessenger.selector); - mockGateway.onDropMessage(new bytes(0)); - - // revert not in drop context - hevm.expectRevert(ErrorNotInDropMessageContext.selector); - mockMessenger.callTarget(address(mockGateway), abi.encodeCall(mockGateway.onDropMessage, (new bytes(0)))); - - // revert when reentrant - mockMessenger.setXDomainMessageSender(ScrollConstants.DROP_XDOMAIN_MESSAGE_SENDER); - hevm.expectRevert("ReentrancyGuard: reentrant call"); - mockGateway.reentrantCall( - address(mockMessenger), - abi.encodeCall( - mockMessenger.callTarget, - (address(mockGateway), abi.encodeCall(mockGateway.onDropMessage, (message))) - ) - ); - - // revert when invalid selector - hevm.expectRevert("invalid selector"); - mockMessenger.callTarget(address(mockGateway), abi.encodeCall(mockGateway.onDropMessage, (new bytes(4)))); - - // revert when l1 token not supported - hevm.expectRevert(ErrorUnsupportedL1Token.selector); - mockMessenger.callTarget( - address(mockGateway), - abi.encodeCall( - mockGateway.onDropMessage, - ( - abi.encodeCall( - IL2ERC20Gateway.finalizeDepositERC20, - (address(l2Token), address(l2Token), address(this), recipient, amount, new bytes(0)) - ) - ) - ) - ); - - // revert when nonzero msg.value - hevm.expectRevert(ErrorNonZeroMsgValue.selector); - mockMessenger.callTarget{value: 1}( - address(mockGateway), - abi.encodeWithSelector(mockGateway.onDropMessage.selector, message) - ); - - // succeed on drop - // skip message 0 - hevm.startPrank(address(rollup)); - messageQueueV1.popCrossDomainMessage(0, 1, 0x1); - messageQueueV1.finalizePoppedCrossDomainMessage(1); - assertEq(messageQueueV1.nextUnfinalizedQueueIndex(), 1); - assertEq(messageQueueV1.pendingQueueIndex(), 1); - hevm.stopPrank(); - - // should emit RefundERC20 - hevm.expectEmit(true, true, false, true); - emit RefundERC20(address(l1Token), address(this), amount); - - uint256 balance = l1Token.balanceOf(address(this)); - uint256 gatewayBalance = l1Token.balanceOf(address(gateway)); - l1Messenger.dropMessage(address(gateway), address(counterpartGateway), 0, 0, message); - assertEq(gatewayBalance - amount, l1Token.balanceOf(address(gateway))); - assertEq(balance + amount, l1Token.balanceOf(address(this))); - } - function testFinalizeWithdrawERC20( address sender, uint256 amount, diff --git a/src/test/misc/PauseController.t.sol b/src/test/misc/PauseController.t.sol new file mode 100644 index 00000000..e7a018b6 --- /dev/null +++ b/src/test/misc/PauseController.t.sol @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {Test} from "forge-std/Test.sol"; + +import {ProxyAdmin} from "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import {ITransparentUpgradeableProxy, TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; + +import {PauseController} from "../../misc/PauseController.sol"; +import {IPausable} from "../../misc/IPausable.sol"; +import {ScrollOwner} from "../../misc/ScrollOwner.sol"; + +contract MockPausable is IPausable { + bool private _paused; + + function setPause(bool _status) external { + _paused = _status; + } + + function paused() external view returns (bool) { + return _paused; + } +} + +contract PauseControllerTest is Test { + event Pause(address indexed component); + event Unpause(address indexed component); + event ResetPauseCooldownPeriod(address indexed component); + event UpdatePauseCooldownPeriod(uint256 oldPauseCooldownPeriod, uint256 newPauseCooldownPeriod); + + uint256 public constant PAUSE_COOLDOWN_PERIOD = 1 days; + + ProxyAdmin public admin; + PauseController public pauseController; + MockPausable public mockPausable; + ScrollOwner public scrollOwner; + address public owner; + + function setUp() public { + owner = makeAddr("owner"); + vm.startPrank(owner); + + admin = new ProxyAdmin(); + scrollOwner = new ScrollOwner(); + PauseController impl = new PauseController(address(scrollOwner)); + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(impl), + address(admin), + abi.encodeCall(PauseController.initialize, (PAUSE_COOLDOWN_PERIOD)) + ); + pauseController = PauseController(address(proxy)); + mockPausable = new MockPausable(); + + bytes4[] memory selectors = new bytes4[](1); + selectors[0] = IPausable.setPause.selector; + scrollOwner.updateAccess(address(mockPausable), selectors, pauseController.PAUSE_CONTROLLER_ROLE(), true); + scrollOwner.grantRole(pauseController.PAUSE_CONTROLLER_ROLE(), address(pauseController)); + + vm.stopPrank(); + + vm.warp(1e9); + } + + function test_Pause() public { + vm.startPrank(owner); + + vm.expectEmit(true, false, false, true); + emit Pause(address(mockPausable)); + pauseController.pause(mockPausable); + assertTrue(mockPausable.paused()); + + vm.stopPrank(); + } + + function test_Pause_AlreadyPaused() public { + vm.startPrank(owner); + + pauseController.pause(mockPausable); + + vm.expectRevert(PauseController.ErrorComponentAlreadyPaused.selector); + pauseController.pause(mockPausable); + + vm.stopPrank(); + } + + function test_Pause_CooldownPeriodNotPassed() public { + vm.startPrank(owner); + + pauseController.pause(mockPausable); + pauseController.unpause(mockPausable); + uint256 lastUnpauseTime = pauseController.getLastUnpauseTime(mockPausable); + assertEq(lastUnpauseTime, block.timestamp); + + vm.warp(lastUnpauseTime + PAUSE_COOLDOWN_PERIOD - 1); + vm.expectRevert(PauseController.ErrorCooldownPeriodNotPassed.selector); + pauseController.pause(mockPausable); + assertFalse(mockPausable.paused()); + + vm.warp(lastUnpauseTime + PAUSE_COOLDOWN_PERIOD); + vm.expectRevert(PauseController.ErrorCooldownPeriodNotPassed.selector); + pauseController.pause(mockPausable); + assertFalse(mockPausable.paused()); + + vm.warp(lastUnpauseTime + PAUSE_COOLDOWN_PERIOD + 1); + pauseController.pause(mockPausable); + assertTrue(mockPausable.paused()); + + vm.stopPrank(); + } + + function test_Unpause() public { + vm.startPrank(owner); + + pauseController.pause(mockPausable); + + assertEq(pauseController.getLastUnpauseTime(mockPausable), 0); + vm.expectEmit(true, false, false, true); + emit Unpause(address(mockPausable)); + pauseController.unpause(mockPausable); + assertEq(pauseController.getLastUnpauseTime(mockPausable), block.timestamp); + assertFalse(mockPausable.paused()); + + // cannot pause before cooldown period + vm.expectRevert(PauseController.ErrorCooldownPeriodNotPassed.selector); + pauseController.pause(mockPausable); + + // reset pause cooldown period + vm.expectEmit(true, false, false, true); + emit ResetPauseCooldownPeriod(address(mockPausable)); + pauseController.resetPauseCooldownPeriod(mockPausable); + assertEq(pauseController.getLastUnpauseTime(mockPausable), 0); + + // can pause after reset + assertFalse(mockPausable.paused()); + pauseController.pause(mockPausable); + assertTrue(mockPausable.paused()); + + vm.stopPrank(); + } + + function test_Unpause_NotPaused() public { + vm.startPrank(owner); + + vm.expectRevert(PauseController.ErrorComponentNotPaused.selector); + pauseController.unpause(mockPausable); + + vm.stopPrank(); + } + + function test_UpdatePauseCooldownPeriod() public { + vm.startPrank(owner); + + uint256 newCooldownPeriod = 2 days; + + vm.expectEmit(false, false, false, true); + emit UpdatePauseCooldownPeriod(PAUSE_COOLDOWN_PERIOD, newCooldownPeriod); + pauseController.updatePauseCooldownPeriod(newCooldownPeriod); + + assertEq(pauseController.pauseCooldownPeriod(), newCooldownPeriod); + + vm.stopPrank(); + } + + function test_UpdatePauseCooldownPeriod_NotOwner() public { + address notOwner = makeAddr("notOwner"); + vm.startPrank(notOwner); + + vm.expectRevert("Ownable: caller is not the owner"); + pauseController.updatePauseCooldownPeriod(2 days); + + vm.stopPrank(); + } + + function test_Pause_NotOwner() public { + address notOwner = makeAddr("notOwner"); + vm.startPrank(notOwner); + + vm.expectRevert("Ownable: caller is not the owner"); + pauseController.pause(mockPausable); + + vm.stopPrank(); + } + + function test_Unpause_NotOwner() public { + address notOwner = makeAddr("notOwner"); + vm.startPrank(notOwner); + + vm.expectRevert("Ownable: caller is not the owner"); + pauseController.unpause(mockPausable); + + vm.stopPrank(); + } +} diff --git a/yarn.lock b/yarn.lock index d64785ef..976e6b69 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2438,9 +2438,9 @@ balanced-match@^1.0.0: integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== base-x@^3.0.2, base-x@^3.0.8: - version "3.0.9" - resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" - integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== + version "3.0.11" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.11.tgz#40d80e2a1aeacba29792ccc6c5354806421287ff" + integrity sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA== dependencies: safe-buffer "^5.0.1"