├── .editorconfig ├── .eslintrc ├── .githooks ├── pre-commit └── pre-push ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── SECURITY.md ├── pull_request_template.md └── workflows │ ├── buld-release.yaml │ ├── l1-contracts-ci.yaml │ ├── l2-contracts-ci.yaml │ ├── label-external-contributions.yaml │ ├── nodejs-license.yaml │ ├── secrets_scanner.yaml │ └── system-contracts-ci.yaml ├── .gitignore ├── .gitmodules ├── .markdownlintignore ├── .markdownlintrc ├── .nvmrc ├── .prettierignore ├── .prettierrc.js ├── .solhint.json ├── .solhintignore ├── CODEOWNERS ├── CONTRIBUTING.md ├── LICENSE-MIT ├── README.md ├── SECURITY.md ├── SystemConfig.json ├── docs └── Overview.md ├── eraLogo.svg ├── l1-contracts ├── .env ├── contracts │ ├── bridge │ │ ├── L1ERC20Bridge.sol │ │ ├── L1WethBridge.sol │ │ ├── interfaces │ │ │ ├── IL1Bridge.sol │ │ │ ├── IL1BridgeLegacy.sol │ │ │ ├── IL2Bridge.sol │ │ │ ├── IL2ERC20Bridge.sol │ │ │ ├── IL2WethBridge.sol │ │ │ └── IWETH9.sol │ │ └── libraries │ │ │ └── BridgeInitializationHelper.sol │ ├── common │ │ ├── Dependencies.sol │ │ ├── L2ContractAddresses.sol │ │ ├── ReentrancyGuard.sol │ │ ├── interfaces │ │ │ └── IL2ContractDeployer.sol │ │ └── libraries │ │ │ ├── L2ContractHelper.sol │ │ │ ├── UncheckedMath.sol │ │ │ └── UnsafeBytes.sol │ ├── dev-contracts │ │ ├── ConstructorForwarder.sol │ │ ├── EventOnFallback.sol │ │ ├── Forwarder.sol │ │ ├── Multicall.sol │ │ ├── Multicall3.sol │ │ ├── ReturnSomething.sol │ │ ├── RevertFallback.sol │ │ ├── RevertReceiveAccount.sol │ │ ├── RevertTransferERC20.sol │ │ ├── SingletonFactory.sol │ │ ├── TestnetERC20Token.sol │ │ ├── WETH9.sol │ │ └── test │ │ │ ├── AdminFacetTest.sol │ │ │ ├── CustomUpgradeTest.sol │ │ │ ├── DiamondCutTestContract.sol │ │ │ ├── DiamondProxyTest.sol │ │ │ ├── DummyERC20BytesTransferReturnValue.sol │ │ │ ├── DummyERC20NoTransferReturnValue.sol │ │ │ ├── DummyExecutor.sol │ │ │ ├── ExecutorProvingTest.sol │ │ │ ├── L1ERC20BridgeTest.sol │ │ │ ├── MailboxFacetTest.sol │ │ │ ├── MerkleTest.sol │ │ │ ├── MockExecutor.sol │ │ │ ├── PriorityQueueTest.sol │ │ │ ├── ReenterGovernance.sol │ │ │ ├── UnsafeBytesTest.sol │ │ │ ├── VerifierRecursiveTest.sol │ │ │ └── VerifierTest.sol │ ├── governance │ │ ├── Governance.sol │ │ └── IGovernance.sol │ ├── upgrades │ │ ├── BaseZkSyncUpgrade.sol │ │ ├── DefaultUpgrade.sol │ │ └── Upgrade_v1_4_1.sol │ ├── vendor │ │ └── AddressAliasHelper.sol │ └── zksync │ │ ├── Config.sol │ │ ├── DiamondInit.sol │ │ ├── DiamondProxy.sol │ │ ├── Storage.sol │ │ ├── ValidatorTimelock.sol │ │ ├── Verifier.sol │ │ ├── facets │ │ ├── Admin.sol │ │ ├── Base.sol │ │ ├── Executor.sol │ │ ├── Getters.sol │ │ └── Mailbox.sol │ │ ├── interfaces │ │ ├── IAdmin.sol │ │ ├── IBase.sol │ │ ├── IExecutor.sol │ │ ├── IGetters.sol │ │ ├── IL2Gateway.sol │ │ ├── ILegacyGetters.sol │ │ ├── IMailbox.sol │ │ ├── IVerifier.sol │ │ ├── IZkLink.sol │ │ └── IZkSync.sol │ │ ├── libraries │ │ ├── Diamond.sol │ │ ├── LibMap.sol │ │ ├── Merkle.sol │ │ ├── PriorityQueue.sol │ │ └── TransactionValidator.sol │ │ └── upgrade-initializers │ │ ├── DIamondUpgradeInit2.sol │ │ ├── DiamondUpgradeInit1.sol │ │ ├── DiamondUpgradeInit3.sol │ │ ├── DiamondUpgradeInit4.sol │ │ ├── DiamondUpgradeInit5.sol │ │ └── DiamondUpgradeInit6.sol ├── foundry.toml ├── hardhat.config.ts ├── package.json ├── remappings.txt ├── scripts │ ├── deploy-create2.ts │ ├── deploy-erc20.ts │ ├── deploy-l1-erc20-bridge-impl.ts │ ├── deploy-testkit.ts │ ├── deploy-testnet-token.ts │ ├── deploy-weth-bridges.ts │ ├── deploy-withdrawal-helpers.ts │ ├── deploy.ts │ ├── display-governance.ts │ ├── initialize-bridges.ts │ ├── initialize-governance.ts │ ├── initialize-l2-weth-token.ts │ ├── initialize-secondary-chain-bridges.ts │ ├── initialize-validator.ts │ ├── initialize-weth-bridges.ts │ ├── migrate-governance.ts │ ├── read-variable.ts │ ├── replace-tx.ts │ ├── revert-reason.ts │ ├── set-admin.ts │ ├── set-gateway.ts │ ├── token-info.ts │ ├── upgrades │ │ ├── upgrade-1.ts │ │ ├── upgrade-2.ts │ │ ├── upgrade-3.ts │ │ ├── upgrade-4.ts │ │ ├── upgrade-5.ts │ │ └── upgrade-6.ts │ ├── utils.ts │ ├── verify-governance.ts │ └── verify.ts ├── src.ts │ ├── deploy-utils.ts │ ├── deploy.ts │ └── diamondCut.ts ├── test │ ├── foundry │ │ └── unit │ │ │ └── concrete │ │ │ ├── Admin │ │ │ ├── Authorization.t.sol │ │ │ └── _Admin_Shared.t.sol │ │ │ ├── Bridge │ │ │ └── L1WethBridge │ │ │ │ ├── ClaimFailedDeposit.t.sol │ │ │ │ ├── Deposit.t.sol │ │ │ │ ├── FinalizeWithdrawal.t.sol │ │ │ │ ├── L2TokenAddress.t.sol │ │ │ │ ├── Receive.t.sol │ │ │ │ └── _L1WethBridge_Shared.t.sol │ │ │ ├── DiamondCut │ │ │ ├── FacetCut.t.sol │ │ │ ├── Initialization.t.sol │ │ │ ├── UpgradeLogic.t.sol │ │ │ └── _DiamondCut_Shared.t.sol │ │ │ ├── Executor │ │ │ ├── Authorization.t.sol │ │ │ ├── Committing.t.sol │ │ │ ├── Executing.t.sol │ │ │ ├── Proving.t.sol │ │ │ ├── Reverting.t.sol │ │ │ └── _Executor_Shared.t.sol │ │ │ ├── Governance │ │ │ ├── Authorization.t.sol │ │ │ ├── Executing.t.sol │ │ │ ├── Fallback.t.sol │ │ │ ├── OperationStatus.t.sol │ │ │ ├── Reentrancy.t.sol │ │ │ ├── SelfUpgrades.t.sol │ │ │ └── _Governance_Shared.t.sol │ │ │ ├── Merkle │ │ │ ├── Merkle.t.sol │ │ │ └── MerkleTreeNoSort.sol │ │ │ ├── PriorityQueue │ │ │ ├── OnEmptyQueue.sol │ │ │ ├── PopOperations.sol │ │ │ ├── PushOperations.sol │ │ │ └── _PriorityQueue_Shared.t.sol │ │ │ ├── TransactionValidator │ │ │ ├── ValidateL1L2Tx.t.sol │ │ │ ├── ValidateUpgradeTransaction.t.sol │ │ │ └── _TransactionValidator_Shared.t.sol │ │ │ ├── UncheckedMath │ │ │ ├── UncheckedAdd.t.sol │ │ │ ├── UncheckedInc.t.sol │ │ │ └── _UncheckedMath_Shared.t.sol │ │ │ ├── UnsafeBytes │ │ │ └── UnsafeBytes.t.sol │ │ │ ├── Utils │ │ │ ├── Utils.sol │ │ │ └── Utils.t.sol │ │ │ └── Verifier │ │ │ ├── Verifier.t.sol │ │ │ └── VerifierRecursive.t.sol │ └── unit_tests │ │ ├── erc20-bridge-upgrade.fork.ts │ │ ├── executor_proof.spec.ts │ │ ├── governance_test.spec.ts │ │ ├── l1_erc20_bridge_test.spec.ts │ │ ├── l1_weth_bridge_test.spec.ts │ │ ├── l2-upgrade.test.spec.ts │ │ ├── mailbox_test.spec.ts │ │ ├── proxy_test.spec.ts │ │ ├── utils.ts │ │ ├── validator_timelock_test.spec.ts │ │ └── zksync-upgrade.fork.ts ├── tsconfig.json └── upgrade-system │ ├── facets.ts │ ├── index.ts │ ├── utils.ts │ └── verifier.ts ├── l2-contracts ├── .env ├── contracts │ ├── Dependencies.sol │ ├── ForceDeployUpgrader.sol │ ├── L2ContractHelper.sol │ ├── SystemContractsCaller.sol │ ├── TestnetPaymaster.sol │ ├── bridge │ │ ├── L2ERC20Bridge.sol │ │ ├── L2StandardERC20.sol │ │ ├── L2Weth.sol │ │ ├── L2WethBridge.sol │ │ └── interfaces │ │ │ ├── IL1Bridge.sol │ │ │ ├── IL2Bridge.sol │ │ │ ├── IL2StandardToken.sol │ │ │ ├── IL2Weth.sol │ │ │ └── IMergeTokenPortal.sol │ ├── interfaces │ │ ├── IPaymaster.sol │ │ └── IPaymasterFlow.sol │ └── vendor │ │ └── AddressAliasHelper.sol ├── hardhat.config.ts ├── package.json ├── src │ ├── deployForceDeployUpgrader.ts │ ├── deployL2Weth.ts │ ├── deployTestnetPaymaster.ts │ ├── execute-governance-tx.ts │ ├── publish-bridge-preimages.ts │ ├── upgradeBridgeImpl.ts │ ├── utils.ts │ ├── verify-l2-erc20-bridge.ts │ └── verify.ts ├── test │ ├── erc20.test.ts │ └── weth.test.ts └── tsconfig.json ├── package.json ├── system-contracts ├── README.md ├── SystemContractsHashes.json ├── bootloader │ ├── bootloader.yul │ ├── test_infra │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── rust-toolchain │ │ └── src │ │ │ ├── hook.rs │ │ │ ├── main.rs │ │ │ ├── test_count_tracer.rs │ │ │ ├── test_transactions │ │ │ ├── 0.json │ │ │ └── README.md │ │ │ └── tracer.rs │ └── tests │ │ ├── README.md │ │ ├── bootloader │ │ └── bootloader_test.yul │ │ ├── dummy.yul │ │ ├── transfer_test.yul │ │ └── utils │ │ └── test_utils.yul ├── contracts │ ├── AccountCodeStorage.sol │ ├── BootloaderUtilities.sol │ ├── ComplexUpgrader.sol │ ├── Compressor.sol │ ├── Constants.sol │ ├── ContractDeployer.sol │ ├── DefaultAccount.sol │ ├── EmptyContract.sol │ ├── EventWriter.yul │ ├── ImmutableSimulator.sol │ ├── KnownCodesStorage.sol │ ├── L1Messenger.sol │ ├── L2EthToken.sol │ ├── MsgValueSimulator.sol │ ├── NonceHolder.sol │ ├── SystemContext.sol │ ├── interfaces │ │ ├── IAccount.sol │ │ ├── IAccountCodeStorage.sol │ │ ├── IBootloaderUtilities.sol │ │ ├── IComplexUpgrader.sol │ │ ├── ICompressor.sol │ │ ├── IContractDeployer.sol │ │ ├── IEthToken.sol │ │ ├── IImmutableSimulator.sol │ │ ├── IKnownCodesStorage.sol │ │ ├── IL1Messenger.sol │ │ ├── IL2StandardToken.sol │ │ ├── IMailbox.sol │ │ ├── INonceHolder.sol │ │ ├── IPaymaster.sol │ │ ├── IPaymasterFlow.sol │ │ ├── ISystemContext.sol │ │ ├── ISystemContextDeprecated.sol │ │ └── ISystemContract.sol │ ├── libraries │ │ ├── EfficientCall.sol │ │ ├── RLPEncoder.sol │ │ ├── SystemContractHelper.sol │ │ ├── SystemContractsCaller.sol │ │ ├── TransactionHelper.sol │ │ ├── UnsafeBytesCalldata.sol │ │ └── Utils.sol │ ├── openzeppelin │ │ ├── token │ │ │ └── ERC20 │ │ │ │ ├── IERC20.sol │ │ │ │ ├── extensions │ │ │ │ └── IERC20Permit.sol │ │ │ │ └── utils │ │ │ │ └── SafeERC20.sol │ │ └── utils │ │ │ └── Address.sol │ ├── precompiles │ │ ├── EcAdd.yul │ │ ├── EcMul.yul │ │ ├── Ecrecover.yul │ │ ├── Keccak256.yul │ │ ├── SHA256.yul │ │ └── test-contracts │ │ │ └── Keccak256Mock.yul │ └── test-contracts │ │ ├── AlwaysRevert.sol │ │ ├── DelegateCaller.sol │ │ ├── Deployable.sol │ │ ├── ExtraAbiCaller.zasm │ │ ├── KeccakTest.sol │ │ ├── MockContract.sol │ │ └── SystemCaller.sol ├── hardhat.config.ts ├── package.json ├── scripts │ ├── calculate-hashes.ts │ ├── compile-yul.ts │ ├── compile-zasm.ts │ ├── constants.ts │ ├── deploy-preimages.ts │ ├── preprocess-bootloader.ts │ ├── preprocess-system-contracts.ts │ └── utils.ts └── test │ ├── AccountCodeStorage.spec.ts │ ├── BootloaderUtilities.spec.ts │ ├── ComplexUpgrader.spec.ts │ ├── Compressor.spec.ts │ ├── ContractDeployer.spec.ts │ ├── DefaultAccount.spec.ts │ ├── EmptyContract.spec.ts │ ├── EventWriter.spec.ts │ ├── ImmutableSimulator.spec.ts │ ├── Keccak256.spec.ts │ ├── KnownCodesStorage.spec.ts │ ├── L2EthToken.spec.ts │ ├── precompiles │ ├── EcAdd.spec.ts │ └── EcMul.spec.ts │ └── shared │ ├── constants.ts │ ├── extraAbiCaller.ts │ ├── mocks.ts │ ├── transactions.ts │ └── utils.ts ├── tools ├── Cargo.lock ├── Cargo.toml ├── README.md ├── data │ ├── scheduler_key.json │ └── verifier_contract_template.txt ├── rust-toolchain └── src │ └── main.rs └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | end_of_line = lf 4 | insert_final_newline = true 5 | trim_trailing_whitespace = true 6 | 7 | [*.sol] 8 | indent_style = space 9 | indent_size = 4 10 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@matterlabs/eslint-config-typescript"], 3 | "rules": { 4 | "no-multiple-empty-lines": ["error", { "max": 1 }], 5 | "@typescript-eslint/no-namespace": "off", 6 | "@typescript-eslint/ban-ts-comment": "off", 7 | "import/no-named-as-default-member": "off", 8 | "import/namespace": "off", 9 | "import/no-unresolved": "off", 10 | "import/order": "off" 11 | }, 12 | "ignorePatterns": [ 13 | "**/lib/*" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /.githooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | CYAN='\033[0;36m' 4 | NC='\033[0m' # No Color 5 | RED='\033[0;31m' 6 | 7 | # Check that the code is formatted in the given directory provided in the first argument 8 | function check_prettier { 9 | if ! yarn prettier:check; then 10 | echo "${RED}Commit error! Cannot commit unformatted code!${NC}" 11 | echo "Prettier errors found. Please format the code via ${CYAN}yarn prettier:fix${NC}!" 12 | exit 1 13 | fi 14 | } 15 | 16 | check_prettier 17 | -------------------------------------------------------------------------------- /.githooks/pre-push: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | CYAN='\033[0;36m' 4 | NC='\033[0m' # No Color 5 | RED='\033[0;31m' 6 | 7 | # Checking that the code is linted and formatted in the given directory provided in the first argument 8 | function check_lint { 9 | if ! yarn lint:check; then 10 | echo "${RED}Push error! Cannot push unlinted code!${NC}" 11 | echo "Lint errors found. Please lint the code via ${CYAN}yarn lint:fix${NC} and/or fix the errors manually!" 12 | exit 1 13 | fi 14 | } 15 | 16 | check_lint 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Scripts-Related Bug Report 3 | about: Use this template for reporting script-related bugs. For contract-related bugs, see our security policy. 4 | title: "" 5 | labels: bug 6 | assignees: "" 7 | --- 8 | 9 | ### 🐛 Script Bug Report 10 | 11 | #### 📝 Description 12 | 13 | Provide a clear and concise description of the bug. 14 | 15 | #### 🔄 Reproduction Steps 16 | 17 | Steps to reproduce the behaviour 18 | 19 | #### 🤔 Expected Behavior 20 | 21 | Describe what you expected to happen. 22 | 23 | #### 😯 Current Behavior 24 | 25 | Describe what actually happened. 26 | 27 | #### 🖥️ Environment 28 | 29 | Any relevant environment details. 30 | 31 | #### 📋 Additional Context 32 | 33 | Add any other context about the problem here. If applicable, add screenshots to help explain. 34 | 35 | #### 📎 Log Output 36 | 37 | ``` 38 | Paste any relevant log output here. 39 | ``` 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Use this template for requesting features 4 | title: "" 5 | labels: feat 6 | assignees: "" 7 | --- 8 | 9 | ### 🌟 Feature Request 10 | 11 | #### 📝 Description 12 | 13 | Provide a clear and concise description of the feature you'd like to see. 14 | 15 | #### 🤔 Rationale 16 | 17 | Explain why this feature is important and how it benefits the project. 18 | 19 | #### 📋 Additional Context 20 | 21 | Add any other context or information about the feature request here. 22 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # What ❔ 2 | 3 | 4 | 5 | 6 | 7 | ## Why ❔ 8 | 9 | 10 | 11 | 12 | ## Checklist 13 | 14 | 15 | 16 | 17 | - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). 18 | - [ ] Tests for the changes have been added / updated. 19 | - [ ] Documentation comments have been added / updated. 20 | -------------------------------------------------------------------------------- /.github/workflows/buld-release.yaml: -------------------------------------------------------------------------------- 1 | name: Build and release 2 | 3 | on: 4 | push: 5 | branches: 6 | - "*" 7 | 8 | jobs: 9 | build-contracts: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout the repository 14 | uses: actions/checkout@v3 15 | 16 | - name: Use Node.js 17 | uses: actions/setup-node@v3 18 | with: 19 | node-version: 18.18.0 20 | cache: yarn 21 | 22 | - name: Init 23 | id: init 24 | run: | 25 | yarn 26 | echo "release_tag=$(echo ${GITHUB_REF#refs/heads/})-$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT 27 | 28 | - name: Build contracts 29 | run: | 30 | yarn l1 build 31 | yarn l2 build 32 | yarn sc build 33 | 34 | - name: Prepare artifacts 35 | run: | 36 | tar -czvf l1-contracts.tar.gz ./l1-contracts 37 | tar -czvf l2-contracts.tar.gz ./l2-contracts 38 | tar -czvf system-contracts.tar.gz ./system-contracts 39 | 40 | - name: Release 41 | uses: softprops/action-gh-release@v1 42 | with: 43 | tag_name: ${{ steps.init.outputs.release_tag }} 44 | fail_on_unmatched_files: true 45 | body: "" 46 | files: | 47 | l1-contracts.tar.gz 48 | l2-contracts.tar.gz 49 | system-contracts.tar.gz 50 | -------------------------------------------------------------------------------- /.github/workflows/l2-contracts-ci.yaml: -------------------------------------------------------------------------------- 1 | name: L2 contracts CI 2 | 3 | on: 4 | pull_request: 5 | 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - name: Checkout the repository 12 | uses: actions/checkout@v3 13 | 14 | - name: Use Node.js 15 | uses: actions/setup-node@v3 16 | with: 17 | node-version: 18.18.0 18 | cache: yarn 19 | 20 | - name: Install dependencies 21 | run: yarn 22 | 23 | - name: Build L2 artifacts 24 | run: yarn l2 build 25 | 26 | - name: Build L1 artifacts 27 | run: yarn l1 build 28 | 29 | - name: Create cache 30 | uses: actions/cache/save@v3 31 | with: 32 | key: artifacts-l2-${{ github.sha }} 33 | path: | 34 | l2-contracts/artifacts-zk 35 | l2-contracts/cache-zk 36 | l2-contracts/typechain 37 | l1-contracts/artifacts 38 | l1-contracts/cache 39 | l1-contracts/typechain 40 | 41 | lint: 42 | runs-on: ubuntu-latest 43 | 44 | steps: 45 | - name: Checkout the repository 46 | uses: actions/checkout@v3 47 | 48 | - name: Use Node.js 49 | uses: actions/setup-node@v3 50 | with: 51 | node-version: 18.18.0 52 | cache: yarn 53 | 54 | - name: Install dependencies 55 | run: yarn 56 | 57 | - name: Lint 58 | run: yarn lint:check 59 | 60 | test: 61 | needs: [build, lint] 62 | runs-on: ubuntu-latest 63 | 64 | steps: 65 | - name: Checkout the repository 66 | uses: actions/checkout@v3 67 | with: 68 | submodules: recursive 69 | 70 | - name: Use Node.js 71 | uses: actions/setup-node@v3 72 | with: 73 | node-version: 18.18.0 74 | cache: yarn 75 | 76 | - name: Install dependencies 77 | run: yarn 78 | 79 | - name: Restore artifacts cache 80 | uses: actions/cache/restore@v3 81 | with: 82 | fail-on-cache-miss: true 83 | key: artifacts-l2-${{ github.sha }} 84 | path: | 85 | l2-contracts/artifacts-zk 86 | l2-contracts/cache-zk 87 | l2-contracts/typechain 88 | l1-contracts/artifacts 89 | l1-contracts/cache 90 | l1-contracts/typechain 91 | 92 | - name: Run Era test node 93 | uses: dutterbutter/era-test-node-action@v0.1.3 94 | 95 | - name: Run tests 96 | run: yarn l2 test 97 | -------------------------------------------------------------------------------- /.github/workflows/label-external-contributions.yaml: -------------------------------------------------------------------------------- 1 | name: Workflow to label external contributions 2 | 3 | on: 4 | pull_request_target: 5 | types: [opened, ready_for_review] 6 | 7 | permissions: 8 | contents: read 9 | pull-requests: write 10 | 11 | jobs: 12 | label: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Add external-contribution label 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | run: | 19 | PR_NUMBER=$(jq --raw-output .pull_request.number "$GITHUB_EVENT_PATH") 20 | REPO_FULL_NAME=$(jq --raw-output .repository.full_name "$GITHUB_EVENT_PATH") 21 | IS_FORK=$(jq --raw-output .pull_request.head.repo.fork "$GITHUB_EVENT_PATH") 22 | 23 | if [[ "$IS_FORK" == "true" ]]; then 24 | echo "This PR is created from a fork." 25 | HTTP_STATUS=$(curl -o /dev/null -s -w "%{http_code}\n" \ 26 | -X POST \ 27 | -H "Authorization: Bearer $GITHUB_TOKEN" \ 28 | -H "Accept: application/vnd.github.v3+json" \ 29 | "https://api.github.com/repos/$REPO_FULL_NAME/issues/$PR_NUMBER/labels" \ 30 | -d '{"labels": ["external-contribution"]}') 31 | 32 | if [[ $HTTP_STATUS -ge 300 ]]; then 33 | echo "Failed to add label to PR, exiting." 34 | exit 1 35 | fi 36 | else 37 | echo "This PR is not created from a fork." 38 | fi 39 | -------------------------------------------------------------------------------- /.github/workflows/nodejs-license.yaml: -------------------------------------------------------------------------------- 1 | name: Node license check 2 | 3 | on: pull_request 4 | 5 | env: 6 | ALLOWED_LICENSES: > 7 | MIT; 8 | BSD; 9 | ISC; 10 | Apache-2.0; 11 | Apache 2.0; 12 | MPL-2.0; 13 | LGPL-3.0; 14 | LGPL-3.0-or-later; 15 | CC0-1.0; 16 | CC-BY-3.0; 17 | CC-BY-4.0; 18 | Python-2.0; 19 | PSF; 20 | Public Domain; 21 | WTFPL; 22 | Unlicense; 23 | # It has to be one line, there must be no space between packages. 24 | EXCLUDE_PACKAGES: testrpc@0.0.1;uuid@2.0.1;era-contracts@0.1.0; 25 | 26 | jobs: 27 | generate-matrix: 28 | name: Lists modules 29 | runs-on: ubuntu-latest 30 | outputs: 31 | matrix: ${{ steps.set-matrix.outputs.matrix }} 32 | steps: 33 | - uses: actions/checkout@v3 34 | - run: | 35 | DIRS=$(find -not \( -path \*node_modules -prune \) -type f -name yarn.lock | xargs dirname | awk -v RS='' -v OFS='","' 'NF { $1 = $1; print "\"" $0 "\"" }') 36 | echo "matrix=[${DIRS}]" >> $GITHUB_OUTPUT 37 | id: set-matrix 38 | 39 | license-check: 40 | needs: [generate-matrix] 41 | runs-on: ubuntu-latest 42 | strategy: 43 | matrix: 44 | dir: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} 45 | steps: 46 | - name: Checkout latest code 47 | uses: actions/checkout@v3 48 | 49 | - name: Use Node.js 50 | uses: actions/setup-node@v3 51 | with: 52 | node-version: 18.18.0 53 | 54 | - name: Install yarn 55 | run: npm install -g yarn license-checker 56 | 57 | - name: Install dependencies in ${{ matrix.dir }} 58 | working-directory: ${{ matrix.dir }} 59 | run: yarn install 60 | 61 | - name: Check licenses in ${{ matrix.dir }} 62 | working-directory: ${{ matrix.dir }} 63 | run: npx license-checker --json --onlyAllow="$ALLOWED_LICENSES" --excludePackages "$EXCLUDE_PACKAGES" 64 | -------------------------------------------------------------------------------- /.github/workflows/secrets_scanner.yaml: -------------------------------------------------------------------------------- 1 | name: Leaked Secrets Scan 2 | on: [pull_request] 3 | jobs: 4 | TruffleHog: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - name: Checkout code 8 | uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3 9 | with: 10 | fetch-depth: 0 11 | - name: TruffleHog OSS 12 | uses: trufflesecurity/trufflehog@0c66d30c1f4075cee1aada2e1ab46dabb1b0071a 13 | with: 14 | path: ./ 15 | base: ${{ github.event.repository.default_branch }} 16 | head: HEAD 17 | extra_args: --debug --only-verified 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | .vscode 4 | artifacts-forge/ 5 | artifacts-zk/ 6 | artifacts/ 7 | build/ 8 | cache-forge/ 9 | cache-zk/ 10 | cache/ 11 | contracts-preprocessed/ 12 | era_test_node.log* 13 | node_modules/ 14 | target/ 15 | tools/data/Verifier.sol 16 | typechain/ 17 | yarn-debug.log* 18 | yarn-error.log* 19 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "l1-contracts/lib/forge-std"] 2 | path = l1-contracts/lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | [submodule "l1-contracts/lib/murky"] 5 | path = l1-contracts/lib/murky 6 | url = https://github.com/dmfxyz/murky 7 | -------------------------------------------------------------------------------- /.markdownlintignore: -------------------------------------------------------------------------------- 1 | # root 2 | node_modules 3 | 4 | # l1-contracts 5 | l1-contracts/lib 6 | l1-contracts/node_modules 7 | 8 | # l2-contracts 9 | l2-contracts/node_modules 10 | 11 | # system-contracts 12 | system-contracts/node_modules 13 | system-contracts/bootloader/test_infra/target 14 | -------------------------------------------------------------------------------- /.markdownlintrc: -------------------------------------------------------------------------------- 1 | { 2 | "default": true, 3 | "header-increment": false, 4 | "no-duplicate-header": false, 5 | "no-inline-html": false, 6 | "line-length": false, 7 | "fenced-code-language": false, 8 | "no-multiple-blanks": false 9 | } 10 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v18.18.0 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | tools/data 2 | l1-contracts/lib 3 | system-contracts/contracts/openzeppelin 4 | system-contracts/contracts/Constants.sol 5 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ...require("@matterlabs/prettier-config"), 3 | plugins: ["prettier-plugin-solidity"], 4 | overrides: [ 5 | { 6 | files: "*.sol", 7 | options: { 8 | bracketSpacing: false, 9 | printWidth: 120, 10 | singleQuote: false, 11 | tabWidth: 4, 12 | useTabs: false, 13 | }, 14 | }, 15 | ], 16 | }; 17 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "rules": { 4 | "state-visibility": "off", 5 | "func-visibility": ["warn", { "ignoreConstructors": true }], 6 | "var-name-mixedcase": "off", 7 | "avoid-call-value": "off", 8 | "no-empty-blocks": "off", 9 | "not-rely-on-time": "off", 10 | "avoid-low-level-calls": "off", 11 | "no-inline-assembly": "off", 12 | "const-name-snakecase": "off", 13 | "no-complex-fallback": "off", 14 | "reason-string": "off", 15 | "func-name-mixedcase": "off", 16 | "no-unused-vars": "off", 17 | "max-states-count": "off", 18 | "compiler-version": ["warn", "^0.8.0"] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.solhintignore: -------------------------------------------------------------------------------- 1 | # root 2 | node_modules 3 | 4 | # l1-contracts 5 | l1-contracts/cache 6 | l1-contracts/lib 7 | l1-contracts/node_modules 8 | 9 | # l2-contracts 10 | l2-contracts/cache-zk 11 | l2-contracts/node_modules 12 | 13 | # system-contracts 14 | system-contracts/contracts/openzeppelin 15 | system-contracts/contracts/Constants.sol 16 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @vladbochok @StanislavBreadless 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | Hello! Thanks for your interest in joining the mission to accelerate the mass adoption of crypto for personal 4 | sovereignty! We welcome contributions from anyone on the internet, and are grateful for even the smallest of fixes! 5 | 6 | ## Ways to contribute 7 | 8 | There are many ways to contribute to the ZK Stack: 9 | 10 | 1. Open issues: if you find a bug, have something you believe needs to be fixed, or have an idea for a feature, please 11 | open an issue. 12 | 2. Add color to existing issues: provide screenshots, code snippets, and whatever you think would be helpful to resolve 13 | issues. 14 | 3. Resolve issues: either by showing an issue isn't a problem and the current state is ok as is or by fixing the problem 15 | and opening a PR. 16 | 4. Report security issues, see [our security policy](./github/SECURITY.md). 17 | 5. [Join the team!](https://matterlabs.notion.site/Shape-the-future-of-Ethereum-at-Matter-Labs-dfb3b5a037044bb3a8006af2eb0575e0) 18 | 19 | ## Fixing issues 20 | 21 | To contribute code fixing issues, please fork the repo, fix an issue, commit, add documentation as per the PR template, 22 | and the repo's maintainers will review the PR. 23 | [here](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) 24 | for guidance how to work with PRs created from a fork. 25 | 26 | ## Licenses 27 | 28 | If you contribute to this project, your contributions will be made to the project under both Apache 2.0 and the MIT 29 | license. 30 | 31 | ## Resources 32 | 33 | We aim to make it as easy as possible to contribute to the mission. This is still WIP, and we're happy for contributions 34 | and suggestions here too. Some resources to help: 35 | 36 | 1. [In-repo docs aimed at developers](docs) 37 | 2. [zkSync Era docs!](https://era.zksync.io/docs/) 38 | 3. Company links can be found in the [repo's readme](README.md) 39 | 40 | ## Code of Conduct 41 | 42 | Be polite and respectful. 43 | 44 | ### Thank you 45 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Matter Labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zkSync Era: Smart Contracts 2 | 3 | [![Logo](eraLogo.svg)](https://zksync.io/) 4 | 5 | zkSync Era is a layer 2 rollup that uses zero-knowledge proofs to scale Ethereum without compromising on security or 6 | decentralization. Since it's EVM compatible (Solidity/Vyper), 99% of Ethereum projects can redeploy without refactoring 7 | or re-auditing a single line of code. zkSync Era also uses an LLVM-based compiler that will eventually let developers 8 | write smart contracts in C++, Rust and other popular languages. 9 | 10 | This repository contains both L1 and L2 zkSync smart contracts. For their description see the 11 | [system overview](docs/Overview.md). 12 | 13 | ## Disclaimer 14 | 15 | It is used as a submodule of a private repo. Compilation and test scripts should work without additional tooling, but 16 | others may not. 17 | 18 | ## License 19 | 20 | zkSync Era contracts are distributed under the terms of the MIT license. 21 | 22 | See [LICENSE-MIT](LICENSE-MIT) for details. 23 | 24 | ## Official Links 25 | 26 | - [Website](https://zksync.io/) 27 | - [GitHub](https://github.com/matter-labs) 28 | - [ZK Credo](https://github.com/zksync/credo) 29 | - [Twitter](https://twitter.com/zksync) 30 | - [Twitter for Devs](https://twitter.com/zkSyncDevs) 31 | - [Discord](https://join.zksync.dev/) 32 | - [Mirror](https://zksync.mirror.xyz/) 33 | 34 | ## Disclaimer 35 | 36 | zkSync Era has been through lots of testing and audits. Although it is live, it is still in alpha state and will go 37 | through more audits and bug bounties programs. We would love to hear our community's thoughts and suggestions about it! 38 | It is important to state that forking it now can potentially lead to missing important security updates, critical 39 | features, and performance improvements. 40 | -------------------------------------------------------------------------------- /SystemConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "GUARANTEED_PUBDATA_BYTES": 2500, 3 | "MAX_TRANSACTIONS_IN_BATCH": 10000, 4 | "REQUIRED_L2_GAS_PRICE_PER_PUBDATA": 800, 5 | "L1_GAS_PER_PUBDATA_BYTE": 17, 6 | "PRIORITY_TX_MAX_PUBDATA": 99000, 7 | "BATCH_OVERHEAD_L1_GAS": 1000000, 8 | "L1_TX_INTRINSIC_L2_GAS": 167157, 9 | "L1_TX_INTRINSIC_PUBDATA": 88, 10 | "L1_TX_MIN_L2_GAS_BASE": 173484, 11 | "L1_TX_DELTA_544_ENCODING_BYTES": 1656, 12 | "L1_TX_DELTA_FACTORY_DEPS_L2_GAS": 2473, 13 | "L1_TX_DELTA_FACTORY_DEPS_PUBDATA": 64, 14 | "L2_TX_INTRINSIC_GAS": 14070, 15 | "L2_TX_INTRINSIC_PUBDATA": 0, 16 | "MAX_NEW_FACTORY_DEPS": 32, 17 | "MAX_GAS_PER_TRANSACTION": 80000000, 18 | "KECCAK_ROUND_COST_GAS": 40, 19 | "SHA256_ROUND_COST_GAS": 7, 20 | "ECRECOVER_COST_GAS": 7000, 21 | "PRIORITY_TX_MINIMAL_GAS_PRICE": 250000000, 22 | "PRIORITY_TX_MAX_GAS_PER_BATCH": 80000000, 23 | "PRIORITY_TX_PUBDATA_PER_BATCH": 120000, 24 | "PRIORITY_TX_BATCH_OVERHEAD_L1_GAS": 1000000 25 | } 26 | -------------------------------------------------------------------------------- /l1-contracts/.env: -------------------------------------------------------------------------------- 1 | CHAIN_ETH_NETWORK=localhost 2 | CONTRACTS_PRIORITY_TX_MAX_GAS_LIMIT=72000000 3 | CONTRACTS_DEPLOY_L2_BRIDGE_COUNTERPART_GAS_LIMIT=10000000 4 | ETH_CLIENT_WEB3_URL=http://127.0.0.1:8545 5 | CONTRACTS_MAILBOX_FACET_ADDR=0x0000000000000000000000000000000000000000 6 | CONTRACTS_GOVERNANCE_FACET_ADDR=0x0000000000000000000000000000000000000000 7 | CONTRACTS_DIAMOND_CUT_FACET_ADDR=0x0000000000000000000000000000000000000000 8 | CONTRACTS_EXECUTOR_FACET_ADDR=0x0000000000000000000000000000000000000000 9 | CONTRACTS_GETTERS_FACET_ADDR=0x0000000000000000000000000000000000000000 10 | CONTRACTS_DIAMOND_INIT_ADDR=0x0000000000000000000000000000000000000000 11 | CONTRACTS_DIAMOND_UPGRADE_INIT_ADDR=0x0000000000000000000000000000000000000000 12 | CONTRACTS_DIAMOND_PROXY_ADDR=0x0000000000000000000000000000000000000000 13 | CONTRACTS_VERIFIER_ADDR=0x0000000000000000000000000000000000000000 14 | CONTRACTS_L1_ERC20_BRIDGE_IMPL_ADDR=0x0000000000000000000000000000000000000000 15 | CONTRACTS_L1_ERC20_BRIDGE_PROXY_ADDR=0x0000000000000000000000000000000000000000 16 | CONTRACTS_L1_ALLOW_LIST_ADDR=0x0000000000000000000000000000000000000000 17 | CONTRACTS_CREATE2_FACTORY_ADDR=0x0000000000000000000000000000000000000000 18 | CONTRACTS_VALIDATOR_TIMELOCK_ADDR=0x0000000000000000000000000000000000000000 19 | CONTRACTS_VALIDATOR_TIMELOCK_EXECUTION_DELAY=0 20 | -------------------------------------------------------------------------------- /l1-contracts/contracts/bridge/interfaces/IL1Bridge.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | /// @title L1 Bridge contract interface 6 | /// @author Matter Labs 7 | /// @custom:security-contact security@matterlabs.dev 8 | interface IL1Bridge { 9 | error FunctionNotSupported(); 10 | 11 | event DepositInitiated( 12 | bytes32 indexed l2DepositTxHash, 13 | address indexed from, 14 | address indexed to, 15 | address l1Token, 16 | uint256 amount 17 | ); 18 | 19 | event DepositToMergeInitiated( 20 | bytes32 indexed l2DepositTxHash, 21 | address indexed from, 22 | address indexed to, 23 | address l1Token, 24 | uint256 amount, 25 | bool toMerge 26 | ); 27 | 28 | event WithdrawalFinalized(address indexed to, address indexed l1Token, uint256 amount); 29 | 30 | event ClaimedFailedDeposit(address indexed to, address indexed l1Token, uint256 amount); 31 | 32 | function isWithdrawalFinalized(uint256 _l2BatchNumber, uint256 _l2MessageIndex) external view returns (bool); 33 | 34 | function deposit( 35 | address _l2Receiver, 36 | address _l1Token, 37 | uint256 _amount, 38 | uint256 _l2TxGasLimit, 39 | uint256 _l2TxGasPerPubdataByte, 40 | address _refundRecipient 41 | ) external payable returns (bytes32 txHash); 42 | 43 | function depositToMerge( 44 | address _l2Receiver, 45 | address _l1Token, 46 | uint256 _amount, 47 | uint256 _l2TxGasLimit, 48 | uint256 _l2TxGasPerPubdataByte, 49 | address _refundRecipient 50 | ) external payable returns (bytes32 txHash); 51 | 52 | function claimFailedDeposit( 53 | address _depositSender, 54 | address _l1Token, 55 | bytes32 _l2TxHash, 56 | uint256 _l2BatchNumber, 57 | uint256 _l2MessageIndex, 58 | uint16 _l2TxNumberInBatch, 59 | bytes32[] calldata _merkleProof 60 | ) external; 61 | 62 | function finalizeWithdrawal( 63 | uint256 _l2BatchNumber, 64 | uint256 _l2MessageIndex, 65 | uint16 _l2TxNumberInBatch, 66 | bytes calldata _message, 67 | bytes32[] calldata _merkleProof 68 | ) external; 69 | 70 | function l2TokenAddress(address _l1Token) external view returns (address); 71 | 72 | function l2Bridge() external view returns (address); 73 | } 74 | -------------------------------------------------------------------------------- /l1-contracts/contracts/bridge/interfaces/IL1BridgeLegacy.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | /// @title L1 Bridge contract legacy interface 6 | /// @author Matter Labs 7 | /// @custom:security-contact security@matterlabs.dev 8 | interface IL1BridgeLegacy { 9 | function deposit( 10 | address _l2Receiver, 11 | address _l1Token, 12 | uint256 _amount, 13 | uint256 _l2TxGasLimit, 14 | uint256 _l2TxGasPerPubdataByte 15 | ) external payable returns (bytes32 txHash); 16 | } 17 | -------------------------------------------------------------------------------- /l1-contracts/contracts/bridge/interfaces/IL2Bridge.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | /// @author Matter Labs 6 | interface IL2Bridge { 7 | function finalizeDeposit( 8 | address _l1Sender, 9 | address _l2Receiver, 10 | address _l1Token, 11 | uint256 _amount, 12 | bytes calldata _data 13 | ) external payable; 14 | 15 | function finalizeDepositToMerge( 16 | address _l1Sender, 17 | address _l2Receiver, 18 | address _l1Token, 19 | uint256 _amount, 20 | bytes calldata _data 21 | ) external payable; 22 | 23 | function withdraw(address _l1Receiver, address _l2Token, uint256 _amount) external; 24 | 25 | function l1TokenAddress(address _l2Token) external view returns (address); 26 | 27 | function l2TokenAddress(address _l1Token) external view returns (address); 28 | 29 | function l1Bridge() external view returns (address); 30 | } 31 | -------------------------------------------------------------------------------- /l1-contracts/contracts/bridge/interfaces/IL2ERC20Bridge.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | /// @author Matter Labs 6 | interface IL2ERC20Bridge { 7 | function initialize(address _l1Bridge, bytes32 _l2TokenProxyBytecodeHash, address _governor) external; 8 | } 9 | -------------------------------------------------------------------------------- /l1-contracts/contracts/bridge/interfaces/IL2WethBridge.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | interface IL2WethBridge { 6 | function initialize(address _l1Bridge, address _l1WethAddress, address _l2WethAddress) external; 7 | } 8 | -------------------------------------------------------------------------------- /l1-contracts/contracts/bridge/interfaces/IWETH9.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | pragma solidity 0.8.19; 3 | 4 | interface IWETH9 { 5 | function deposit() external payable; 6 | 7 | function withdraw(uint256 wad) external; 8 | } 9 | -------------------------------------------------------------------------------- /l1-contracts/contracts/common/Dependencies.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; 6 | -------------------------------------------------------------------------------- /l1-contracts/contracts/common/L2ContractAddresses.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | /// @dev The address of the L2 deployer system contract. 6 | address constant L2_DEPLOYER_SYSTEM_CONTRACT_ADDR = address(0x8006); 7 | 8 | /// @dev The special reserved L2 address. It is located in the system contracts space but doesn't have deployed 9 | /// bytecode. 10 | /// @dev The L2 deployer system contract allows changing bytecodes on any address if the `msg.sender` is this address. 11 | /// @dev So, whenever the governor wants to redeploy system contracts, it just initiates the L1 upgrade call deployer 12 | /// system contract 13 | /// via the L1 -> L2 transaction with `sender == L2_FORCE_DEPLOYER_ADDR`. For more details see the 14 | /// `diamond-initializers` contracts. 15 | address constant L2_FORCE_DEPLOYER_ADDR = address(0x8007); 16 | 17 | /// @dev The address of the special smart contract that can send arbitrary length message as an L2 log 18 | address constant L2_TO_L1_MESSENGER_SYSTEM_CONTRACT_ADDR = address(0x8008); 19 | 20 | /// @dev The formal address of the initial program of the system: the bootloader 21 | address constant L2_BOOTLOADER_ADDRESS = address(0x8001); 22 | 23 | /// @dev The address of the eth token system contract 24 | address constant L2_ETH_TOKEN_SYSTEM_CONTRACT_ADDR = address(0x800a); 25 | 26 | /// @dev The address of the known code storage system contract 27 | address constant L2_KNOWN_CODE_STORAGE_SYSTEM_CONTRACT_ADDR = address(0x8004); 28 | 29 | /// @dev The address of the context system contract 30 | address constant L2_SYSTEM_CONTEXT_SYSTEM_CONTRACT_ADDR = address(0x800b); 31 | -------------------------------------------------------------------------------- /l1-contracts/contracts/common/interfaces/IL2ContractDeployer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | /** 6 | * @author Matter Labs 7 | * @notice System smart contract that is responsible for deploying other smart contracts on zkSync. 8 | */ 9 | interface IL2ContractDeployer { 10 | /// @notice A struct that describes a forced deployment on an address. 11 | /// @param bytecodeHash The bytecode hash to put on an address. 12 | /// @param newAddress The address on which to deploy the bytecodehash to. 13 | /// @param callConstructor Whether to run the constructor on the force deployment. 14 | /// @param value The `msg.value` with which to initialize a contract. 15 | /// @param input The constructor calldata. 16 | struct ForceDeployment { 17 | bytes32 bytecodeHash; 18 | address newAddress; 19 | bool callConstructor; 20 | uint256 value; 21 | bytes input; 22 | } 23 | 24 | /// @notice This method is to be used only during an upgrade to set bytecodes on specific addresses. 25 | function forceDeployOnAddresses(ForceDeployment[] calldata _deployParams) external; 26 | 27 | /// @notice Deploys a contract with similar address derivation rules to the EVM's `CREATE2` opcode. 28 | /// @param _salt The create2 salt. 29 | /// @param _bytecodeHash The correctly formatted hash of the bytecode. 30 | /// @param _input The constructor calldata. 31 | function create2(bytes32 _salt, bytes32 _bytecodeHash, bytes calldata _input) external; 32 | } 33 | -------------------------------------------------------------------------------- /l1-contracts/contracts/common/libraries/UncheckedMath.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | /** 6 | * @author Matter Labs 7 | * @custom:security-contact security@matterlabs.dev 8 | * @notice The library for unchecked math. 9 | */ 10 | library UncheckedMath { 11 | function uncheckedInc(uint256 _number) internal pure returns (uint256) { 12 | unchecked { 13 | return _number + 1; 14 | } 15 | } 16 | 17 | function uncheckedAdd(uint256 _lhs, uint256 _rhs) internal pure returns (uint256) { 18 | unchecked { 19 | return _lhs + _rhs; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /l1-contracts/contracts/common/libraries/UnsafeBytes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | /** 6 | * @author Matter Labs 7 | * @custom:security-contact security@matterlabs.dev 8 | * @dev The library provides a set of functions that help read data from an "abi.encodePacked" byte array. 9 | * @dev Each of the functions accepts the `bytes memory` and the offset where data should be read and returns a value of a certain type. 10 | * 11 | * @dev WARNING! 12 | * 1) Functions don't check the length of the bytes array, so it can go out of bounds. 13 | * The user of the library must check for bytes length before using any functions from the library! 14 | * 15 | * 2) Read variables are not cleaned up - https://docs.soliditylang.org/en/v0.8.16/internals/variable_cleanup.html. 16 | * Using data in inline assembly can lead to unexpected behavior! 17 | */ 18 | library UnsafeBytes { 19 | function readUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32 result, uint256 offset) { 20 | assembly { 21 | offset := add(_start, 4) 22 | result := mload(add(_bytes, offset)) 23 | } 24 | } 25 | 26 | function readAddress(bytes memory _bytes, uint256 _start) internal pure returns (address result, uint256 offset) { 27 | assembly { 28 | offset := add(_start, 20) 29 | result := mload(add(_bytes, offset)) 30 | } 31 | } 32 | 33 | function readUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256 result, uint256 offset) { 34 | assembly { 35 | offset := add(_start, 32) 36 | result := mload(add(_bytes, offset)) 37 | } 38 | } 39 | 40 | function readBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32 result, uint256 offset) { 41 | assembly { 42 | offset := add(_start, 32) 43 | result := mload(add(_bytes, offset)) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/ConstructorForwarder.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | contract ConstructorForwarder { 6 | constructor(address to, bytes memory data) payable { 7 | (bool success, ) = payable(to).call{value: msg.value}(data); 8 | require(success); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/EventOnFallback.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | contract EventOnFallback { 6 | event Called(address msgSender, uint256 value, bytes data); 7 | 8 | fallback() external payable { 9 | emit Called(msg.sender, msg.value, msg.data); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/Forwarder.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | contract Forwarder { 6 | function forward(address to, bytes calldata data) external payable returns (bytes memory returnValue) { 7 | bool success; 8 | (success, returnValue) = payable(to).call{value: msg.value}(data); 9 | require(success); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/Multicall.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | /* 4 | 5 | MIT License 6 | 7 | Copyright (c) 2018 Maker Foundation 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy 10 | of this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in all 17 | copies or substantial portions of the Software. 18 | 19 | */ 20 | 21 | pragma solidity 0.8.19; 22 | 23 | /// @title Multicall - Aggregate results from multiple read-only function calls 24 | contract Multicall { 25 | struct Call { 26 | address target; 27 | bytes callData; 28 | } 29 | 30 | function aggregate(Call[] memory calls) public returns (uint256 blockNumber, bytes[] memory returnData) { 31 | blockNumber = block.number; 32 | returnData = new bytes[](calls.length); 33 | for (uint256 i = 0; i < calls.length; ++i) { 34 | (bool success, bytes memory ret) = calls[i].target.call(calls[i].callData); 35 | require(success); 36 | returnData[i] = ret; 37 | } 38 | } 39 | 40 | // Helper functions 41 | function getEthBalance(address addr) public view returns (uint256 balance) { 42 | balance = addr.balance; 43 | } 44 | 45 | function getBlockHash(uint256 blockNumber) public view returns (bytes32 blockHash) { 46 | blockHash = blockhash(blockNumber); 47 | } 48 | 49 | function getLastBlockHash() public view returns (bytes32 blockHash) { 50 | blockHash = blockhash(block.number - 1); 51 | } 52 | 53 | function getCurrentBlockTimestamp() public view returns (uint256 timestamp) { 54 | timestamp = block.timestamp; 55 | } 56 | 57 | function getCurrentBlockDifficulty() public view returns (uint256 difficulty) { 58 | difficulty = block.difficulty; 59 | } 60 | 61 | function getCurrentBlockGasLimit() public view returns (uint256 gaslimit) { 62 | gaslimit = block.gaslimit; 63 | } 64 | 65 | function getCurrentBlockCoinbase() public view returns (address coinbase) { 66 | coinbase = block.coinbase; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/ReturnSomething.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | contract ReturnSomething { 6 | fallback() external payable { 7 | assembly { 8 | return(0, 0x20) 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/RevertFallback.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | contract RevertFallback { 6 | fallback() external payable { 7 | revert(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/RevertReceiveAccount.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | /// @title RevertReceiveAccount - An account which reverts receiving funds depending on the flag 6 | /// @dev Used for testing failed withdrawals from the zkSync smart contract 7 | contract RevertReceiveAccount { 8 | bool public revertReceive; 9 | 10 | constructor() { 11 | revertReceive = false; 12 | } 13 | 14 | function setRevertReceive(bool newValue) public { 15 | revertReceive = newValue; 16 | } 17 | 18 | receive() external payable { 19 | // Assert is used here to also simulate the out-of-gas error, since failed asserion 20 | // consumes up all the remaining gas 21 | assert(!revertReceive); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/RevertTransferERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import "./TestnetERC20Token.sol"; 6 | 7 | /// @title RevertTransferERC20Token - A ERC20 token contract which can revert transfers depending on a flag 8 | /// @dev Used for testing failed ERC-20 withdrawals from the zkSync smart contract 9 | contract RevertTransferERC20 is TestnetERC20Token { 10 | bool public revertTransfer; 11 | 12 | constructor(string memory name, string memory symbol, uint8 decimals) TestnetERC20Token(name, symbol, decimals) { 13 | revertTransfer = false; 14 | } 15 | 16 | function setRevertTransfer(bool newValue) public { 17 | revertTransfer = newValue; 18 | } 19 | 20 | function transfer(address recipient, uint256 amount) public virtual override returns (bool) { 21 | // Assert is used here to also simulate the out-of-gas error, since failed asserion 22 | // consumes up all the remaining gas 23 | assert(!revertTransfer); 24 | 25 | _transfer(_msgSender(), recipient, amount); 26 | return true; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/SingletonFactory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.19; 2 | 3 | /** 4 | * @title Singleton Factory (EIP-2470) 5 | * @notice Exposes CREATE2 (EIP-1014) to deploy bytecode on deterministic addresses based on initialization code 6 | * and salt. 7 | * @author Ricardo Guilherme Schmidt (Status Research & Development GmbH) 8 | */ 9 | contract SingletonFactory { 10 | /** 11 | * @notice Deploys `_initCode` using `_salt` for defining the deterministic address. 12 | * @param _initCode Initialization code. 13 | * @param _salt Arbitrary value to modify resulting address. 14 | * @return createdContract Created contract address. 15 | */ 16 | function deploy(bytes memory _initCode, bytes32 _salt) public returns (address payable createdContract) { 17 | assembly { 18 | createdContract := create2(0, add(_initCode, 0x20), mload(_initCode), _salt) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/TestnetERC20Token.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | 7 | contract TestnetERC20Token is ERC20 { 8 | uint8 private _decimals; 9 | 10 | constructor(string memory name_, string memory symbol_, uint8 decimals_) ERC20(name_, symbol_) { 11 | _decimals = decimals_; 12 | } 13 | 14 | function mint(address _to, uint256 _amount) public returns (bool) { 15 | _mint(_to, _amount); 16 | return true; 17 | } 18 | 19 | function decimals() public view override returns (uint8) { 20 | return _decimals; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/WETH9.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | pragma solidity 0.8.19; 4 | 5 | contract WETH9 { 6 | string public name = "Wrapped Ether"; 7 | string public symbol = "WETH"; 8 | uint8 public decimals = 18; 9 | 10 | event Approval(address indexed src, address indexed guy, uint256 wad); 11 | event Transfer(address indexed src, address indexed dst, uint256 wad); 12 | event Deposit(address indexed dst, uint256 wad); 13 | event Withdrawal(address indexed src, uint256 wad); 14 | 15 | mapping(address => uint256) public balanceOf; 16 | mapping(address => mapping(address => uint256)) public allowance; 17 | 18 | receive() external payable { 19 | deposit(); 20 | } 21 | 22 | function deposit() public payable { 23 | balanceOf[msg.sender] += msg.value; 24 | emit Deposit(msg.sender, msg.value); 25 | } 26 | 27 | function withdraw(uint256 wad) public { 28 | require(balanceOf[msg.sender] >= wad); 29 | balanceOf[msg.sender] -= wad; 30 | payable(msg.sender).transfer(wad); 31 | emit Withdrawal(msg.sender, wad); 32 | } 33 | 34 | function totalSupply() public view returns (uint256) { 35 | return address(this).balance; 36 | } 37 | 38 | function approve(address guy, uint256 wad) public returns (bool) { 39 | allowance[msg.sender][guy] = wad; 40 | emit Approval(msg.sender, guy, wad); 41 | return true; 42 | } 43 | 44 | function transfer(address dst, uint256 wad) public returns (bool) { 45 | return transferFrom(msg.sender, dst, wad); 46 | } 47 | 48 | function transferFrom(address src, address dst, uint256 wad) public returns (bool) { 49 | require(balanceOf[src] >= wad); 50 | 51 | if (src != msg.sender && allowance[src][msg.sender] != type(uint256).max) { 52 | require(allowance[src][msg.sender] >= wad); 53 | allowance[src][msg.sender] -= wad; 54 | } 55 | 56 | balanceOf[src] -= wad; 57 | balanceOf[dst] += wad; 58 | 59 | emit Transfer(src, dst, wad); 60 | 61 | return true; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/test/AdminFacetTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import "../../zksync/facets/Admin.sol"; 6 | 7 | contract AdminFacetTest is AdminFacet { 8 | constructor() { 9 | s.governor = msg.sender; 10 | } 11 | 12 | function getPorterAvailability() external view returns (bool) { 13 | return s.zkPorterIsAvailable; 14 | } 15 | 16 | function isValidator(address _address) external view returns (bool) { 17 | return s.validators[_address]; 18 | } 19 | 20 | function getPriorityTxMaxGasLimit() external view returns (uint256) { 21 | return s.priorityTxMaxGasLimit; 22 | } 23 | 24 | function getGovernor() external view returns (address) { 25 | return s.governor; 26 | } 27 | 28 | function getPendingGovernor() external view returns (address) { 29 | return s.pendingGovernor; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/test/CustomUpgradeTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import "../../zksync/libraries/Diamond.sol"; 6 | import "../../upgrades/BaseZkSyncUpgrade.sol"; 7 | 8 | contract CustomUpgradeTest is BaseZkSyncUpgrade { 9 | event Test(); 10 | 11 | /// @notice Placeholder function for custom logic for upgrading L1 contract. 12 | /// Typically this function will never be used. 13 | /// @param _customCallDataForUpgrade Custom data for upgrade, which may be interpreted differently for each upgrade. 14 | function _upgradeL1Contract(bytes calldata _customCallDataForUpgrade) internal override { 15 | emit Test(); 16 | } 17 | 18 | /// @notice placeholder function for custom logic for post-upgrade logic. 19 | /// Typically this function will never be used. 20 | /// @param _customCallDataForUpgrade Custom data for an upgrade, which may be interpreted differently for each 21 | /// upgrade. 22 | function _postUpgrade(bytes calldata _customCallDataForUpgrade) internal override {} 23 | 24 | /// @notice The main function that will be called by the upgrade proxy. 25 | /// @param _proposedUpgrade The upgrade to be executed. 26 | function upgrade(ProposedUpgrade calldata _proposedUpgrade) public override returns (bytes32) { 27 | _setNewProtocolVersion(_proposedUpgrade.newProtocolVersion); 28 | _upgradeL1Contract(_proposedUpgrade.l1ContractsUpgradeCalldata); 29 | _upgradeVerifier(_proposedUpgrade.verifier, _proposedUpgrade.verifierParams); 30 | _setBaseSystemContracts(_proposedUpgrade.bootloaderHash, _proposedUpgrade.defaultAccountHash); 31 | 32 | bytes32 txHash; 33 | txHash = _setL2SystemContractUpgrade( 34 | _proposedUpgrade.l2ProtocolUpgradeTx, 35 | _proposedUpgrade.factoryDeps, 36 | _proposedUpgrade.newProtocolVersion 37 | ); 38 | 39 | _postUpgrade(_proposedUpgrade.postUpgradeCalldata); 40 | 41 | emit UpgradeComplete(_proposedUpgrade.newProtocolVersion, txHash, _proposedUpgrade); 42 | 43 | return Diamond.DIAMOND_INIT_SUCCESS_RETURN_VALUE; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/test/DiamondCutTestContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import "../../zksync/libraries/Diamond.sol"; 6 | import "../../zksync/facets/Getters.sol"; 7 | 8 | contract DiamondCutTestContract is GettersFacet { 9 | function diamondCut(Diamond.DiamondCutData memory _diamondCut) external { 10 | Diamond.diamondCut(_diamondCut); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/test/DiamondProxyTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import "../../zksync/libraries/Diamond.sol"; 6 | import "../../zksync/facets/Base.sol"; 7 | 8 | contract DiamondProxyTest is Base { 9 | function setFreezability(bool _freeze) external returns (bytes32) { 10 | Diamond.DiamondStorage storage diamondStorage = Diamond.getDiamondStorage(); 11 | diamondStorage.isFrozen = _freeze; 12 | return Diamond.DIAMOND_INIT_SUCCESS_RETURN_VALUE; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/test/DummyERC20BytesTransferReturnValue.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | contract DummyERC20BytesTransferReturnValue { 6 | bytes returnValue; 7 | 8 | constructor(bytes memory _returnValue) { 9 | returnValue = _returnValue; 10 | } 11 | 12 | function transfer(address _recipient, uint256 _amount) external view returns (bytes memory) { 13 | // Hack to prevent Solidity warnings 14 | _recipient; 15 | _amount; 16 | 17 | return returnValue; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/test/DummyERC20NoTransferReturnValue.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | contract DummyERC20NoTransferReturnValue { 6 | function transfer(address recipient, uint256 amount) external {} 7 | } 8 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/test/ExecutorProvingTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.19; 2 | 3 | import {ExecutorFacet} from "../../zksync/facets/Executor.sol"; 4 | import {VerifierParams} from "../../zksync/Storage.sol"; 5 | 6 | contract ExecutorProvingTest is ExecutorFacet { 7 | function getBatchProofPublicInput( 8 | bytes32 _prevBatchCommitment, 9 | bytes32 _currentBatchCommitment, 10 | VerifierParams memory _verifierParams 11 | ) external pure returns (uint256) { 12 | return _getBatchProofPublicInput(_prevBatchCommitment, _currentBatchCommitment, _verifierParams); 13 | } 14 | 15 | function createBatchCommitment( 16 | CommitBatchInfo calldata _newBatchData, 17 | bytes32 _stateDiffHash 18 | ) external view returns (bytes32) { 19 | return _createBatchCommitment(_newBatchData, _stateDiffHash); 20 | } 21 | 22 | function processL2Logs( 23 | CommitBatchInfo calldata _newBatch, 24 | bytes32 _expectedSystemContractUpgradeTxHash 25 | ) 26 | external 27 | pure 28 | returns ( 29 | uint256 numberOfLayer1Txs, 30 | bytes32 chainedPriorityTxsHash, 31 | bytes32 previousBatchHash, 32 | bytes32 stateDiffHash, 33 | bytes32 l2LogsTreeRoot, 34 | uint256 packedBatchAndL2BlockTimestamp 35 | ) 36 | { 37 | return _processL2Logs(_newBatch, _expectedSystemContractUpgradeTxHash); 38 | } 39 | 40 | /// Sets the DefaultAccount Hash and Bootloader Hash. 41 | function setHashes(bytes32 l2DefaultAccountBytecodeHash, bytes32 l2BootloaderBytecodeHash) external { 42 | s.l2DefaultAccountBytecodeHash = l2DefaultAccountBytecodeHash; 43 | s.l2BootloaderBytecodeHash = l2BootloaderBytecodeHash; 44 | s.zkPorterIsAvailable = false; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/test/L1ERC20BridgeTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import "../../bridge/L1ERC20Bridge.sol"; 6 | import {IMailbox} from "../../zksync/interfaces/IMailbox.sol"; 7 | 8 | /// @author Matter Labs 9 | contract L1ERC20BridgeTest is L1ERC20Bridge { 10 | constructor(IZkSync _zkSync) L1ERC20Bridge(_zkSync) {} 11 | 12 | function getZkSyncMailbox() public view returns (IMailbox) { 13 | return zkSync; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/test/MailboxFacetTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import "../../zksync/facets/Mailbox.sol"; 6 | import "../../zksync/Config.sol"; 7 | 8 | contract MailboxFacetTest is MailboxFacet { 9 | constructor() { 10 | s.governor = msg.sender; 11 | } 12 | 13 | function setFeeParams(FeeParams memory _feeParams) external { 14 | s.feeParams = _feeParams; 15 | } 16 | 17 | function getL2GasPrice(uint256 _l1GasPrice) external view returns (uint256) { 18 | return _deriveL2GasPrice(_l1GasPrice, REQUIRED_L2_GAS_PRICE_PER_PUBDATA); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/test/MerkleTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import "../../zksync/libraries/Merkle.sol"; 6 | 7 | contract MerkleTest { 8 | function calculateRoot( 9 | bytes32[] calldata _path, 10 | uint256 _index, 11 | bytes32 _itemHash 12 | ) external pure returns (bytes32) { 13 | return Merkle.calculateRoot(_path, _index, _itemHash); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/test/MockExecutor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import "../../zksync/facets/Base.sol"; 6 | 7 | contract MockExecutorFacet is Base { 8 | function saveL2LogsRootHash(uint256 _batchNumber, bytes32 _l2LogsTreeRoot) external { 9 | s.totalBatchesExecuted = _batchNumber; 10 | s.l2LogsRootHashes[_batchNumber] = _l2LogsTreeRoot; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/test/PriorityQueueTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import "../../zksync/libraries/PriorityQueue.sol"; 6 | 7 | contract PriorityQueueTest { 8 | using PriorityQueue for PriorityQueue.Queue; 9 | 10 | PriorityQueue.Queue priorityQueue; 11 | 12 | function getFirstUnprocessedPriorityTx() external view returns (uint256) { 13 | return priorityQueue.getFirstUnprocessedPriorityTx(); 14 | } 15 | 16 | function getTotalPriorityTxs() external view returns (uint256) { 17 | return priorityQueue.getTotalPriorityTxs(); 18 | } 19 | 20 | function getSize() external view returns (uint256) { 21 | return priorityQueue.getSize(); 22 | } 23 | 24 | function isEmpty() external view returns (bool) { 25 | return priorityQueue.isEmpty(); 26 | } 27 | 28 | function pushBack(PriorityOperation memory _operation) external { 29 | return priorityQueue.pushBack(_operation); 30 | } 31 | 32 | function front() external view returns (PriorityOperation memory) { 33 | return priorityQueue.front(); 34 | } 35 | 36 | function popFront() external returns (PriorityOperation memory operation) { 37 | return priorityQueue.popFront(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/test/ReenterGovernance.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import {IGovernance} from "../../governance/IGovernance.sol"; 6 | 7 | contract ReenterGovernance { 8 | IGovernance governance; 9 | 10 | // Store call, predecessor and salt separately, 11 | // because Operation struct can't be stored on storage. 12 | IGovernance.Call call; 13 | bytes32 predecessor; 14 | bytes32 salt; 15 | 16 | // Save one value to determine whether reentrancy already happen. 17 | bool alreadyReentered; 18 | 19 | enum FunctionToCall { 20 | Unset, 21 | Execute, 22 | ExecuteInstant, 23 | Cancel 24 | } 25 | 26 | FunctionToCall functionToCall; 27 | 28 | function initialize( 29 | IGovernance _governance, 30 | IGovernance.Operation memory _op, 31 | FunctionToCall _functionToCall 32 | ) external { 33 | governance = _governance; 34 | require(_op.calls.length == 1, "Only 1 calls supported"); 35 | call = _op.calls[0]; 36 | predecessor = _op.predecessor; 37 | salt = _op.salt; 38 | 39 | functionToCall = _functionToCall; 40 | } 41 | 42 | fallback() external payable { 43 | if (!alreadyReentered) { 44 | alreadyReentered = true; 45 | IGovernance.Call[] memory calls = new IGovernance.Call[](1); 46 | calls[0] = call; 47 | IGovernance.Operation memory op = IGovernance.Operation({ 48 | calls: calls, 49 | predecessor: predecessor, 50 | salt: salt 51 | }); 52 | 53 | if (functionToCall == ReenterGovernance.FunctionToCall.Execute) { 54 | governance.execute(op); 55 | } else if (functionToCall == ReenterGovernance.FunctionToCall.ExecuteInstant) { 56 | governance.executeInstant(op); 57 | } else if (functionToCall == ReenterGovernance.FunctionToCall.Cancel) { 58 | bytes32 opId = governance.hashOperation(op); 59 | governance.cancel(opId); 60 | } else { 61 | revert("Unset function to call"); 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /l1-contracts/contracts/dev-contracts/test/UnsafeBytesTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import "../../common/libraries/UnsafeBytes.sol"; 6 | 7 | contract UnsafeBytesTest { 8 | using UnsafeBytes for bytes; 9 | 10 | function readUint32(bytes memory _bytes, uint256 _start) external pure returns (uint32 readValue, uint256 offset) { 11 | return _bytes.readUint32(_start); 12 | } 13 | 14 | function readAddress( 15 | bytes memory _bytes, 16 | uint256 _start 17 | ) external pure returns (address readValue, uint256 offset) { 18 | return _bytes.readAddress(_start); 19 | } 20 | 21 | function readUint256( 22 | bytes memory _bytes, 23 | uint256 _start 24 | ) external pure returns (uint256 readValue, uint256 offset) { 25 | return _bytes.readUint256(_start); 26 | } 27 | 28 | function readBytes32( 29 | bytes memory _bytes, 30 | uint256 _start 31 | ) external pure returns (bytes32 readValue, uint256 offset) { 32 | return _bytes.readBytes32(_start); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /l1-contracts/contracts/upgrades/DefaultUpgrade.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import {Diamond} from "../zksync/libraries/Diamond.sol"; 6 | import {BaseZkSyncUpgrade} from "./BaseZkSyncUpgrade.sol"; 7 | 8 | /// @author Matter Labs 9 | /// @custom:security-contact security@matterlabs.dev 10 | contract DefaultUpgrade is BaseZkSyncUpgrade { 11 | /// @notice The main function that will be called by the upgrade proxy. 12 | /// @param _proposedUpgrade The upgrade to be executed. 13 | function upgrade(ProposedUpgrade calldata _proposedUpgrade) public override returns (bytes32) { 14 | super.upgrade(_proposedUpgrade); 15 | return Diamond.DIAMOND_INIT_SUCCESS_RETURN_VALUE; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /l1-contracts/contracts/upgrades/Upgrade_v1_4_1.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import {Diamond} from "../zksync/libraries/Diamond.sol"; 6 | import {BaseZkSyncUpgrade} from "./BaseZkSyncUpgrade.sol"; 7 | import {PubdataPricingMode, FeeParams} from "../zksync/Storage.sol"; 8 | 9 | /// @author Matter Labs 10 | /// @custom:security-contact security@matterlabs.dev 11 | contract Upgrade_v1_4_1 is BaseZkSyncUpgrade { 12 | /// This event is an exact copy of the "IAdmin.NewFeeParams" event. Since they have the same name and parameters, 13 | /// these will be tracked by indexers in the same manner. 14 | event NewFeeParams(FeeParams oldFeeParams, FeeParams newFeeParams); 15 | 16 | /// This function is a copy of the "Admin.changeFeeParams" function. 17 | /// It is to be used once to set the new fee params for the first time as they needed for the correct functioning of the upgrade. 18 | function changeFeeParams(FeeParams memory _newFeeParams) private { 19 | // Double checking that the new fee params are valid, i.e. 20 | // the maximal pubdata per batch is not less than the maximal pubdata per priority transaction. 21 | require(_newFeeParams.maxPubdataPerBatch >= _newFeeParams.priorityTxMaxPubdata, "n6"); 22 | 23 | FeeParams memory oldFeeParams = s.feeParams; 24 | s.feeParams = _newFeeParams; 25 | 26 | emit NewFeeParams(oldFeeParams, _newFeeParams); 27 | } 28 | 29 | /// @notice The main function that will be called by the upgrade proxy. 30 | /// @param _proposedUpgrade The upgrade to be executed. 31 | function upgrade(ProposedUpgrade calldata _proposedUpgrade) public override returns (bytes32) { 32 | // The execution of the next parts of the upgrade does depend on these fee params being already set correctly 33 | changeFeeParams( 34 | FeeParams({ 35 | pubdataPricingMode: PubdataPricingMode.Rollup, 36 | batchOverheadL1Gas: $(PRIORITY_TX_BATCH_OVERHEAD_L1_GAS), 37 | maxPubdataPerBatch: $(PRIORITY_TX_PUBDATA_PER_BATCH), 38 | maxL2GasPerBatch: $(PRIORITY_TX_MAX_GAS_PER_BATCH), 39 | priorityTxMaxPubdata: $(PRIORITY_TX_MAX_PUBDATA), 40 | minimalL2GasPrice: $(PRIORITY_TX_MINIMAL_GAS_PRICE) 41 | }) 42 | ); 43 | 44 | super.upgrade(_proposedUpgrade); 45 | 46 | return Diamond.DIAMOND_INIT_SUCCESS_RETURN_VALUE; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /l1-contracts/contracts/vendor/AddressAliasHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | /* 4 | * Copyright 2019-2021, Offchain Labs, Inc. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | pragma solidity 0.8.19; 20 | 21 | library AddressAliasHelper { 22 | uint160 constant offset = uint160(0x1111000000000000000000000000000000001111); 23 | 24 | /// @notice Utility function converts the address that submitted a tx 25 | /// to the inbox on L1 to the msg.sender viewed on L2 26 | /// @param l1Address the address in the L1 that triggered the tx to L2 27 | /// @return l2Address L2 address as viewed in msg.sender 28 | function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) { 29 | unchecked { 30 | l2Address = address(uint160(l1Address) + offset); 31 | } 32 | } 33 | 34 | /// @notice Utility function that converts the msg.sender viewed on L2 to the 35 | /// address that submitted a tx to the inbox on L1 36 | /// @param l2Address L2 address as viewed in msg.sender 37 | /// @return l1Address the address in the L1 that triggered the tx to L2 38 | function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) { 39 | unchecked { 40 | l1Address = address(uint160(l2Address) - offset); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /l1-contracts/contracts/zksync/facets/Base.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import {AppStorage} from "../Storage.sol"; 6 | import {ReentrancyGuard} from "../../common/ReentrancyGuard.sol"; 7 | 8 | /// @title Base contract containing functions accessible to the other facets. 9 | /// @author Matter Labs 10 | /// @custom:security-contact security@matterlabs.dev 11 | contract Base is ReentrancyGuard { 12 | AppStorage internal s; 13 | 14 | /// @notice Checks that the message sender is an active governor 15 | modifier onlyGovernor() { 16 | require(msg.sender == s.governor, "1g"); // only by governor 17 | _; 18 | } 19 | 20 | /// @notice Checks that the message sender is an active governor or admin 21 | modifier onlyGovernorOrAdmin() { 22 | require(msg.sender == s.governor || msg.sender == s.admin, "1k"); 23 | _; 24 | } 25 | 26 | /// @notice Checks that the message sender is L2 gateway 27 | modifier onlyGateway() { 28 | require(msg.sender == address(s.gateway), "1j"); 29 | _; 30 | } 31 | 32 | /// @notice Checks if validator is active 33 | modifier onlyValidator() { 34 | require(s.validators[msg.sender], "1h"); // validator is not active 35 | _; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /l1-contracts/contracts/zksync/interfaces/IBase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.19; 3 | 4 | /// @title The interface of the zkSync contract, responsible for the main zkSync logic. 5 | /// @author Matter Labs 6 | /// @custom:security-contact security@matterlabs.dev 7 | interface IBase { 8 | /// @return Returns facet name. 9 | function getName() external view returns (string memory); 10 | } 11 | -------------------------------------------------------------------------------- /l1-contracts/contracts/zksync/interfaces/IL2Gateway.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity 0.8.19; 3 | 4 | interface IL2Gateway { 5 | /// @notice Send message to remote gateway 6 | /// @param value The msg value 7 | /// @param callData The call data 8 | function sendMessage(uint256 value, bytes memory callData) external payable; 9 | } 10 | -------------------------------------------------------------------------------- /l1-contracts/contracts/zksync/interfaces/ILegacyGetters.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import {IBase} from "./IBase.sol"; 6 | 7 | /// @author Matter Labs 8 | /// @dev This interface contains getters for the zkSync contract that should not be used, 9 | /// but still are kept for backward compatibility. 10 | /// @custom:security-contact security@matterlabs.dev 11 | interface ILegacyGetters is IBase { 12 | /// @return The total number of batches that were committed 13 | /// @dev It is a *deprecated* method, please use `getTotalBatchesCommitted` instead 14 | function getTotalBlocksCommitted() external view returns (uint256); 15 | 16 | /// @return The total number of batches that were committed & verified 17 | /// @dev It is a *deprecated* method, please use `getTotalBatchesVerified` instead. 18 | function getTotalBlocksVerified() external view returns (uint256); 19 | 20 | /// @return The total number of batches that were committed & verified & executed 21 | /// @dev It is a *deprecated* method, please use `getTotalBatchesExecuted` instead. 22 | function getTotalBlocksExecuted() external view returns (uint256); 23 | 24 | /// @notice For unfinalized (non executed) batches may change 25 | /// @dev It is a *deprecated* method, please use `storedBatchHash` instead. 26 | /// @dev returns zero for non-committed batches 27 | /// @return The hash of committed L2 batch. 28 | function storedBlockHash(uint256 _batchNumber) external view returns (bytes32); 29 | 30 | /// @return The L2 batch number in which the upgrade transaction was processed. 31 | /// @dev It is a *deprecated* method, please use `getL2SystemContractsUpgradeBatchNumber` instead. 32 | /// @dev It is equal to 0 in the following two cases: 33 | /// - No upgrade transaction has ever been processed. 34 | /// - The upgrade transaction has been processed and the batch with such transaction has been 35 | /// executed (i.e. finalized). 36 | function getL2SystemContractsUpgradeBlockNumber() external view returns (uint256); 37 | } 38 | -------------------------------------------------------------------------------- /l1-contracts/contracts/zksync/interfaces/IVerifier.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | /// @title The interface of the Verifier contract, responsible for the zero knowledge proof verification. 6 | /// @author Matter Labs 7 | /// @custom:security-contact security@matterlabs.dev 8 | interface IVerifier { 9 | /// @dev Verifies a zk-SNARK proof. 10 | /// @return A boolean value indicating whether the zk-SNARK proof is valid. 11 | /// Note: The function may revert execution instead of returning false in some cases. 12 | function verify( 13 | uint256[] calldata _publicInputs, 14 | uint256[] calldata _proof, 15 | uint256[] calldata _recursiveAggregationInput 16 | ) external view returns (bool); 17 | 18 | /// @notice Calculates a keccak256 hash of the runtime loaded verification keys. 19 | /// @return vkHash The keccak256 hash of the loaded verification keys. 20 | function verificationKeyHash() external pure returns (bytes32); 21 | } 22 | -------------------------------------------------------------------------------- /l1-contracts/contracts/zksync/interfaces/IZkLink.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | pragma solidity 0.8.19; 4 | 5 | /// @title ZkLink interface contract 6 | /// @author zk.link 7 | interface IZkLink { 8 | /// @notice Receive batch root from primary chain 9 | /// @param _batchNumber The batch number 10 | /// @param _l2LogsRootHash The L2 to L1 log root hash 11 | /// @param _forwardEthAmount The forward eth amount 12 | function syncBatchRoot(uint256 _batchNumber, bytes32 _l2LogsRootHash, uint256 _forwardEthAmount) external payable; 13 | 14 | /// @notice Receive range batch root from primary chain 15 | /// @param _fromBatchNumber The batch number from 16 | /// @param _toBatchNumber The batch number to 17 | /// @param _rangeRootHash The range root hash 18 | /// @param _forwardEthAmount The forward eth amount 19 | function syncRangeBatchRoot( 20 | uint256 _fromBatchNumber, 21 | uint256 _toBatchNumber, 22 | bytes32 _rangeRootHash, 23 | uint256 _forwardEthAmount 24 | ) external payable; 25 | 26 | /// @notice Receive l2 tx hash from primary chain 27 | /// @param _l2TxHash The l2 tx hash on local chain 28 | /// @param _primaryChainL2TxHash The l2 tx hash on primary chain 29 | function syncL2TxHash(bytes32 _l2TxHash, bytes32 _primaryChainL2TxHash) external; 30 | } 31 | -------------------------------------------------------------------------------- /l1-contracts/contracts/zksync/interfaces/IZkSync.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import {IMailbox} from "./IMailbox.sol"; 6 | import {IAdmin} from "./IAdmin.sol"; 7 | import {IExecutor} from "./IExecutor.sol"; 8 | import {IGetters} from "./IGetters.sol"; 9 | 10 | /// @title The interface of the zkSync contract, responsible for the main zkSync logic. 11 | /// @author Matter Labs 12 | /// @dev This interface combines the interfaces of all the facets of the zkSync contract. 13 | /// @custom:security-contact security@matterlabs 14 | interface IZkSync is IMailbox, IAdmin, IExecutor, IGetters {} 15 | -------------------------------------------------------------------------------- /l1-contracts/contracts/zksync/libraries/Merkle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import {UncheckedMath} from "../../common/libraries/UncheckedMath.sol"; 6 | 7 | /// @author Matter Labs 8 | /// @custom:security-contact security@matterlabs.dev 9 | library Merkle { 10 | using UncheckedMath for uint256; 11 | 12 | /// @dev Calculate Merkle root by the provided Merkle proof. 13 | /// NOTE: When using this function, check that the _path length is equal to the tree height to prevent shorter/longer paths attack 14 | /// @param _path Merkle path from the leaf to the root 15 | /// @param _index Leaf index in the tree 16 | /// @param _itemHash Hash of leaf content 17 | /// @return The Merkle root 18 | function calculateRoot( 19 | bytes32[] calldata _path, 20 | uint256 _index, 21 | bytes32 _itemHash 22 | ) internal pure returns (bytes32) { 23 | uint256 pathLength = _path.length; 24 | require(pathLength > 0, "xc"); 25 | require(pathLength < 256, "bt"); 26 | require(_index < (1 << pathLength), "px"); 27 | 28 | bytes32 currentHash = _itemHash; 29 | for (uint256 i; i < pathLength; i = i.uncheckedInc()) { 30 | currentHash = (_index % 2 == 0) 31 | ? _efficientHash(currentHash, _path[i]) 32 | : _efficientHash(_path[i], currentHash); 33 | _index /= 2; 34 | } 35 | 36 | return currentHash; 37 | } 38 | 39 | /// @dev Keccak hash of the concatenation of two 32-byte words 40 | function _efficientHash(bytes32 _lhs, bytes32 _rhs) internal pure returns (bytes32 result) { 41 | assembly { 42 | mstore(0x00, _lhs) 43 | mstore(0x20, _rhs) 44 | result := keccak256(0x00, 0x40) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /l1-contracts/contracts/zksync/upgrade-initializers/DIamondUpgradeInit2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import "../Config.sol"; 6 | import "../facets/Mailbox.sol"; 7 | import "../libraries/Diamond.sol"; 8 | import "../../common/libraries/L2ContractHelper.sol"; 9 | import "../../common/L2ContractAddresses.sol"; 10 | 11 | interface IOldContractDeployer { 12 | function forceDeployOnAddress( 13 | bytes32 _bytecodeHash, 14 | address _newAddress, 15 | bytes calldata _input 16 | ) external payable returns (address); 17 | } 18 | 19 | /// @author Matter Labs 20 | contract DiamondUpgradeInit2 is MailboxFacet { 21 | function forceDeploy2( 22 | bytes calldata _upgradeDeployerCalldata, 23 | bytes calldata _upgradeSystemContractsCalldata, 24 | bytes[] calldata _factoryDeps 25 | ) external payable returns (bytes32) { 26 | // 1. Update bytecode for the deployer smart contract 27 | _requestL2Transaction( 28 | L2_FORCE_DEPLOYER_ADDR, 29 | L2_DEPLOYER_SYSTEM_CONTRACT_ADDR, 30 | 0, 31 | _upgradeDeployerCalldata, 32 | $(PRIORITY_TX_MAX_GAS_LIMIT), 33 | REQUIRED_L2_GAS_PRICE_PER_PUBDATA, 34 | _factoryDeps, 35 | true, 36 | address(0) 37 | ); 38 | 39 | // 2. Redeploy other contracts by one transaction 40 | _requestL2Transaction( 41 | L2_FORCE_DEPLOYER_ADDR, 42 | L2_DEPLOYER_SYSTEM_CONTRACT_ADDR, 43 | 0, 44 | _upgradeSystemContractsCalldata, 45 | $(PRIORITY_TX_MAX_GAS_LIMIT), 46 | REQUIRED_L2_GAS_PRICE_PER_PUBDATA, 47 | _factoryDeps, 48 | true, 49 | address(0) 50 | ); 51 | 52 | return Diamond.DIAMOND_INIT_SUCCESS_RETURN_VALUE; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /l1-contracts/contracts/zksync/upgrade-initializers/DiamondUpgradeInit1.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import "../facets/Mailbox.sol"; 6 | import "../libraries/Diamond.sol"; 7 | import "../../common/libraries/L2ContractHelper.sol"; 8 | import "../../common/L2ContractAddresses.sol"; 9 | import "../Config.sol"; 10 | 11 | /// @author Matter Labs 12 | contract DiamondUpgradeInit1 is MailboxFacet { 13 | /// @dev Request priority operation on behalf of force deployer address to the deployer system contract 14 | /// @return The message indicating the successful force deployment of contract on L2 15 | function forceDeployL2Contract( 16 | bytes calldata _forceDeployCalldata, 17 | bytes[] calldata _factoryDeps, 18 | uint256 _l2GasLimit 19 | ) external payable returns (bytes32) { 20 | _requestL2Transaction( 21 | L2_FORCE_DEPLOYER_ADDR, 22 | L2_DEPLOYER_SYSTEM_CONTRACT_ADDR, 23 | 0, 24 | _forceDeployCalldata, 25 | _l2GasLimit, 26 | REQUIRED_L2_GAS_PRICE_PER_PUBDATA, 27 | _factoryDeps, 28 | true, 29 | address(0) 30 | ); 31 | 32 | return Diamond.DIAMOND_INIT_SUCCESS_RETURN_VALUE; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /l1-contracts/contracts/zksync/upgrade-initializers/DiamondUpgradeInit3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import "../libraries/Diamond.sol"; 6 | import "../interfaces/IVerifier.sol"; 7 | import "../facets/Base.sol"; 8 | 9 | interface IOldDiamondCut { 10 | function proposeDiamondCut(Diamond.FacetCut[] calldata _facetCuts, address _initAddress) external; 11 | 12 | function cancelDiamondCutProposal() external; 13 | 14 | function executeDiamondCutProposal(Diamond.DiamondCutData calldata _diamondCut) external; 15 | 16 | function emergencyFreezeDiamond() external; 17 | 18 | function unfreezeDiamond() external; 19 | 20 | function approveEmergencyDiamondCutAsSecurityCouncilMember(bytes32 _diamondCutHash) external; 21 | 22 | // FIXME: token holders should have the ability to cancel the upgrade 23 | 24 | event DiamondCutProposal(Diamond.FacetCut[] _facetCuts, address _initAddress); 25 | 26 | event DiamondCutProposalCancelation(uint256 currentProposalId, bytes32 indexed proposedDiamondCutHash); 27 | 28 | event DiamondCutProposalExecution(Diamond.DiamondCutData _diamondCut); 29 | 30 | event EmergencyFreeze(); 31 | 32 | event Unfreeze(uint256 lastDiamondFreezeTimestamp); 33 | 34 | event EmergencyDiamondCutApproved( 35 | address indexed _address, 36 | uint256 currentProposalId, 37 | uint256 securityCouncilEmergencyApprovals, 38 | bytes32 indexed proposedDiamondCutHash 39 | ); 40 | } 41 | 42 | /// @author Matter Labs 43 | contract DiamondUpgradeInit3 is Base { 44 | function upgrade( 45 | uint256 _priorityTxMaxGasLimit, 46 | address _allowList, 47 | IVerifier _verifier 48 | ) external payable returns (bytes32) { 49 | // Zero out the deprecated storage slots 50 | delete s.__DEPRECATED_diamondCutStorage; 51 | 52 | s.priorityTxMaxGasLimit = _priorityTxMaxGasLimit; 53 | s.__DEPRECATED_allowList = _allowList; 54 | s.verifier = _verifier; 55 | 56 | return Diamond.DIAMOND_INIT_SUCCESS_RETURN_VALUE; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /l1-contracts/contracts/zksync/upgrade-initializers/DiamondUpgradeInit4.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import "../Config.sol"; 6 | import "../facets/Mailbox.sol"; 7 | import "../libraries/Diamond.sol"; 8 | import "../../common/libraries/L2ContractHelper.sol"; 9 | import "../../common/L2ContractAddresses.sol"; 10 | 11 | interface IOldContractDeployer { 12 | struct ForceDeployment { 13 | bytes32 bytecodeHash; 14 | address newAddress; 15 | uint256 value; 16 | bytes input; 17 | } 18 | 19 | function forceDeployOnAddresses(ForceDeployment[] calldata _deployParams) external; 20 | } 21 | 22 | /// @author Matter Labs 23 | contract DiamondUpgradeInit4 is MailboxFacet { 24 | function forceDeploy2( 25 | bytes calldata _upgradeDeployerCalldata, 26 | bytes calldata _upgradeSystemContractsCalldata, 27 | bytes[] calldata _factoryDeps 28 | ) external payable returns (bytes32) { 29 | // 1. Update bytecode for the deployer smart contract 30 | _requestL2Transaction( 31 | L2_FORCE_DEPLOYER_ADDR, 32 | L2_DEPLOYER_SYSTEM_CONTRACT_ADDR, 33 | 0, 34 | _upgradeDeployerCalldata, 35 | $(PRIORITY_TX_MAX_GAS_LIMIT), 36 | REQUIRED_L2_GAS_PRICE_PER_PUBDATA, 37 | _factoryDeps, 38 | true, 39 | address(0) 40 | ); 41 | 42 | // 2. Redeploy other contracts by one transaction 43 | _requestL2Transaction( 44 | L2_FORCE_DEPLOYER_ADDR, 45 | L2_DEPLOYER_SYSTEM_CONTRACT_ADDR, 46 | 0, 47 | _upgradeSystemContractsCalldata, 48 | $(PRIORITY_TX_MAX_GAS_LIMIT), 49 | REQUIRED_L2_GAS_PRICE_PER_PUBDATA, 50 | _factoryDeps, 51 | true, 52 | address(0) 53 | ); 54 | 55 | return Diamond.DIAMOND_INIT_SUCCESS_RETURN_VALUE; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /l1-contracts/contracts/zksync/upgrade-initializers/DiamondUpgradeInit5.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import "../Config.sol"; 6 | import "../facets/Mailbox.sol"; 7 | import "../libraries/Diamond.sol"; 8 | import "../../common/libraries/L2ContractHelper.sol"; 9 | import "../../common/L2ContractAddresses.sol"; 10 | 11 | /// @author Matter Labs 12 | contract DiamondUpgradeInit5 is MailboxFacet { 13 | function forceDeploy( 14 | bytes calldata _upgradeDeployerCalldata, 15 | bytes calldata _upgradeSystemContractsCalldata, 16 | bytes[] calldata _factoryDeps 17 | ) external payable returns (bytes32) { 18 | // 1. Update bytecode for the deployer smart contract 19 | _requestL2Transaction( 20 | L2_FORCE_DEPLOYER_ADDR, 21 | L2_DEPLOYER_SYSTEM_CONTRACT_ADDR, 22 | 0, 23 | _upgradeDeployerCalldata, 24 | $(PRIORITY_TX_MAX_GAS_LIMIT), 25 | REQUIRED_L2_GAS_PRICE_PER_PUBDATA, 26 | _factoryDeps, 27 | true, 28 | address(0) 29 | ); 30 | 31 | // 2. Redeploy system contracts by one priority transaction 32 | _requestL2Transaction( 33 | L2_FORCE_DEPLOYER_ADDR, 34 | L2_DEPLOYER_SYSTEM_CONTRACT_ADDR, 35 | 0, 36 | _upgradeSystemContractsCalldata, 37 | $(PRIORITY_TX_MAX_GAS_LIMIT), 38 | REQUIRED_L2_GAS_PRICE_PER_PUBDATA, 39 | _factoryDeps, 40 | true, 41 | address(0) 42 | ); 43 | 44 | return Diamond.DIAMOND_INIT_SUCCESS_RETURN_VALUE; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /l1-contracts/contracts/zksync/upgrade-initializers/DiamondUpgradeInit6.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import "../Config.sol"; 6 | import "../facets/Mailbox.sol"; 7 | import "../libraries/Diamond.sol"; 8 | import "../../common/libraries/L2ContractHelper.sol"; 9 | import "../../common/L2ContractAddresses.sol"; 10 | 11 | /// @author Matter Labs 12 | contract DiamondUpgradeInit6 is MailboxFacet { 13 | function forceDeploy( 14 | bytes calldata _upgradeL2WethTokenCalldata, 15 | bytes calldata _upgradeSystemContractsCalldata, 16 | bytes[] calldata _factoryDeps 17 | ) external payable returns (bytes32) { 18 | // 1. Update bytecode for the L2 WETH smart contract 19 | _requestL2Transaction( 20 | L2_FORCE_DEPLOYER_ADDR, 21 | L2_DEPLOYER_SYSTEM_CONTRACT_ADDR, 22 | 0, 23 | _upgradeL2WethTokenCalldata, 24 | $(PRIORITY_TX_MAX_GAS_LIMIT), 25 | REQUIRED_L2_GAS_PRICE_PER_PUBDATA, 26 | _factoryDeps, 27 | true, 28 | address(0) 29 | ); 30 | 31 | // 2. Redeploy system contracts by one priority transaction 32 | _requestL2Transaction( 33 | L2_FORCE_DEPLOYER_ADDR, 34 | L2_DEPLOYER_SYSTEM_CONTRACT_ADDR, 35 | 0, 36 | _upgradeSystemContractsCalldata, 37 | $(PRIORITY_TX_MAX_GAS_LIMIT), 38 | REQUIRED_L2_GAS_PRICE_PER_PUBDATA, 39 | _factoryDeps, 40 | true, 41 | address(0) 42 | ); 43 | 44 | return Diamond.DIAMOND_INIT_SUCCESS_RETURN_VALUE; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /l1-contracts/foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = 'cache/solpp-generated-contracts' 3 | out = 'artifacts-forge' 4 | libs = ['node_modules', 'lib'] 5 | cache_path = 'cache-forge' 6 | test = 'test/foundry' 7 | 8 | # See more config options https://github.com/foundry-rs/foundry/tree/master/config 9 | -------------------------------------------------------------------------------- /l1-contracts/remappings.txt: -------------------------------------------------------------------------------- 1 | @ensdomains/=node_modules/@ensdomains/ 2 | @openzeppelin/=node_modules/@openzeppelin/ 3 | ds-test/=lib/forge-std/lib/ds-test/src/ 4 | eth-gas-reporter/=node_modules/eth-gas-reporter/ 5 | forge-std/=lib/forge-std/src/ 6 | hardhat/=node_modules/hardhat/ 7 | solpp/=cache/solpp-generated-contracts/ 8 | murky/=lib/murky/src/ 9 | -------------------------------------------------------------------------------- /l1-contracts/scripts/deploy-create2.ts: -------------------------------------------------------------------------------- 1 | import { Command } from "commander"; 2 | import { Wallet, ethers } from "ethers"; 3 | import { Deployer } from "../src.ts/deploy"; 4 | import { formatUnits, parseUnits } from "ethers/lib/utils"; 5 | import * as fs from "fs"; 6 | import * as path from "path"; 7 | import { web3Provider } from "./utils"; 8 | 9 | const provider = web3Provider(); 10 | const testConfigPath = path.join(process.env.ZKSYNC_HOME as string, "etc/test_config/constant"); 11 | const ethTestConfig = JSON.parse(fs.readFileSync(`${testConfigPath}/eth.json`, { encoding: "utf-8" })); 12 | 13 | async function main() { 14 | const program = new Command(); 15 | 16 | program.version("0.1.0").name("deploy-create2").description("deploy create2 factory and multicall3 contracts"); 17 | 18 | program 19 | .option("--private-key ") 20 | .option("--gas-price ") 21 | .option("--nonce ") 22 | .option("--owner-address ") 23 | .option("--create2-salt ") 24 | .action(async (cmd) => { 25 | const deployWallet = cmd.privateKey 26 | ? new Wallet(cmd.privateKey, provider) 27 | : Wallet.fromMnemonic( 28 | process.env.MNEMONIC ? process.env.MNEMONIC : ethTestConfig.mnemonic, 29 | "m/44'/60'/0'/0/1" 30 | ).connect(provider); 31 | console.log(`Using deployer wallet: ${deployWallet.address}`); 32 | 33 | const ownerAddress = cmd.ownerAddress ? cmd.ownerAddress : deployWallet.address; 34 | console.log(`Using owner address: ${ownerAddress}`); 35 | 36 | const gasPrice = cmd.gasPrice ? parseUnits(cmd.gasPrice, "gwei") : await provider.getGasPrice(); 37 | console.log(`Using gas price: ${formatUnits(gasPrice, "gwei")} gwei`); 38 | 39 | let nonce = cmd.nonce ? parseInt(cmd.nonce) : await deployWallet.getTransactionCount(); 40 | console.log(`Using nonce: ${nonce}`); 41 | 42 | const create2Salt = cmd.create2Salt ? cmd.create2Salt : ethers.utils.hexlify(ethers.utils.randomBytes(32)); 43 | 44 | const deployer = new Deployer({ 45 | deployWallet, 46 | ownerAddress, 47 | verbose: true, 48 | }); 49 | 50 | await deployer.deployCreate2Factory({ gasPrice, nonce }); 51 | nonce++; 52 | 53 | await deployer.deployMulticall3(create2Salt, { gasPrice, nonce }); 54 | nonce++; 55 | }); 56 | 57 | await program.parseAsync(process.argv); 58 | } 59 | 60 | main() 61 | .then(() => process.exit(0)) 62 | .catch((err) => { 63 | console.error("Error:", err); 64 | process.exit(1); 65 | }); 66 | -------------------------------------------------------------------------------- /l1-contracts/scripts/deploy-l1-erc20-bridge-impl.ts: -------------------------------------------------------------------------------- 1 | import { Command } from "commander"; 2 | import { Wallet, ethers } from "ethers"; 3 | import { Deployer } from "../src.ts/deploy"; 4 | import { formatUnits, parseUnits } from "ethers/lib/utils"; 5 | import * as fs from "fs"; 6 | import * as path from "path"; 7 | import { web3Provider } from "./utils"; 8 | 9 | const provider = web3Provider(); 10 | const testConfigPath = path.join(process.env.ZKSYNC_HOME as string, "etc/test_config/constant"); 11 | const ethTestConfig = JSON.parse(fs.readFileSync(`${testConfigPath}/eth.json`, { encoding: "utf-8" })); 12 | 13 | async function main() { 14 | const program = new Command(); 15 | 16 | program.version("0.1.0").name("deployL1ERC20BridgeImpl").description("deploy L1 erc20 bridge implementation"); 17 | 18 | program 19 | .option("--private-key ") 20 | .option("--gas-price ") 21 | .option("--nonce ") 22 | .option("--owner-address ") 23 | .option("--create2-salt ") 24 | .action(async (cmd) => { 25 | const deployWallet = cmd.privateKey 26 | ? new Wallet(cmd.privateKey, provider) 27 | : Wallet.fromMnemonic( 28 | process.env.MNEMONIC ? process.env.MNEMONIC : ethTestConfig.mnemonic, 29 | "m/44'/60'/0'/0/1" 30 | ).connect(provider); 31 | console.log(`Using deployer wallet: ${deployWallet.address}`); 32 | 33 | const ownerAddress = cmd.ownerAddress ? cmd.ownerAddress : deployWallet.address; 34 | console.log(`Using owner address: ${ownerAddress}`); 35 | 36 | const gasPrice = cmd.gasPrice ? parseUnits(cmd.gasPrice, "gwei") : await provider.getGasPrice(); 37 | console.log(`Using gas price: ${formatUnits(gasPrice, "gwei")} gwei`); 38 | 39 | const nonce = cmd.nonce ? parseInt(cmd.nonce) : await deployWallet.getTransactionCount(); 40 | console.log(`Using nonce: ${nonce}`); 41 | 42 | const create2Salt = cmd.create2Salt ? cmd.create2Salt : ethers.utils.hexlify(ethers.utils.randomBytes(32)); 43 | 44 | const deployer = new Deployer({ 45 | deployWallet, 46 | ownerAddress, 47 | verbose: true, 48 | }); 49 | 50 | await deployer.deployERC20BridgeImplementation(create2Salt, { gasPrice, nonce: nonce }); 51 | }); 52 | 53 | await program.parseAsync(process.argv); 54 | } 55 | 56 | main() 57 | .then(() => process.exit(0)) 58 | .catch((err) => { 59 | console.error("Error:", err); 60 | process.exit(1); 61 | }); 62 | -------------------------------------------------------------------------------- /l1-contracts/scripts/deploy-weth-bridges.ts: -------------------------------------------------------------------------------- 1 | import { Command } from "commander"; 2 | import { Wallet, ethers } from "ethers"; 3 | import { Deployer } from "../src.ts/deploy"; 4 | import { formatUnits, parseUnits } from "ethers/lib/utils"; 5 | import * as fs from "fs"; 6 | import * as path from "path"; 7 | import { web3Provider } from "./utils"; 8 | 9 | const provider = web3Provider(); 10 | const testConfigPath = path.join(process.env.ZKSYNC_HOME as string, "etc/test_config/constant"); 11 | const ethTestConfig = JSON.parse(fs.readFileSync(`${testConfigPath}/eth.json`, { encoding: "utf-8" })); 12 | 13 | async function main() { 14 | const program = new Command(); 15 | 16 | program.version("0.1.0").name("deploy").description("deploy weth bridges"); 17 | 18 | program 19 | .option("--private-key ") 20 | .option("--gas-price ") 21 | .option("--nonce ") 22 | .option("--create2-salt ") 23 | .action(async (cmd) => { 24 | const deployWallet = cmd.privateKey 25 | ? new Wallet(cmd.privateKey, provider) 26 | : Wallet.fromMnemonic( 27 | process.env.MNEMONIC ? process.env.MNEMONIC : ethTestConfig.mnemonic, 28 | "m/44'/60'/0'/0/0" 29 | ).connect(provider); 30 | console.log(`Using deployer wallet: ${deployWallet.address}`); 31 | 32 | const gasPrice = cmd.gasPrice ? parseUnits(cmd.gasPrice, "gwei") : await provider.getGasPrice(); 33 | console.log(`Using gas price: ${formatUnits(gasPrice, "gwei")} gwei`); 34 | 35 | const nonce = cmd.nonce ? parseInt(cmd.nonce) : await deployWallet.getTransactionCount(); 36 | console.log(`Using nonce: ${nonce}`); 37 | 38 | const create2Salt = cmd.create2Salt ? cmd.create2Salt : ethers.utils.hexlify(ethers.utils.randomBytes(32)); 39 | 40 | const deployer = new Deployer({ 41 | deployWallet, 42 | verbose: true, 43 | }); 44 | 45 | await deployer.deployWethBridgeContracts(create2Salt, gasPrice); 46 | }); 47 | await program.parseAsync(process.argv); 48 | } 49 | 50 | main() 51 | .then(() => process.exit(0)) 52 | .catch((err) => { 53 | console.error("Error:", err); 54 | process.exit(1); 55 | }); 56 | -------------------------------------------------------------------------------- /l1-contracts/scripts/deploy-withdrawal-helpers.ts: -------------------------------------------------------------------------------- 1 | // This script deploys the contracts required both for production and 2 | // for testing of the contracts required for the `withdrawal-helpers` library 3 | 4 | import * as hardhat from "hardhat"; 5 | import "@nomiclabs/hardhat-ethers"; 6 | import { ethers } from "ethers"; 7 | import * as fs from "fs"; 8 | import * as path from "path"; 9 | import { web3Provider } from "./utils"; 10 | 11 | const testConfigPath = path.join(process.env.ZKSYNC_HOME as string, "etc/test_config/constant"); 12 | const ethTestConfig = JSON.parse(fs.readFileSync(`${testConfigPath}/eth.json`, { encoding: "utf-8" })); 13 | 14 | async function main() { 15 | try { 16 | if (!["test", "localhost"].includes(process.env.CHAIN_ETH_NETWORK)) { 17 | console.error("This deploy script is only for localhost-test network"); 18 | process.exit(1); 19 | } 20 | 21 | const provider = web3Provider(); 22 | provider.pollingInterval = 10; 23 | 24 | const deployWallet = ethers.Wallet.fromMnemonic(ethTestConfig.test_mnemonic, "m/44'/60'/0'/0/0").connect(provider); 25 | const multicallFactory = await hardhat.ethers.getContractFactory("Multicall", deployWallet); 26 | const multicallContract = await multicallFactory.deploy({ 27 | gasLimit: 5000000, 28 | }); 29 | 30 | const revertReceiveFactory = await hardhat.ethers.getContractFactory("RevertReceiveAccount", deployWallet); 31 | const revertReceiveAccount = await revertReceiveFactory.deploy({ 32 | gasLimit: 5000000, 33 | }); 34 | 35 | const outConfig = { 36 | multicall_address: multicallContract.address, 37 | revert_receive_address: revertReceiveAccount.address, 38 | }; 39 | const outConfigPath = path.join(process.env.ZKSYNC_HOME, "etc/test_config/volatile/withdrawal-helpers.json"); 40 | fs.writeFileSync(outConfigPath, JSON.stringify(outConfig), { encoding: "utf-8" }); 41 | process.exit(0); 42 | } catch (err) { 43 | console.log(`Error: ${err}`); 44 | process.exit(1); 45 | } 46 | } 47 | 48 | main() 49 | .then(() => process.exit(0)) 50 | .catch((err) => { 51 | console.error("Error:", err.message || err); 52 | process.exit(1); 53 | }); 54 | -------------------------------------------------------------------------------- /l1-contracts/scripts/initialize-validator.ts: -------------------------------------------------------------------------------- 1 | import { Command } from "commander"; 2 | import { Wallet } from "ethers"; 3 | import { Deployer } from "../src.ts/deploy"; 4 | import { formatUnits, parseUnits } from "ethers/lib/utils"; 5 | import { web3Provider } from "./utils"; 6 | 7 | import * as fs from "fs"; 8 | import * as path from "path"; 9 | 10 | const provider = web3Provider(); 11 | const testConfigPath = path.join(process.env.ZKSYNC_HOME as string, "etc/test_config/constant"); 12 | const ethTestConfig = JSON.parse(fs.readFileSync(`${testConfigPath}/eth.json`, { encoding: "utf-8" })); 13 | 14 | async function main() { 15 | const program = new Command(); 16 | 17 | program 18 | .option("--private-key ") 19 | .option("--gas-price ") 20 | .option("--nonce ") 21 | .action(async (cmd) => { 22 | const deployWallet = cmd.privateKey 23 | ? new Wallet(cmd.privateKey, provider) 24 | : Wallet.fromMnemonic( 25 | process.env.MNEMONIC ? process.env.MNEMONIC : ethTestConfig.mnemonic, 26 | "m/44'/60'/0'/0/1" 27 | ).connect(provider); 28 | console.log(`Using deployer wallet: ${deployWallet.address}`); 29 | 30 | const gasPrice = cmd.gasPrice ? parseUnits(cmd.gasPrice, "gwei") : await provider.getGasPrice(); 31 | console.log(`Using gas price: ${formatUnits(gasPrice, "gwei")} gwei`); 32 | 33 | const nonce = cmd.nonce ? parseInt(cmd.nonce) : await deployWallet.getTransactionCount(); 34 | console.log(`Using nonce: ${nonce}`); 35 | 36 | const deployer = new Deployer({ 37 | deployWallet, 38 | verbose: true, 39 | }); 40 | 41 | const zkSync = deployer.zkSyncContract(deployWallet); 42 | const validatorTimelock = deployer.validatorTimelock(deployWallet); 43 | const tx = await zkSync.setValidator(validatorTimelock.address, true, { gasPrice: gasPrice }); 44 | console.log(`Transaction sent with hash ${tx.hash} and nonce ${tx.nonce}`); 45 | const receipt = await tx.wait(); 46 | 47 | console.log(`Validator is set, gasUsed: ${receipt.gasUsed.toString()}`); 48 | }); 49 | 50 | await program.parseAsync(process.argv); 51 | } 52 | 53 | main() 54 | .then(() => process.exit(0)) 55 | .catch((err) => { 56 | console.error("Error:", err); 57 | process.exit(1); 58 | }); 59 | -------------------------------------------------------------------------------- /l1-contracts/scripts/replace-tx.ts: -------------------------------------------------------------------------------- 1 | import { Command } from "commander"; 2 | import { Wallet } from "ethers"; 3 | import { formatUnits } from "ethers/lib/utils"; 4 | import { web3Provider } from "./utils"; 5 | 6 | const provider = web3Provider(); 7 | 8 | async function main() { 9 | const program = new Command(); 10 | 11 | program.version("0.1.0").name("replace-tx").description("replace a tx"); 12 | 13 | program 14 | .requiredOption("--private-key ") 15 | .requiredOption("--to ") 16 | .requiredOption("--value ") 17 | .requiredOption("--data ") 18 | .requiredOption("--nonce ") 19 | .requiredOption("--gas-price ") 20 | .requiredOption("--gas-limit ") 21 | .action(async (cmd) => { 22 | const deployWallet = new Wallet(cmd.privateKey, provider); 23 | console.log(`Using deployer wallet: ${deployWallet.address}`); 24 | 25 | const nonce = cmd.nonce; 26 | console.log(`Using nonce: ${nonce}`); 27 | 28 | const gasPrice = cmd.gasPrice; 29 | console.log(`Using gas price: ${formatUnits(gasPrice, "gwei")} gwei`); 30 | 31 | const gasLimit = cmd.gasLimit; 32 | console.log(`Using gas limit: ${gasLimit}`); 33 | 34 | await deployWallet.sendTransaction({ 35 | to: cmd.to, 36 | value: cmd.value, 37 | data: cmd.data, 38 | nonce, 39 | gasPrice, 40 | gasLimit, 41 | }); 42 | }); 43 | 44 | await program.parseAsync(process.argv); 45 | } 46 | 47 | main() 48 | .then(() => process.exit(0)) 49 | .catch((err) => { 50 | console.error("Error:", err); 51 | process.exit(1); 52 | }); 53 | -------------------------------------------------------------------------------- /l1-contracts/scripts/set-admin.ts: -------------------------------------------------------------------------------- 1 | import { Command } from "commander"; 2 | import { ethers, Wallet } from "ethers"; 3 | import { Deployer } from "../src.ts/deploy"; 4 | import { formatUnits, parseUnits } from "ethers/lib/utils"; 5 | import { getAddressFromEnv, web3Provider } from "./utils"; 6 | 7 | const provider = web3Provider(); 8 | 9 | async function main() { 10 | const program = new Command(); 11 | 12 | program.version("0.1.0").name("set-admin"); 13 | 14 | program 15 | .option("--validator ", "Validator address") 16 | .option("--gas-price ") 17 | .action(async (cmd) => { 18 | const deployWallet = new Wallet(process.env.GOVERNOR_PRIVATE_KEY, provider); 19 | console.log(`Using deployer wallet: ${deployWallet.address}`); 20 | 21 | const gasPrice = cmd.gasPrice ? parseUnits(cmd.gasPrice, "gwei") : await provider.getGasPrice(); 22 | console.log(`Using gas price: ${formatUnits(gasPrice, "gwei")} gwei`); 23 | 24 | const ownerAddress = deployWallet.address; 25 | if (!cmd.validator) { 26 | console.log("Validator address is not provided"); 27 | return; 28 | } 29 | const validatorAddress = cmd.validator; 30 | 31 | const deployer = new Deployer({ 32 | deployWallet, 33 | ownerAddress, 34 | verbose: true, 35 | }); 36 | 37 | const governance = deployer.governanceContract(deployWallet); 38 | const zkSync = deployer.zkSyncContract(deployWallet); 39 | 40 | const call = { 41 | target: zkSync.address, 42 | value: 0, 43 | data: zkSync.interface.encodeFunctionData("setValidator", [validatorAddress, true]), 44 | }; 45 | 46 | const operation = { 47 | calls: [call], 48 | predecessor: ethers.constants.HashZero, 49 | salt: ethers.constants.HashZero, 50 | }; 51 | 52 | await (await governance.scheduleTransparent(operation, 0, { gasPrice: gasPrice })).wait(); 53 | await (await governance.execute(operation, { gasPrice: gasPrice })).wait(); 54 | }); 55 | 56 | await program.parseAsync(process.argv); 57 | } 58 | 59 | main() 60 | .then(() => process.exit(0)) 61 | .catch((err) => { 62 | console.error("Error:", err); 63 | process.exit(1); 64 | }); 65 | -------------------------------------------------------------------------------- /l1-contracts/scripts/set-gateway.ts: -------------------------------------------------------------------------------- 1 | import { Command } from "commander"; 2 | import { ethers, Wallet } from "ethers"; 3 | import { Deployer } from "../src.ts/deploy"; 4 | import { formatUnits, parseUnits } from "ethers/lib/utils"; 5 | import { getHashFromEnv, web3Provider } from "./utils"; 6 | 7 | const provider = web3Provider(); 8 | 9 | async function main() { 10 | const program = new Command(); 11 | 12 | program.version("0.1.0").name("set-gateway"); 13 | 14 | program 15 | .option("--gateway ") 16 | .option("--gas-price ") 17 | .action(async (cmd) => { 18 | const deployWallet = new Wallet(getHashFromEnv("GOVERNOR_PRIVATE_KEY"), provider); 19 | console.log(`Using deployer wallet: ${deployWallet.address}`); 20 | 21 | const gasPrice = cmd.gasPrice ? parseUnits(cmd.gasPrice, "gwei") : await provider.getGasPrice(); 22 | console.log(`Using gas price: ${formatUnits(gasPrice, "gwei")} gwei`); 23 | 24 | const ownerAddress = deployWallet.address; 25 | const gatewayAddress = cmd.gateway; 26 | 27 | const deployer = new Deployer({ 28 | deployWallet, 29 | ownerAddress, 30 | verbose: true, 31 | }); 32 | 33 | const governance = deployer.governanceContract(deployWallet); 34 | const zkSync = deployer.zkSyncContract(deployWallet); 35 | 36 | const call = { 37 | target: zkSync.address, 38 | value: 0, 39 | data: zkSync.interface.encodeFunctionData("setGateway", [gatewayAddress]), 40 | }; 41 | 42 | const operation = { 43 | calls: [call], 44 | predecessor: ethers.constants.HashZero, 45 | salt: ethers.constants.HashZero, 46 | }; 47 | 48 | await (await governance.scheduleTransparent(operation, 0, { gasPrice: gasPrice })).wait(); 49 | await (await governance.execute(operation, { gasPrice: gasPrice })).wait(); 50 | }); 51 | 52 | await program.parseAsync(process.argv); 53 | } 54 | 55 | main() 56 | .then(() => process.exit(0)) 57 | .catch((err) => { 58 | console.error("Error:", err); 59 | process.exit(1); 60 | }); 61 | -------------------------------------------------------------------------------- /l1-contracts/scripts/token-info.ts: -------------------------------------------------------------------------------- 1 | import { Command } from "commander"; 2 | import { ethers } from "ethers"; 3 | import { web3Provider } from "./utils"; 4 | 5 | const provider = web3Provider(); 6 | 7 | type Token = { 8 | address: string; 9 | name: string | null; 10 | symbol: string | null; 11 | decimals: number | null; 12 | }; 13 | 14 | const TokenInterface = [ 15 | "function name() view returns (string)", 16 | "function symbol() view returns (string)", 17 | "function decimals() view returns (uint)", 18 | ]; 19 | 20 | async function tokenInfo(address: string): Promise { 21 | const contract = new ethers.Contract(address, TokenInterface, provider); 22 | 23 | return { 24 | address: address, 25 | name: await contract.name().catch(() => null), 26 | symbol: await contract.symbol().catch(() => null), 27 | decimals: await contract 28 | .decimals() 29 | .then((decimals) => Number(decimals)) 30 | .catch(() => null), 31 | }; 32 | } 33 | 34 | async function main() { 35 | const program = new Command(); 36 | 37 | program.version("0.1.0").name("token-info").description("deploy testnet erc20 token"); 38 | 39 | program.command("info
").action(async (address: string) => { 40 | console.log(JSON.stringify(await tokenInfo(address), null, 2)); 41 | }); 42 | 43 | await program.parseAsync(process.argv); 44 | } 45 | 46 | main() 47 | .then(() => process.exit(0)) 48 | .catch((err) => { 49 | console.error("Error:", err.message || err); 50 | process.exit(1); 51 | }); 52 | -------------------------------------------------------------------------------- /l1-contracts/scripts/verify-governance.ts: -------------------------------------------------------------------------------- 1 | import * as hardhat from "hardhat"; 2 | import { web3Provider } from "../scripts/utils"; 3 | import { ethers, Wallet } from "ethers"; 4 | 5 | const provider = web3Provider(); 6 | 7 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 8 | function verifyPromise(address: string, constructorArguments?: Array, libraries?: object): Promise { 9 | return new Promise((resolve, reject) => { 10 | hardhat 11 | .run("verify:verify", { address, constructorArguments, libraries }) 12 | .then(() => resolve(`Successfully verified ${address}`)) 13 | .catch((e) => reject(`Failed to verify ${address}\nError: ${e.message}`)); 14 | }); 15 | } 16 | 17 | async function main() { 18 | if (process.env.CHAIN_ETH_NETWORK == "localhost") { 19 | console.log("Skip contract verification on localhost"); 20 | return; 21 | } 22 | 23 | if (!process.env.MISC_ETHERSCAN_API_KEY) { 24 | console.log("Skip contract verification given etherscan api key is missing"); 25 | return; 26 | } 27 | 28 | const deployWallet = new Wallet(process.env.GOVERNOR_PRIVATE_KEY, provider); 29 | const governanceAddress = process.env.CONTRACTS_GOVERNANCE_ADDR; 30 | 31 | console.log(`Verifying governance contract: ${governanceAddress}`); 32 | await verifyPromise(governanceAddress, [deployWallet.address, ethers.constants.AddressZero, 0]); 33 | } 34 | 35 | main() 36 | .then(() => process.exit(0)) 37 | .catch((err) => { 38 | console.error("Error:", err.message || err); 39 | process.exit(1); 40 | }); 41 | -------------------------------------------------------------------------------- /l1-contracts/src.ts/deploy-utils.ts: -------------------------------------------------------------------------------- 1 | import * as hardhat from "hardhat"; 2 | import "@nomiclabs/hardhat-ethers"; 3 | import { ethers } from "ethers"; 4 | import { SingletonFactoryFactory } from "../typechain"; 5 | 6 | export async function deployViaCreate2( 7 | deployWallet: ethers.Wallet, 8 | contractName: string, 9 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 10 | args: any[], 11 | create2Salt: string, 12 | ethTxOptions: ethers.providers.TransactionRequest, 13 | create2FactoryAddress: string, 14 | verbose: boolean = true, 15 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 16 | libraries?: any 17 | ): Promise<[string, string]> { 18 | // [address, txHash] 19 | 20 | const log = (msg: string) => { 21 | if (verbose) { 22 | console.log(msg); 23 | } 24 | }; 25 | log(`Deploying ${contractName}`); 26 | 27 | const create2Factory = SingletonFactoryFactory.connect(create2FactoryAddress, deployWallet); 28 | const contractFactory = await hardhat.ethers.getContractFactory(contractName, { 29 | signer: deployWallet, 30 | libraries, 31 | }); 32 | const bytecode = contractFactory.getDeployTransaction(...[...args, ethTxOptions]).data; 33 | const expectedAddress = ethers.utils.getCreate2Address( 34 | create2Factory.address, 35 | create2Salt, 36 | ethers.utils.keccak256(bytecode) 37 | ); 38 | 39 | const deployedBytecodeBefore = await deployWallet.provider.getCode(expectedAddress); 40 | if (ethers.utils.hexDataLength(deployedBytecodeBefore) > 0) { 41 | log(`Contract ${contractName} already deployed`); 42 | return [expectedAddress, ethers.constants.HashZero]; 43 | } 44 | 45 | const tx = await create2Factory.deploy(bytecode, create2Salt, ethTxOptions); 46 | const receipt = await tx.wait(2); 47 | 48 | const gasUsed = receipt.gasUsed; 49 | log(`${contractName} deployed, gasUsed: ${gasUsed.toString()}`); 50 | 51 | while (true) { 52 | const deployedBytecodeAfter = await deployWallet.provider.getCode(expectedAddress); 53 | if (ethers.utils.hexDataLength(deployedBytecodeAfter) == 0) { 54 | console.log("Failed to deploy bytecode via create2 factory, trying again"); 55 | await new Promise((r) => setTimeout(r, 60000)); 56 | } else { 57 | console.log(`Successfully deployed ${contractName} bytecode via create2 factory`); 58 | break; 59 | } 60 | } 61 | 62 | return [expectedAddress, tx.hash]; 63 | } 64 | -------------------------------------------------------------------------------- /l1-contracts/test/foundry/unit/concrete/Admin/Authorization.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import {AdminTest} from "./_Admin_Shared.t.sol"; 6 | import {FeeParams, PubdataPricingMode} from "solpp/zksync/Storage.sol"; 7 | 8 | contract AuthorizationTest is AdminTest { 9 | function test_SetPendingAdmin_RevertWhen_AdminNotGovernanceOwner() public { 10 | address newAdmin = address(0x1337); 11 | vm.prank(owner); 12 | vm.expectRevert(bytes.concat("1g")); 13 | proxyAsAdmin.setPendingAdmin(newAdmin); 14 | } 15 | 16 | function test_changeFeeParams() public { 17 | FeeParams memory newParams = FeeParams({ 18 | pubdataPricingMode: PubdataPricingMode.Rollup, 19 | batchOverheadL1Gas: 1_000, 20 | maxPubdataPerBatch: 1_000, 21 | maxL2GasPerBatch: 80_000_000, 22 | priorityTxMaxPubdata: 99, 23 | minimalL2GasPrice: 500_000_000 24 | }); 25 | vm.prank(governor); 26 | proxyAsAdmin.changeFeeParams(newParams); 27 | 28 | bytes32 correctNewFeeParamsHash = keccak256(abi.encode(newParams)); 29 | bytes32 currentFeeParamsHash = keccak256(abi.encode(proxyAsGettersMock.getFeeParams())); 30 | 31 | require(currentFeeParamsHash == correctNewFeeParamsHash, "Fee params were not changed correctly"); 32 | } 33 | 34 | function test_changeFeeParams_RevertWhen_PriorityTxMaxPubdataHigherThanMaxPubdataPerBatch() public { 35 | FeeParams memory newParams = FeeParams({ 36 | pubdataPricingMode: PubdataPricingMode.Rollup, 37 | batchOverheadL1Gas: 1_000, 38 | maxPubdataPerBatch: 1_000, 39 | maxL2GasPerBatch: 80_000_000, 40 | priorityTxMaxPubdata: 1_001, 41 | minimalL2GasPrice: 500_000_000 42 | }); 43 | vm.prank(governor); 44 | vm.expectRevert(bytes.concat("n6")); 45 | proxyAsAdmin.changeFeeParams(newParams); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /l1-contracts/test/foundry/unit/concrete/Bridge/L1WethBridge/ClaimFailedDeposit.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import {L1WethBridgeTest} from "./_L1WethBridge_Shared.t.sol"; 6 | 7 | contract ClaimFailedDepositTest is L1WethBridgeTest { 8 | function test_RevertWhen_Claiming() public { 9 | vm.expectRevert("Method not supported. Failed deposit funds are sent to the L2 refund recipient address."); 10 | bridgeProxy.claimFailedDeposit(address(0), address(0), bytes32(0), 0, 0, 0, new bytes32[](0)); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /l1-contracts/test/foundry/unit/concrete/Bridge/L1WethBridge/L2TokenAddress.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import {L1WethBridgeTest} from "./_L1WethBridge_Shared.t.sol"; 6 | 7 | contract L2TokenAddressTest is L1WethBridgeTest { 8 | function test_l1TokenSameAsL1WethAddress() public { 9 | address l1Token = address(l1Weth); 10 | 11 | address l2Token = bridgeProxy.l2TokenAddress(l1Token); 12 | 13 | address expectedAddress = bridgeProxy.l2WethAddress(); 14 | bool isSameAddress = l2Token == expectedAddress; 15 | assertTrue(isSameAddress, "l2TokenAddress != l2WethAddress"); 16 | } 17 | 18 | function test_l1TokenNotSameAsL1WethAddress() public { 19 | address l1Token = makeAddr("l1Token"); 20 | 21 | address l2Token = bridgeProxy.l2TokenAddress(l1Token); 22 | 23 | address expectedAddress = address(0); 24 | bool isSameAddress = l2Token == expectedAddress; 25 | assertTrue(isSameAddress, "l2TokenAddress != address(0)"); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /l1-contracts/test/foundry/unit/concrete/Bridge/L1WethBridge/Receive.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.19; 4 | 5 | import {Vm} from "forge-std/Test.sol"; 6 | import {L1WethBridgeTest} from "./_L1WethBridge_Shared.t.sol"; 7 | 8 | contract ReceiveTest is L1WethBridgeTest { 9 | function test_ReceiveEthFromL1WethAddress() public { 10 | uint256 amount = 10000; 11 | 12 | hoax(address(l1Weth)); 13 | 14 | vm.recordLogs(); 15 | 16 | (bool success, ) = payable(address(bridgeProxy)).call{value: amount}(""); 17 | require(success, "received unexpected revert"); 18 | 19 | Vm.Log[] memory entries = vm.getRecordedLogs(); 20 | assertEq(entries.length, 1); 21 | assertEq(entries[0].topics.length, 1); 22 | assertEq(entries[0].topics[0], keccak256("EthReceived(uint256)")); 23 | assertEq(abi.decode(entries[0].data, (uint256)), amount); 24 | } 25 | 26 | function test_ReceiveEthFromZkSyncAddress() public { 27 | uint256 amount = 10000; 28 | 29 | hoax(address(bridgeProxy.zkSync())); 30 | 31 | vm.recordLogs(); 32 | 33 | (bool success, ) = payable(address(bridgeProxy)).call{value: amount}(""); 34 | require(success, "received unexpected revert"); 35 | 36 | Vm.Log[] memory entries = vm.getRecordedLogs(); 37 | assertEq(entries.length, 1); 38 | assertEq(entries[0].topics.length, 1); 39 | assertEq(entries[0].topics[0], keccak256("EthReceived(uint256)")); 40 | assertEq(abi.decode(entries[0].data, (uint256)), amount); 41 | } 42 | 43 | function test_RevertWhen_ReceiveEthFromRandomAddress() public { 44 | uint256 amount = 10000; 45 | 46 | hoax(randomSigner); 47 | 48 | vm.expectRevert(bytes.concat("pn")); 49 | (bool revertAsExpected, ) = payable(address(bridgeProxy)).call{value: amount}(""); 50 | assertTrue(revertAsExpected, "expectRevert: call did not revert"); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /l1-contracts/test/foundry/unit/concrete/DiamondCut/_DiamondCut_Shared.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.19; 3 | 4 | // solhint-disable max-line-length 5 | 6 | import {Test} from "forge-std/Test.sol"; 7 | import {DiamondCutTestContract} from "../../../../../cache/solpp-generated-contracts/dev-contracts/test/DiamondCutTestContract.sol"; 8 | import {GettersFacet} from "../../../../../cache/solpp-generated-contracts/zksync/facets/Getters.sol"; 9 | 10 | // solhint-enable max-line-length 11 | 12 | contract DiamondCutTest is Test { 13 | DiamondCutTestContract internal diamondCutTestContract; 14 | GettersFacet internal gettersFacet; 15 | } 16 | -------------------------------------------------------------------------------- /l1-contracts/test/foundry/unit/concrete/Governance/Fallback.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.19; 3 | 4 | import {GovernanceTest} from "./_Governance_Shared.t.sol"; 5 | 6 | contract ExecutingTest is GovernanceTest { 7 | function test_SendEtherToGovernance() public { 8 | startHoax(randomSigner); 9 | payable(address(governance)).transfer(100); 10 | } 11 | 12 | function test_RevertWhen_CallWithRandomData() public { 13 | startHoax(randomSigner); 14 | (bool success, ) = address(governance).call{value: 100}("11223344"); 15 | assertFalse(success); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /l1-contracts/test/foundry/unit/concrete/Governance/SelfUpgrades.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.19; 3 | 4 | import {GovernanceTest} from "./_Governance_Shared.t.sol"; 5 | import {Utils} from "../Utils/Utils.sol"; 6 | import {IGovernance} from "../../../../../cache/solpp-generated-contracts/governance/IGovernance.sol"; 7 | 8 | contract SeflUpgradesTest is GovernanceTest { 9 | event ChangeSecurityCouncil(address _securityCouncilBefore, address _securityCouncilAfter); 10 | 11 | event ChangeMinDelay(uint256 _delayBefore, uint256 _delayAfter); 12 | 13 | function test_UpgradeDelay() public { 14 | vm.startPrank(owner); 15 | uint256 delayBefore = governance.minDelay(); 16 | uint256 newDelay = 100000; 17 | IGovernance.Operation memory op = operationWithOneCallZeroSaltAndPredecessor( 18 | address(governance), 19 | 0, 20 | abi.encodeCall(IGovernance.updateDelay, (newDelay)) 21 | ); 22 | 23 | governance.scheduleTransparent(op, 0); 24 | // Check event 25 | vm.expectEmit(false, false, false, true); 26 | emit ChangeMinDelay(delayBefore, newDelay); 27 | governance.execute(op); 28 | uint256 delayAfter = governance.minDelay(); 29 | assertTrue(delayBefore != delayAfter); 30 | assertTrue(newDelay == delayAfter); 31 | } 32 | 33 | function test_UpgradeSecurityCouncil() public { 34 | vm.startPrank(owner); 35 | address securityCouncilBefore = governance.securityCouncil(); 36 | address newSecurityCouncil = address(bytes20(Utils.randomBytes32("newSecurityCouncil"))); 37 | IGovernance.Operation memory op = operationWithOneCallZeroSaltAndPredecessor( 38 | address(governance), 39 | 0, 40 | abi.encodeCall(IGovernance.updateSecurityCouncil, (newSecurityCouncil)) 41 | ); 42 | 43 | governance.scheduleTransparent(op, 0); 44 | 45 | // Check event 46 | vm.expectEmit(false, false, false, true); 47 | emit ChangeSecurityCouncil(securityCouncilBefore, newSecurityCouncil); 48 | governance.execute(op); 49 | address securityCouncilAfter = governance.securityCouncil(); 50 | assertTrue(securityCouncilBefore != securityCouncilAfter); 51 | assertTrue(newSecurityCouncil == securityCouncilAfter); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /l1-contracts/test/foundry/unit/concrete/Merkle/Merkle.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.19; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {MerkleTest} from "solpp/dev-contracts/test/MerkleTest.sol"; 6 | import {MerkleTreeNoSort} from "./MerkleTreeNoSort.sol"; 7 | 8 | contract MerkleTestTest is Test { 9 | MerkleTreeNoSort merkleTree; 10 | MerkleTest merkleTest; 11 | bytes32[] elements; 12 | bytes32 root; 13 | 14 | function setUp() public { 15 | merkleTree = new MerkleTreeNoSort(); 16 | merkleTest = new MerkleTest(); 17 | 18 | for (uint256 i = 0; i < 65; i++) { 19 | elements.push(keccak256(abi.encodePacked(i))); 20 | } 21 | 22 | root = merkleTree.getRoot(elements); 23 | } 24 | 25 | function testElements(uint256 i) public { 26 | vm.assume(i < elements.length); 27 | bytes32 leaf = elements[i]; 28 | bytes32[] memory proof = merkleTree.getProof(elements, i); 29 | 30 | bytes32 rootFromContract = merkleTest.calculateRoot(proof, i, leaf); 31 | 32 | assertEq(rootFromContract, root); 33 | } 34 | 35 | function testFirstElement() public { 36 | testElements(0); 37 | } 38 | 39 | function testLastElement() public { 40 | testElements(elements.length - 1); 41 | } 42 | 43 | function testEmptyProof_shouldRevert() public { 44 | bytes32 leaf = elements[0]; 45 | bytes32[] memory proof; 46 | 47 | vm.expectRevert(bytes("xc")); 48 | merkleTest.calculateRoot(proof, 0, leaf); 49 | } 50 | 51 | function testLeafIndexTooBig_shouldRevert() public { 52 | bytes32 leaf = elements[0]; 53 | bytes32[] memory proof = merkleTree.getProof(elements, 0); 54 | 55 | vm.expectRevert(bytes("px")); 56 | merkleTest.calculateRoot(proof, 2 ** 255, leaf); 57 | } 58 | 59 | function testProofLengthTooLarge_shouldRevert() public { 60 | bytes32 leaf = elements[0]; 61 | bytes32[] memory proof = new bytes32[](256); 62 | 63 | vm.expectRevert(bytes("bt")); 64 | merkleTest.calculateRoot(proof, 0, leaf); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /l1-contracts/test/foundry/unit/concrete/Merkle/MerkleTreeNoSort.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.19; 3 | 4 | import "murky/common/MurkyBase.sol"; 5 | 6 | contract MerkleTreeNoSort is MurkyBase { 7 | /******************** 8 | * HASHING FUNCTION * 9 | ********************/ 10 | 11 | /// The original Merkle tree contains the ascending sort and concat prior to hashing, so we need to override it 12 | function hashLeafPairs(bytes32 left, bytes32 right) public pure override returns (bytes32 _hash) { 13 | assembly { 14 | mstore(0x0, left) 15 | mstore(0x20, right) 16 | _hash := keccak256(0x0, 0x40) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /l1-contracts/test/foundry/unit/concrete/PriorityQueue/OnEmptyQueue.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.19; 2 | 3 | import {PriorityQueueSharedTest} from "./_PriorityQueue_Shared.t.sol"; 4 | 5 | contract OnEmptyQueueTest is PriorityQueueSharedTest { 6 | function test_gets() public { 7 | assertEq(0, priorityQueue.getSize()); 8 | assertEq(0, priorityQueue.getFirstUnprocessedPriorityTx()); 9 | assertEq(0, priorityQueue.getTotalPriorityTxs()); 10 | assertTrue(priorityQueue.isEmpty()); 11 | } 12 | 13 | function test_failGetFront() public { 14 | vm.expectRevert(bytes("D")); 15 | priorityQueue.front(); 16 | } 17 | 18 | function test_failPopFront() public { 19 | vm.expectRevert(bytes("s")); 20 | priorityQueue.popFront(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /l1-contracts/test/foundry/unit/concrete/PriorityQueue/PushOperations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.8.19; 2 | 3 | import {PriorityQueueSharedTest} from "./_PriorityQueue_Shared.t.sol"; 4 | import {PriorityOperation} from "../../../../../cache/solpp-generated-contracts/dev-contracts/test/PriorityQueueTest.sol"; 5 | 6 | contract PushOperationsTest is PriorityQueueSharedTest { 7 | uint public constant NUMBER_OPERATIONS = 10; 8 | 9 | function setUp() public { 10 | push_mock_entries(NUMBER_OPERATIONS); 11 | } 12 | 13 | function test_front() public { 14 | assertEq(NUMBER_OPERATIONS, priorityQueue.getSize()); 15 | PriorityOperation memory front = priorityQueue.front(); 16 | assertEq(keccak256(abi.encode(0)), front.canonicalTxHash); 17 | assertEq(uint64(0), front.expirationTimestamp); 18 | assertEq(uint192(0), front.layer2Tip); 19 | // This is 'front' and not popFront, so the amount should not change. 20 | assertEq(NUMBER_OPERATIONS, priorityQueue.getSize()); 21 | assertEq(0, priorityQueue.getFirstUnprocessedPriorityTx()); 22 | assertEq(NUMBER_OPERATIONS, priorityQueue.getTotalPriorityTxs()); 23 | assertFalse(priorityQueue.isEmpty()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /l1-contracts/test/foundry/unit/concrete/PriorityQueue/_PriorityQueue_Shared.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.19; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {PriorityQueueTest, PriorityOperation} from "solpp/dev-contracts/test/PriorityQueueTest.sol"; 6 | 7 | contract PriorityQueueSharedTest is Test { 8 | PriorityQueueTest internal priorityQueue; 9 | 10 | constructor() { 11 | priorityQueue = new PriorityQueueTest(); 12 | } 13 | 14 | // Pushes 'count' entries into the priority queue. 15 | function push_mock_entries(uint count) public { 16 | for (uint i = 0; i < count; ++i) { 17 | PriorityOperation memory dummyOp = PriorityOperation({ 18 | canonicalTxHash: keccak256(abi.encode(i)), 19 | expirationTimestamp: uint64(i), 20 | layer2Tip: uint192(i) 21 | }); 22 | priorityQueue.pushBack(dummyOp); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /l1-contracts/test/foundry/unit/concrete/TransactionValidator/_TransactionValidator_Shared.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.19; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | import {IMailbox} from "solpp/zksync/interfaces/IMailbox.sol"; 6 | //import {TransactionValidator} from "solpp/zksync/libraries/TransactionValidator.sol"; 7 | import {TransactionValidator} from "cache/solpp-generated-contracts/zksync/libraries/TransactionValidator.sol"; 8 | 9 | contract TransactionValidatorSharedTest is Test { 10 | constructor() {} 11 | 12 | function createTestTransaction() public pure returns (IMailbox.L2CanonicalTransaction memory testTx) { 13 | testTx = IMailbox.L2CanonicalTransaction({ 14 | txType: 0, 15 | from: uint256(uint160(1_000_000_000)), 16 | to: uint256(uint160(0)), 17 | gasLimit: 500000, 18 | gasPerPubdataByteLimit: 800, 19 | maxFeePerGas: uint256(0), 20 | maxPriorityFeePerGas: uint256(0), 21 | paymaster: uint256(0), 22 | nonce: uint256(0), 23 | value: 0, 24 | reserved: [uint256(0), uint256(0), uint256(0), uint256(0)], 25 | data: new bytes(0), 26 | signature: new bytes(0), 27 | factoryDeps: new uint256[](0), 28 | paymasterInput: new bytes(0), 29 | reservedDynamic: new bytes(0) 30 | }); 31 | } 32 | 33 | function createUpgradeTransaction() public pure returns (IMailbox.L2CanonicalTransaction memory testTx) { 34 | testTx = createTestTransaction(); 35 | testTx.from = uint256(0x8001); 36 | testTx.to = uint256(0x8007); 37 | } 38 | 39 | function validateL1ToL2Transaction( 40 | IMailbox.L2CanonicalTransaction memory _transaction, 41 | uint256 _priorityTxMaxGasLimit, 42 | uint256 _priorityTxMaxPubdata 43 | ) public pure { 44 | TransactionValidator.validateL1ToL2Transaction( 45 | _transaction, 46 | abi.encode(_transaction), 47 | _priorityTxMaxGasLimit, 48 | _priorityTxMaxPubdata 49 | ); 50 | } 51 | 52 | function getOverheadForTransaction(uint256 _encodingLength) public pure returns (uint256) { 53 | return TransactionValidator.getOverheadForTransaction(_encodingLength); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /l1-contracts/test/foundry/unit/concrete/UncheckedMath/UncheckedAdd.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.19; 3 | 4 | import {UncheckedMathTest} from "./_UncheckedMath_Shared.t.sol"; 5 | import {UncheckedMath} from "solpp/common/libraries/UncheckedMath.sol"; 6 | 7 | contract UncheckedAddTest is UncheckedMathTest { 8 | using UncheckedMath for uint256; 9 | 10 | function test_Add() public { 11 | uint256 a = 1234; 12 | uint256 b = 4321; 13 | uint256 c = a.uncheckedAdd(b); 14 | assertEq(c, 5555); 15 | } 16 | 17 | function test_AddWithOverflow() public { 18 | uint256 a = type(uint256).max; 19 | uint256 b = 1; 20 | 21 | // uncheckedAdd does not fail 22 | uint256 c = a.uncheckedAdd(b); 23 | assertEq(c, 0); 24 | 25 | // regular addition fails with overflow 26 | vm.expectRevert(); 27 | a + b; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /l1-contracts/test/foundry/unit/concrete/UncheckedMath/UncheckedInc.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.19; 3 | 4 | import {UncheckedMathTest} from "./_UncheckedMath_Shared.t.sol"; 5 | import {UncheckedMath} from "solpp/common/libraries/UncheckedMath.sol"; 6 | 7 | contract UncheckedIncTest is UncheckedMathTest { 8 | using UncheckedMath for uint256; 9 | 10 | function test_Inc() public { 11 | uint256 a = 1234; 12 | uint256 c = a.uncheckedInc(); 13 | assertEq(c, 1235); 14 | } 15 | 16 | function test_IncWithOverflow() public { 17 | uint256 a = type(uint256).max; 18 | 19 | // uncheckedInc does not fail 20 | uint256 c = a.uncheckedInc(); 21 | assertEq(c, 0); 22 | 23 | // regular addition fails with overflow 24 | vm.expectRevert(); 25 | a + 1; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /l1-contracts/test/foundry/unit/concrete/UncheckedMath/_UncheckedMath_Shared.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.19; 3 | 4 | import {Test} from "forge-std/Test.sol"; 5 | 6 | contract UncheckedMathTest is Test {} 7 | -------------------------------------------------------------------------------- /l1-contracts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "types": ["node", "mocha"], 4 | "downlevelIteration": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /l1-contracts/upgrade-system/index.ts: -------------------------------------------------------------------------------- 1 | import { program } from "commander"; 2 | import { command as facetCuts } from "./facets"; 3 | import { command as verifier } from "./verifier"; 4 | const COMMANDS = [facetCuts, verifier]; 5 | 6 | async function main() { 7 | const ZKSYNC_HOME = process.env.ZKSYNC_HOME; 8 | 9 | if (!ZKSYNC_HOME) { 10 | throw new Error("Please set $ZKSYNC_HOME to the root of zkSync repo!"); 11 | } else { 12 | process.chdir(ZKSYNC_HOME); 13 | } 14 | program.version("0.1.0").name("upgrade-system").description("set of tools for upgrade l1 part of the system"); 15 | 16 | for (const command of COMMANDS) { 17 | program.addCommand(command); 18 | } 19 | await program.parseAsync(process.argv); 20 | } 21 | 22 | main().catch((err: Error) => { 23 | console.error("Error:", err.message || err); 24 | process.exitCode = 1; 25 | }); 26 | -------------------------------------------------------------------------------- /l1-contracts/upgrade-system/utils.ts: -------------------------------------------------------------------------------- 1 | /// @dev This method checks if the overrides contain a gasPrice (or maxFeePerGas), if not it will insert 2 | /// the maxFeePerGas 3 | import type { ethers } from "ethers"; 4 | 5 | export async function insertGasPrice(l1Provider: ethers.providers.Provider, overrides: ethers.PayableOverrides) { 6 | if (!overrides.gasPrice && !overrides.maxFeePerGas) { 7 | const l1FeeData = await l1Provider.getFeeData(); 8 | 9 | // Sometimes baseFeePerGas is not available, so we use gasPrice instead. 10 | const baseFee = l1FeeData.lastBaseFeePerGas || l1FeeData.gasPrice; 11 | 12 | // ethers.js by default uses multiplcation by 2, but since the price for the L2 part 13 | // will depend on the L1 part, doubling base fee is typically too much. 14 | const maxFeePerGas = baseFee.mul(3).div(2).add(l1FeeData.maxPriorityFeePerGas); 15 | 16 | overrides.maxFeePerGas = maxFeePerGas; 17 | overrides.maxPriorityFeePerGas = l1FeeData.maxPriorityFeePerGas; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /l1-contracts/upgrade-system/verifier.ts: -------------------------------------------------------------------------------- 1 | import { Command } from "commander"; 2 | import type { BigNumber } from "ethers"; 3 | import { ethers } from "ethers"; 4 | import * as fs from "fs"; 5 | import { deployViaCreate2 } from "../src.ts/deploy-utils"; 6 | import { web3Url } from "zk/build/utils"; 7 | import * as path from "path"; 8 | import { insertGasPrice } from "./utils"; 9 | 10 | const testConfigPath = path.join(process.env.ZKSYNC_HOME as string, "etc/test_config/constant"); 11 | const ethTestConfig = JSON.parse(fs.readFileSync(`${testConfigPath}/eth.json`, { encoding: "utf-8" })); 12 | 13 | async function deployVerifier( 14 | l1Rpc: string, 15 | create2Address: string, 16 | nonce?: number, 17 | gasPrice?: BigNumber, 18 | privateKey?: string, 19 | file?: string, 20 | create2Salt?: string 21 | ) { 22 | const provider = new ethers.providers.JsonRpcProvider(l1Rpc); 23 | const wallet = privateKey 24 | ? new ethers.Wallet(privateKey, provider) 25 | : ethers.Wallet.fromMnemonic( 26 | process.env.MNEMONIC ? process.env.MNEMONIC : ethTestConfig.mnemonic, 27 | "m/44'/60'/0'/0/1" 28 | ).connect(provider); 29 | 30 | create2Salt = create2Salt ?? ethers.constants.HashZero; 31 | 32 | const ethTxOptions = {}; 33 | if (!nonce) { 34 | ethTxOptions["nonce"] = await wallet.getTransactionCount(); 35 | } 36 | if (!gasPrice) { 37 | await insertGasPrice(provider, ethTxOptions); 38 | } 39 | ethTxOptions["gasLimit"] = 10_000_000; 40 | const [address, txHash] = await deployViaCreate2( 41 | wallet, 42 | "Verifier", 43 | [], 44 | create2Salt, 45 | ethTxOptions, 46 | create2Address, 47 | true 48 | ); 49 | 50 | console.log(JSON.stringify({ address, txHash }, null, 2)); 51 | if (file) { 52 | fs.writeFileSync(file, JSON.stringify({ address, txHash }, null, 2)); 53 | } 54 | return [address, txHash]; 55 | } 56 | 57 | export const command = new Command("verifier").description("Verifier commands"); 58 | 59 | command 60 | .command("deploy") 61 | .option("--l1rpc ") 62 | .option("--private-key ") 63 | .option("--create2-address ") 64 | .option("--file ") 65 | .option("--nonce ") 66 | .option("--gas-price ") 67 | .option("--create2-salt ") 68 | .description("deploy verifier") 69 | .action(async (cmd) => { 70 | const l1Rpc = cmd.l1Rpc ?? web3Url(); 71 | await deployVerifier(l1Rpc, cmd.create2Address, cmd.nonce, cmd.gasPrice, cmd.privateKey, cmd.file, cmd.create2Salt); 72 | }); 73 | -------------------------------------------------------------------------------- /l2-contracts/.env: -------------------------------------------------------------------------------- 1 | CHAIN_ETH_NETWORK=localhost 2 | CONTRACTS_PRIORITY_TX_MAX_ERGS_LIMIT=50000 3 | ETH_CLIENT_WEB3_URL=http://127.0.0.1:8545 4 | -------------------------------------------------------------------------------- /l2-contracts/contracts/Dependencies.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; 6 | -------------------------------------------------------------------------------- /l2-contracts/contracts/ForceDeployUpgrader.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 2 | 3 | pragma solidity 0.8.20; 4 | 5 | import {IContractDeployer, DEPLOYER_SYSTEM_CONTRACT} from "./L2ContractHelper.sol"; 6 | 7 | /// @custom:security-contact security@matterlabs.dev 8 | /// @notice The contract that calls force deployment during the L2 system contract upgrade. 9 | /// @notice It is supposed to be used as an implementation of the ComplexUpgrader. 10 | contract ForceDeployUpgrader { 11 | /// @notice A function that performs force deploy 12 | /// @param _forceDeployments The force deployments to perform. 13 | function forceDeploy(IContractDeployer.ForceDeployment[] calldata _forceDeployments) external payable { 14 | IContractDeployer(DEPLOYER_SYSTEM_CONTRACT).forceDeployOnAddresses{value: msg.value}(_forceDeployments); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /l2-contracts/contracts/bridge/interfaces/IL1Bridge.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | /// @title L1 Bridge contract interface 6 | /// @author Matter Labs 7 | /// @custom:security-contact security@matterlabs.dev 8 | interface IL1Bridge { 9 | function finalizeWithdrawal( 10 | uint256 _l2BatchNumber, 11 | uint256 _l2MessageIndex, 12 | uint16 _l2TxNumberInBatch, 13 | bytes calldata _message, 14 | bytes32[] calldata _merkleProof 15 | ) external; 16 | } 17 | -------------------------------------------------------------------------------- /l2-contracts/contracts/bridge/interfaces/IL2Bridge.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | /// @author Matter Labs 6 | interface IL2Bridge { 7 | error FunctionNotSupported(); 8 | 9 | event FinalizeDeposit( 10 | address indexed l1Sender, 11 | address indexed l2Receiver, 12 | address indexed l2Token, 13 | uint256 amount 14 | ); 15 | 16 | event FinalizeDepositToMerge( 17 | address indexed l1Sender, 18 | address indexed l2Receiver, 19 | address indexed l2Token, 20 | address mergeToken, 21 | uint256 amount 22 | ); 23 | 24 | event WithdrawalInitiated( 25 | address indexed l2Sender, 26 | address indexed l1Receiver, 27 | address indexed l2Token, 28 | uint256 amount 29 | ); 30 | 31 | function finalizeDeposit( 32 | address _l1Sender, 33 | address _l2Receiver, 34 | address _l1Token, 35 | uint256 _amount, 36 | bytes calldata _data 37 | ) external payable; 38 | 39 | function finalizeDepositToMerge( 40 | address _l1Sender, 41 | address _l2Receiver, 42 | address _l1Token, 43 | uint256 _amount, 44 | bytes calldata _data 45 | ) external payable; 46 | 47 | function withdraw(address _l1Receiver, address _l2Token, uint256 _amount) external; 48 | 49 | function l1TokenAddress(address _l2Token) external view returns (address); 50 | 51 | function l2TokenAddress(address _l1Token) external view returns (address); 52 | 53 | function l1Bridge() external view returns (address); 54 | } 55 | -------------------------------------------------------------------------------- /l2-contracts/contracts/bridge/interfaces/IL2StandardToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | interface IL2StandardToken { 6 | event BridgeInitialize(address indexed l1Token, string name, string symbol, uint8 decimals); 7 | 8 | event BridgeMint(address indexed _account, uint256 _amount); 9 | 10 | event BridgeBurn(address indexed _account, uint256 _amount); 11 | 12 | function bridgeMint(address _account, uint256 _amount) external; 13 | 14 | function bridgeBurn(address _account, uint256 _amount) external; 15 | 16 | function l1Address() external view returns (address); 17 | 18 | function l2Bridge() external view returns (address); 19 | } 20 | -------------------------------------------------------------------------------- /l2-contracts/contracts/bridge/interfaces/IL2Weth.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | interface IL2Weth { 5 | event Initialize(string name, string symbol, uint8 decimals); 6 | 7 | function deposit() external payable; 8 | 9 | function withdraw(uint256 _amount) external; 10 | 11 | function depositTo(address _to) external payable; 12 | 13 | function withdrawTo(address _to, uint256 _amount) external; 14 | } 15 | -------------------------------------------------------------------------------- /l2-contracts/contracts/bridge/interfaces/IMergeTokenPortal.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.20; 3 | 4 | interface IMergeTokenPortal { 5 | /// @notice Source token info 6 | /// @param isSupported Is the source token supported 7 | /// @param isLocked Is the source token locked 8 | /// @param mergeToken Deposit source token will receive the merge token 9 | /// @param balance Source token balance 10 | /// @param depositLimit Source token deposit limit 11 | struct SourceTokenInfo { 12 | bool isSupported; 13 | bool isLocked; 14 | address mergeToken; 15 | uint256 balance; 16 | uint256 depositLimit; 17 | } 18 | 19 | /** 20 | * @notice Get source token info 21 | * @param _sourceToken Source token address 22 | * @return Source token info 23 | */ 24 | function getSourceTokenInfos(address _sourceToken) external view returns (SourceTokenInfo memory); 25 | 26 | /** 27 | * @notice Deposit source token to mint merge token 28 | * @param _sourceToken Source token address 29 | * @param _amount Deposit amount 30 | * @param _receiver Receiver address 31 | */ 32 | function deposit(address _sourceToken, uint256 _amount, address _receiver) external; 33 | 34 | /** 35 | * @notice Burn merge token and get source token back 36 | * @param _sourceToken Source token address 37 | * @param _amount Withdraw amount 38 | * @param _receiver Recceiver address 39 | */ 40 | function withdraw(address _sourceToken, uint256 _amount, address _receiver) external; 41 | } 42 | -------------------------------------------------------------------------------- /l2-contracts/contracts/interfaces/IPaymasterFlow.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | /** 6 | * @author Matter Labs 7 | * @custom:security-contact security@matterlabs.dev 8 | * @dev The interface that is used for encoding/decoding of 9 | * different types of paymaster flows. 10 | * @notice This is NOT an interface to be implementated 11 | * by contracts. It is just used for encoding. 12 | */ 13 | interface IPaymasterFlow { 14 | function general(bytes calldata input) external; 15 | 16 | function approvalBased(address _token, uint256 _minAllowance, bytes calldata _innerInput) external; 17 | } 18 | -------------------------------------------------------------------------------- /l2-contracts/contracts/vendor/AddressAliasHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | /* 4 | * Copyright 2019-2021, Offchain Labs, Inc. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | pragma solidity 0.8.20; 20 | 21 | library AddressAliasHelper { 22 | uint160 constant offset = uint160(0x1111000000000000000000000000000000001111); 23 | 24 | /// @notice Utility function converts the address that submitted a tx 25 | /// to the inbox on L1 to the msg.sender viewed on L2 26 | /// @param l1Address the address in the L1 that triggered the tx to L2 27 | /// @return l2Address L2 address as viewed in msg.sender 28 | function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) { 29 | unchecked { 30 | l2Address = address(uint160(l1Address) + offset); 31 | } 32 | } 33 | 34 | /// @notice Utility function that converts the msg.sender viewed on L2 to the 35 | /// address that submitted a tx to the inbox on L1 36 | /// @param l2Address L2 address as viewed in msg.sender 37 | /// @return l1Address the address in the L1 that triggered the tx to L2 38 | function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) { 39 | unchecked { 40 | l1Address = address(uint160(l2Address) - offset); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /l2-contracts/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "@matterlabs/hardhat-zksync-solc"; 2 | import "@matterlabs/hardhat-zksync-verify"; 3 | import "@nomicfoundation/hardhat-chai-matchers"; 4 | import "@nomiclabs/hardhat-ethers"; 5 | import "@nomiclabs/hardhat-solpp"; 6 | import "hardhat-typechain"; 7 | 8 | // If no network is specified, use the default config 9 | if (!process.env.CHAIN_ETH_NETWORK) { 10 | // eslint-disable-next-line @typescript-eslint/no-var-requires 11 | require("dotenv").config(); 12 | } 13 | 14 | export default { 15 | zksolc: { 16 | version: "1.3.18", 17 | compilerSource: "binary", 18 | settings: { 19 | isSystem: true, 20 | }, 21 | }, 22 | solidity: { 23 | version: "0.8.20", 24 | }, 25 | defaultNetwork: process.env.CHAIN_ETH_NETWORK, 26 | networks: { 27 | localhost: { 28 | // era-test-node default url 29 | url: "http://127.0.0.1:8011", 30 | ethNetwork: null, 31 | zksync: true, 32 | }, 33 | lineatest: { 34 | url: "https://goerli.rpc.zklink.io", 35 | ethNetwork: "goerli", 36 | zksync: true, 37 | // contract verification endpoint 38 | verifyURL: "https://goerli.explorer.zklink.io/contract_verification", 39 | }, 40 | linea: { 41 | url: "https://rpc.zklink.io", 42 | ethNetwork: "mainnet", 43 | zksync: true, 44 | // contract verification endpoint 45 | verifyURL: "https://explorer.zklink.io/contract_verification", 46 | }, 47 | zkSyncTestnet: { 48 | url: "https://zksync2-testnet.zksync.dev", 49 | ethNetwork: "goerli", 50 | zksync: true, 51 | // contract verification endpoint 52 | verifyURL: "https://zksync2-testnet-explorer.zksync.dev/contract_verification", 53 | }, 54 | zkSyncTestnetSepolia: { 55 | url: "https://sepolia.era.zksync.dev", 56 | ethNetwork: "sepolia", 57 | zksync: true, 58 | // contract verification endpoint 59 | verifyURL: "https://explorer.sepolia.era.zksync.dev/contract_verification", 60 | }, 61 | zksyncMainnet: { 62 | url: "https://mainnet.era.zksync.io", 63 | ethNetwork: "mainnet", 64 | zksync: true, 65 | // contract verification endpoint 66 | verifyURL: "https://zksync2-mainnet-explorer.zksync.io/contract_verification", 67 | }, 68 | }, 69 | }; 70 | -------------------------------------------------------------------------------- /l2-contracts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "l2-contracts", 3 | "version": "0.1.0", 4 | "license": "MIT", 5 | "devDependencies": { 6 | "@matterlabs/hardhat-zksync-deploy": "^0.6.5", 7 | "@matterlabs/hardhat-zksync-solc": "^0.3.15", 8 | "@matterlabs/hardhat-zksync-verify": "^0.2.0", 9 | "@nomicfoundation/hardhat-chai-matchers": "^1.0.6", 10 | "@nomicfoundation/hardhat-ethers": "^3.0.4", 11 | "@nomicfoundation/hardhat-verify": "^1.1.0", 12 | "@nomiclabs/hardhat-ethers": "^2.0.0", 13 | "@nomiclabs/hardhat-etherscan": "^3.1.7", 14 | "@nomiclabs/hardhat-solpp": "^2.0.0", 15 | "@openzeppelin/contracts": "4.9.5", 16 | "@openzeppelin/contracts-upgradeable": "4.9.5", 17 | "@typechain/ethers-v5": "^2.0.0", 18 | "@types/chai": "^4.2.21", 19 | "@types/chai-as-promised": "^7.1.4", 20 | "@types/mocha": "^8.2.3", 21 | "chai": "^4.3.10", 22 | "chai-as-promised": "^7.1.1", 23 | "chalk": "^4.1.0", 24 | "commander": "^6.0.0", 25 | "ethers": "^5.7.0", 26 | "hardhat": "^2.18.3", 27 | "hardhat-typechain": "^0.3.3", 28 | "mocha": "^9.0.2", 29 | "ts-node": "^10.1.0", 30 | "typechain": "^4.0.0", 31 | "typescript": "^5.2.2", 32 | "zksync-web3": "^0.15.4" 33 | }, 34 | "scripts": { 35 | "build": "hardhat compile", 36 | "clean": "hardhat clean", 37 | "test": "hardhat test", 38 | "verify": "hardhat run src/verify.ts", 39 | "verify-bridge": "ts-node src/verify-l2-erc20-bridge.ts", 40 | "deploy-testnet-paymaster": "ts-node src/deployTestnetPaymaster.ts", 41 | "deploy-force-deploy-upgrader": "ts-node src/deployForceDeployUpgrader.ts", 42 | "publish-bridge-preimages": "ts-node src/publish-bridge-preimages.ts", 43 | "deploy-l2-weth": "ts-node src/deployL2Weth.ts", 44 | "upgrade-bridge-contracts": "ts-node src/upgradeBridgeImpl.ts", 45 | "execute-governance-tx": "ts-node src/execute-governance-tx.ts" 46 | }, 47 | "dependencies": { 48 | "dotenv": "^16.0.3" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /l2-contracts/src/deployForceDeployUpgrader.ts: -------------------------------------------------------------------------------- 1 | import { Command } from "commander"; 2 | import { ethers, Wallet } from "ethers"; 3 | import { computeL2Create2Address, create2DeployFromL1, getNumberFromEnv } from "./utils"; 4 | import { web3Provider } from "../../l1-contracts/scripts/utils"; 5 | import * as fs from "fs"; 6 | import * as path from "path"; 7 | import * as hre from "hardhat"; 8 | 9 | const provider = web3Provider(); 10 | const testConfigPath = path.join(process.env.ZKSYNC_HOME as string, "etc/test_config/constant"); 11 | const ethTestConfig = JSON.parse(fs.readFileSync(`${testConfigPath}/eth.json`, { encoding: "utf-8" })); 12 | 13 | const priorityTxMaxGasLimit = getNumberFromEnv("CONTRACTS_PRIORITY_TX_MAX_GAS_LIMIT"); 14 | 15 | // Script to deploy the force deploy upgrader contract and output its address. 16 | // Note, that this script expects that the L2 contracts have been compiled PRIOR 17 | // to running this script. 18 | async function main() { 19 | const program = new Command(); 20 | 21 | program 22 | .version("0.1.0") 23 | .name("deploy-force-deploy-upgrader") 24 | .description("Deploys the force deploy upgrader contract to L2"); 25 | 26 | program.option("--private-key ").action(async (cmd) => { 27 | const deployWallet = cmd.privateKey 28 | ? new Wallet(cmd.privateKey, provider) 29 | : Wallet.fromMnemonic( 30 | process.env.MNEMONIC ? process.env.MNEMONIC : ethTestConfig.mnemonic, 31 | "m/44'/60'/0'/0/1" 32 | ).connect(provider); 33 | console.log(`Using deployer wallet: ${deployWallet.address}`); 34 | 35 | const forceDeployUpgraderBytecode = hre.artifacts.readArtifactSync("ForceDeployUpgrader").bytecode; 36 | const create2Salt = ethers.constants.HashZero; 37 | const forceDeployUpgraderAddress = computeL2Create2Address( 38 | deployWallet, 39 | forceDeployUpgraderBytecode, 40 | "0x", 41 | create2Salt 42 | ); 43 | 44 | // TODO: request from API how many L2 gas needs for the transaction. 45 | await create2DeployFromL1(deployWallet, forceDeployUpgraderBytecode, "0x", create2Salt, priorityTxMaxGasLimit); 46 | 47 | console.log(`CONTRACTS_L2_DEFAULT_UPGRADE_ADDR=${forceDeployUpgraderAddress}`); 48 | }); 49 | 50 | await program.parseAsync(process.argv); 51 | } 52 | 53 | main() 54 | .then(() => process.exit(0)) 55 | .catch((err) => { 56 | console.error("Error:", err); 57 | process.exit(1); 58 | }); 59 | -------------------------------------------------------------------------------- /l2-contracts/src/deployTestnetPaymaster.ts: -------------------------------------------------------------------------------- 1 | import { Command } from "commander"; 2 | import { ethers, Wallet } from "ethers"; 3 | import { computeL2Create2Address, create2DeployFromL1, getNumberFromEnv } from "./utils"; 4 | import { web3Provider } from "../../l1-contracts/scripts/utils"; 5 | import * as fs from "fs"; 6 | import * as path from "path"; 7 | import * as hre from "hardhat"; 8 | 9 | const provider = web3Provider(); 10 | const testConfigPath = path.join(process.env.ZKSYNC_HOME as string, "etc/test_config/constant"); 11 | const ethTestConfig = JSON.parse(fs.readFileSync(`${testConfigPath}/eth.json`, { encoding: "utf-8" })); 12 | 13 | const priorityTxMaxGasLimit = getNumberFromEnv("CONTRACTS_PRIORITY_TX_MAX_GAS_LIMIT"); 14 | 15 | // Script to deploy the testnet paymaster and output its address. 16 | // Note, that this script expects that the L2 contracts have been compiled PRIOR 17 | // to running this script. 18 | async function main() { 19 | const program = new Command(); 20 | 21 | program.version("0.1.0").name("deploy-testnet-paymaster").description("Deploys the testnet paymaster to L2"); 22 | 23 | program.option("--private-key ").action(async (cmd) => { 24 | const deployWallet = cmd.privateKey 25 | ? new Wallet(cmd.privateKey, provider) 26 | : Wallet.fromMnemonic( 27 | process.env.MNEMONIC ? process.env.MNEMONIC : ethTestConfig.mnemonic, 28 | "m/44'/60'/0'/0/1" 29 | ).connect(provider); 30 | console.log(`Using deployer wallet: ${deployWallet.address}`); 31 | 32 | const testnetPaymasterBytecode = hre.artifacts.readArtifactSync("TestnetPaymaster").bytecode; 33 | const create2Salt = ethers.constants.HashZero; 34 | const paymasterAddress = computeL2Create2Address(deployWallet, testnetPaymasterBytecode, "0x", create2Salt); 35 | 36 | // TODO: request from API how many L2 gas needs for the transaction. 37 | await ( 38 | await create2DeployFromL1(deployWallet, testnetPaymasterBytecode, "0x", create2Salt, priorityTxMaxGasLimit) 39 | ).wait(); 40 | 41 | console.log(`CONTRACTS_L2_TESTNET_PAYMASTER_ADDR=${paymasterAddress}`); 42 | }); 43 | 44 | await program.parseAsync(process.argv); 45 | } 46 | 47 | main() 48 | .then(() => process.exit(0)) 49 | .catch((err) => { 50 | console.error("Error:", err); 51 | process.exit(1); 52 | }); 53 | -------------------------------------------------------------------------------- /l2-contracts/src/publish-bridge-preimages.ts: -------------------------------------------------------------------------------- 1 | import { Command } from "commander"; 2 | import { Wallet, ethers } from "ethers"; 3 | import * as fs from "fs"; 4 | import { Deployer } from "../../l1-contracts/src.ts/deploy"; 5 | import * as path from "path"; 6 | import { getNumberFromEnv, web3Provider } from "../../l1-contracts/scripts/utils"; 7 | import * as hre from "hardhat"; 8 | import { REQUIRED_L2_GAS_PRICE_PER_PUBDATA } from "./utils"; 9 | 10 | const PRIORITY_TX_MAX_GAS_LIMIT = getNumberFromEnv("CONTRACTS_PRIORITY_TX_MAX_GAS_LIMIT"); 11 | const provider = web3Provider(); 12 | const testConfigPath = path.join(process.env.ZKSYNC_HOME as string, "etc/test_config/constant"); 13 | const ethTestConfig = JSON.parse(fs.readFileSync(`${testConfigPath}/eth.json`, { encoding: "utf-8" })); 14 | 15 | function getContractBytecode(contractName: string) { 16 | return hre.artifacts.readArtifactSync(contractName).bytecode; 17 | } 18 | 19 | async function main() { 20 | const program = new Command(); 21 | 22 | program.version("0.1.0").name("publish-bridge-preimages"); 23 | 24 | program 25 | .option("--private-key ") 26 | .option("--nonce ") 27 | .option("--gas-price ") 28 | .action(async (cmd) => { 29 | const wallet = cmd.privateKey 30 | ? new Wallet(cmd.privateKey, provider) 31 | : Wallet.fromMnemonic( 32 | process.env.MNEMONIC ? process.env.MNEMONIC : ethTestConfig.mnemonic, 33 | "m/44'/60'/0'/0/1" 34 | ).connect(provider); 35 | console.log(`Using wallet: ${wallet.address}`); 36 | 37 | const nonce = cmd.nonce ? parseInt(cmd.nonce) : await wallet.getTransactionCount(); 38 | console.log(`Using nonce: ${nonce}`); 39 | 40 | const gasPrice = cmd.gasPrice ? parseInt(cmd.gasPrice) : await wallet.getGasPrice(); 41 | console.log(`Using gas price: ${gasPrice}`); 42 | 43 | const deployer = new Deployer({ deployWallet: wallet }); 44 | const zkSync = deployer.zkSyncContract(wallet); 45 | 46 | const publishL2ERC20BridgeTx = await zkSync.requestL2Transaction( 47 | ethers.constants.AddressZero, 48 | 0, 49 | "0x", 50 | PRIORITY_TX_MAX_GAS_LIMIT, 51 | REQUIRED_L2_GAS_PRICE_PER_PUBDATA, 52 | [getContractBytecode("L2ERC20Bridge")], 53 | wallet.address, 54 | { nonce, gasPrice } 55 | ); 56 | await publishL2ERC20BridgeTx.wait(); 57 | }); 58 | 59 | await program.parseAsync(process.argv); 60 | } 61 | 62 | main() 63 | .then(() => process.exit(0)) 64 | .catch((err) => { 65 | console.error("Error:", err); 66 | process.exit(1); 67 | }); 68 | -------------------------------------------------------------------------------- /l2-contracts/src/verify-l2-erc20-bridge.ts: -------------------------------------------------------------------------------- 1 | import * as hardhat from "hardhat"; 2 | import { Command } from "commander"; 3 | import { getAddressFromEnv } from "../../l1-contracts/scripts/utils"; 4 | 5 | const mergeTokenPortalAddress = getAddressFromEnv("CONTRACTS_MERGE_TOKEN_PORTAL_ADDR"); 6 | 7 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 8 | function verifyPromise(address: string, constructorArguments?: Array, libraries?: object): Promise { 9 | return new Promise((resolve, reject) => { 10 | hardhat 11 | .run("verify:verify", { address, constructorArguments, libraries }) 12 | .then(() => resolve(`Successfully verified ${address}`)) 13 | .catch((e) => { 14 | if (e.message.includes("contract is already verified")) { 15 | resolve(`Contract source code already verified: ${address}`); 16 | } else { 17 | reject(`Failed to verify ${address}\nError: ${e.message}`); 18 | } 19 | }); 20 | }); 21 | } 22 | 23 | async function main() { 24 | const program = new Command(); 25 | 26 | program.version("0.1.0").name("verify").description("verify L2 contracts"); 27 | 28 | program.requiredOption("--impl-address ").action(async (cmd) => { 29 | const promises = []; 30 | 31 | // Contracts without constructor parameters 32 | const constructorArguments = [mergeTokenPortalAddress]; 33 | const promise = verifyPromise(cmd.implAddress, constructorArguments); 34 | promises.push(promise); 35 | 36 | const messages = await Promise.allSettled(promises); 37 | for (const message of messages) { 38 | console.log(message.status == "fulfilled" ? message.value : message.reason); 39 | } 40 | }); 41 | await program.parseAsync(process.argv); 42 | } 43 | 44 | main() 45 | .then(() => process.exit(0)) 46 | .catch((err) => { 47 | console.error("Error:", err); 48 | process.exit(1); 49 | }); 50 | -------------------------------------------------------------------------------- /l2-contracts/src/verify.ts: -------------------------------------------------------------------------------- 1 | import * as hardhat from "hardhat"; 2 | 3 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 4 | function verifyPromise(address: string, constructorArguments?: Array, libraries?: object): Promise { 5 | return new Promise((resolve, reject) => { 6 | hardhat 7 | .run("verify:verify", { address, constructorArguments, libraries }) 8 | .then(() => resolve(`Successfully verified ${address}`)) 9 | .catch((e) => { 10 | if (e.message.includes("contract is already verified")) { 11 | resolve(`Contract source code already verified: ${address}`); 12 | } else { 13 | reject(`Failed to verify ${address}\nError: ${e.message}`); 14 | } 15 | }); 16 | }); 17 | } 18 | 19 | async function main() { 20 | if (process.env.CHAIN_ETH_NETWORK == "localhost") { 21 | console.log("Skip contract verification on localhost"); 22 | return; 23 | } 24 | 25 | const promises = []; 26 | 27 | // Contracts without constructor parameters 28 | for (const address of [ 29 | process.env.CONTRACTS_L2_WETH_TOKEN_IMPL_ADDR, 30 | process.env.CONTRACTS_L2_ERC20_BRIDGE_IMPL_ADDR, 31 | ]) { 32 | const promise = verifyPromise(address); 33 | promises.push(promise); 34 | } 35 | 36 | const messages = await Promise.allSettled(promises); 37 | for (const message of messages) { 38 | console.log(message.status == "fulfilled" ? message.value : message.reason); 39 | } 40 | } 41 | 42 | main() 43 | .then(() => process.exit(0)) 44 | .catch((err) => { 45 | console.error("Error:", err.message || err); 46 | process.exit(1); 47 | }); 48 | -------------------------------------------------------------------------------- /l2-contracts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "types": ["node", "mocha"], 4 | "downlevelIteration": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "era-contracts", 3 | "version": "0.1.0", 4 | "private": true, 5 | "workspaces": { 6 | "packages": [ 7 | "l1-contracts", 8 | "l2-contracts", 9 | "system-contracts" 10 | ], 11 | "nohoist": [ 12 | "**/@openzeppelin/**" 13 | ] 14 | }, 15 | "devDependencies": { 16 | "@matterlabs/eslint-config-typescript": "^1.1.2", 17 | "@matterlabs/prettier-config": "^1.0.3", 18 | "@typescript-eslint/eslint-plugin": "^6.7.4", 19 | "@typescript-eslint/parser": "^6.7.4", 20 | "eslint-import-resolver-typescript": "^3.6.1", 21 | "eslint-plugin-import": "^2.29.0", 22 | "eslint-plugin-prettier": "^5.0.1", 23 | "eslint": "^8.51.0", 24 | "markdownlint-cli": "^0.33.0", 25 | "prettier-plugin-solidity": "^1.1.3", 26 | "prettier": "^3.0.3", 27 | "solhint": "^3.6.2" 28 | }, 29 | "scripts": { 30 | "lint:check": "yarn lint:md && yarn lint:sol && yarn lint:ts && yarn prettier:check", 31 | "lint:fix": "yarn lint:md --fix && yarn lint:sol --fix && yarn lint:ts --fix && yarn prettier:fix", 32 | "lint:md": "markdownlint \"**/*.md\"", 33 | "lint:sol": "solhint \"**/*.sol\"", 34 | "lint:ts": "eslint .", 35 | "prettier:check": "prettier --check \"**/*.{js,json,md,sol,ts,yaml}\"", 36 | "prettier:fix": "prettier --write \"**/*.{js,json,md,sol,ts,yaml}\"", 37 | "l1": "yarn workspace l1-contracts", 38 | "l2": "yarn workspace l2-contracts", 39 | "sc": "yarn workspace system-contracts" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /system-contracts/bootloader/test_infra/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "test_infra" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | 10 | multivm = { git = "https://github.com/matter-labs/zksync-era.git", branch = "sb-short-term-fee-model-1-4-1" } 11 | zksync_types = { git = "https://github.com/matter-labs/zksync-era.git", branch = "sb-short-term-fee-model-1-4-1" } 12 | zksync_contracts = { git = "https://github.com/matter-labs/zksync-era.git", branch = "sb-short-term-fee-model-1-4-1" } 13 | zksync_utils = { git = "https://github.com/matter-labs/zksync-era.git", branch = "sb-short-term-fee-model-1-4-1" } 14 | zksync_state = { git = "https://github.com/matter-labs/zksync-era.git", branch = "sb-short-term-fee-model-1-4-1" } 15 | vlog = { git = "https://github.com/matter-labs/zksync-era.git", branch = "sb-short-term-fee-model-1-4-1" } 16 | 17 | colored = "2.0" 18 | hex = "0.4" 19 | once_cell = "1.7" 20 | tracing = { version = "0.1.26", features = ["log"] } 21 | tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter", "time", "json"] } 22 | serde_json = "1.0.67" 23 | serde = { version = "1.0", features = ["derive"] } 24 | -------------------------------------------------------------------------------- /system-contracts/bootloader/test_infra/README.md: -------------------------------------------------------------------------------- 1 | # Testing infrastructure for bootloader 2 | 3 | This crate allows you to run the unittests against the bootloader code. 4 | 5 | You should put your tests in `../tests/bootloader/bootloader_test.yul`, then compile the yul with: 6 | 7 | ```shell 8 | yarn build 9 | ``` 10 | 11 | And afterwards run the testing infrastructure: 12 | 13 | ```shell 14 | cargo run 15 | ``` 16 | -------------------------------------------------------------------------------- /system-contracts/bootloader/test_infra/rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly-2023-08-21 2 | -------------------------------------------------------------------------------- /system-contracts/bootloader/test_infra/src/test_count_tracer.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use multivm::interface::dyn_tracers::vm_1_4_1::DynTracer; 4 | use multivm::vm_latest::{HistoryMode, SimpleMemory, VmTracer}; 5 | use multivm::zk_evm_1_4_1::tracing::{BeforeExecutionData, VmLocalStateData}; 6 | use once_cell::sync::OnceCell; 7 | use zksync_state::{StoragePtr, WriteStorage}; 8 | 9 | use crate::hook::TestVmHook; 10 | 11 | /// Tracer that returns number of tests in the bootloader test file. 12 | pub struct TestCountTracer { 13 | /// Returns number of tests in the yul file. 14 | pub test_count: Arc>, 15 | } 16 | 17 | impl TestCountTracer { 18 | /// Creates the tracer that should also report the amount of tests in a file. 19 | pub fn new(test_count_result: Arc>) -> Self { 20 | TestCountTracer { 21 | test_count: test_count_result, 22 | } 23 | } 24 | } 25 | 26 | impl DynTracer> for TestCountTracer { 27 | fn before_execution( 28 | &mut self, 29 | state: VmLocalStateData<'_>, 30 | data: BeforeExecutionData, 31 | memory: &SimpleMemory, 32 | _storage: StoragePtr, 33 | ) { 34 | if let TestVmHook::TestCount(test_count) = 35 | TestVmHook::from_opcode_memory(&state, &data, memory) 36 | { 37 | self.test_count.set(test_count).unwrap(); 38 | } 39 | } 40 | } 41 | 42 | impl VmTracer for TestCountTracer {} 43 | -------------------------------------------------------------------------------- /system-contracts/bootloader/test_infra/src/test_transactions/README.md: -------------------------------------------------------------------------------- 1 | # Test Transactions 2 | 3 | This directory contains JSON serialized 'Transaction' objects that are inserted into bootloader memory during 4 | unittesting. 5 | 6 | Please add files with consecutive numbers (0.json, 1.json) - and insert into bootloader in the same order. 7 | 8 | Then, they can be accessed in the unittest, by calling `testing_txDataOffset(x)`. 9 | -------------------------------------------------------------------------------- /system-contracts/bootloader/tests/README.md: -------------------------------------------------------------------------------- 1 | # Testing 2 | 3 | ## Full tests 4 | 5 | `dummy.yul` and `transfer_tests.yul` are full Yul files, which are replacing the bootloader, and are used in 6 | `zksync-era` crate. 7 | 8 | ## Unittests 9 | 10 | Please put bootloader unittests in `bootloader/bootloader_test.yul` file, and any testing utility functions in 11 | `utils/test_utils.yul`. 12 | 13 | To execute tests, you should first run yarn to prepare the source code: 14 | 15 | ```shell 16 | yarn build-yul 17 | ``` 18 | 19 | And then run the test framework: 20 | 21 | ```shell 22 | cd test_infa && cargo run 23 | ``` 24 | -------------------------------------------------------------------------------- /system-contracts/bootloader/tests/dummy.yul: -------------------------------------------------------------------------------- 1 | // A really basic test that only sets one memory cell to 1. 2 | object "Bootloader" { 3 | code { 4 | } 5 | object "Bootloader_deployed" { 6 | code { 7 | let DUMMY_TEST_CELL := 0x00 8 | let DUMMY_TEST_VALUE := 0x123123123 9 | mstore(DUMMY_TEST_CELL, DUMMY_TEST_VALUE) 10 | // Need to return. Otherwise, the compiler will optimize out 11 | // the mstore 12 | return(0,32) 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /system-contracts/bootloader/tests/transfer_test.yul: -------------------------------------------------------------------------------- 1 | // A really basic test that only sets one memory cell to 1. 2 | object "Bootloader" { 3 | code { 4 | } 5 | object "Bootloader_deployed" { 6 | code { 7 | // This test is used to calculate the number of gas required to 8 | // do a simple internal transfer 9 | 10 | function ETH_L2_TOKEN_ADDR() -> ret { 11 | ret := 0x000000000000000000000000000000000000800a 12 | } 13 | function BOOTLOADER_FORMAL_ADDR() -> ret { 14 | ret := 0x0000000000000000000000000000000000008001 15 | } 16 | 17 | // Getting the balance of the account in order to make sure 18 | // that the decommit of the account has already happened and so 19 | // the call of the actual transfer is cheaper. 20 | let myBalance := selfbalance() 21 | // Storing the value to avoid compiler optimization 22 | mstore(100, myBalance) 23 | 24 | let gasBeforeCall := gas() 25 | let transferSuccess := call( 26 | gas(), 27 | ETH_L2_TOKEN_ADDR(), 28 | 0, 29 | 0, 30 | 100, 31 | 0, 32 | 0 33 | ) 34 | let gasSpent := sub(gasBeforeCall, gas()) 35 | 36 | if iszero(transferSuccess) { 37 | // The transfer should succeed 38 | revert(0,0) 39 | } 40 | 41 | 42 | mstore(0, gasSpent) 43 | return(0, 256) 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /system-contracts/bootloader/tests/utils/test_utils.yul: -------------------------------------------------------------------------------- 1 | 2 | 3 | // We're locating the test hooks 'before' the last free slot. 4 | function TEST_HOOK_PTR() -> ret { 5 | ret := LAST_FREE_SLOT() 6 | } 7 | 8 | function TEST_HOOK_PARAMS_OFFSET() -> ret { 9 | ret := sub(TEST_HOOK_PTR(), mul(5, 32)) 10 | } 11 | 12 | function setTestHook(hook) { 13 | mstore(TEST_HOOK_PTR(), $llvm_NoInline_llvm$_unoptimized(hook)) 14 | } 15 | 16 | function storeTestHookParam(paramId, value) { 17 | let offset := add(TEST_HOOK_PARAMS_OFFSET(), mul(32, paramId)) 18 | mstore(offset, $llvm_NoInline_llvm$_unoptimized(value)) 19 | } 20 | 21 | 22 | function testing_log(msg, data) { 23 | storeTestHookParam(0, msg) 24 | storeTestHookParam(1, data) 25 | setTestHook(100) 26 | } 27 | 28 | function testing_start(test_name) { 29 | storeTestHookParam(0, test_name) 30 | setTestHook(104) 31 | } 32 | 33 | function testing_assertEq(a, b, message) { 34 | if iszero(eq(a, b)) { 35 | storeTestHookParam(0, a) 36 | storeTestHookParam(1, b) 37 | storeTestHookParam(2, message) 38 | setTestHook(101) 39 | } 40 | } 41 | 42 | function testing_testWillFailWith(message) { 43 | storeTestHookParam(0, $llvm_NoInline_llvm$_unoptimized(message)) 44 | setTestHook(102) 45 | } 46 | function testing_totalTests(tests) { 47 | storeTestHookParam(0, $llvm_NoInline_llvm$_unoptimized(tests)) 48 | setTestHook(103) 49 | } 50 | 51 | // Returns txDataOffset for the index transaction. 52 | function testing_txDataOffset(index) -> txDataOffset { 53 | let txPtr := add(TX_DESCRIPTION_BEGIN_BYTE(), mul(index, TX_DESCRIPTION_SIZE())) 54 | txDataOffset := mload(add(txPtr, 0x20)) 55 | } 56 | -------------------------------------------------------------------------------- /system-contracts/contracts/ComplexUpgrader.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | import {IComplexUpgrader} from "./interfaces/IComplexUpgrader.sol"; 6 | import {FORCE_DEPLOYER} from "./Constants.sol"; 7 | 8 | /** 9 | * @author Matter Labs 10 | * @custom:security-contact security@matterlabs.dev 11 | * @notice Upgrader which should be used to perform complex multistep upgrades on L2. In case some custom logic for an upgrade is needed 12 | * this logic should be deployed into the user space and then this contract will delegatecall to the deployed contract. 13 | */ 14 | contract ComplexUpgrader is IComplexUpgrader { 15 | /// @notice Executes an upgrade process by delegating calls to another contract. 16 | /// @dev This function allows only the `FORCE_DEPLOYER` to initiate the upgrade. 17 | /// If the delegate call fails, the function will revert the transaction, returning the error message 18 | /// provided by the delegated contract. 19 | /// @param _delegateTo the address of the contract to which the calls will be delegated 20 | /// @param _calldata the calldata to be delegate called in the `_delegateTo` contract 21 | function upgrade(address _delegateTo, bytes calldata _calldata) external payable { 22 | require(msg.sender == FORCE_DEPLOYER, "Can only be called by FORCE_DEPLOYER"); 23 | 24 | require(_delegateTo.code.length > 0, "Delegatee is an EOA"); 25 | (bool success, bytes memory returnData) = _delegateTo.delegatecall(_calldata); 26 | assembly { 27 | if iszero(success) { 28 | revert(add(returnData, 0x20), mload(returnData)) 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /system-contracts/contracts/EmptyContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | /** 6 | * @author Matter Labs 7 | * @custom:security-contact security@matterlabs.dev 8 | * @notice The "empty" contract that is put into some system contracts by default. 9 | * @dev The bytecode of the contract is set by default for all addresses for which no other bytecodes are deployed. 10 | */ 11 | contract EmptyContract { 12 | fallback() external payable {} 13 | 14 | receive() external payable {} 15 | } 16 | -------------------------------------------------------------------------------- /system-contracts/contracts/ImmutableSimulator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | import {IImmutableSimulator, ImmutableData} from "./interfaces/IImmutableSimulator.sol"; 6 | import {DEPLOYER_SYSTEM_CONTRACT} from "./Constants.sol"; 7 | 8 | /** 9 | * @author Matter Labs 10 | * @custom:security-contact security@matterlabs.dev 11 | * @notice System smart contract that simulates the behavior of immutable variables in Solidity. 12 | * @dev The contract stores the immutable variables created during deployment by other contracts on his storage. 13 | * @dev This simulator is needed so that smart contracts with the same Solidity code but different 14 | * constructor parameters have the same bytecode. 15 | * @dev The users are not expected to call this contract directly, only indirectly via the compiler simulations 16 | * for the immutable variables in Solidity. 17 | */ 18 | contract ImmutableSimulator is IImmutableSimulator { 19 | /// @dev mapping (contract address) => (index of immutable variable) => value 20 | /// @notice that address uses `uint256` type to leave the option to introduce 32-byte address space in future. 21 | mapping(uint256 contractAddress => mapping(uint256 index => bytes32 value)) internal immutableDataStorage; 22 | 23 | /// @notice Method that returns the immutable with a certain index for a user. 24 | /// @param _dest The address which the immutable belongs to. 25 | /// @param _index The index of the immutable. 26 | /// @return The value of the immutables. 27 | function getImmutable(address _dest, uint256 _index) external view override returns (bytes32) { 28 | return immutableDataStorage[uint256(uint160(_dest))][_index]; 29 | } 30 | 31 | /// @notice Method used by the contract deployer to store the immutables for an account 32 | /// @param _dest The address which to store the immutables for. 33 | /// @param _immutables The list of the immutables. 34 | function setImmutables(address _dest, ImmutableData[] calldata _immutables) external override { 35 | require(msg.sender == address(DEPLOYER_SYSTEM_CONTRACT), "Callable only by the deployer system contract"); 36 | unchecked { 37 | uint256 immutablesLength = _immutables.length; 38 | for (uint256 i = 0; i < immutablesLength; ++i) { 39 | uint256 index = _immutables[i].index; 40 | bytes32 value = _immutables[i].value; 41 | immutableDataStorage[uint256(uint160(_dest))][index] = value; 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /system-contracts/contracts/interfaces/IAccount.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | import "../libraries/TransactionHelper.sol"; 6 | 7 | bytes4 constant ACCOUNT_VALIDATION_SUCCESS_MAGIC = IAccount.validateTransaction.selector; 8 | 9 | interface IAccount { 10 | /// @notice Called by the bootloader to validate that an account agrees to process the transaction 11 | /// (and potentially pay for it). 12 | /// @param _txHash The hash of the transaction to be used in the explorer 13 | /// @param _suggestedSignedHash The hash of the transaction is signed by EOAs 14 | /// @param _transaction The transaction itself 15 | /// @return magic The magic value that should be equal to the signature of this function 16 | /// if the user agrees to proceed with the transaction. 17 | /// @dev The developer should strive to preserve as many steps as possible both for valid 18 | /// and invalid transactions as this very method is also used during the gas fee estimation 19 | /// (without some of the necessary data, e.g. signature). 20 | function validateTransaction( 21 | bytes32 _txHash, 22 | bytes32 _suggestedSignedHash, 23 | Transaction calldata _transaction 24 | ) external payable returns (bytes4 magic); 25 | 26 | function executeTransaction( 27 | bytes32 _txHash, 28 | bytes32 _suggestedSignedHash, 29 | Transaction calldata _transaction 30 | ) external payable; 31 | 32 | // There is no point in providing possible signed hash in the `executeTransactionFromOutside` method, 33 | // since it typically should not be trusted. 34 | function executeTransactionFromOutside(Transaction calldata _transaction) external payable; 35 | 36 | function payForTransaction( 37 | bytes32 _txHash, 38 | bytes32 _suggestedSignedHash, 39 | Transaction calldata _transaction 40 | ) external payable; 41 | 42 | function prepareForPaymaster( 43 | bytes32 _txHash, 44 | bytes32 _possibleSignedHash, 45 | Transaction calldata _transaction 46 | ) external payable; 47 | } 48 | -------------------------------------------------------------------------------- /system-contracts/contracts/interfaces/IAccountCodeStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | interface IAccountCodeStorage { 6 | function storeAccountConstructingCodeHash(address _address, bytes32 _hash) external; 7 | 8 | function storeAccountConstructedCodeHash(address _address, bytes32 _hash) external; 9 | 10 | function markAccountCodeHashAsConstructed(address _address) external; 11 | 12 | function getRawCodeHash(address _address) external view returns (bytes32 codeHash); 13 | 14 | function getCodeHash(uint256 _input) external view returns (bytes32 codeHash); 15 | 16 | function getCodeSize(uint256 _input) external view returns (uint256 codeSize); 17 | } 18 | -------------------------------------------------------------------------------- /system-contracts/contracts/interfaces/IBootloaderUtilities.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | import "../libraries/TransactionHelper.sol"; 6 | 7 | interface IBootloaderUtilities { 8 | function getTransactionHashes( 9 | Transaction calldata _transaction 10 | ) external view returns (bytes32 txHash, bytes32 signedTxHash); 11 | } 12 | -------------------------------------------------------------------------------- /system-contracts/contracts/interfaces/IComplexUpgrader.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | /** 6 | * @author Matter Labs 7 | * @custom:security-contact security@matterlabs.dev 8 | * @notice The interface for the ComplexUpgrader contract. 9 | */ 10 | interface IComplexUpgrader { 11 | function upgrade(address _delegateTo, bytes calldata _calldata) external payable; 12 | } 13 | -------------------------------------------------------------------------------- /system-contracts/contracts/interfaces/ICompressor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | // The bitmask by applying which to the compressed state diff metadata we retrieve its operation. 6 | uint8 constant OPERATION_BITMASK = 7; 7 | // The number of bits shifting the compressed state diff metadata by which we retrieve its length. 8 | uint8 constant LENGTH_BITS_OFFSET = 3; 9 | // The maximal length in bytes that an enumeration index can have. 10 | uint8 constant MAX_ENUMERATION_INDEX_SIZE = 8; 11 | 12 | /** 13 | * @author Matter Labs 14 | * @custom:security-contact security@matterlabs.dev 15 | * @notice The interface for the Compressor contract, responsible for verifying the correctness of 16 | * the compression of the state diffs and bytecodes. 17 | */ 18 | interface ICompressor { 19 | function publishCompressedBytecode( 20 | bytes calldata _bytecode, 21 | bytes calldata _rawCompressedData 22 | ) external payable returns (bytes32 bytecodeHash); 23 | 24 | function verifyCompressedStateDiffs( 25 | uint256 _numberOfStateDiffs, 26 | uint256 _enumerationIndexSize, 27 | bytes calldata _stateDiffs, 28 | bytes calldata _compressedStateDiffs 29 | ) external payable returns (bytes32 stateDiffHash); 30 | } 31 | -------------------------------------------------------------------------------- /system-contracts/contracts/interfaces/IEthToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | interface IEthToken { 6 | function balanceOf(uint256) external view returns (uint256); 7 | 8 | function transferFromTo(address _from, address _to, uint256 _amount) external; 9 | 10 | function totalSupply() external view returns (uint256); 11 | 12 | function name() external pure returns (string memory); 13 | 14 | function symbol() external pure returns (string memory); 15 | 16 | function decimals() external pure returns (uint8); 17 | 18 | function mint(address _account, uint256 _amount) external; 19 | 20 | function withdraw(address _l1Receiver) external payable; 21 | 22 | function withdrawWithMessage(address _l1Receiver, bytes calldata _additionalData) external payable; 23 | 24 | event Mint(address indexed account, uint256 amount); 25 | 26 | event Transfer(address indexed from, address indexed to, uint256 value); 27 | 28 | event Withdrawal(address indexed _l2Sender, address indexed _l1Receiver, uint256 _amount); 29 | 30 | event WithdrawalWithMessage( 31 | address indexed _l2Sender, 32 | address indexed _l1Receiver, 33 | uint256 _amount, 34 | bytes _additionalData 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /system-contracts/contracts/interfaces/IImmutableSimulator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | struct ImmutableData { 6 | uint256 index; 7 | bytes32 value; 8 | } 9 | 10 | interface IImmutableSimulator { 11 | function getImmutable(address _dest, uint256 _index) external view returns (bytes32); 12 | 13 | function setImmutables(address _dest, ImmutableData[] calldata _immutables) external; 14 | } 15 | -------------------------------------------------------------------------------- /system-contracts/contracts/interfaces/IKnownCodesStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | /** 6 | * @author Matter Labs 7 | * @custom:security-contact security@matterlabs.dev 8 | * @notice The interface for the KnownCodesStorage contract, which is responsible 9 | * for storing the hashes of the bytecodes that have been published to the network. 10 | */ 11 | interface IKnownCodesStorage { 12 | event MarkedAsKnown(bytes32 indexed bytecodeHash, bool indexed sendBytecodeToL1); 13 | 14 | function markFactoryDeps(bool _shouldSendToL1, bytes32[] calldata _hashes) external; 15 | 16 | function markBytecodeAsPublished(bytes32 _bytecodeHash) external; 17 | 18 | function getMarker(bytes32 _hash) external view returns (uint256); 19 | } 20 | -------------------------------------------------------------------------------- /system-contracts/contracts/interfaces/IL2StandardToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | interface IL2StandardToken { 6 | event BridgeMint(address indexed _account, uint256 _amount); 7 | 8 | event BridgeBurn(address indexed _account, uint256 _amount); 9 | 10 | function bridgeMint(address _account, uint256 _amount) external; 11 | 12 | function bridgeBurn(address _account, uint256 _amount) external; 13 | 14 | function l1Address() external view returns (address); 15 | 16 | function l2Bridge() external view returns (address); 17 | } 18 | -------------------------------------------------------------------------------- /system-contracts/contracts/interfaces/IMailbox.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | interface IMailbox { 6 | function finalizeEthWithdrawal( 7 | uint256 _l2BatchNumber, 8 | uint256 _l2MessageIndex, 9 | uint16 _l2TxNumberInBlock, 10 | bytes calldata _message, 11 | bytes32[] calldata _merkleProof 12 | ) external; 13 | } 14 | -------------------------------------------------------------------------------- /system-contracts/contracts/interfaces/INonceHolder.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | /** 6 | * @author Matter Labs 7 | * @dev Interface of the nonce holder contract -- a contract used by the system to ensure 8 | * that there is always a unique identifier for a transaction with a particular account (we call it nonce). 9 | * In other words, the pair of (address, nonce) should always be unique. 10 | * @dev Custom accounts should use methods of this contract to store nonces or other possible unique identifiers 11 | * for the transaction. 12 | */ 13 | interface INonceHolder { 14 | event ValueSetUnderNonce(address indexed accountAddress, uint256 indexed key, uint256 value); 15 | 16 | /// @dev Returns the current minimal nonce for account. 17 | function getMinNonce(address _address) external view returns (uint256); 18 | 19 | /// @dev Returns the raw version of the current minimal nonce 20 | /// (equal to minNonce + 2^128 * deployment nonce). 21 | function getRawNonce(address _address) external view returns (uint256); 22 | 23 | /// @dev Increases the minimal nonce for the msg.sender. 24 | function increaseMinNonce(uint256 _value) external returns (uint256); 25 | 26 | /// @dev Sets the nonce value `key` as used. 27 | function setValueUnderNonce(uint256 _key, uint256 _value) external; 28 | 29 | /// @dev Gets the value stored inside a custom nonce. 30 | function getValueUnderNonce(uint256 _key) external view returns (uint256); 31 | 32 | /// @dev A convenience method to increment the minimal nonce if it is equal 33 | /// to the `_expectedNonce`. 34 | function incrementMinNonceIfEquals(uint256 _expectedNonce) external; 35 | 36 | /// @dev Returns the deployment nonce for the accounts used for CREATE opcode. 37 | function getDeploymentNonce(address _address) external view returns (uint256); 38 | 39 | /// @dev Increments the deployment nonce for the account and returns the previous one. 40 | function incrementDeploymentNonce(address _address) external returns (uint256); 41 | 42 | /// @dev Determines whether a certain nonce has been already used for an account. 43 | function validateNonceUsage(address _address, uint256 _key, bool _shouldBeUsed) external view; 44 | 45 | /// @dev Returns whether a nonce has been used for an account. 46 | function isNonceUsed(address _address, uint256 _nonce) external view returns (bool); 47 | } 48 | -------------------------------------------------------------------------------- /system-contracts/contracts/interfaces/IPaymasterFlow.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | /** 6 | * @author Matter Labs 7 | * @dev The interface that is used for encoding/decoding of 8 | * different types of paymaster flows. 9 | * @notice This is NOT an interface to be implementated 10 | * by contracts. It is just used for encoding. 11 | */ 12 | interface IPaymasterFlow { 13 | function general(bytes calldata input) external; 14 | 15 | function approvalBased(address _token, uint256 _minAllowance, bytes calldata _innerInput) external; 16 | } 17 | -------------------------------------------------------------------------------- /system-contracts/contracts/interfaces/ISystemContext.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | /** 6 | * @author Matter Labs 7 | * @custom:security-contact security@matterlabs.dev 8 | * @notice Contract that stores some of the context variables, that may be either 9 | * block-scoped, tx-scoped or system-wide. 10 | */ 11 | interface ISystemContext { 12 | struct BlockInfo { 13 | uint128 timestamp; 14 | uint128 number; 15 | } 16 | 17 | /// @notice A structure representing the timeline for the upgrade from the batch numbers to the L2 block numbers. 18 | /// @dev It will used for the L1 batch -> L2 block migration in Q3 2023 only. 19 | struct VirtualBlockUpgradeInfo { 20 | /// @notice In order to maintain consistent results for `blockhash` requests, we'll 21 | /// have to remember the number of the batch when the upgrade to the virtual blocks has been done. 22 | /// The hashes for virtual blocks before the upgrade are identical to the hashes of the corresponding batches. 23 | uint128 virtualBlockStartBatch; 24 | /// @notice L2 block when the virtual blocks have caught up with the L2 blocks. Starting from this block, 25 | /// all the information returned to users for block.timestamp/number, etc should be the information about the L2 blocks and 26 | /// not virtual blocks. 27 | uint128 virtualBlockFinishL2Block; 28 | } 29 | 30 | function chainId() external view returns (uint256); 31 | 32 | function origin() external view returns (address); 33 | 34 | function gasPrice() external view returns (uint256); 35 | 36 | function blockGasLimit() external view returns (uint256); 37 | 38 | function coinbase() external view returns (address); 39 | 40 | function difficulty() external view returns (uint256); 41 | 42 | function baseFee() external view returns (uint256); 43 | 44 | function txNumberInBlock() external view returns (uint16); 45 | 46 | function getBlockHashEVM(uint256 _block) external view returns (bytes32); 47 | 48 | function getBatchHash(uint256 _batchNumber) external view returns (bytes32 hash); 49 | 50 | function getBlockNumber() external view returns (uint128); 51 | 52 | function getBlockTimestamp() external view returns (uint128); 53 | 54 | function getBatchNumberAndTimestamp() external view returns (uint128 blockNumber, uint128 blockTimestamp); 55 | 56 | function getL2BlockNumberAndTimestamp() external view returns (uint128 blockNumber, uint128 blockTimestamp); 57 | } 58 | -------------------------------------------------------------------------------- /system-contracts/contracts/interfaces/ISystemContextDeprecated.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | /** 6 | * @author Matter Labs 7 | * @custom:security-contact security@matterlabs.dev 8 | * @notice The interface with deprecated functions of the SystemContext contract. It is aimed for backward compatibility. 9 | */ 10 | interface ISystemContextDeprecated { 11 | function currentBlockInfo() external view returns (uint256); 12 | 13 | function getBlockNumberAndTimestamp() external view returns (uint256 blockNumber, uint256 blockTimestamp); 14 | 15 | function blockHash(uint256 _blockNumber) external view returns (bytes32 hash); 16 | } 17 | -------------------------------------------------------------------------------- /system-contracts/contracts/interfaces/ISystemContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | import {SystemContractHelper} from "../libraries/SystemContractHelper.sol"; 6 | import {BOOTLOADER_FORMAL_ADDRESS} from "../Constants.sol"; 7 | 8 | /** 9 | * @author Matter Labs 10 | * @custom:security-contact security@matterlabs.dev 11 | * @notice An abstract contract that is used to reuse modifiers across the system contracts. 12 | * @dev Solidity does not allow exporting modifiers via libraries, so 13 | * the only way to do reuse modifiers is to have a base contract 14 | * @dev Never add storage variables into this contract as some 15 | * system contracts rely on this abstract contract as on interface! 16 | */ 17 | abstract contract ISystemContract { 18 | /// @notice Modifier that makes sure that the method 19 | /// can only be called via a system call. 20 | modifier onlySystemCall() { 21 | require( 22 | SystemContractHelper.isSystemCall() || SystemContractHelper.isSystemContract(msg.sender), 23 | "This method require system call flag" 24 | ); 25 | _; 26 | } 27 | 28 | /// @notice Modifier that makes sure that the method 29 | /// can only be called from a system contract. 30 | modifier onlyCallFromSystemContract() { 31 | require( 32 | SystemContractHelper.isSystemContract(msg.sender), 33 | "This method require the caller to be system contract" 34 | ); 35 | _; 36 | } 37 | 38 | /// @notice Modifier that makes sure that the method 39 | /// can only be called from a special given address. 40 | modifier onlyCallFrom(address caller) { 41 | require(msg.sender == caller, "Inappropriate caller"); 42 | _; 43 | } 44 | 45 | /// @notice Modifier that makes sure that the method 46 | /// can only be called from the bootloader. 47 | modifier onlyCallFromBootloader() { 48 | require(msg.sender == BOOTLOADER_FORMAL_ADDRESS, "Callable only by the bootloader"); 49 | _; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /system-contracts/contracts/libraries/UnsafeBytesCalldata.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | /** 6 | * @author Matter Labs 7 | * @custom:security-contact security@matterlabs.dev 8 | * @dev The library provides a set of functions that help read data from calldata bytes. 9 | * @dev Each of the functions accepts the `bytes calldata` and the offset where data should be read and returns a value of a certain type. 10 | * 11 | * @dev WARNING! 12 | * 1) Functions don't check the length of the bytes array, so it can go out of bounds. 13 | * The user of the library must check for bytes length before using any functions from the library! 14 | * 15 | * 2) Read variables are not cleaned up - https://docs.soliditylang.org/en/v0.8.16/internals/variable_cleanup.html. 16 | * Using data in inline assembly can lead to unexpected behavior! 17 | */ 18 | library UnsafeBytesCalldata { 19 | function readUint16(bytes calldata _bytes, uint256 _start) internal pure returns (uint16 result) { 20 | assembly { 21 | let offset := sub(_bytes.offset, 30) 22 | result := calldataload(add(offset, _start)) 23 | } 24 | } 25 | 26 | function readUint32(bytes calldata _bytes, uint256 _start) internal pure returns (uint32 result) { 27 | assembly { 28 | let offset := sub(_bytes.offset, 28) 29 | result := calldataload(add(offset, _start)) 30 | } 31 | } 32 | 33 | function readUint64(bytes calldata _bytes, uint256 _start) internal pure returns (uint64 result) { 34 | assembly { 35 | let offset := sub(_bytes.offset, 24) 36 | result := calldataload(add(offset, _start)) 37 | } 38 | } 39 | 40 | function readBytes32(bytes calldata _bytes, uint256 _start) internal pure returns (bytes32 result) { 41 | assembly { 42 | result := calldataload(add(_bytes.offset, _start)) 43 | } 44 | } 45 | 46 | function readUint256(bytes calldata _bytes, uint256 _start) internal pure returns (uint256 result) { 47 | assembly { 48 | result := calldataload(add(_bytes.offset, _start)) 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /system-contracts/contracts/openzeppelin/token/ERC20/extensions/IERC20Permit.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Permit.sol) 3 | 4 | pragma solidity ^0.8.0; 5 | 6 | /** 7 | * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in 8 | * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. 9 | * 10 | * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by 11 | * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't 12 | * need to send a transaction, and thus is not required to hold Ether at all. 13 | */ 14 | interface IERC20Permit { 15 | /** 16 | * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, 17 | * given ``owner``'s signed approval. 18 | * 19 | * IMPORTANT: The same issues {IERC20-approve} has related to transaction 20 | * ordering also apply here. 21 | * 22 | * Emits an {Approval} event. 23 | * 24 | * Requirements: 25 | * 26 | * - `spender` cannot be the zero address. 27 | * - `deadline` must be a timestamp in the future. 28 | * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` 29 | * over the EIP712-formatted function arguments. 30 | * - the signature must use ``owner``'s current nonce (see {nonces}). 31 | * 32 | * For more information on the signature format, see the 33 | * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP 34 | * section]. 35 | */ 36 | function permit( 37 | address owner, 38 | address spender, 39 | uint256 value, 40 | uint256 deadline, 41 | uint8 v, 42 | bytes32 r, 43 | bytes32 s 44 | ) external; 45 | 46 | /** 47 | * @dev Returns the current nonce for `owner`. This value must be 48 | * included whenever a signature is generated for {permit}. 49 | * 50 | * Every successful call to {permit} increases ``owner``'s nonce by one. This 51 | * prevents a signature from being used multiple times. 52 | */ 53 | function nonces(address owner) external view returns (uint256); 54 | 55 | /** 56 | * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. 57 | */ 58 | // solhint-disable-next-line func-name-mixedcase 59 | function DOMAIN_SEPARATOR() external view returns (bytes32); 60 | } 61 | -------------------------------------------------------------------------------- /system-contracts/contracts/test-contracts/AlwaysRevert.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | contract AlwaysRevert { 6 | fallback() external { 7 | revert(""); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /system-contracts/contracts/test-contracts/DelegateCaller.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | contract DelegateCaller { 6 | function delegateCall(address _to) external payable { 7 | assembly { 8 | calldatacopy(0, 0, calldatasize()) 9 | let result := delegatecall(gas(), _to, 0, calldatasize(), 0, 0) 10 | returndatacopy(0, 0, returndatasize()) 11 | switch result 12 | case 0 { 13 | revert(0, returndatasize()) 14 | } 15 | default { 16 | return(0, returndatasize()) 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /system-contracts/contracts/test-contracts/Deployable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | contract Deployable { 6 | event Deployed(uint256 value, bytes data); 7 | 8 | constructor() payable { 9 | uint256 len; 10 | assembly { 11 | len := codesize() 12 | } 13 | bytes memory data = new bytes(len); 14 | assembly { 15 | codecopy(add(data, 0x20), 0, len) 16 | } 17 | emit Deployed(msg.value, data); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /system-contracts/contracts/test-contracts/ExtraAbiCaller.zasm: -------------------------------------------------------------------------------- 1 | .text 2 | .file "main" 3 | .globl __entry 4 | __entry: 5 | .func_begin0: 6 | sub.s! 0, r2, r0 7 | jump.eq @.RUNTIME_CODE 8 | ; deployment code 9 | add 32, r0, r1 10 | st.1 r0, r1 11 | st.1 r1, r0 12 | add @CPI0_1[0], r0, r1 13 | ret.ok.to_label r1, @DEFAULT_FAR_RETURN 14 | .RUNTIME_CODE: 15 | ; ABI: 16 | ; 0-32 address(in the lowest 20 bytes) 17 | ; 32-64 msg.value 18 | ; 64-384 extra data 19 | ; 384+ calldata 20 | ; 21 | ; load address into r2 22 | ld.inc r1, r2, r1 23 | ; set msg.value 24 | ld.inc r1, r3, r1 25 | context.set_context_u128 r3 26 | ; load extra abi data into r3-r12 27 | ld.inc r1, r3, r1 28 | ld.inc r1, r4, r1 29 | ld.inc r1, r5, r1 30 | ld.inc r1, r6, r1 31 | ld.inc r1, r7, r1 32 | ld.inc r1, r8, r1 33 | ld.inc r1, r9, r1 34 | ld.inc r1, r10, r1 35 | ld.inc r1, r11, r1 36 | ld.inc r1, r12, r1 37 | ptr.pack.s @CPI0_0[0], r1, r1 38 | far_call r1, r2, @.CALL_REVERT 39 | ptr.pack.s @CPI0_2[0], r1, r1 40 | ret.ok r1 41 | .CALL_REVERT: 42 | ptr.pack.s @CPI0_2[0], r1, r1 43 | ret.revert r1 44 | .func_end0: 45 | .note.GNU-stack 46 | .rodata 47 | ; far call abi: 48 | ; gas amount = 0xFFFFFFFF(max) 49 | ; forwarding mode = fat ptr 50 | ; shard id = 0 51 | ; constructor flag = false 52 | ; system call flag = true 53 | ; 01 00 00 01 FFFFFFFF 0000000000000000 00000000000000000000000000000000 54 | CPI0_0: 55 | .cell 452312902503159716397502014137536550255307801666780882257920705274096648192 56 | CPI0_1: 57 | .cell 5070602400912917605986812821504 58 | ; 01 0000000000000000 00000000000000000000000000000000 59 | CPI0_2: 60 | .cell 6277101735386680763835789423207666416102355444464034512896 61 | -------------------------------------------------------------------------------- /system-contracts/contracts/test-contracts/MockContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.20; 4 | 5 | contract MockContract { 6 | event Called(uint256 value, bytes data); 7 | 8 | struct CallResult { 9 | bytes input; 10 | bool failure; 11 | bytes returnData; 12 | } 13 | 14 | CallResult[] private results; 15 | 16 | constructor() { 17 | // Clean results if mock was redeployed. 18 | delete results; 19 | } 20 | 21 | // This function call will not pass to fallback, but this is fine for the tests. 22 | function setResult(CallResult calldata result) external { 23 | bytes32 inputKeccak = keccak256(result.input); 24 | for (uint256 i = 0; i < results.length; i++) { 25 | if (keccak256(results[i].input) == inputKeccak) { 26 | results[i] = result; 27 | return; 28 | } 29 | } 30 | results.push(result); 31 | } 32 | 33 | fallback() external payable { 34 | bytes memory data = msg.data; 35 | bytes32 inputKeccak = keccak256(data); 36 | 37 | // empty return data with successful result by default. 38 | bool failure; 39 | bytes memory returnData; 40 | 41 | for (uint256 i = 0; i < results.length; i++) { 42 | if (keccak256(results[i].input) == inputKeccak) { 43 | failure = results[i].failure; 44 | returnData = results[i].returnData; 45 | break; 46 | } 47 | } 48 | 49 | // Emitting event only if empty successful result expected. 50 | // Can fail if call context is static, but usually it's not a case, 51 | // because view/pure call without return data doesn't make sense. 52 | // Useful, because for such calls we can check for this event, 53 | // to be sure that the needed call was made. 54 | if (!failure && returnData.length == 0) { 55 | emit Called(msg.value, data); 56 | } 57 | 58 | assembly { 59 | switch failure 60 | case 0 { 61 | return(add(returnData, 0x20), mload(returnData)) 62 | } 63 | default { 64 | revert(add(returnData, 0x20), mload(returnData)) 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /system-contracts/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "@matterlabs/hardhat-zksync-chai-matchers"; 2 | import "@matterlabs/hardhat-zksync-node"; 3 | import "@matterlabs/hardhat-zksync-solc"; 4 | import "@nomiclabs/hardhat-ethers"; 5 | import "hardhat-typechain"; 6 | 7 | export default { 8 | zksolc: { 9 | version: "1.3.18", 10 | compilerSource: "binary", 11 | settings: { 12 | isSystem: true, 13 | }, 14 | }, 15 | zkSyncDeploy: { 16 | zkSyncNetwork: "http://localhost:3050", 17 | ethNetwork: "http://localhost:8545", 18 | }, 19 | solidity: { 20 | version: "0.8.20", 21 | settings: { 22 | optimizer: { 23 | enabled: true, 24 | runs: 9999999, 25 | }, 26 | outputSelection: { 27 | "*": { 28 | "*": ["storageLayout"], 29 | }, 30 | }, 31 | }, 32 | }, 33 | networks: { 34 | hardhat: { 35 | zksync: true, 36 | }, 37 | zkSyncTestNode: { 38 | url: "http://127.0.0.1:8011", 39 | ethNetwork: "", 40 | zksync: true, 41 | }, 42 | }, 43 | paths: { 44 | sources: "./contracts-preprocessed", 45 | }, 46 | }; 47 | -------------------------------------------------------------------------------- /system-contracts/scripts/compile-yul.ts: -------------------------------------------------------------------------------- 1 | import type { CompilerPaths } from "./utils"; 2 | import { spawn, compilerLocation, prepareCompilerPaths } from "./utils"; 3 | import * as fs from "fs"; 4 | import { Command } from "commander"; 5 | 6 | const COMPILER_VERSION = "1.3.18"; 7 | const IS_COMPILER_PRE_RELEASE = false; 8 | 9 | export async function compileYul(paths: CompilerPaths, file: string) { 10 | const zksolcLocation = await compilerLocation(COMPILER_VERSION, IS_COMPILER_PRE_RELEASE); 11 | await spawn( 12 | `${zksolcLocation} ${paths.absolutePathSources}/${file} --optimization 3 --system-mode --yul --bin --overwrite -o ${paths.absolutePathArtifacts}` 13 | ); 14 | } 15 | 16 | export async function compileYulFolder(path: string) { 17 | const paths = prepareCompilerPaths(path); 18 | const files: string[] = (await fs.promises.readdir(path)).filter((fn) => fn.endsWith(".yul")); 19 | for (const file of files) { 20 | await compileYul(paths, `${file}`); 21 | } 22 | } 23 | 24 | async function main() { 25 | const program = new Command(); 26 | 27 | program.version("0.1.0").name("compile yul").description("publish preimages for the L2 contracts"); 28 | 29 | program.command("compile-bootloader").action(async () => { 30 | await compileYulFolder("bootloader/build"); 31 | await compileYulFolder("bootloader/tests"); 32 | }); 33 | 34 | program.command("compile-precompiles").action(async () => { 35 | await compileYulFolder("contracts-preprocessed"); 36 | await compileYulFolder("contracts-preprocessed/precompiles"); 37 | await compileYulFolder("contracts-preprocessed/precompiles/test-contracts"); 38 | }); 39 | 40 | await program.parseAsync(process.argv); 41 | } 42 | 43 | main() 44 | .then(() => process.exit(0)) 45 | .catch((err) => { 46 | console.error("Error:", err.message || err); 47 | process.exit(1); 48 | }); 49 | -------------------------------------------------------------------------------- /system-contracts/scripts/compile-zasm.ts: -------------------------------------------------------------------------------- 1 | import type { CompilerPaths } from "./utils"; 2 | import { spawn, compilerLocation, prepareCompilerPaths } from "./utils"; 3 | import * as fs from "fs"; 4 | 5 | const COMPILER_VERSION = "1.3.18"; 6 | const IS_COMPILER_PRE_RELEASE = false; 7 | 8 | export async function compileZasm(paths: CompilerPaths, file: string) { 9 | const zksolcLocation = await compilerLocation(COMPILER_VERSION, IS_COMPILER_PRE_RELEASE); 10 | await spawn( 11 | `${zksolcLocation} ${paths.absolutePathSources}/${file} --zkasm --bin --overwrite -o ${paths.absolutePathArtifacts}` 12 | ); 13 | } 14 | 15 | export async function compileZasmFolder(path: string) { 16 | const paths = prepareCompilerPaths(path); 17 | const files: string[] = (await fs.promises.readdir(path)).filter((fn) => fn.endsWith(".zasm")); 18 | for (const file of files) { 19 | await compileZasm(paths, `${file}`); 20 | } 21 | } 22 | 23 | // Currently used only for the test contracts 24 | async function main() { 25 | await compileZasmFolder("contracts-preprocessed/test-contracts"); 26 | } 27 | 28 | main() 29 | .then(() => process.exit(0)) 30 | .catch((err) => { 31 | console.error("Error:", err.message || err); 32 | process.exit(1); 33 | }); 34 | -------------------------------------------------------------------------------- /system-contracts/scripts/preprocess-system-contracts.ts: -------------------------------------------------------------------------------- 1 | import { existsSync, mkdirSync, writeFileSync } from "fs"; 2 | import path from "path"; 3 | import { renderFile } from "template-file"; 4 | import { glob } from "fast-glob"; 5 | import { Command } from "commander"; 6 | 7 | const CONTRACTS_DIR = "contracts"; 8 | const OUTPUT_DIR = "contracts-preprocessed"; 9 | 10 | const params = { 11 | SYSTEM_CONTRACTS_OFFSET: "0x8000", 12 | }; 13 | 14 | async function preprocess(testMode: boolean) { 15 | if (testMode) { 16 | console.log("\x1b[31mWarning: test mode for the preprocessing being used!\x1b[0m"); 17 | params.SYSTEM_CONTRACTS_OFFSET = "0x9000"; 18 | } 19 | 20 | const contracts = await glob( 21 | [`${CONTRACTS_DIR}/**/*.sol`, `${CONTRACTS_DIR}/**/*.yul`, `${CONTRACTS_DIR}/**/*.zasm`], 22 | { onlyFiles: true } 23 | ); 24 | 25 | for (const contract of contracts) { 26 | const preprocessed = await renderFile(contract, params); 27 | const fileName = `${OUTPUT_DIR}/${contract.slice(CONTRACTS_DIR.length)}`; 28 | const directory = path.dirname(fileName); 29 | if (!existsSync(directory)) { 30 | mkdirSync(directory, { recursive: true }); 31 | } 32 | writeFileSync(fileName, preprocessed); 33 | } 34 | 35 | console.log("System Contracts preprocessing done!"); 36 | } 37 | 38 | async function main() { 39 | const program = new Command(); 40 | 41 | program.version("0.1.0").name("system contracts preprocessor").description("preprocess the system contracts"); 42 | 43 | program.option("--test-mode").action(async (cmd) => { 44 | await preprocess(cmd.testMode); 45 | }); 46 | 47 | await program.parseAsync(process.argv); 48 | } 49 | 50 | main(); 51 | -------------------------------------------------------------------------------- /system-contracts/test/ComplexUpgrader.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { ethers, network } from "hardhat"; 3 | import type { ComplexUpgrader, MockContract } from "../typechain"; 4 | import { ComplexUpgraderFactory } from "../typechain"; 5 | import { TEST_COMPLEX_UPGRADER_CONTRACT_ADDRESS, TEST_FORCE_DEPLOYER_ADDRESS } from "./shared/constants"; 6 | import { deployContract, deployContractOnAddress, getWallets } from "./shared/utils"; 7 | 8 | describe("ComplexUpgrader tests", function () { 9 | let complexUpgrader: ComplexUpgrader; 10 | let dummyUpgrade: MockContract; 11 | 12 | before(async () => { 13 | const wallet = (await getWallets())[0]; 14 | await deployContractOnAddress(TEST_COMPLEX_UPGRADER_CONTRACT_ADDRESS, "ComplexUpgrader"); 15 | complexUpgrader = ComplexUpgraderFactory.connect(TEST_COMPLEX_UPGRADER_CONTRACT_ADDRESS, wallet); 16 | dummyUpgrade = (await deployContract("MockContract")) as MockContract; 17 | }); 18 | 19 | describe("upgrade", function () { 20 | it("non force deployer failed to call", async () => { 21 | await expect(complexUpgrader.upgrade(dummyUpgrade.address, "0xdeadbeef")).to.be.revertedWith( 22 | "Can only be called by FORCE_DEPLOYER" 23 | ); 24 | }); 25 | 26 | it("successfully upgraded", async () => { 27 | const force_deployer = await ethers.getImpersonatedSigner(TEST_FORCE_DEPLOYER_ADDRESS); 28 | 29 | await expect(complexUpgrader.connect(force_deployer).upgrade(dummyUpgrade.address, "0xdeadbeef")) 30 | .to.emit(dummyUpgrade.attach(TEST_COMPLEX_UPGRADER_CONTRACT_ADDRESS), "Called") 31 | .withArgs(0, "0xdeadbeef"); 32 | 33 | await network.provider.request({ 34 | method: "hardhat_stopImpersonatingAccount", 35 | params: [TEST_FORCE_DEPLOYER_ADDRESS], 36 | }); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /system-contracts/test/EmptyContract.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { ethers } from "hardhat"; 3 | import type { Wallet } from "zksync-web3"; 4 | import type { EmptyContract } from "../typechain"; 5 | import { deployContract, getWallets, provider } from "./shared/utils"; 6 | 7 | describe("EmptyContract tests", function () { 8 | let wallet: Wallet; 9 | let emptyContract: EmptyContract; 10 | 11 | before(async () => { 12 | wallet = getWallets()[0]; 13 | emptyContract = (await deployContract("EmptyContract")) as EmptyContract; 14 | }); 15 | 16 | it("zero value", async () => { 17 | const tx = { 18 | from: wallet.address, 19 | to: emptyContract.address, 20 | value: 0, 21 | data: "0x1234567890deadbeef1234567890", 22 | }; 23 | expect(await provider.call(tx)).to.be.eq("0x"); 24 | }); 25 | 26 | it("non-zero value", async () => { 27 | const tx = { 28 | from: wallet.address, 29 | to: emptyContract.address, 30 | value: ethers.utils.parseEther("1.0"), 31 | data: "0x1234567890deadbeef1234567890", 32 | }; 33 | expect(await provider.call(tx)).to.be.eq("0x"); 34 | }); 35 | 36 | it("empty calldata", async () => { 37 | const tx = { 38 | from: wallet.address, 39 | to: emptyContract.address, 40 | data: "", 41 | }; 42 | expect(await provider.call(tx)).to.be.eq("0x"); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /system-contracts/test/ImmutableSimulator.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { ethers, network } from "hardhat"; 3 | import type { ImmutableSimulator } from "../typechain"; 4 | import { ImmutableSimulatorFactory } from "../typechain"; 5 | import { 6 | TEST_DEPLOYER_SYSTEM_CONTRACT_ADDRESS, 7 | TEST_IMMUTABLE_SIMULATOR_SYSTEM_CONTRACT_ADDRESS, 8 | } from "./shared/constants"; 9 | import { deployContractOnAddress, getWallets } from "./shared/utils"; 10 | 11 | describe("ImmutableSimulator tests", function () { 12 | let immutableSimulator: ImmutableSimulator; 13 | 14 | const RANDOM_ADDRESS = "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; 15 | const IMMUTABLES_DATA = [ 16 | { 17 | index: 0, 18 | value: "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", 19 | }, 20 | { 21 | index: 23, 22 | value: "0x0000000000000000000000000000000000000000000000000000000000000111", 23 | }, 24 | ]; 25 | 26 | before(async () => { 27 | const wallet = getWallets()[0]; 28 | await deployContractOnAddress(TEST_IMMUTABLE_SIMULATOR_SYSTEM_CONTRACT_ADDRESS, "ImmutableSimulator"); 29 | immutableSimulator = ImmutableSimulatorFactory.connect(TEST_IMMUTABLE_SIMULATOR_SYSTEM_CONTRACT_ADDRESS, wallet); 30 | }); 31 | 32 | describe("setImmutables", function () { 33 | it("non-deployer failed to call", async () => { 34 | await expect(immutableSimulator.setImmutables(RANDOM_ADDRESS, IMMUTABLES_DATA)).to.be.revertedWith( 35 | "Callable only by the deployer system contract" 36 | ); 37 | }); 38 | 39 | it("successfully set", async () => { 40 | await network.provider.request({ 41 | method: "hardhat_impersonateAccount", 42 | params: [TEST_DEPLOYER_SYSTEM_CONTRACT_ADDRESS], 43 | }); 44 | 45 | const deployer_account = await ethers.getSigner(TEST_DEPLOYER_SYSTEM_CONTRACT_ADDRESS); 46 | 47 | await immutableSimulator.connect(deployer_account).setImmutables(RANDOM_ADDRESS, IMMUTABLES_DATA); 48 | 49 | await network.provider.request({ 50 | method: "hardhat_stopImpersonatingAccount", 51 | params: [TEST_DEPLOYER_SYSTEM_CONTRACT_ADDRESS], 52 | }); 53 | 54 | for (const immutable of IMMUTABLES_DATA) { 55 | expect(await immutableSimulator.getImmutable(RANDOM_ADDRESS, immutable.index)).to.be.eq(immutable.value); 56 | } 57 | }); 58 | }); 59 | 60 | describe("getImmutable", function () { 61 | it("zero", async () => { 62 | expect(await immutableSimulator.getImmutable(RANDOM_ADDRESS, 333)).to.be.eq(ethers.constants.HashZero); 63 | }); 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /system-contracts/test/shared/constants.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "ethers"; 2 | 3 | export const TEST_BOOTLOADER_FORMAL_ADDRESS = "0x0000000000000000000000000000000000009001"; 4 | export const TEST_ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT_ADDRESS = "0x0000000000000000000000000000000000009002"; 5 | export const TEST_NONCE_HOLDER_SYSTEM_CONTRACT_ADDRESS = "0x0000000000000000000000000000000000009003"; 6 | export const TEST_KNOWN_CODE_STORAGE_CONTRACT_ADDRESS = "0x0000000000000000000000000000000000009004"; 7 | export const TEST_IMMUTABLE_SIMULATOR_SYSTEM_CONTRACT_ADDRESS = "0x0000000000000000000000000000000000009005"; 8 | export const TEST_DEPLOYER_SYSTEM_CONTRACT_ADDRESS = "0x0000000000000000000000000000000000009006"; 9 | export const TEST_FORCE_DEPLOYER_ADDRESS = "0x0000000000000000000000000000000000009007"; 10 | export const TEST_L1_MESSENGER_SYSTEM_CONTRACT_ADDRESS = "0x0000000000000000000000000000000000009008"; 11 | export const TEST_MSG_VALUE_SYSTEM_CONTRACT_ADDRESS = "0x0000000000000000000000000000000000009009"; 12 | export const TEST_ETH_TOKEN_SYSTEM_CONTRACT_ADDRESS = "0x000000000000000000000000000000000000900a"; 13 | export const TEST_BOOTLOADER_UTILITIES_ADDRESS = "0x000000000000000000000000000000000000900c"; 14 | export const TEST_COMPRESSOR_CONTRACT_ADDRESS = "0x000000000000000000000000000000000000900e"; 15 | export const TEST_COMPLEX_UPGRADER_CONTRACT_ADDRESS = "0x000000000000000000000000000000000000900f"; 16 | 17 | // event writer should be on the original address because event logs are filtered by address 18 | export const REAL_EVENT_WRITER_CONTRACT_ADDRESS = "0x000000000000000000000000000000000000800d"; 19 | export const REAL_DEPLOYER_SYSTEM_CONTRACT_ADDRESS = "0x0000000000000000000000000000000000008006"; 20 | export const REAL_ACCOUNT_CODE_STORAGE_SYSTEM_CONTRACT_ADDRESS = "0x0000000000000000000000000000000000008002"; 21 | export const REAL_KECCAK256_CONTRACT_ADDRESS = "0x0000000000000000000000000000000000008010"; 22 | 23 | export const EMPTY_STRING_KECCAK = "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"; 24 | export const TWO_IN_256 = BigNumber.from(2).pow(256); 25 | export const ONE_BYTES32_HEX = "0x0000000000000000000000000000000000000000000000000000000000000001"; 26 | -------------------------------------------------------------------------------- /system-contracts/test/shared/extraAbiCaller.ts: -------------------------------------------------------------------------------- 1 | import type { BigNumber } from "ethers"; 2 | import { ethers } from "hardhat"; 3 | 4 | // kernel space is required to set the context u128 value 5 | export const EXTRA_ABI_CALLER_ADDRESS = "0x000000000000000000000000000000000000BEEF"; 6 | const EXTRA_ABI_REGISTERS_NUMBER = 10; 7 | 8 | export function encodeExtraAbiCallerCalldata( 9 | to: string, 10 | value: BigNumber, 11 | extraData: string[], 12 | calldata: string 13 | ): string { 14 | if (extraData.length > EXTRA_ABI_REGISTERS_NUMBER) throw "Too big extraData length"; 15 | extraData.push(...Array(EXTRA_ABI_REGISTERS_NUMBER - extraData.length).fill(ethers.constants.HashZero)); 16 | const encodedData = ethers.utils.defaultAbiCoder.encode( 17 | ["address", "uint256", `uint256[${EXTRA_ABI_REGISTERS_NUMBER}]`], 18 | [to, value, extraData] 19 | ); 20 | return ethers.utils.hexConcat([encodedData, calldata]); 21 | } 22 | -------------------------------------------------------------------------------- /tools/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zksync_verifier_contract_generator" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | serde_derive = "1.0" 8 | serde_json = "1.0" 9 | lazy_static = "1.4" 10 | structopt = "0.3.26" 11 | handlebars = "4.4.0" 12 | 13 | [workspace] 14 | -------------------------------------------------------------------------------- /tools/README.md: -------------------------------------------------------------------------------- 1 | # Tool for generating `Verifier.sol` using json Verification key 2 | 3 | `cargo run --bin zksync_verifier_contract_generator --release -- --input_path /path/to/scheduler_verification_key.json --output_path /path/to/Verifier.sol` 4 | 5 | To generate the verifier from the scheduler key in 'data' directory, just run: 6 | 7 | ```shell 8 | cargo run --bin zksync_verifier_contract_generator --release -- --input_path data/scheduler_key.json --output_path ../l1-contracts/contracts/zksync/Verifier.sol 9 | ``` 10 | -------------------------------------------------------------------------------- /tools/rust-toolchain: -------------------------------------------------------------------------------- 1 | 1.72.0 2 | --------------------------------------------------------------------------------