├── .github ├── CODEOWNERS ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ └── feature_report.md ├── RELEASE.md ├── pull_request_template.md └── workflows │ ├── release.yml │ ├── test.yml │ └── validate.yml ├── .gitignore ├── .releaserc ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE-APACHE ├── LICENSE-MIT ├── Makefile ├── README.md ├── SECURITY.md ├── accounts ├── deployer.go ├── signer.go ├── smart_account.go ├── smart_account_utils.go ├── smart_account_utils_test.go ├── types.go ├── util.go ├── wallet.go ├── wallet_l1.go └── wallet_l2.go ├── clients ├── client.go ├── types.go └── util.go ├── contracts ├── allowlist │ └── allow_list.go ├── assetrouterbase │ └── asset_router_base.go ├── bridgedstandardtoken │ └── bridged_standard_token.go ├── bridgehub │ └── bridgehub.go ├── contractdeployer │ └── contract_deployer.go ├── erc1271 │ └── erc1271.go ├── erc20 │ └── erc20.go ├── ethtoken │ └── eth_token.go ├── l1assetrouter │ └── l1_asset_router.go ├── l1bridge │ └── l1_bridge.go ├── l1erc20bridge │ └── l1_erc20_bridge.go ├── l1messenger │ └── l1_messenger.go ├── l1nativetokenvault │ └── l1_native_token_vault.go ├── l1nullifier │ └── l1_nullifier.go ├── l1sharedbridge │ └── l1_shared_bridge.go ├── l2assetrouter │ └── l2_asset_router.go ├── l2bridge │ └── l2_bridge.go ├── l2nativetokenvault │ └── l2_native_token_vault.go ├── l2sharedbridge │ └── l2_shared_bridge.go ├── nonceholder │ └── nonce_holder.go ├── paymasterflow │ └── paymaster_flow.go ├── testneterc20token │ └── testnet_erc20_token.go ├── zksync │ └── zk_sync.go └── zksynchyperchain │ └── zksync_hyperchain.go ├── eip712 └── domain.go ├── go.mod ├── go.sum ├── logo.svg ├── scripts ├── check-format.sh ├── fetch-abi │ ├── entrypoint.sh │ └── execute.sh └── generate-contracts │ ├── entrypoint.sh │ └── execute.sh ├── test ├── account_abstraction_test.go ├── base_client_test.go ├── const.go ├── incrementer.go ├── setup_test.go ├── smart_account_test.go ├── smart_account_utils_test.go ├── testdata │ ├── Demo.zbin │ ├── Foo.zbin │ ├── Incrementer.zbin │ ├── Paymaster.json │ ├── Storage.zbin │ ├── TwoUserMultisig.json │ └── tokens.json ├── token.go └── wallet_test.go ├── types ├── block.go ├── call_msg.go ├── config.go ├── contract.go ├── fee.go ├── log.go ├── network.go ├── paymaster.go ├── proof.go ├── token.go ├── transaction.go └── transaction_test.go └── utils ├── bridge.go ├── bridge_test.go ├── config.go ├── contract.go ├── contract_test.go ├── gas.go ├── gas_test.go ├── mics.go ├── paymaster.go ├── paymaster_test.go ├── signature.go └── signature_test.go /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This CODEOWNERS file sets the individuals responsible for code in the zksync2-go Golang SDK repository. 2 | 3 | # These users are the default owners for everything in the repo. 4 | # They will be requested for review when someone opens a pull request. 5 | * @zksync-sdk/txfusion 6 | 7 | # You can also specify code owners for specific directories or files. 8 | # For example: 9 | # /src/ @developer1 @developer2 10 | # /docs/ @documenter 11 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Welcome! 👋 4 | 5 | Hello there, contributor! We're delighted that you're considering contributing to the `zksync2-go` project. This document is here to guide you through the steps and best practices for contributing to this Golang-based repository. 6 | 7 | Please take a moment to review this document to ensure a smooth and efficient contribution process for everyone involved. 8 | 9 | ## Getting Started 10 | 11 | - **Fork the repository.** Begin by forking the main `zksync2-go` repository to your personal GitHub account. 12 | 13 | - **Clone the repository.** After forking, clone the repository to your local machine: 14 | 15 | ```bash 16 | git clone https://github.com//zksync2-go.git 17 | ``` 18 | 19 | - **Create a new branch.** Use descriptive names for your branches to help identify the feature, bugfix, or enhancement you're addressing: 20 | 21 | ```bash 22 | git checkout -b feature/description-of-your-feature 23 | ``` 24 | 25 | ## Making Changes 26 | 27 | - **Write your code.** Ensure your code is thoroughly tested and functions as expected. Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines. 28 | - **Compile and test.** Before submitting a pull request, ensure your code compiles, passes lint checks, and all tests are successful. You should also write unit tests for your contributions. Use the following command for these checks: 29 | 30 | ```bash 31 | make format 32 | make run-tests 33 | ``` 34 | 35 | - **Commit your changes.** Adhere to the [Conventional Commits](https://www.conventionalcommits.org/) standard when writing commit messages. 36 | 37 | - **Push your changes.** Push the changes to your forked repository: 38 | 39 | ```bash 40 | git push origin feature/description-of-your-feature 41 | ``` 42 | 43 | ## Submitting a Pull Request 44 | 45 | - **Initiate a pull request (PR).** Go to the main `zksync2-go` repository. Your recently pushed branch should be highlighted, showing a "Compare & pull request" button. Click on it and provide a clear, detailed description of your changes in the PR. 46 | 47 | - **Await a review.** Our maintainers will review your PR. They might request changes or clarifications, so be ready to address any feedback. 48 | 49 | ## Code Style Guide 50 | 51 | We follow basic coding style guidelines. Before committing, ensure your code is formatted and lint checks pass: 52 | 53 | ```bash 54 | go fmt ./... 55 | ``` 56 | 57 | This ensures consistent code style throughout the project and helps identify potential issues early. 58 | 59 | ## Need Assistance? 60 | 61 | If you're unsure about something or have questions, don't hesitate to open an issue or initiate a discussion in our [zkSync Community Hub](https://github.com/zkSync-Community-Hub/zkSync-developers/discussions). We're here to assist! 62 | 63 | ## What's Next? 64 | 65 | Once your PR is approved and merged, your contribution will be integrated into the `zksync2-go` repository. Congratulations, and thank you! We value your contribution and look forward to future collaborations. 66 | 67 | Remember, the best contributions come from enjoying the process, being respectful, and continuously learning. Thanks for being a part of our community! 68 | 69 | --- 70 | 71 | *Last updated: Nov 10, 2023* -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Use this template for reporting issues 4 | title: "" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### 🐛 Bug Report for zksync2-js JavaScript SDK 11 | 12 | #### 📝 Description 13 | 14 | Provide a clear and concise description of the bug. 15 | 16 | 17 | #### 🔄 Reproduction Steps 18 | 19 | 1. Step 1 20 | 2. Step 2 21 | 3. ... 22 | 23 | #### 🤔 Expected Behavior 24 | 25 | Describe what you expected to happen. 26 | 27 | #### 😯 Current Behavior 28 | 29 | Describe what actually happened. 30 | 31 | #### 🖥️ Environment 32 | 33 | - **Node version**: [e.g., Node 18.16.1] 34 | - **Operating System & Version**: [e.g., Ubuntu 20.04] 35 | - **Other relevant environment details**: 36 | 37 | #### 📋 Additional Context 38 | 39 | Add any other context about the problem here. If applicable, add screenshots to help explain. 40 | 41 | #### 📎 Log Output 42 | 43 | ``` 44 | Paste any relevant log output here. 45 | ``` -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: zksync-developers Discussion 4 | url: https://github.com/zkSync-Community-Hub/zkync-developers/discussions 5 | about: Please provide feedback, and ask questions here. 6 | - name: zksync-web3 SDK documentation page 7 | url: https://era.zksync.io/docs/api/js/ 8 | about: Please refer to the documentation for immediate answers. 9 | - name: zksync2-js SDK documentation page 10 | url: https://era.zksync.io/docs/api/js/zksync2-js 11 | about: Please refer to the documentation for immediate answers. 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Use this template for requesting features 4 | title: "" 5 | labels: feat 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### 🌟 Feature Request 11 | 12 | #### 📝 Description 13 | 14 | Provide a clear and concise description of the feature you'd like to see. 15 | 16 | #### 🤔 Rationale 17 | 18 | Explain why this feature is important and how it benefits the project. 19 | 20 | #### 🖼️ Mockups/Examples 21 | 22 | If applicable, provide mockups or examples of how the feature would work. 23 | 24 | #### 📋 Additional Context 25 | 26 | Add any other context or information about the feature request here. -------------------------------------------------------------------------------- /.github/RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release Process 2 | 3 | This document provides a comprehensive guide on the steps to execute a release for this project. 4 | 5 | ## Prerequisites 6 | 7 | Before initiating the release process, ensure the following prerequisites are met: 8 | 9 | - Commit messages must adhere to the [conventional commit specification](https://www.conventionalcommits.org/). 10 | - Proper permissions are in place to create and manage releases. 11 | 12 | ## Steps 13 | 14 | ### 1. Create Release 15 | 16 | 1. **Merge Pull Request to appropriate branch:** 17 | - Ensure that the changes intended for release are encapsulated in a Pull Request. 18 | - Merge the approved Pull Request into the `main`. Currently, 19 | only the `main`, `beta` branches are allowed for releasing. 20 | 21 | 2. **Manual execution of [Release](workflows/release.yml) Action:** 22 | - The `Release` GitHub Action automates versioning based on conventional commit messages. Pipeline creates tag, release, 23 | and pushes new commit in which updates `CHANGELOG.md` and version in `package.json`. 24 | - Click on the `Run workflow` button and select the desired branch or run the following command: 25 | `gh workflow run --ref release.yml`. 26 | 27 | ### 2. Publish Release 28 | 29 | 1. Releases on GitHub are automatically uploaded to `https://pkg.go.dev` (it requires some time to registry index the release). -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # What :computer: 2 | * First thing updated with this PR 3 | * Second thing updated with this PR 4 | * Third thing updated with this PR 5 | 6 | # Why :hand: 7 | * Reason why first thing was added to PR 8 | * Reason why second thing was added to PR 9 | * Reason why third thing was added to PR 10 | 11 | # Evidence :camera: 12 | Include screenshots, screen recordings, or `console` output here demonstrating that your changes work as intended 13 | 14 | 15 | 16 | # Notes :memo: 17 | * Any notes/thoughts that the reviewers should know prior to reviewing the code? 18 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | workflow_dispatch: 4 | 5 | permissions: 6 | contents: read # for checkout 7 | 8 | jobs: 9 | release: 10 | name: Release 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: write # to be able to publish a GitHub release 14 | issues: write # to be able to comment on released issues 15 | pull-requests: write # to be able to comment on released pull requests 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 0 21 | - name: Setup Node.js 22 | uses: actions/setup-node@v4 23 | with: 24 | node-version: "lts/*" 25 | - name: Install dependencies 26 | run: | 27 | npm install "semantic-release@21.0.7" \ 28 | "@semantic-release/commit-analyzer@11.1.0" \ 29 | "@semantic-release/changelog@6.0.3" \ 30 | "@semantic-release/git@10.0.1" \ 31 | "@semantic-release/github@9.0.4" 32 | - name: Release 33 | env: 34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 35 | run: npx semantic-release -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Run tests 2 | on: 3 | push: 4 | branches: [ main, beta ] 5 | workflow_dispatch: 6 | pull_request: 7 | branches: [main, beta ] 8 | types: [ opened, reopened, synchronize ] 9 | 10 | permissions: 11 | contents: read # for checkout 12 | 13 | jobs: 14 | eth-based-chain-test: 15 | name: ETH-based Chain Test 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v4 20 | with: 21 | fetch-depth: 0 22 | - name: Install Go 23 | uses: actions/setup-go@v5.0.0 24 | - name: Install dependencies 25 | run: go mod download 26 | - name: Run local-setup 27 | run: | 28 | git clone https://github.com/matter-labs/local-setup.git 29 | pushd local-setup 30 | ./start-zk-chains.sh 31 | popd 32 | - name: Extract custom token address 33 | run: | 34 | echo "Waiting for token deployment..." 35 | until docker exec local-setup-zksync-1 test -f /configs/erc20.yaml; do 36 | echo "Token config file not found yet, checking again in 5 seconds..." 37 | sleep 5 38 | done 39 | 40 | echo "Extracting deployed token address from inside zksync container..." 41 | CUSTOM_TOKEN_ADDRESS=$(docker exec local-setup-zksync-1 awk -F": " '/tokens:/ {found_tokens=1} found_tokens && /DAI:/ {found_dai=1} found_dai && /address:/ {print $2; exit}' /configs/erc20.yaml) 42 | 43 | if [ -z "$CUSTOM_TOKEN_ADDRESS" ]; then 44 | echo "❌ Error: Could not retrieve token address. Exiting." 45 | exit 1 46 | fi 47 | 48 | echo "Custom token address: $CUSTOM_TOKEN_ADDRESS" 49 | # Export the variable so subsequent steps have access to it 50 | echo "CUSTOM_TOKEN_ADDRESS=$CUSTOM_TOKEN_ADDRESS" >> $GITHUB_ENV 51 | - name: Run tests 52 | env: 53 | CUSTOM_TOKEN_ADDRESS: ${{ env.CUSTOM_TOKEN_ADDRESS }} 54 | run: make run-tests-on-eth-based-chain 55 | 56 | non-eth-based-chain-test: 57 | name: Non-ETH-based Chain Test 58 | runs-on: ubuntu-latest 59 | steps: 60 | - name: Checkout 61 | uses: actions/checkout@v4 62 | with: 63 | fetch-depth: 0 64 | - name: Install Go 65 | uses: actions/setup-go@v5.0.0 66 | - name: Install dependencies 67 | run: go mod download 68 | - name: Run local-setup 69 | run: | 70 | git clone https://github.com/matter-labs/local-setup.git 71 | pushd local-setup 72 | ./start-zk-chains.sh 73 | popd 74 | - name: Extract custom token address 75 | run: | 76 | echo "Waiting for token deployment..." 77 | until docker exec local-setup-zksync-1 test -f /configs/erc20.yaml; do 78 | echo "Token config file not found yet, checking again in 5 seconds..." 79 | sleep 5 80 | done 81 | 82 | echo "Extracting deployed token address from inside zksync container..." 83 | CUSTOM_TOKEN_ADDRESS=$(docker exec local-setup-zksync-1 awk -F": " '/tokens:/ {found_tokens=1} found_tokens && /DAI:/ {found_dai=1} found_dai && /address:/ {print $2; exit}' /configs/erc20.yaml) 84 | 85 | if [ -z "$CUSTOM_TOKEN_ADDRESS" ]; then 86 | echo "❌ Error: Could not retrieve token address. Exiting." 87 | exit 1 88 | fi 89 | 90 | echo "Custom token address: $CUSTOM_TOKEN_ADDRESS" 91 | # Export the variable so subsequent steps have access to it 92 | echo "CUSTOM_TOKEN_ADDRESS=$CUSTOM_TOKEN_ADDRESS" >> $GITHUB_ENV 93 | - name: Run tests 94 | env: 95 | CUSTOM_TOKEN_ADDRESS: ${{ env.CUSTOM_TOKEN_ADDRESS }} 96 | run: make run-tests-on-non-eth-based-chain 97 | -------------------------------------------------------------------------------- /.github/workflows/validate.yml: -------------------------------------------------------------------------------- 1 | name: Validate 2 | on: 3 | pull_request: 4 | branches: [main] 5 | types: [ opened, reopened, synchronize ] 6 | 7 | permissions: 8 | contents: read # for checkout 9 | 10 | jobs: 11 | lint: 12 | name: Check code format 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 0 19 | - name: Install Go 20 | uses: actions/setup-go@v5.0.0 21 | - name: Check code 22 | run: make check-format 23 | commits: 24 | name: Check commits 25 | runs-on: ubuntu-latest 26 | if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v4 30 | - name: Setup Node.js 31 | uses: actions/setup-node@v4 32 | with: 33 | node-version: "lts/*" 34 | - name: Install dependencies 35 | run: npm install -g @commitlint/cli@18.6.1 @commitlint/config-conventional@18.6.2 36 | - name: Configure 37 | run: | 38 | echo 'module.exports = {"extends": ["@commitlint/config-conventional"]}' > commitlint.config.js 39 | - name: Validate 40 | run: | 41 | git fetch 42 | npx commitlint \ 43 | --from ${{ github.event.pull_request.head.sha }}~${{ github.event.pull_request.commits }} \ 44 | --to ${{ github.event.pull_request.head.sha }} \ 45 | --verbose 46 | commits-fork: 47 | name: Check commits from forks 48 | runs-on: ubuntu-latest 49 | if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository 50 | steps: 51 | - name: Checkout 52 | uses: actions/checkout@v4 53 | - name: Setup Node.js 54 | uses: actions/setup-node@v4 55 | with: 56 | node-version: "lts/*" 57 | - name: Install dependencies 58 | run: npm install -g @commitlint/cli@18.6.1 @commitlint/config-conventional@18.6.2 59 | - name: Configure 60 | run: | 61 | echo 'module.exports = {"extends": ["@commitlint/config-conventional"]}' > commitlint.config.js 62 | - name: Validate 63 | run: | 64 | git fetch origin "+refs/pull/${{ github.event.pull_request.number }}/head:refs/pull/${{ github.event.pull_request.number }}/head" 65 | git checkout "refs/pull/${{ github.event.pull_request.number }}/head" 66 | npx commitlint \ 67 | --from HEAD~${{ github.event.pull_request.commits }} \ 68 | --to HEAD \ 69 | --verbose -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | abi -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "branches": "main", 3 | "debug": true, 4 | "addReleases": "top", 5 | "npmPublish": false, 6 | "plugins": [ 7 | "@semantic-release/commit-analyzer", 8 | "@semantic-release/release-notes-generator", 9 | [ 10 | "@semantic-release/changelog", 11 | { 12 | "changelogFile": "CHANGELOG.md" 13 | } 14 | ], 15 | [ 16 | "@semantic-release/git", 17 | { 18 | "assets": ["CHANGELOG.md"], 19 | "message": "${nextRelease.version} CHANGELOG [skip ci]\n\n${nextRelease.notes}" 20 | } 21 | ], 22 | "@semantic-release/github" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | Version: 1.1 4 | 5 | Apache 2.0 license, derived from the Apache Foundation Code of Conduct. 6 | Also, CC BY-SA 3.0 derived from the Mozilla Community Participation Guidelines. 7 | 8 | Our goal is to cultivate a safe, friendly, and inclusive space that benefits all participants in the ZKsync ecosystem. 9 | This Code of Conduct outlines our shared values and expectations to help ensure that the community remains a positive and enriching environment for everyone. 10 | 11 | ## When and how to use this Code of Conduct 12 | 13 | This is your guide for engaging as a participant in the ZKsync ecosystem. 14 | It applies to all physical and digital spaces related to ZKsync. 15 | 16 | ## Expected behaviors 17 | 18 | **Be ethical**: 19 | We endeavor to enrich the ZKsync ecosystem, while not infringing on the rights and wellbeing of others. 20 | We also endeavor to enrich ourselves without causing harm to the ZKsync community. 21 | We do not encourage tax evasion, promoting information leaks, speculating on tokens or token prices, or otherwise breaking the law. 22 | 23 | **Be kind and respectful**: 24 | Treat everyone with kindness, empathy, and respect. 25 | We all come from different backgrounds, perspectives and experiences, 26 | so let's celebrate our differences and foster a culture of openness and understanding. 27 | We may have strong feelings about other layer 1 and layer 2 blockchains, 28 | but that is no reason to disparage, defame, or slander any competitor to ZKsync or what other chains are doing. 29 | Feel free to compare metrics and features, but keep to the facts and be respectful of all the builders in web3 30 | trying to advance freedom through blockchain technology! 31 | 32 | **Share and learn**: 33 | Our community is a space for sharing knowledge, experiences, and ideas. 34 | Positively contribute to discussions, offer helpful feedback, 35 | be willing to educate others on your work and remain open to learning from others. 36 | 37 | **Give credit**: 38 | When sharing content or ideas that aren't your own, ensure you give proper credit to the original creator. 39 | Plagiarism and intellectual property infringement are strictly prohibited. 40 | 41 | **Respect privacy**: 42 | Always seek consent before sharing personal information about yourself or others. 43 | Respecting each other's privacy is vital to building trust within our community. 44 | 45 | **Be inquisitive and embrace continuous improvement**: 46 | We strive to improve from each experience, and are open to constructive criticism. 47 | We encourage questions, and redirect them to the appropriate channel if we do not have the answer. 48 | 49 | **Mind your language**: 50 | Communication is key. 51 | Use clear and considerate language in your interactions. 52 | We aim to create a welcoming environment for users of all ages, so please avoid excessive profanity or explicit content. 53 | Remember that ZKsync community members are a diverse bunch. 54 | English is our primary working language, but to help others where English is not their first language, 55 | be succinct and avoid acronyms where possible. 56 | 57 | **Stay on topic**: 58 | While we encourage friendly conversations, please ensure your discussions remain relevant to the community's purpose. 59 | To keep our space focused and valuable, off-topic or irrelevant content may be redirected or removed. 60 | Specific topics that are not appropriate include offering to buy or sell any cryptocurrency or engage in price speculation. 61 | 62 | **No hate speech or harassment**: 63 | Let's maintain a constructive and uplifting atmosphere in all interactions. 64 | We have a zero-tolerance policy for any form of hate speech, bullying, harassment, or discrimination. 65 | This includes, but is not limited to: 66 | 67 | - Violent threats or language directed against another person. 68 | - Sexist, racist, or otherwise discriminatory jokes and language. 69 | - Posting sexually explicit or violent material. 70 | - Posting (or threatening to post) other people's personally identifying information ("doxing"). 71 | - Sharing private content without explicit consent, such as messages sent privately or non-publicly. 72 | - Personal insults. 73 | - Unwelcome sexual attention. 74 | - Excessive or unnecessary profanity. 75 | - Repeated harassment of others. In general, if someone asks you to stop, then stop. 76 | - Advocating for, or encouraging, any of the above behavior. 77 | 78 | **Have fun and connect**: 79 | Finally, remember that ZK Squad and the ZKsync community is a place to connect, learn, and enjoy. 80 | Participate in a manner that encourages positive interactions and enhances the experiences of all. 81 | 82 | ## Managing violations 83 | 84 | If you come across inappropriate content or behavior, please report it without delay. 85 | By working together, we can maintain a positive and safe environment. 86 | 87 | If you are the subject of, or witness to, any violations of this Code of Conduct, please contact us at community@zksync.io. 88 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | fetch-contracts: 2 | cd scripts/fetch-abi && ./execute.sh && cd ../.. 3 | 4 | generate-contracts: 5 | cd scripts/generate-contracts && ./execute.sh && cd ../.. 6 | 7 | run-tests-on-eth-based-chain: 8 | go test -p 1 -timeout 20m -v -skip='^.*_NonEthBasedChain_.*$\' ./test ./accounts ./utils ./types 9 | 10 | run-tests-on-non-eth-based-chain: 11 | ETH_BASED_CHAIN=false go test -p 1 -timeout 20m -v -skip='^.*_EthBasedChain_.*$\' ./test ./accounts ./utils ./types 12 | 13 | check-format: 14 | cd scripts/ && ./check-format.sh && cd ../.. 15 | 16 | format: 17 | gofmt -w . 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🚀 zksync2-go Golang SDK 🚀 2 | 3 | [![License](https://img.shields.io/badge/license-MIT-blue)](LICENSE-MIT) 4 | [![License: Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-orange)](LICENSE-APACHE) 5 | [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](https://www.contributor-covenant.org/) 6 | [![Contributions Welcome](https://img.shields.io/badge/contributions-welcome-orange)](.github/CONTRIBUTING.md) 7 | [![X (formerly Twitter) Follow](https://badgen.net/badge/twitter/@zksyncDevs/1DA1F2?icon&label)](https://x.com/zksyncDevs) 8 | 9 | [![ZKsync Era Logo](logo.svg)](https://zksync.io/) 10 | 11 | > [!WARNING] 12 | > Development of this SDK has been put on the community support. There is no active development of the repository, but the pull requests will be reviewed and merged. 13 | 14 | In order to provide easy access to all the features of ZKsync Era, the `zksync2-go` Golang SDK was created, 15 | which is made in a way that has an interface very similar to those of [geth](https://geth.ethereum.org/). In 16 | fact, `geth` is a dependency of our library and most of the objects exported by `zksync2-go` ( 17 | e.g. `Wallet`, `Client` etc.) inherit from the corresponding `geth` objects and override only the fields that need 18 | to be changed. 19 | 20 | While most of the existing SDKs should work out of the box, deploying smart contracts or using unique ZKsync Era features, 21 | like account abstraction, requires providing additional fields to those that Ethereum transactions have by default. 22 | 23 | The library is made in such a way that after replacing `geth` with `zksync2-go` most client apps will work out of 24 | box. 25 | 26 | 🔗 For a detailed walkthrough, refer to the [official documentation](https://docs.zksync.io/sdk/go/getting-started). 27 | 28 | ## 📌 Overview 29 | 30 | To begin, it is useful to have a basic understanding of the types of objects available and what they are responsible for, at a high level: 31 | 32 | - `Client` provides connection to the ZKsync Era blockchain, which allows querying the blockchain state, such as account, block or transaction details, 33 | querying event logs or evaluating read-only code using call. Additionally, the client facilitates writing to the blockchain by sending 34 | transactions. 35 | - `Wallet` wraps all operations that interact with an account. An account generally has a private key, which can be used to sign a variety of 36 | types of payloads. It provides easy usage of the most common features. 37 | 38 | ## 🛠 Prerequisites 39 | 40 | - `go: >= 1.21` ([installation guide](https://go.dev/doc/install)) 41 | 42 | ## 📥 Installation & Setup 43 | 44 | ```bash 45 | go get github.com/zksync-sdk/zksync2-go 46 | ``` 47 | 48 | ## 📝 Examples 49 | 50 | The complete examples with various use cases are available [here](https://github.com/zksync-sdk/zksync2-examples/tree/main/go). 51 | 52 | ### Connect to the ZKsync Era network: 53 | 54 | ```go 55 | import ( 56 | "github.com/ethereum/go-ethereum/ethclient" 57 | "github.com/zksync-sdk/zksync2-go/clients" 58 | "log" 59 | "os" 60 | ) 61 | 62 | var ( 63 | ZkSyncProvider = "https://sepolia.era.zksync.dev" // zkSync Era testnet 64 | EthereumProvider = "https://rpc.ankr.com/eth_sepolia" // Sepolia testnet 65 | ) 66 | // Connect to ZKsync network 67 | client, err := clients.Dial(ZkSyncProvider) 68 | if err != nil { 69 | log.Panic(err) 70 | } 71 | defer client.Close() 72 | 73 | // Connect to Ethereum network 74 | ethClient, err := ethclient.Dial(EthereumProvider) 75 | if err != nil { 76 | log.Panic(err) 77 | } 78 | defer ethClient.Close() 79 | ``` 80 | 81 | ### Get the latest block number 82 | 83 | ```ts 84 | blockNumber, err := client.BlockNumber(context.Background()) 85 | if err != nil { 86 | log.Panic(err) 87 | } 88 | fmt.Println("Block number: ", blockNumber) 89 | ``` 90 | 91 | ### Get the latest block 92 | 93 | ```ts 94 | block, err := client.BlockByNumber(context.Background(), nil) 95 | if err != nil { 96 | log.Panic(err) 97 | } 98 | fmt.Printf("%+v\n", block) 99 | ``` 100 | 101 | ### Create a wallet 102 | 103 | ```ts 104 | privateKey := os.Getenv("PRIVATE_KEY") 105 | w, err := accounts.NewWallet(common.Hex2Bytes(privateKey), client, ethClient) 106 | if err != nil { 107 | log.Panic(err) 108 | } 109 | ``` 110 | 111 | ### Check account balances 112 | 113 | ```go 114 | balance, err := w.Balance(nil, utils.EthAddress) // balance on ZKsync Era network 115 | if err != nil { 116 | log.Panic(err) 117 | } 118 | fmt.Println("Balance: ", balance) 119 | 120 | balanceL1, err := w.BalanceL1(nil, utils.EthAddress) // balance on goerli network 121 | if err != nil { 122 | log.Panic(err) 123 | } 124 | fmt.Println("Balance L1: ", balanceL1) 125 | ``` 126 | 127 | ### Transfer funds 128 | 129 | Transfer funds among accounts on L2 network. 130 | 131 | 132 | ```go 133 | chainID, err := client.ChainID(context.Background()) 134 | if err != nil { 135 | log.Panic(err) 136 | } 137 | receiver, err := accounts.NewRandomWallet(chainID.Int64(), client, ethClient) 138 | if err != nil { 139 | log.Panic(err) 140 | } 141 | 142 | tx, err := w.Transfer(accounts.TransferTransaction{ 143 | To: receiver.Address(), 144 | Amount: big.NewInt(7_000_000_000_000_000_000), 145 | Token: utils.EthAddress, 146 | }) 147 | if err != nil { 148 | log.Panic(err) 149 | } 150 | fmt.Println("Transaction: ", tx.Hash()) 151 | ``` 152 | 153 | ### Deposit funds 154 | 155 | Transfer funds from L1 to L2 network. 156 | 157 | ```ts 158 | tx, err := w.Deposit(accounts.DepositTransaction{ 159 | Token: utils.EthAddress, 160 | Amount: big.NewInt(2_000_000_000_000_000_000), 161 | To: w.Address(), 162 | }) 163 | if err != nil { 164 | log.Panic(err) 165 | } 166 | fmt.Println("L1 transaction: ", tx.Hash()) 167 | ``` 168 | 169 | ### Withdraw funds 170 | 171 | Transfer funds from L2 to L1 network. 172 | 173 | ```ts 174 | tx, err := w.Withdraw(accounts.WithdrawalTransaction{ 175 | To: w.Address(), 176 | Amount: big.NewInt(1_000_000_000_000_000_000), 177 | Token: utils.EthAddress, 178 | }) 179 | if err != nil { 180 | log.Panic(err) 181 | } 182 | fmt.Println("Withdraw transaction: ", tx.Hash()) 183 | ``` 184 | 185 | ## 🤖 Running tests 186 | 187 | In order to run test you need to run [local-setup](https://github.com/matter-labs/local-setup) on your machine. 188 | For running tests, use: 189 | 190 | ```shell 191 | make run-tests 192 | ``` 193 | 194 | 195 | ## 🤝 Contributing 196 | 197 | We welcome contributions from the community! If you're interested in contributing to the `zksync2-go` Golang SDK, 198 | please take a look at our [CONTRIBUTING.md](./.github/CONTRIBUTING.md) for guidelines and details on the process. 199 | 200 | Thank you for making `zksync2-go` Golang SDK better! 🙌 201 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | We truly appreciate efforts to discover and disclose security issues responsibly! 4 | 5 | ## Vulnerabilities 6 | 7 | If you'd like to report a security issue in the repositories of matter-labs organization, please proceed to our 8 | [Bug Bounty Program on Immunefi](https://era.zksync.io/docs/reference/troubleshooting/audit-bug-bounty.html#bug-bounty-program). 9 | 10 | ## Other Security Issues 11 | 12 | We take an impact-first approach instead of a rules-first approach. Therefore, if you believe you found the impactful 13 | issue but can't report it via the Bug Bounty, please email us at 14 | [security@matterlabs.dev](mailto:security@matterlabs.dev). 15 | 16 | ### PGP Key 17 | 18 | The following PGP key may be used to communicate sensitive information to developers: 19 | 20 | Fingerprint: `5FED B2D0 EA2C 4906 DD66 71D7 A2C5 0B40 CE3C F297` 21 | 22 | ```text 23 | -----BEGIN PGP PUBLIC KEY BLOCK----- 24 | 25 | mQINBGEBmQkBEAD6tlkBEZFMvR8kOgxXX857nC2+oTik6TopJz4uCskuqDaeldMy 26 | l+26BBzLkIeO1loS+bzVgnNFJRrGt9gv98MzNEHJVv6D7GsSLlUX/pz7Lxn0J4ry 27 | o5XIk3MQTCUBdaXGs6GBLl5Xe8o+zNj4MKd4zjgDLinITNlE/YZCDsXyvYS3YFTQ 28 | cwaUTNlawkKgw4BLaEqwB2JuyEhI9wx5X7ibjFL32sWMolYsNAlzFQzM09HCurTn 29 | q0DYau9kPJARcEk9/DK2iq0z3gMCQ8iRTDaOWd8IbSP3HxcEoM5j5ZVAlULmjmUE 30 | StDaMPLj0Kh01Tesh/j+vjchPXHT0n4zqi1+KOesAOk7SIwLadHfQMTpkU7G2fR1 31 | BrA5MtlzY+4Rm6o7qu3dpZ+Nc4iM3FUnaQRpvn4g5nTh8vjG94OCzX8DXWrCKyxx 32 | amCs9PLDYOpx84fXYv4frkWpKh2digDSUGKhoHaOSnqyyvu3BNWXBCQZJ20rqEIu 33 | sXOQMxWIoWCOOPRRvrHrKDA2hpoKjs3pGsProfpVRzb9702jhWpTfbDp9WjQlFtX 34 | 2ZIDxlwAxcugClgrp5JiUxvhg2A9lDNwCF7r1e68uNv5usBZQVKPJmnvS2nWgKy8 35 | x9oJsnwrEjxwiRHd34UvfMkwY9RENSJ+NoXqBdS7Lwz4m6vgbzq6K56WPQARAQAB 36 | tCRaa1N5bmMgU2VjdXJpdHkgPHNlY3VyaXR5QHprc3luYy5pbz6JAk4EEwEKADgW 37 | IQRf7bLQ6ixJBt1mcdeixQtAzjzylwUCYQGZCQIbAwULCQgHAgYVCgkICwIEFgID 38 | AQIeAQIXgAAKCRCixQtAzjzyl5y8EAC/T3oq88Dak2b+5TlWdU2Gpm6924eAqlMt 39 | y1KksDezzNQUlPiCUVllpin2PIjU/S+yzMWKXJA04LoVkEPfPOWjAaavLOjRumxu 40 | MR6P2dVUg1InqzYVsJuRhKSpeexzNA5qO2BPM7/I2Iea1IoJPjogGbfXCo0r5kne 41 | KU7a5GEa9eDHxpHTsbphQe2vpQ1239mUJrFpzAvILn6jV1tawMn5pNCXbsa8l6l2 42 | gtlyQPdOQECy77ZJxrgzaUBcs/RPzUGhwA/qNuvpF0whaCvZuUFMVuCTEu5LZka2 43 | I9Rixy+3jqBeONBgb+Fiz5phbiMX33M9JQwGONFaxdvpFTerLwPK2N1T8zcufa01 44 | ypzkWGheScFZemBxUwXwK4x579wjsnfrY11w0p1jtDgPTnLlXUA2mom4+7MyXPg0 45 | F75qh6vU1pdXaCVkruFgPVtIw+ccw2AxD50iZQ943ZERom9k165dR9+QxOVMXQ4P 46 | VUxsFZWvK70/s8TLjsGljvSdSOa85iEUqSqh0AlCwIAxLMiDwh5s/ZgiHoIM6Xih 47 | oCpuZyK9p0dn+DF/XkgAZ/S91PesMye3cGm6M5r0tS26aoc2Pk6X37Hha1pRALwo 48 | MOHyaGjc/jjcXXxv6o55ALrOrzS0LQmLZ+EHuteCT15kmeY3kqYJ3og62KgiDvew 49 | dKHENvg7d7kCDQRhAZleARAA6uD6WfdqGeKV5i170+kLsxR3QGav0qGNAbxpSJyn 50 | iHQ8u7mQk3S+ziwN2AAopfBk1je+vCWtEGC3+DWRRfJSjLbtaBG8e6kLP3/cGA75 51 | qURz6glTG4nl5fcEAa6B1st0OxjVWiSLX3g/yjz8lznQb9awuRjdeHMnyx5DsJUN 52 | d+Iu5KxGupQvKGOMKivSvC8VWk9taaQRpRF+++6stLCDk3ZtlxiopMs3X2jAp6xG 53 | sOBbix1cv9BTsfaiL7XDL/gviqBPXYY5L42x6+jnPo5lROfnlLYkWrv6KZr7HD4k 54 | tRXeaSwxLD2EkUyb16Jpp0be/ofvBtITGUDDLCGBiaXtx/v8d52MARjsyLJSYloj 55 | 1yiW01LfAiWHUC4z5jl2T7E7sicrlLH1M8Z6WbuqjdeaYwtfyPA2YCKr/3fn6pIo 56 | D+pYaBSESmhA92P+XVaf5y2BZ6Qf8LveDpWwsVGdBGh9T0raA1ooe1GESLjmIjUa 57 | z5AeQ/uXL5Md9I6bpMUUJYQiH19RPcFlJriI3phXyyf6Wlkk8oVEeCWyzcmw+x1V 58 | deRTvE2x4WIwKGLXRNjin2j1AP7vU2HaNwlPrLijqdyi68+0irRQONoH7Qonr4ca 59 | xWgL+pAaa3dWxf0xqK7uZFp4aTVWlr2uXtV/eaUtLmGMCU0jnjb109wg5L0F7WRT 60 | PfEAEQEAAYkCNgQYAQoAIBYhBF/tstDqLEkG3WZx16LFC0DOPPKXBQJhAZleAhsM 61 | AAoJEKLFC0DOPPKXAAEP/jK7ch9GkoaYlsuqY/aHtxEwVddUDOxjyn3FMDoln85L 62 | /n8AmLQb2bcpKSqpaJwMbmfEyr5MDm8xnsBTfx3u6kgaLOWfKxjLQ6PM7kgIMdi4 63 | bfaRRuSEI1/R6c/hNpiGnzAeeexldH1we+eH1IVmh4crdat49S2xh7Qlv9ahvgsP 64 | LfKl3rJ+aaX/Ok0AHzhvSfhFpPr1gAaGeaRt+rhlZsx2QyG4Ez8p2nDAcAzPiB3T 65 | 73ENoBIX6mTPfPm1UgrRyFKBqtUzAodz66j3r6ebBlWzIRg8iZenVMAxzjINAsxN 66 | w1Bzfgsi5ZespfsSlmEaa7jJkqqDuEcLa2YuiFAue7Euqwz1aGeq1GfTicQioSCb 67 | Ur/LGyz2Mj3ykbaP8p5mFVcUN51yQy6OcpvR/W1DfRT9SHFT/bCf9ixsjB2HlZGo 68 | uxPJowwqmMgHd755ZzPDUM9YDgLI1yXdcYshObv3Wq537JAxnZJCGRK4Y8SwrMSh 69 | 8WRxlaM0AGWXiJFIDD4bQPIdnF3X8w0cGWE5Otkb8mMHOT+rFTVlDODwm1zF6oIG 70 | PTwfVrpiZBwiUtfJol1exr/MzSPyGoJnYs3cRf2E3O+D1LbcR8w0LbjGuUy38Piz 71 | ZO/vCeyJ3JZC5kE8nD+XBA4idwzh0BKEfH9t+WchQ3Up9rxyzLyQamoqt5Xby4pY 72 | =xkM3 73 | -----END PGP PUBLIC KEY BLOCK----- 74 | ``` 75 | -------------------------------------------------------------------------------- /accounts/deployer.go: -------------------------------------------------------------------------------- 1 | package accounts 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | ) 6 | 7 | // Deployer is associated with an account and provides deployment of smart contracts 8 | // and smart accounts on L2 network for the associated account. 9 | type Deployer struct { 10 | runner *WalletL2 11 | } 12 | 13 | // NewDeployer creates an instance of BaseDeployer. 14 | func NewDeployer(adapter *WalletL2) *Deployer { 15 | return &Deployer{adapter} 16 | } 17 | 18 | // Deploy deploys smart contract using CREATE2 opcode. 19 | func (a *Deployer) Deploy(auth *TransactOpts, tx Create2Transaction) (common.Hash, error) { 20 | opts := ensureTransactOpts(auth) 21 | preparedTx, err := tx.ToTransaction(DeployContract, opts) 22 | if err != nil { 23 | return [32]byte{}, err 24 | } 25 | return a.runner.SendTransaction(opts.Context, preparedTx) 26 | } 27 | 28 | // DeployWithCreate deploys smart contract using CREATE opcode. 29 | func (a *Deployer) DeployWithCreate(auth *TransactOpts, tx CreateTransaction) (common.Hash, error) { 30 | opts := ensureTransactOpts(auth) 31 | preparedTx, err := tx.ToTransaction(DeployContract, opts) 32 | if err != nil { 33 | return [32]byte{}, err 34 | } 35 | return a.runner.SendTransaction(opts.Context, preparedTx) 36 | } 37 | 38 | // DeployAccount deploys smart account using CREATE2 opcode. 39 | func (a *Deployer) DeployAccount(auth *TransactOpts, tx Create2Transaction) (common.Hash, error) { 40 | opts := ensureTransactOpts(auth) 41 | preparedTx, err := tx.ToTransaction(DeployAccount, opts) 42 | if err != nil { 43 | return [32]byte{}, err 44 | } 45 | return a.runner.SendTransaction(opts.Context, preparedTx) 46 | } 47 | 48 | // DeployAccountWithCreate deploys smart account using CREATE opcode. 49 | func (a *Deployer) DeployAccountWithCreate(auth *TransactOpts, tx CreateTransaction) (common.Hash, error) { 50 | opts := ensureTransactOpts(auth) 51 | preparedTx, err := tx.ToTransaction(DeployAccount, opts) 52 | if err != nil { 53 | return [32]byte{}, err 54 | } 55 | return a.runner.SendTransaction(opts.Context, preparedTx) 56 | } 57 | -------------------------------------------------------------------------------- /accounts/signer.go: -------------------------------------------------------------------------------- 1 | package accounts 2 | 3 | import ( 4 | "context" 5 | "crypto/ecdsa" 6 | "fmt" 7 | "github.com/ethereum/go-ethereum/accounts" 8 | "github.com/ethereum/go-ethereum/common" 9 | "github.com/ethereum/go-ethereum/crypto" 10 | "github.com/ethereum/go-ethereum/signer/core/apitypes" 11 | "github.com/pkg/errors" 12 | "github.com/stephenlacy/go-ethereum-hdwallet" 13 | "github.com/zksync-sdk/zksync2-go/types" 14 | "math/big" 15 | ) 16 | 17 | // Signer provides support for signing various types of payloads using some kind of secret. 18 | type Signer interface { 19 | // SignMessage sings an arbitrary message. 20 | SignMessage(ctx context.Context, message []byte) ([]byte, error) 21 | // SignTransaction signs the given transaction. 22 | SignTransaction(ctx context.Context, tx *types.Transaction) ([]byte, error) 23 | // SignTypedData signs the given EIP712 typed data. 24 | SignTypedData(ctx context.Context, typedData *apitypes.TypedData) ([]byte, error) 25 | } 26 | 27 | // ECDSASigner represents basis implementation of Signer interface. 28 | type ECDSASigner struct { 29 | pk *ecdsa.PrivateKey 30 | address common.Address 31 | chainId *big.Int 32 | } 33 | 34 | // NewECDSASignerFromMnemonic creates a new instance of ECDSASigner based on the provided mnemonic phrase. 35 | func NewECDSASignerFromMnemonic(mnemonic string, chainId *big.Int) (*ECDSASigner, error) { 36 | return NewECDSASignerFromMnemonicAndAccountId(mnemonic, 0, chainId) 37 | } 38 | 39 | // NewECDSASignerFromMnemonicAndAccountId creates a new instance of ECDSASigner based on the provided mnemonic phrase and 40 | // account ID. 41 | func NewECDSASignerFromMnemonicAndAccountId(mnemonic string, accountId uint32, chainId *big.Int) (*ECDSASigner, error) { 42 | wallet, err := hdwallet.NewFromMnemonic(mnemonic) 43 | if err != nil { 44 | return nil, errors.Wrap(err, "failed to create HD wallet from mnemonic") 45 | } 46 | path, err := accounts.ParseDerivationPath(fmt.Sprintf("m/44'/60'/0'/0/%d", accountId)) 47 | if err != nil { 48 | return nil, errors.Wrap(err, "failed to parse derivation path") 49 | } 50 | account, err := wallet.Derive(path, true) 51 | if err != nil { 52 | return nil, errors.Wrap(err, "failed to derive account from HD wallet") 53 | } 54 | pk, err := wallet.PrivateKey(account) 55 | if err != nil { 56 | return nil, errors.Wrap(err, "failed to get account's private key from HD wallet") 57 | } 58 | pub := pk.Public().(*ecdsa.PublicKey) 59 | return &ECDSASigner{ 60 | pk: pk, 61 | address: crypto.PubkeyToAddress(*pub), 62 | chainId: chainId, 63 | }, nil 64 | } 65 | 66 | // NewECDSASignerFromRawPrivateKey creates a new instance of ECDSASigner based on the provided raw private key. 67 | func NewECDSASignerFromRawPrivateKey(rawPk []byte, chainId *big.Int) (*ECDSASigner, error) { 68 | pk, err := crypto.ToECDSA(rawPk) 69 | if err != nil { 70 | return nil, errors.Wrap(err, "invalid raw private key") 71 | } 72 | pub := pk.Public().(*ecdsa.PublicKey) 73 | return &ECDSASigner{ 74 | pk: pk, 75 | address: crypto.PubkeyToAddress(*pub), 76 | chainId: chainId, 77 | }, nil 78 | } 79 | 80 | // NewRandomBaseSigner creates an instance of Signer with a randomly generated private key. 81 | func NewRandomBaseSigner(chainId *big.Int) (*ECDSASigner, error) { 82 | privateKey, err := crypto.GenerateKey() 83 | if err != nil { 84 | return nil, fmt.Errorf("failed to generate radnom private key: %w", err) 85 | } 86 | publicKey, ok := privateKey.Public().(*ecdsa.PublicKey) 87 | if !ok { 88 | return nil, fmt.Errorf("failed to convert public key to ECDSA") 89 | } 90 | return &ECDSASigner{ 91 | pk: privateKey, 92 | address: crypto.PubkeyToAddress(*publicKey), 93 | chainId: chainId, 94 | }, nil 95 | } 96 | 97 | // Address returns the address of the associated account. 98 | func (s *ECDSASigner) Address() common.Address { 99 | return s.address 100 | } 101 | 102 | // ChainID returns the chain ID of the associated account. 103 | func (s *ECDSASigner) ChainID() *big.Int { 104 | return s.chainId 105 | } 106 | 107 | // PrivateKey returns the private key of the associated account. 108 | func (s *ECDSASigner) PrivateKey() *ecdsa.PrivateKey { 109 | return s.pk 110 | } 111 | 112 | // SignMessage signs the message using the private key. 113 | func (s *ECDSASigner) SignMessage(_ context.Context, msg []byte) ([]byte, error) { 114 | sig, err := crypto.Sign(msg, s.pk) 115 | if err != nil { 116 | return nil, errors.Wrap(err, "failed to sign hash") 117 | } 118 | return sig, nil 119 | } 120 | 121 | // SignTransaction signs the L2 transaction. 122 | func (s *ECDSASigner) SignTransaction(ctx context.Context, tx *types.Transaction) ([]byte, error) { 123 | typedData, err := tx.TypedData() 124 | if err != nil { 125 | return nil, err 126 | } 127 | return s.SignTypedData(ctx, typedData) 128 | } 129 | 130 | // SignTypedData signs the typed data. 131 | func (s *ECDSASigner) SignTypedData(_ context.Context, typedData *apitypes.TypedData) ([]byte, error) { 132 | hash, err := s.hashTypedData(typedData) 133 | if err != nil { 134 | return nil, fmt.Errorf("failed to get hash of typed data: %w", err) 135 | } 136 | sig, err := crypto.Sign(hash, s.pk) 137 | if err != nil { 138 | return nil, fmt.Errorf("failed to sign hash of typed data: %w", err) 139 | } 140 | // crypto.Sign uses the traditional implementation where v is either 0 or 1, 141 | // while Ethereum uses newer implementation where v is either 27 or 28. 142 | if sig[64] < 27 { 143 | sig[64] += 27 144 | } 145 | return sig, nil 146 | } 147 | 148 | func (s *ECDSASigner) hashTypedData(data *apitypes.TypedData) ([]byte, error) { 149 | domain, err := data.HashStruct("EIP712Domain", data.Domain.Map()) 150 | if err != nil { 151 | return nil, fmt.Errorf("failed to get hash of typed data domain: %w", err) 152 | } 153 | dataHash, err := data.HashStruct(data.PrimaryType, data.Message) 154 | if err != nil { 155 | return nil, fmt.Errorf("failed to get hash of typed message: %w", err) 156 | } 157 | prefixedData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domain), string(dataHash))) 158 | prefixedDataHash := crypto.Keccak256(prefixedData) 159 | return prefixedDataHash, nil 160 | } 161 | -------------------------------------------------------------------------------- /accounts/smart_account_utils.go: -------------------------------------------------------------------------------- 1 | package accounts 2 | 3 | import ( 4 | "context" 5 | "crypto/ecdsa" 6 | "errors" 7 | "fmt" 8 | "github.com/ethereum/go-ethereum/common" 9 | "github.com/ethereum/go-ethereum/common/hexutil" 10 | "github.com/ethereum/go-ethereum/crypto" 11 | "github.com/zksync-sdk/zksync2-go/clients" 12 | "github.com/zksync-sdk/zksync2-go/types" 13 | "github.com/zksync-sdk/zksync2-go/utils" 14 | "math/big" 15 | ) 16 | 17 | // SignPayloadWithECDSA signs the payload using an ECDSA private key. 18 | var SignPayloadWithECDSA PayloadSigner = func(ctx context.Context, payload []byte, secret interface{}, client *clients.Client) ([]byte, error) { 19 | privateKeyHex, ok := (secret).(string) 20 | if !ok { 21 | return nil, errors.New("secret should be hex of ECDSA private key") 22 | } 23 | privateKey, err := crypto.HexToECDSA(privateKeyHex) 24 | if err != nil { 25 | return nil, err 26 | } 27 | signature, err := crypto.Sign(payload, privateKey) 28 | if err != nil { 29 | return nil, err 30 | } 31 | // crypto.Sign uses the traditional implementation where v is either 0 or 1, 32 | // while Ethereum uses newer implementation where v is either 27 or 28. 33 | if signature[64] < 27 { 34 | signature[64] += 27 35 | } 36 | return signature, nil 37 | } 38 | 39 | // SignPayloadWithMultipleECDSA signs the payload using multiple ECDSA private keys. 40 | // The signature is generated by concatenating signatures created by signing with each key individually. 41 | // The length of the resulting signature is len(secret) * 65. 42 | var SignPayloadWithMultipleECDSA PayloadSigner = func(ctx context.Context, payload []byte, secret interface{}, client *clients.Client) ([]byte, error) { 43 | privateKeys, ok := (secret).([]string) 44 | if !ok { 45 | return nil, errors.New("secret should be a slice of ECDSA private keys") 46 | } 47 | if len(privateKeys) < 2 { 48 | return nil, errors.New("secret should be a slice of ECDSA private keys") 49 | } 50 | multiSig := make([]byte, len(privateKeys)*65) 51 | for i, privateKeyHex := range privateKeys { 52 | privateKey, err := crypto.HexToECDSA(privateKeyHex) 53 | if err != nil { 54 | return nil, err 55 | } 56 | signature, err := crypto.Sign(payload, privateKey) 57 | if err != nil { 58 | return nil, err 59 | } 60 | // crypto.Sign uses the traditional implementation where v is either 0 or 1, 61 | // while Ethereum uses newer implementation where v is either 27 or 28. 62 | if signature[64] < 27 { 63 | signature[64] += 27 64 | } 65 | copy(multiSig[i*65:(i+1)*65], signature) 66 | } 67 | return multiSig, nil 68 | } 69 | 70 | // PopulateTransactionECDSA Populates missing properties meant for signing using an ECDSA private key: 71 | // 72 | // - Populates tx.From using the address derived from the ECDSA private key. 73 | // - Populates tx.Nonce via client.NonceAt(). 74 | // - Populates tx.Gas via client.EstimateFee().GasLimit. If tx.From is not EOA, the estimation is done with address 75 | // derived from the ECDSA private key. 76 | // - Populates tx.GasFeeCap via client.EstimateFee().MaxFeePerGas. 77 | // - Populates tx.GasTipCap with 0 if is not set. 78 | // - Populates tx.ChainID via client.ChainID(). 79 | // - Populates tx.Data with "0x". 80 | // - Populates tx.Meta.GasPerPubdata with utils.DefaultGasPerPubdataLimit. 81 | // 82 | // Expects the secret to be ECDSA private in hex format. 83 | var PopulateTransactionECDSA TransactionBuilder = func(ctx context.Context, tx *types.Transaction, secret interface{}, client *clients.Client) error { 84 | var err error 85 | if client == nil { 86 | return errors.New("client is required but is not provided") 87 | } 88 | 89 | if tx.ChainID == nil { 90 | if tx.ChainID, err = client.ChainID(ensureContext(ctx)); err != nil { 91 | return err 92 | } 93 | } 94 | if tx.Nonce == nil { 95 | nonce, err := client.NonceAt(ensureContext(ctx), *tx.From, nil) 96 | if err != nil { 97 | return fmt.Errorf("failed to get nonce: %w", err) 98 | } 99 | tx.Nonce = new(big.Int).SetUint64(nonce) 100 | } 101 | if tx.GasTipCap == nil { 102 | tx.GasTipCap = common.Big0 103 | } 104 | if tx.GasPerPubdata == nil { 105 | tx.GasPerPubdata = utils.DefaultGasPerPubdataLimit 106 | } 107 | if (tx.Gas == nil || tx.Gas.Uint64() == 0) || (tx.GasFeeCap == nil) { 108 | from := *tx.From 109 | // Gas estimation does not work when initiator is contract account (works only with EOA). 110 | // In order to estimation gas, the transaction's from value is replaced with signer's address. 111 | if bytecode, err := client.CodeAt(ensureContext(ctx), *tx.From, nil); len(bytecode) > 0 { 112 | privateKeyHex, ok := (secret).(string) 113 | if !ok { 114 | return errors.New("secret should be hex of ECDSA private key") 115 | } 116 | privateKey, err := crypto.HexToECDSA(privateKeyHex) 117 | if err != nil { 118 | return err 119 | } 120 | publicKey := privateKey.Public().(*ecdsa.PublicKey) 121 | from = crypto.PubkeyToAddress(*publicKey) 122 | } else if err != nil { 123 | return err 124 | } 125 | 126 | fee, err := client.EstimateFee(ensureContext(ctx), types.CallMsg{ 127 | From: from, 128 | To: tx.To, 129 | GasFeeCap: tx.GasFeeCap, 130 | GasTipCap: tx.GasTipCap, 131 | Value: tx.Value, 132 | Data: tx.Data, 133 | GasPerPubdata: tx.GasPerPubdata, 134 | CustomSignature: tx.CustomSignature, 135 | FactoryDeps: tx.FactoryDeps, 136 | PaymasterParams: tx.PaymasterParams, 137 | }) 138 | if err != nil { 139 | return fmt.Errorf("failed to EstimateFee: %w", err) 140 | } 141 | 142 | if tx.Gas == nil || tx.Gas.Uint64() == 0 { 143 | tx.Gas = fee.GasLimit.ToInt() 144 | } 145 | if tx.GasFeeCap == nil || tx.GasFeeCap.Uint64() == 0 { 146 | tx.GasFeeCap = fee.MaxFeePerGas.ToInt() 147 | } 148 | } 149 | if tx.Data == nil { 150 | tx.Data = hexutil.Bytes{} 151 | } 152 | return nil 153 | } 154 | 155 | // PopulateTransactionMultipleECDSA populates missing properties meant for signing using multiple ECDSA private keys. 156 | // It uses PopulateTransactionECDSA, where the address of the first ECDSA key is set as the secret argument. 157 | // Expects the secret to be a slice of ECDSA private in hex format. 158 | var PopulateTransactionMultipleECDSA TransactionBuilder = func(ctx context.Context, tx *types.Transaction, secret interface{}, client *clients.Client) error { 159 | privateKeys, ok := (secret).([]string) 160 | if !ok { 161 | return errors.New("secret should be a slice of ECDSA private keys") 162 | } 163 | if len(privateKeys) < 2 { 164 | return errors.New("secret should be a slice of ECDSA private keys") 165 | } 166 | // estimates gas accepts only one address, so the first signer is chosen. 167 | return PopulateTransactionECDSA(ctx, tx, privateKeys[0], client) 168 | } 169 | -------------------------------------------------------------------------------- /accounts/smart_account_utils_test.go: -------------------------------------------------------------------------------- 1 | package accounts 2 | 3 | import ( 4 | "context" 5 | "github.com/ethereum/go-ethereum/accounts" 6 | "github.com/ethereum/go-ethereum/common" 7 | "github.com/ethereum/go-ethereum/common/hexutil" 8 | "github.com/ethereum/go-ethereum/common/math" 9 | "github.com/ethereum/go-ethereum/signer/core/apitypes" 10 | "github.com/stretchr/testify/assert" 11 | "github.com/zksync-sdk/zksync2-go/types" 12 | "github.com/zksync-sdk/zksync2-go/utils" 13 | "math/big" 14 | "testing" 15 | ) 16 | 17 | const PrivateKey1 = "7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110" 18 | const PrivateKey2 = "ac1e735be8536c6534bb4f17f06f6afc73b2b5ba84ac2cfb12f7461b20c0bbe3" 19 | 20 | var Address1 = common.HexToAddress("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049") 21 | var Address2 = common.HexToAddress("0xa61464658AfeAf65CccaaFD3a512b69A83B77618") 22 | 23 | func TestSignPayloadWithECDSASignTransaction(t *testing.T) { 24 | signature := "0x475e207d1e5da85721e37118cea54b2a3ac8e5dcd79cd7935c59bdd5cbc71e9824d4ab9dbaa5f8542e51588f4187c406fc4311c2ce9a9aa2a269f14298e5777d1b" 25 | 26 | tx := types.Transaction{ 27 | Nonce: big.NewInt(0), 28 | GasTipCap: big.NewInt(0), 29 | GasFeeCap: big.NewInt(1_000_000_000), 30 | Gas: big.NewInt(1_000_000_000), 31 | To: &Address2, 32 | Value: big.NewInt(7_000_000_000), 33 | Data: hexutil.Bytes{}, 34 | ChainID: big.NewInt(270), 35 | From: &Address1, 36 | GasPerPubdata: utils.DefaultGasPerPubdataLimit, 37 | } 38 | 39 | hash, err := tx.Hash() 40 | assert.NoError(t, err, "TypedDataAndHash should not return an error") 41 | 42 | sig, err := SignPayloadWithECDSA(context.Background(), hash, PrivateKey1, nil) 43 | assert.NoError(t, err, "SignPayloadWithECDSA should not return an error") 44 | assert.Equal(t, signature, hexutil.Encode(sig), "Signatures must be the same") 45 | } 46 | 47 | func TestSignPayloadWithECDSASignMessage(t *testing.T) { 48 | signature := "0x7c15eb760c394b0ca49496e71d841378d8bfd4f9fb67e930eb5531485329ab7c67068d1f8ef4b480ec327214ee6ed203687e3fbe74b92367b259281e340d16fd1c" 49 | 50 | sig, err := SignPayloadWithECDSA(context.Background(), accounts.TextHash([]byte("Hello World!")), PrivateKey1, nil) 51 | assert.NoError(t, err, "SignPayloadWithECDSA should not return an error") 52 | assert.Equal(t, signature, hexutil.Encode(sig), "Signatures must be the same") 53 | } 54 | 55 | func TestSignPayloadWithECDSASignTypedData(t *testing.T) { 56 | signature := "0xbcaf0673c0c2b0e120165d207d42281d0c6e85f0a7f6b8044b0578a91cf5bda66b4aeb62aca4ae17012a38d71c9943e27285792fa7d788d848f849e3ea2e614b1b" 57 | 58 | hash, _, err := apitypes.TypedDataAndHash(apitypes.TypedData{ 59 | Domain: apitypes.TypedDataDomain{ 60 | Name: "Example", 61 | Version: "1", 62 | ChainId: math.NewHexOrDecimal256(270), 63 | }, 64 | Types: apitypes.Types{ 65 | "Person": []apitypes.Type{ 66 | {Name: "name", Type: "string"}, 67 | {Name: "age", Type: "uint8"}, 68 | }, 69 | "EIP712Domain": []apitypes.Type{ 70 | {Name: "name", Type: "string"}, 71 | {Name: "version", Type: "string"}, 72 | {Name: "chainId", Type: "uint256"}, 73 | }, 74 | }, 75 | PrimaryType: "Person", 76 | Message: apitypes.TypedDataMessage{ 77 | "name": "John", 78 | "age": hexutil.EncodeUint64(30), 79 | }, 80 | }) 81 | assert.NoError(t, err, "TypedDataAndHash should not return an error") 82 | 83 | sig, err := SignPayloadWithECDSA(context.Background(), hash, PrivateKey1, nil) 84 | assert.NoError(t, err, "SignPayloadWithECDSA should not return an error") 85 | assert.Equal(t, signature, hexutil.Encode(sig), "Signatures must be the same") 86 | } 87 | 88 | func TestSignPayloadWithMultipleECDSASignTransaction(t *testing.T) { 89 | signature := "0x475e207d1e5da85721e37118cea54b2a3ac8e5dcd79cd7935c59bdd5cbc71e9824d4ab9dbaa5f8542e51588f4187c406fc4311c2ce9a9aa2a269f14298e5777d1b4ff4f280885d2dd0b2234d82cacec8ba94bd6659b64b1d516668b4ca79faf58a58c469fd95590e2541ca01866e312e56c7e38a74b4a8b72fdb07a69a3b34c19f1c" 90 | 91 | tx := types.Transaction{ 92 | Nonce: big.NewInt(0), 93 | GasTipCap: big.NewInt(0), 94 | GasFeeCap: big.NewInt(1_000_000_000), 95 | Gas: big.NewInt(1_000_000_000), 96 | To: &Address2, 97 | Value: big.NewInt(7_000_000_000), 98 | Data: hexutil.Bytes{}, 99 | ChainID: big.NewInt(270), 100 | From: &Address1, 101 | GasPerPubdata: utils.DefaultGasPerPubdataLimit, 102 | } 103 | 104 | hash, err := tx.Hash() 105 | assert.NoError(t, err, "TypedDataAndHash should not return an error") 106 | 107 | sig, err := SignPayloadWithMultipleECDSA(context.Background(), hash, []string{PrivateKey1, PrivateKey2}, nil) 108 | assert.NoError(t, err, "SignPayloadWithMultipleECDSA should not return an error") 109 | assert.Equal(t, signature, hexutil.Encode(sig), "Signatures must be the same") 110 | } 111 | 112 | func TestSignPayloadWithMultipleECDSASignMessage(t *testing.T) { 113 | signature := "0x7c15eb760c394b0ca49496e71d841378d8bfd4f9fb67e930eb5531485329ab7c67068d1f8ef4b480ec327214ee6ed203687e3fbe74b92367b259281e340d16fd1c2f2f4a312d23de1bcadff9c547fe670a9e21beae16a7c9688fc10b97ba2e286574de339c2b70bd3f02bd021c270a1405942cc3e1268bf3f7a7a419a1c7aea2db1c" 114 | 115 | sig, err := SignPayloadWithMultipleECDSA( 116 | context.Background(), 117 | accounts.TextHash([]byte("Hello World!")), 118 | []string{PrivateKey1, PrivateKey2}, 119 | nil) 120 | assert.NoError(t, err, "SignPayloadWithMultipleECDSA should not return an error") 121 | assert.Equal(t, signature, hexutil.Encode(sig), "Signatures must be the same") 122 | } 123 | 124 | func TestSignPayloadWithMultipleECDSASignTypedData(t *testing.T) { 125 | signature := "0xbcaf0673c0c2b0e120165d207d42281d0c6e85f0a7f6b8044b0578a91cf5bda66b4aeb62aca4ae17012a38d71c9943e27285792fa7d788d848f849e3ea2e614b1b8231ec20acfc86483b908e8f1e88c917b244465c7e73202b6f2643377a6e54f5640f0d3e2f5902695faec96668b2e998148c49a4de613bb7bc4325a3c855cf6a1b" 126 | 127 | hash, _, err := apitypes.TypedDataAndHash(apitypes.TypedData{ 128 | Domain: apitypes.TypedDataDomain{ 129 | Name: "Example", 130 | Version: "1", 131 | ChainId: math.NewHexOrDecimal256(270), 132 | }, 133 | Types: apitypes.Types{ 134 | "Person": []apitypes.Type{ 135 | {Name: "name", Type: "string"}, 136 | {Name: "age", Type: "uint8"}, 137 | }, 138 | "EIP712Domain": []apitypes.Type{ 139 | {Name: "name", Type: "string"}, 140 | {Name: "version", Type: "string"}, 141 | {Name: "chainId", Type: "uint256"}, 142 | }, 143 | }, 144 | PrimaryType: "Person", 145 | Message: apitypes.TypedDataMessage{ 146 | "name": "John", 147 | "age": hexutil.EncodeUint64(30), 148 | }, 149 | }) 150 | assert.NoError(t, err, "TypedDataAndHash should not return an error") 151 | 152 | sig, err := SignPayloadWithMultipleECDSA(context.Background(), hash, []string{PrivateKey1, PrivateKey2}, nil) 153 | assert.NoError(t, err, "SignPayloadWithMultipleECDSA should not return an error") 154 | assert.Equal(t, signature, hexutil.Encode(sig), "Signatures must be the same") 155 | } 156 | -------------------------------------------------------------------------------- /accounts/util.go: -------------------------------------------------------------------------------- 1 | package accounts 2 | 3 | import ( 4 | "context" 5 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 6 | "github.com/ethereum/go-ethereum/common" 7 | "github.com/ethereum/go-ethereum/core/types" 8 | "math/big" 9 | ) 10 | 11 | // ensureContext is a helper method to ensure a context is not nil, even if the 12 | // user specified it as such. 13 | func ensureContext(ctx context.Context) context.Context { 14 | if ctx == nil { 15 | return context.Background() 16 | } 17 | return ctx 18 | } 19 | 20 | // ensureCallOpts is a helper method to ensure a CallOpts is not nil, even if the 21 | // user specified it as such. 22 | func ensureCallOpts(opts *CallOpts) *CallOpts { 23 | if opts == nil { 24 | return &CallOpts{Context: context.Background()} 25 | } 26 | return opts 27 | } 28 | 29 | func ensureTransactOptsL1(auth *TransactOptsL1) *TransactOptsL1 { 30 | if auth == nil { 31 | return &TransactOptsL1{ 32 | Context: context.Background(), 33 | } 34 | } 35 | if auth.Context == nil { 36 | auth.Context = context.Background() 37 | } 38 | return auth 39 | } 40 | 41 | func ensureTransactOpts(auth *TransactOpts) *TransactOpts { 42 | if auth == nil { 43 | return &TransactOpts{ 44 | Context: context.Background(), 45 | } 46 | } 47 | if auth.Context == nil { 48 | auth.Context = context.Background() 49 | } 50 | return auth 51 | } 52 | 53 | func newTransactorWithSigner(signer *ECDSASigner, chainID *big.Int) (*bind.TransactOpts, error) { 54 | if chainID == nil { 55 | return nil, bind.ErrNoChainID 56 | } 57 | keyAddr := signer.Address() 58 | latestSigner := types.LatestSignerForChainID(chainID) 59 | return &bind.TransactOpts{ 60 | From: keyAddr, 61 | Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { 62 | if address != keyAddr { 63 | return nil, bind.ErrNotAuthorized 64 | } 65 | signature, err := signer.SignMessage(context.Background(), latestSigner.Hash(tx).Bytes()) 66 | if err != nil { 67 | return nil, err 68 | } 69 | return tx.WithSignature(latestSigner, signature) 70 | }, 71 | }, nil 72 | } 73 | 74 | func insertGasPrice(opts *TransactOpts) { 75 | if opts.GasPrice != nil { 76 | opts.GasFeeCap = opts.GasPrice 77 | opts.GasTipCap = nil 78 | opts.GasPrice = nil 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /accounts/wallet.go: -------------------------------------------------------------------------------- 1 | package accounts 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "github.com/ethereum/go-ethereum/ethclient" 7 | "github.com/zksync-sdk/zksync2-go/clients" 8 | "math/big" 9 | ) 10 | 11 | // Wallet wraps all operations that interact with an associated account. 12 | // An account typically contains a private key, allowing it to sign various types of payloads. 13 | type Wallet struct { 14 | WalletL1 15 | WalletL2 16 | Deployer 17 | 18 | clientL1 *ethclient.Client 19 | clientL2 *clients.Client 20 | } 21 | 22 | // NewWallet creates an instance of Wallet associated with the account provided by the rawPrivateKey. 23 | // The clientL1 parameters is optional; if not provided, only method form WalletL2 and Deployer can be used, 24 | // as the rest of the functionalities require communication with the L1 network. 25 | // A Wallet can be configured to communicate with L1 networks by using and Wallet.ConnectL1 method. 26 | func NewWallet(rawPrivateKey []byte, clientL2 *clients.Client, clientL1 *ethclient.Client) (*Wallet, error) { 27 | chainID, err := clientL2.ChainID(context.Background()) 28 | if err != nil { 29 | return nil, err 30 | } 31 | signer, err := NewECDSASignerFromRawPrivateKey(rawPrivateKey, chainID) 32 | if err != nil { 33 | return nil, err 34 | } 35 | return NewWalletFromSigner(signer, clientL2, clientL1) 36 | } 37 | 38 | // NewWalletFromSigner creates an instance of Wallet associated with the account provided by the signer. 39 | // The clientL2 and clientL1 parameters are optional; if not provided, only WalletL2.SignTransaction, 40 | // WalletL2.Address, WalletL2.Signer methods can be used, as the rest of the functionalities 41 | // require communication with the network. 42 | // A runner that contains only a signer can be configured to communicate with L2 and L1 networks by 43 | // using Wallet.Connect and Wallet.ConnectL1, respectively. 44 | func NewWalletFromSigner(signer *ECDSASigner, clientL2 *clients.Client, clientL1 *ethclient.Client) (*Wallet, error) { 45 | if signer == nil { 46 | return nil, errors.New("signer must be provided") 47 | } 48 | var ( 49 | walletL1 *WalletL1 50 | walletL2 *WalletL2 51 | err error 52 | ) 53 | 54 | cache := NewCache(clientL2, clientL1) 55 | 56 | walletL1 = new(WalletL1) 57 | if clientL1 != nil { 58 | if walletL1, err = NewWalletL1FromSignerAndCache(signer, clientL1, clientL2, cache); err != nil { 59 | return nil, err 60 | } 61 | } 62 | if walletL2, err = NewWalletL2FromSignerAndCache(signer, clientL2, cache); err != nil { 63 | return nil, err 64 | } 65 | return &Wallet{ 66 | WalletL1: *walletL1, 67 | WalletL2: *walletL2, 68 | Deployer: *NewDeployer(walletL2), 69 | clientL1: clientL1, 70 | clientL2: clientL2, 71 | }, nil 72 | } 73 | 74 | // NewWalletFromMnemonic creates a new instance of Wallet based on the provided mnemonic phrase. 75 | // The clientL2 and clientL1 parameters are optional, and can be configured with Wallet.Connect 76 | // and Wallet.ConnectL1, respectively. 77 | func NewWalletFromMnemonic(mnemonic string, chainId *big.Int, clientL2 *clients.Client, clientL1 *ethclient.Client) (*Wallet, error) { 78 | signer, err := NewECDSASignerFromMnemonic(mnemonic, chainId) 79 | if err != nil { 80 | return nil, err 81 | } 82 | return NewWalletFromSigner(signer, clientL2, clientL1) 83 | } 84 | 85 | // NewWalletFromRawPrivateKey creates a new instance of Wallet based on the provided private key 86 | // of the account and chain ID. 87 | // The clientL2 and clientL1 parameters are optional, and can be configured with Wallet.Connect 88 | // and Wallet.ConnectL1, respectively. 89 | func NewWalletFromRawPrivateKey(rawPk []byte, chainId *big.Int, clientL2 *clients.Client, clientL1 *ethclient.Client) (*Wallet, error) { 90 | signer, err := NewECDSASignerFromRawPrivateKey(rawPk, chainId) 91 | if err != nil { 92 | return nil, err 93 | } 94 | return NewWalletFromSigner(signer, clientL2, clientL1) 95 | } 96 | 97 | // NewRandomWallet creates an instance of Wallet with a randomly generated account. 98 | // The clientL2 and clientL1 parameters are optional, and can be configured with Wallet.Connect 99 | // and Wallet.ConnectL1, respectively. 100 | func NewRandomWallet(chainId *big.Int, clientL2 *clients.Client, clientL1 *ethclient.Client) (*Wallet, error) { 101 | signer, err := NewRandomBaseSigner(chainId) 102 | if err != nil { 103 | return nil, err 104 | } 105 | return NewWalletFromSigner(signer, clientL2, clientL1) 106 | } 107 | 108 | // Nonce returns the account nonce of the associated account. 109 | // The block number can be nil, in which case the nonce is taken from the latest known block. 110 | func (w *Wallet) Nonce(ctx context.Context, blockNumber *big.Int) (uint64, error) { 111 | return w.clientL2.NonceAt(ctx, w.Address(), blockNumber) 112 | } 113 | 114 | // PendingNonce returns the account nonce of the associated account in the pending state. 115 | // This is the nonce that should be used for the next transaction. 116 | func (w *Wallet) PendingNonce(ctx context.Context) (uint64, error) { 117 | return w.clientL2.PendingNonceAt(ctx, w.Address()) 118 | } 119 | 120 | // Connect returns a new instance of Wallet with the provided clientL2 for the L2 network. 121 | func (w *Wallet) Connect(client *clients.Client) (*Wallet, error) { 122 | return NewWalletFromSigner(w.Signer(), client, w.clientL1) 123 | } 124 | 125 | // ConnectL1 returns a new instance of Wallet with the provided clientL2 for the L1 network. 126 | func (w *Wallet) ConnectL1(client *ethclient.Client) (*Wallet, error) { 127 | return NewWalletFromSigner(w.Signer(), w.clientL2, client) 128 | } 129 | -------------------------------------------------------------------------------- /clients/types.go: -------------------------------------------------------------------------------- 1 | package clients 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | "github.com/ethereum/go-ethereum" 8 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 9 | "github.com/ethereum/go-ethereum/common" 10 | "github.com/ethereum/go-ethereum/common/hexutil" 11 | ethTypes "github.com/ethereum/go-ethereum/core/types" 12 | "github.com/zksync-sdk/zksync2-go/contracts/erc20" 13 | "github.com/zksync-sdk/zksync2-go/contracts/ethtoken" 14 | "github.com/zksync-sdk/zksync2-go/contracts/l2assetrouter" 15 | "github.com/zksync-sdk/zksync2-go/contracts/l2bridge" 16 | "github.com/zksync-sdk/zksync2-go/contracts/l2nativetokenvault" 17 | "github.com/zksync-sdk/zksync2-go/types" 18 | "github.com/zksync-sdk/zksync2-go/utils" 19 | "math/big" 20 | ) 21 | 22 | // TransferCallMsg contains parameters for transfer call. 23 | type TransferCallMsg struct { 24 | To common.Address // The address of the recipient. 25 | Amount *big.Int // The amount of the token to transfer. 26 | Token common.Address // The address of the token. ETH by default. 27 | From common.Address // The address of the sender. 28 | 29 | Gas uint64 // If 0, the call executes with near-infinite gas. 30 | GasPrice *big.Int // Wei <-> gas exchange ratio. 31 | GasFeeCap *big.Int // EIP-1559 fee cap per gas. 32 | GasTipCap *big.Int // EIP-1559 tip per gas. 33 | 34 | PaymasterParams *types.PaymasterParams // The paymaster parameters. 35 | // GasPerPubdata denotes the maximum amount of gas the user is willing 36 | // to pay for a single byte of pubdata. 37 | GasPerPubdata *big.Int 38 | // CustomSignature is used for the cases in which the signer's account 39 | // is not an EOA. 40 | CustomSignature hexutil.Bytes 41 | } 42 | 43 | // ToL1CallMsg transforms TransferCallMsg to ethereum.CallMsg. 44 | func (m *TransferCallMsg) ToL1CallMsg() (*ethereum.CallMsg, error) { 45 | var ( 46 | value *big.Int 47 | data []byte 48 | to *common.Address 49 | ) 50 | 51 | if m.Token == utils.L2BaseTokenAddress { 52 | value = m.Amount 53 | to = &m.To 54 | } else { 55 | value = big.NewInt(0) 56 | to = &m.Token 57 | 58 | erc20abi, err := erc20.IERC20MetaData.GetAbi() 59 | if err != nil { 60 | return nil, fmt.Errorf("failed to load erc20abi: %w", err) 61 | } 62 | data, err = erc20abi.Pack("transfer", m.To, m.Amount) 63 | if err != nil { 64 | return nil, fmt.Errorf("failed to pack transfer function: %w", err) 65 | } 66 | } 67 | 68 | return ðereum.CallMsg{ 69 | From: m.From, 70 | To: to, 71 | Gas: m.Gas, 72 | GasPrice: m.GasPrice, 73 | GasFeeCap: m.GasFeeCap, 74 | GasTipCap: m.GasTipCap, 75 | Value: value, 76 | Data: data, 77 | }, nil 78 | } 79 | 80 | // ToCallMsg transforms TransferCallMsg to types.CallMsg. 81 | func (m *TransferCallMsg) ToCallMsg() (*types.CallMsg, error) { 82 | var ( 83 | value *big.Int 84 | data []byte 85 | to *common.Address 86 | gasPerPubdata *big.Int 87 | ) 88 | 89 | if m.Token == utils.L2BaseTokenAddress { 90 | value = m.Amount 91 | to = &m.To 92 | } else { 93 | value = big.NewInt(0) 94 | to = &m.Token 95 | 96 | erc20abi, err := erc20.IERC20MetaData.GetAbi() 97 | if err != nil { 98 | return nil, fmt.Errorf("failed to load erc20abi: %w", err) 99 | } 100 | data, err = erc20abi.Pack("transfer", m.To, m.Amount) 101 | if err != nil { 102 | return nil, fmt.Errorf("failed to pack transfer function: %w", err) 103 | } 104 | } 105 | 106 | if m.GasPerPubdata == nil { 107 | gasPerPubdata = utils.DefaultGasPerPubdataLimit 108 | } 109 | 110 | return &types.CallMsg{ 111 | From: m.From, 112 | To: to, 113 | Gas: m.Gas, 114 | GasPrice: m.GasPrice, 115 | GasFeeCap: m.GasFeeCap, 116 | GasTipCap: m.GasTipCap, 117 | Value: value, 118 | Data: data, 119 | GasPerPubdata: gasPerPubdata, 120 | PaymasterParams: m.PaymasterParams, 121 | CustomSignature: m.CustomSignature, 122 | }, nil 123 | } 124 | 125 | // WithdrawalCallMsg contains parameters for withdrawal call. 126 | type WithdrawalCallMsg struct { 127 | To common.Address // The address of the recipient on L1. 128 | Amount *big.Int // The amount of the token to transfer. 129 | Token common.Address // The address of the token. ETH by default. 130 | BridgeAddress *common.Address // The address of the bridge contract to be used. 131 | From common.Address // The address of the sender. 132 | 133 | Gas uint64 // If 0, the call executes with near-infinite gas. 134 | GasPrice *big.Int // Wei <-> gas exchange ratio. 135 | GasFeeCap *big.Int // EIP-1559 fee cap per gas. 136 | GasTipCap *big.Int // EIP-1559 tip per gas. 137 | 138 | PaymasterParams *types.PaymasterParams // The paymaster parameters. 139 | // GasPerPubdata denotes the maximum amount of gas the user is willing 140 | // to pay for a single byte of pubdata. 141 | GasPerPubdata *big.Int 142 | // CustomSignature is used for the cases in which the signer's account 143 | // is not an EOA. 144 | CustomSignature hexutil.Bytes 145 | } 146 | 147 | // ToL1CallMsg transforms WithdrawalCallMsg to ethereum.CallMsg. 148 | func (m *WithdrawalCallMsg) ToL1CallMsg(ctx context.Context, client *Client, defaultL2Bridge *common.Address) (*ethereum.CallMsg, error) { 149 | if m.Token == utils.L2BaseTokenAddress { 150 | ethTokenAbi, err := ethtoken.IEthTokenMetaData.GetAbi() 151 | if err != nil { 152 | return nil, fmt.Errorf("failed to load ethTokenAbi: %w", err) 153 | } 154 | 155 | data, errPack := ethTokenAbi.Pack("withdraw", m.To) 156 | if errPack != nil { 157 | return nil, fmt.Errorf("failed to pack withdraw function: %w", errPack) 158 | } 159 | return ðereum.CallMsg{ 160 | From: m.From, 161 | To: &utils.L2BaseTokenAddress, 162 | Gas: m.Gas, 163 | GasPrice: m.GasPrice, 164 | GasFeeCap: m.GasFeeCap, 165 | GasTipCap: m.GasTipCap, 166 | Value: m.Amount, 167 | Data: data, 168 | }, nil 169 | } 170 | 171 | ntv, err := l2nativetokenvault.NewIL2NativeTokenVault(utils.L2NativeTokenVaultAddress, client) 172 | if err != nil { 173 | return nil, fmt.Errorf("failed to init l2NativeTokenVault: %w", err) 174 | } 175 | assetId, err := ntv.AssetId(&bind.CallOpts{Context: ctx}, m.Token) 176 | if err != nil { 177 | return nil, fmt.Errorf("failed to get asset id from l2NativeTokenVault: %w", err) 178 | } 179 | originChainId, err := ntv.OriginChainId(&bind.CallOpts{Context: ctx}, assetId) 180 | if err != nil { 181 | return nil, fmt.Errorf("failed to get origin chain id from l2NativeTokenVault: %w", err) 182 | } 183 | l1ChainId, err := client.L1ChainID(ctx) 184 | if err != nil { 185 | return nil, fmt.Errorf("failed to get L1 chain id: %w", err) 186 | } 187 | isTokenL1Native := originChainId == l1ChainId || m.Token == utils.EthAddressInContracts 188 | bridgeAddress := m.BridgeAddress 189 | if bridgeAddress != nil && isTokenL1Native { 190 | // If the legacy L2SharedBridge is deployed we use it for l1 native tokens. 191 | bridgeAddress = defaultL2Bridge 192 | } else if bridgeAddress != nil && !isTokenL1Native { 193 | bridgeAddress = &utils.L2AssetRouterAddress 194 | } 195 | 196 | // For non L1 native tokens we need to use the AssetRouter. 197 | // For L1 native tokens we can use the legacy withdraw method. 198 | if !isTokenL1Native { 199 | chainId, errChainId := client.ChainID(ctx) 200 | if errChainId != nil { 201 | return nil, fmt.Errorf("failed to get chain id: %w", err) 202 | } 203 | nativeAssetId, errNativeAssetId := utils.NativeTokenVaultAssetId(chainId, m.Token) 204 | if errNativeAssetId != nil { 205 | return nil, fmt.Errorf("failed to encode asset id: %w", err) 206 | } 207 | nativeAssetData, errNativeAssetData := utils.NativeTokenVaultTransferData(m.Amount, m.To, m.Token) 208 | if errNativeAssetData != nil { 209 | return nil, fmt.Errorf("failed to encode asset data: %w", err) 210 | } 211 | bridgeAbi, errBridgeAbi := l2assetrouter.IL2AssetRouterMetaData.GetAbi() 212 | if errBridgeAbi != nil { 213 | return nil, fmt.Errorf("failed to load l2AssetRouterAbi: %w", err) 214 | } 215 | data, errData := bridgeAbi.Pack("withdraw", nativeAssetId, nativeAssetData) 216 | if errData != nil { 217 | return nil, fmt.Errorf("failed to pack withdraw function: %w", err) 218 | } 219 | 220 | return ðereum.CallMsg{ 221 | From: m.From, 222 | To: &utils.L2AssetRouterAddress, 223 | Gas: m.Gas, 224 | GasPrice: m.GasPrice, 225 | GasFeeCap: m.GasFeeCap, 226 | GasTipCap: m.GasTipCap, 227 | Value: big.NewInt(0), 228 | Data: data, 229 | }, nil 230 | } 231 | 232 | l2BridgeAbi, err := l2bridge.IL2BridgeMetaData.GetAbi() 233 | if err != nil { 234 | return nil, fmt.Errorf("failed to load l2BridgeAbi: %w", err) 235 | } 236 | data, err := l2BridgeAbi.Pack("withdraw", m.To, m.Token, m.Amount) 237 | if err != nil { 238 | return nil, fmt.Errorf("failed to pack withdraw function: %w", err) 239 | } 240 | 241 | return ðereum.CallMsg{ 242 | From: m.From, 243 | To: bridgeAddress, 244 | Gas: m.Gas, 245 | GasPrice: m.GasPrice, 246 | GasFeeCap: m.GasFeeCap, 247 | GasTipCap: m.GasTipCap, 248 | Value: big.NewInt(0), 249 | Data: data, 250 | }, nil 251 | } 252 | 253 | // ToCallMsg transforms WithdrawalCallMsg to types.CallMsg. 254 | func (m *WithdrawalCallMsg) ToCallMsg(ctx context.Context, client *Client, defaultL2Bridge *common.Address) (*types.CallMsg, error) { 255 | msg, err := m.ToL1CallMsg(ctx, client, defaultL2Bridge) 256 | if err != nil { 257 | return nil, err 258 | } 259 | 260 | gasPerPubdata := m.GasPerPubdata 261 | if gasPerPubdata == nil { 262 | gasPerPubdata = utils.DefaultGasPerPubdataLimit 263 | } 264 | 265 | return &types.CallMsg{ 266 | From: msg.From, 267 | To: msg.To, 268 | Gas: msg.Gas, 269 | GasPrice: msg.GasPrice, 270 | GasFeeCap: msg.GasFeeCap, 271 | GasTipCap: msg.GasTipCap, 272 | Value: msg.Value, 273 | Data: msg.Data, 274 | GasPerPubdata: gasPerPubdata, 275 | PaymasterParams: m.PaymasterParams, 276 | CustomSignature: m.CustomSignature, 277 | }, nil 278 | } 279 | 280 | type blockMarshaling struct { 281 | ParentHash common.Hash `json:"parentHash" gencodec:"required"` 282 | UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` 283 | Coinbase common.Address `json:"miner"` 284 | Root common.Hash `json:"stateRoot" gencodec:"required"` 285 | TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` 286 | ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` 287 | Bloom ethTypes.Bloom `json:"logsBloom" gencodec:"required"` 288 | Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` 289 | Number *hexutil.Big `json:"number" gencodec:"required"` 290 | GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` 291 | GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` 292 | Time hexutil.Uint64 `json:"timestamp" gencodec:"required"` 293 | Extra hexutil.Bytes `json:"extraData" gencodec:"required"` 294 | MixDigest common.Hash `json:"mixHash"` 295 | Nonce ethTypes.BlockNonce `json:"nonce"` 296 | BaseFee *hexutil.Big `json:"baseFeePerGas" rlp:"optional"` 297 | ExcessDataGas *hexutil.Big `json:"excessDataGas" rlp:"optional"` 298 | 299 | Uncles []*common.Hash `json:"uncles"` 300 | Hash *common.Hash `json:"hash"` 301 | L1BatchNumber *hexutil.Big `json:"l1BatchNumber"` 302 | L1BatchTimestamp *hexutil.Big `json:"l1BatchTimestamp"` 303 | TotalDifficulty *hexutil.Big `json:"totalDifficulty"` 304 | Size *hexutil.Big `json:"size"` 305 | SealFields []interface{} `json:"sealFields"` 306 | 307 | Transactions []*types.TransactionResponse `json:"transactions"` 308 | } 309 | 310 | // BlockRange represents a range of blocks with the starting and ending block numbers. 311 | type BlockRange struct { 312 | Beginning *big.Int `json:"beginning"` // Starting block number of the range. 313 | End *big.Int `json:"end"` // Ending block number of the range. 314 | } 315 | 316 | func (r *BlockRange) UnmarshalJSON(input []byte) error { 317 | var data [2]*hexutil.Big 318 | err := json.Unmarshal(input, &data) 319 | if err != nil { 320 | return err 321 | } 322 | r.Beginning = data[0].ToInt() 323 | r.End = data[1].ToInt() 324 | return nil 325 | } 326 | -------------------------------------------------------------------------------- /clients/util.go: -------------------------------------------------------------------------------- 1 | package clients 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/ethereum/go-ethereum" 7 | "github.com/ethereum/go-ethereum/common/hexutil" 8 | "github.com/ethereum/go-ethereum/rpc" 9 | "math/big" 10 | ) 11 | 12 | func toFilterArg(q ethereum.FilterQuery) (interface{}, error) { 13 | arg := map[string]interface{}{ 14 | "address": q.Addresses, 15 | "topics": q.Topics, 16 | } 17 | if q.BlockHash != nil { 18 | arg["blockHash"] = *q.BlockHash 19 | if q.FromBlock != nil || q.ToBlock != nil { 20 | return nil, errors.New("cannot specify both BlockHash and FromBlock/ToBlock") 21 | } 22 | } else { 23 | if q.FromBlock == nil { 24 | arg["fromBlock"] = "0x0" 25 | } else { 26 | arg["fromBlock"] = toBlockNumArg(q.FromBlock) 27 | } 28 | arg["toBlock"] = toBlockNumArg(q.ToBlock) 29 | } 30 | return arg, nil 31 | } 32 | 33 | func toCallArg(msg ethereum.CallMsg) interface{} { 34 | arg := map[string]interface{}{ 35 | "from": msg.From, 36 | "to": msg.To, 37 | } 38 | if len(msg.Data) > 0 { 39 | arg["data"] = hexutil.Bytes(msg.Data) 40 | } 41 | if msg.Value != nil { 42 | arg["value"] = (*hexutil.Big)(msg.Value) 43 | } 44 | if msg.Gas != 0 { 45 | arg["gas"] = hexutil.Uint64(msg.Gas) 46 | } 47 | if msg.GasPrice != nil { 48 | arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice) 49 | } 50 | if msg.GasFeeCap != nil { 51 | arg["maxFeePerGas"] = (*hexutil.Big)(msg.GasFeeCap) 52 | } 53 | if msg.GasTipCap != nil { 54 | arg["maxPriorityFeePerGas"] = (*hexutil.Big)(msg.GasTipCap) 55 | } 56 | if msg.AccessList != nil { 57 | arg["accessList"] = msg.AccessList 58 | } 59 | if msg.BlobGasFeeCap != nil { 60 | arg["maxFeePerBlobGas"] = (*hexutil.Big)(msg.BlobGasFeeCap) 61 | } 62 | if msg.BlobHashes != nil { 63 | arg["blobVersionedHashes"] = msg.BlobHashes 64 | } 65 | return arg 66 | } 67 | 68 | func toBlockNumArg(number *big.Int) string { 69 | if number == nil { 70 | return "latest" 71 | } 72 | if number.Sign() >= 0 { 73 | return hexutil.EncodeBig(number) 74 | } 75 | // It's negative. 76 | if number.IsInt64() { 77 | return rpc.BlockNumber(number.Int64()).String() 78 | } 79 | // It's negative and large, which is invalid. 80 | return fmt.Sprintf("", number) 81 | } 82 | -------------------------------------------------------------------------------- /contracts/erc1271/erc1271.go: -------------------------------------------------------------------------------- 1 | // Code generated - DO NOT EDIT. 2 | // This file is a generated binding and any manual changes will be lost. 3 | 4 | package erc1271 5 | 6 | import ( 7 | "errors" 8 | "math/big" 9 | "strings" 10 | 11 | ethereum "github.com/ethereum/go-ethereum" 12 | "github.com/ethereum/go-ethereum/accounts/abi" 13 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 14 | "github.com/ethereum/go-ethereum/common" 15 | "github.com/ethereum/go-ethereum/core/types" 16 | "github.com/ethereum/go-ethereum/event" 17 | ) 18 | 19 | // Reference imports to suppress errors if they are not otherwise used. 20 | var ( 21 | _ = errors.New 22 | _ = big.NewInt 23 | _ = strings.NewReader 24 | _ = ethereum.NotFound 25 | _ = bind.Bind 26 | _ = common.Big1 27 | _ = types.BloomLookup 28 | _ = event.NewSubscription 29 | _ = abi.ConvertType 30 | ) 31 | 32 | // IERC1271MetaData contains all meta data concerning the IERC1271 contract. 33 | var IERC1271MetaData = &bind.MetaData{ 34 | ABI: "[{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"hash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"isValidSignature\",\"outputs\":[{\"internalType\":\"bytes4\",\"name\":\"magicValue\",\"type\":\"bytes4\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", 35 | } 36 | 37 | // IERC1271ABI is the input ABI used to generate the binding from. 38 | // Deprecated: Use IERC1271MetaData.ABI instead. 39 | var IERC1271ABI = IERC1271MetaData.ABI 40 | 41 | // IERC1271 is an auto generated Go binding around an Ethereum contract. 42 | type IERC1271 struct { 43 | IERC1271Caller // Read-only binding to the contract 44 | IERC1271Transactor // Write-only binding to the contract 45 | IERC1271Filterer // Log filterer for contract events 46 | } 47 | 48 | // IERC1271Caller is an auto generated read-only Go binding around an Ethereum contract. 49 | type IERC1271Caller struct { 50 | contract *bind.BoundContract // Generic contract wrapper for the low level calls 51 | } 52 | 53 | // IERC1271Transactor is an auto generated write-only Go binding around an Ethereum contract. 54 | type IERC1271Transactor struct { 55 | contract *bind.BoundContract // Generic contract wrapper for the low level calls 56 | } 57 | 58 | // IERC1271Filterer is an auto generated log filtering Go binding around an Ethereum contract events. 59 | type IERC1271Filterer struct { 60 | contract *bind.BoundContract // Generic contract wrapper for the low level calls 61 | } 62 | 63 | // IERC1271Session is an auto generated Go binding around an Ethereum contract, 64 | // with pre-set call and transact options. 65 | type IERC1271Session struct { 66 | Contract *IERC1271 // Generic contract binding to set the session for 67 | CallOpts bind.CallOpts // Call options to use throughout this session 68 | TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session 69 | } 70 | 71 | // IERC1271CallerSession is an auto generated read-only Go binding around an Ethereum contract, 72 | // with pre-set call options. 73 | type IERC1271CallerSession struct { 74 | Contract *IERC1271Caller // Generic contract caller binding to set the session for 75 | CallOpts bind.CallOpts // Call options to use throughout this session 76 | } 77 | 78 | // IERC1271TransactorSession is an auto generated write-only Go binding around an Ethereum contract, 79 | // with pre-set transact options. 80 | type IERC1271TransactorSession struct { 81 | Contract *IERC1271Transactor // Generic contract transactor binding to set the session for 82 | TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session 83 | } 84 | 85 | // IERC1271Raw is an auto generated low-level Go binding around an Ethereum contract. 86 | type IERC1271Raw struct { 87 | Contract *IERC1271 // Generic contract binding to access the raw methods on 88 | } 89 | 90 | // IERC1271CallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. 91 | type IERC1271CallerRaw struct { 92 | Contract *IERC1271Caller // Generic read-only contract binding to access the raw methods on 93 | } 94 | 95 | // IERC1271TransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. 96 | type IERC1271TransactorRaw struct { 97 | Contract *IERC1271Transactor // Generic write-only contract binding to access the raw methods on 98 | } 99 | 100 | // NewIERC1271 creates a new instance of IERC1271, bound to a specific deployed contract. 101 | func NewIERC1271(address common.Address, backend bind.ContractBackend) (*IERC1271, error) { 102 | contract, err := bindIERC1271(address, backend, backend, backend) 103 | if err != nil { 104 | return nil, err 105 | } 106 | return &IERC1271{IERC1271Caller: IERC1271Caller{contract: contract}, IERC1271Transactor: IERC1271Transactor{contract: contract}, IERC1271Filterer: IERC1271Filterer{contract: contract}}, nil 107 | } 108 | 109 | // NewIERC1271Caller creates a new read-only instance of IERC1271, bound to a specific deployed contract. 110 | func NewIERC1271Caller(address common.Address, caller bind.ContractCaller) (*IERC1271Caller, error) { 111 | contract, err := bindIERC1271(address, caller, nil, nil) 112 | if err != nil { 113 | return nil, err 114 | } 115 | return &IERC1271Caller{contract: contract}, nil 116 | } 117 | 118 | // NewIERC1271Transactor creates a new write-only instance of IERC1271, bound to a specific deployed contract. 119 | func NewIERC1271Transactor(address common.Address, transactor bind.ContractTransactor) (*IERC1271Transactor, error) { 120 | contract, err := bindIERC1271(address, nil, transactor, nil) 121 | if err != nil { 122 | return nil, err 123 | } 124 | return &IERC1271Transactor{contract: contract}, nil 125 | } 126 | 127 | // NewIERC1271Filterer creates a new log filterer instance of IERC1271, bound to a specific deployed contract. 128 | func NewIERC1271Filterer(address common.Address, filterer bind.ContractFilterer) (*IERC1271Filterer, error) { 129 | contract, err := bindIERC1271(address, nil, nil, filterer) 130 | if err != nil { 131 | return nil, err 132 | } 133 | return &IERC1271Filterer{contract: contract}, nil 134 | } 135 | 136 | // bindIERC1271 binds a generic wrapper to an already deployed contract. 137 | func bindIERC1271(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { 138 | parsed, err := IERC1271MetaData.GetAbi() 139 | if err != nil { 140 | return nil, err 141 | } 142 | return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil 143 | } 144 | 145 | // Call invokes the (constant) contract method with params as input values and 146 | // sets the output to result. The result type might be a single field for simple 147 | // returns, a slice of interfaces for anonymous returns and a struct for named 148 | // returns. 149 | func (_IERC1271 *IERC1271Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { 150 | return _IERC1271.Contract.IERC1271Caller.contract.Call(opts, result, method, params...) 151 | } 152 | 153 | // Transfer initiates a plain transaction to move funds to the contract, calling 154 | // its default method if one is available. 155 | func (_IERC1271 *IERC1271Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { 156 | return _IERC1271.Contract.IERC1271Transactor.contract.Transfer(opts) 157 | } 158 | 159 | // Transact invokes the (paid) contract method with params as input values. 160 | func (_IERC1271 *IERC1271Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { 161 | return _IERC1271.Contract.IERC1271Transactor.contract.Transact(opts, method, params...) 162 | } 163 | 164 | // Call invokes the (constant) contract method with params as input values and 165 | // sets the output to result. The result type might be a single field for simple 166 | // returns, a slice of interfaces for anonymous returns and a struct for named 167 | // returns. 168 | func (_IERC1271 *IERC1271CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { 169 | return _IERC1271.Contract.contract.Call(opts, result, method, params...) 170 | } 171 | 172 | // Transfer initiates a plain transaction to move funds to the contract, calling 173 | // its default method if one is available. 174 | func (_IERC1271 *IERC1271TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { 175 | return _IERC1271.Contract.contract.Transfer(opts) 176 | } 177 | 178 | // Transact invokes the (paid) contract method with params as input values. 179 | func (_IERC1271 *IERC1271TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { 180 | return _IERC1271.Contract.contract.Transact(opts, method, params...) 181 | } 182 | 183 | // IsValidSignature is a free data retrieval call binding the contract method 0x1626ba7e. 184 | // 185 | // Solidity: function isValidSignature(bytes32 hash, bytes signature) view returns(bytes4 magicValue) 186 | func (_IERC1271 *IERC1271Caller) IsValidSignature(opts *bind.CallOpts, hash [32]byte, signature []byte) ([4]byte, error) { 187 | var out []interface{} 188 | err := _IERC1271.contract.Call(opts, &out, "isValidSignature", hash, signature) 189 | 190 | if err != nil { 191 | return *new([4]byte), err 192 | } 193 | 194 | out0 := *abi.ConvertType(out[0], new([4]byte)).(*[4]byte) 195 | 196 | return out0, err 197 | 198 | } 199 | 200 | // IsValidSignature is a free data retrieval call binding the contract method 0x1626ba7e. 201 | // 202 | // Solidity: function isValidSignature(bytes32 hash, bytes signature) view returns(bytes4 magicValue) 203 | func (_IERC1271 *IERC1271Session) IsValidSignature(hash [32]byte, signature []byte) ([4]byte, error) { 204 | return _IERC1271.Contract.IsValidSignature(&_IERC1271.CallOpts, hash, signature) 205 | } 206 | 207 | // IsValidSignature is a free data retrieval call binding the contract method 0x1626ba7e. 208 | // 209 | // Solidity: function isValidSignature(bytes32 hash, bytes signature) view returns(bytes4 magicValue) 210 | func (_IERC1271 *IERC1271CallerSession) IsValidSignature(hash [32]byte, signature []byte) ([4]byte, error) { 211 | return _IERC1271.Contract.IsValidSignature(&_IERC1271.CallOpts, hash, signature) 212 | } 213 | -------------------------------------------------------------------------------- /contracts/paymasterflow/paymaster_flow.go: -------------------------------------------------------------------------------- 1 | // Code generated - DO NOT EDIT. 2 | // This file is a generated binding and any manual changes will be lost. 3 | 4 | package paymasterflow 5 | 6 | import ( 7 | "errors" 8 | "math/big" 9 | "strings" 10 | 11 | ethereum "github.com/ethereum/go-ethereum" 12 | "github.com/ethereum/go-ethereum/accounts/abi" 13 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 14 | "github.com/ethereum/go-ethereum/common" 15 | "github.com/ethereum/go-ethereum/core/types" 16 | "github.com/ethereum/go-ethereum/event" 17 | ) 18 | 19 | // Reference imports to suppress errors if they are not otherwise used. 20 | var ( 21 | _ = errors.New 22 | _ = big.NewInt 23 | _ = strings.NewReader 24 | _ = ethereum.NotFound 25 | _ = bind.Bind 26 | _ = common.Big1 27 | _ = types.BloomLookup 28 | _ = event.NewSubscription 29 | _ = abi.ConvertType 30 | ) 31 | 32 | // IPaymasterFlowMetaData contains all meta data concerning the IPaymasterFlow contract. 33 | var IPaymasterFlowMetaData = &bind.MetaData{ 34 | ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_minAllowance\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"_innerInput\",\"type\":\"bytes\"}],\"name\":\"approvalBased\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"input\",\"type\":\"bytes\"}],\"name\":\"general\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", 35 | } 36 | 37 | // IPaymasterFlowABI is the input ABI used to generate the binding from. 38 | // Deprecated: Use IPaymasterFlowMetaData.ABI instead. 39 | var IPaymasterFlowABI = IPaymasterFlowMetaData.ABI 40 | 41 | // IPaymasterFlow is an auto generated Go binding around an Ethereum contract. 42 | type IPaymasterFlow struct { 43 | IPaymasterFlowCaller // Read-only binding to the contract 44 | IPaymasterFlowTransactor // Write-only binding to the contract 45 | IPaymasterFlowFilterer // Log filterer for contract events 46 | } 47 | 48 | // IPaymasterFlowCaller is an auto generated read-only Go binding around an Ethereum contract. 49 | type IPaymasterFlowCaller struct { 50 | contract *bind.BoundContract // Generic contract wrapper for the low level calls 51 | } 52 | 53 | // IPaymasterFlowTransactor is an auto generated write-only Go binding around an Ethereum contract. 54 | type IPaymasterFlowTransactor struct { 55 | contract *bind.BoundContract // Generic contract wrapper for the low level calls 56 | } 57 | 58 | // IPaymasterFlowFilterer is an auto generated log filtering Go binding around an Ethereum contract events. 59 | type IPaymasterFlowFilterer struct { 60 | contract *bind.BoundContract // Generic contract wrapper for the low level calls 61 | } 62 | 63 | // IPaymasterFlowSession is an auto generated Go binding around an Ethereum contract, 64 | // with pre-set call and transact options. 65 | type IPaymasterFlowSession struct { 66 | Contract *IPaymasterFlow // Generic contract binding to set the session for 67 | CallOpts bind.CallOpts // Call options to use throughout this session 68 | TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session 69 | } 70 | 71 | // IPaymasterFlowCallerSession is an auto generated read-only Go binding around an Ethereum contract, 72 | // with pre-set call options. 73 | type IPaymasterFlowCallerSession struct { 74 | Contract *IPaymasterFlowCaller // Generic contract caller binding to set the session for 75 | CallOpts bind.CallOpts // Call options to use throughout this session 76 | } 77 | 78 | // IPaymasterFlowTransactorSession is an auto generated write-only Go binding around an Ethereum contract, 79 | // with pre-set transact options. 80 | type IPaymasterFlowTransactorSession struct { 81 | Contract *IPaymasterFlowTransactor // Generic contract transactor binding to set the session for 82 | TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session 83 | } 84 | 85 | // IPaymasterFlowRaw is an auto generated low-level Go binding around an Ethereum contract. 86 | type IPaymasterFlowRaw struct { 87 | Contract *IPaymasterFlow // Generic contract binding to access the raw methods on 88 | } 89 | 90 | // IPaymasterFlowCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. 91 | type IPaymasterFlowCallerRaw struct { 92 | Contract *IPaymasterFlowCaller // Generic read-only contract binding to access the raw methods on 93 | } 94 | 95 | // IPaymasterFlowTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. 96 | type IPaymasterFlowTransactorRaw struct { 97 | Contract *IPaymasterFlowTransactor // Generic write-only contract binding to access the raw methods on 98 | } 99 | 100 | // NewIPaymasterFlow creates a new instance of IPaymasterFlow, bound to a specific deployed contract. 101 | func NewIPaymasterFlow(address common.Address, backend bind.ContractBackend) (*IPaymasterFlow, error) { 102 | contract, err := bindIPaymasterFlow(address, backend, backend, backend) 103 | if err != nil { 104 | return nil, err 105 | } 106 | return &IPaymasterFlow{IPaymasterFlowCaller: IPaymasterFlowCaller{contract: contract}, IPaymasterFlowTransactor: IPaymasterFlowTransactor{contract: contract}, IPaymasterFlowFilterer: IPaymasterFlowFilterer{contract: contract}}, nil 107 | } 108 | 109 | // NewIPaymasterFlowCaller creates a new read-only instance of IPaymasterFlow, bound to a specific deployed contract. 110 | func NewIPaymasterFlowCaller(address common.Address, caller bind.ContractCaller) (*IPaymasterFlowCaller, error) { 111 | contract, err := bindIPaymasterFlow(address, caller, nil, nil) 112 | if err != nil { 113 | return nil, err 114 | } 115 | return &IPaymasterFlowCaller{contract: contract}, nil 116 | } 117 | 118 | // NewIPaymasterFlowTransactor creates a new write-only instance of IPaymasterFlow, bound to a specific deployed contract. 119 | func NewIPaymasterFlowTransactor(address common.Address, transactor bind.ContractTransactor) (*IPaymasterFlowTransactor, error) { 120 | contract, err := bindIPaymasterFlow(address, nil, transactor, nil) 121 | if err != nil { 122 | return nil, err 123 | } 124 | return &IPaymasterFlowTransactor{contract: contract}, nil 125 | } 126 | 127 | // NewIPaymasterFlowFilterer creates a new log filterer instance of IPaymasterFlow, bound to a specific deployed contract. 128 | func NewIPaymasterFlowFilterer(address common.Address, filterer bind.ContractFilterer) (*IPaymasterFlowFilterer, error) { 129 | contract, err := bindIPaymasterFlow(address, nil, nil, filterer) 130 | if err != nil { 131 | return nil, err 132 | } 133 | return &IPaymasterFlowFilterer{contract: contract}, nil 134 | } 135 | 136 | // bindIPaymasterFlow binds a generic wrapper to an already deployed contract. 137 | func bindIPaymasterFlow(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { 138 | parsed, err := IPaymasterFlowMetaData.GetAbi() 139 | if err != nil { 140 | return nil, err 141 | } 142 | return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil 143 | } 144 | 145 | // Call invokes the (constant) contract method with params as input values and 146 | // sets the output to result. The result type might be a single field for simple 147 | // returns, a slice of interfaces for anonymous returns and a struct for named 148 | // returns. 149 | func (_IPaymasterFlow *IPaymasterFlowRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { 150 | return _IPaymasterFlow.Contract.IPaymasterFlowCaller.contract.Call(opts, result, method, params...) 151 | } 152 | 153 | // Transfer initiates a plain transaction to move funds to the contract, calling 154 | // its default method if one is available. 155 | func (_IPaymasterFlow *IPaymasterFlowRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { 156 | return _IPaymasterFlow.Contract.IPaymasterFlowTransactor.contract.Transfer(opts) 157 | } 158 | 159 | // Transact invokes the (paid) contract method with params as input values. 160 | func (_IPaymasterFlow *IPaymasterFlowRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { 161 | return _IPaymasterFlow.Contract.IPaymasterFlowTransactor.contract.Transact(opts, method, params...) 162 | } 163 | 164 | // Call invokes the (constant) contract method with params as input values and 165 | // sets the output to result. The result type might be a single field for simple 166 | // returns, a slice of interfaces for anonymous returns and a struct for named 167 | // returns. 168 | func (_IPaymasterFlow *IPaymasterFlowCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { 169 | return _IPaymasterFlow.Contract.contract.Call(opts, result, method, params...) 170 | } 171 | 172 | // Transfer initiates a plain transaction to move funds to the contract, calling 173 | // its default method if one is available. 174 | func (_IPaymasterFlow *IPaymasterFlowTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { 175 | return _IPaymasterFlow.Contract.contract.Transfer(opts) 176 | } 177 | 178 | // Transact invokes the (paid) contract method with params as input values. 179 | func (_IPaymasterFlow *IPaymasterFlowTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { 180 | return _IPaymasterFlow.Contract.contract.Transact(opts, method, params...) 181 | } 182 | 183 | // ApprovalBased is a paid mutator transaction binding the contract method 0x949431dc. 184 | // 185 | // Solidity: function approvalBased(address _token, uint256 _minAllowance, bytes _innerInput) returns() 186 | func (_IPaymasterFlow *IPaymasterFlowTransactor) ApprovalBased(opts *bind.TransactOpts, _token common.Address, _minAllowance *big.Int, _innerInput []byte) (*types.Transaction, error) { 187 | return _IPaymasterFlow.contract.Transact(opts, "approvalBased", _token, _minAllowance, _innerInput) 188 | } 189 | 190 | // ApprovalBased is a paid mutator transaction binding the contract method 0x949431dc. 191 | // 192 | // Solidity: function approvalBased(address _token, uint256 _minAllowance, bytes _innerInput) returns() 193 | func (_IPaymasterFlow *IPaymasterFlowSession) ApprovalBased(_token common.Address, _minAllowance *big.Int, _innerInput []byte) (*types.Transaction, error) { 194 | return _IPaymasterFlow.Contract.ApprovalBased(&_IPaymasterFlow.TransactOpts, _token, _minAllowance, _innerInput) 195 | } 196 | 197 | // ApprovalBased is a paid mutator transaction binding the contract method 0x949431dc. 198 | // 199 | // Solidity: function approvalBased(address _token, uint256 _minAllowance, bytes _innerInput) returns() 200 | func (_IPaymasterFlow *IPaymasterFlowTransactorSession) ApprovalBased(_token common.Address, _minAllowance *big.Int, _innerInput []byte) (*types.Transaction, error) { 201 | return _IPaymasterFlow.Contract.ApprovalBased(&_IPaymasterFlow.TransactOpts, _token, _minAllowance, _innerInput) 202 | } 203 | 204 | // General is a paid mutator transaction binding the contract method 0x8c5a3445. 205 | // 206 | // Solidity: function general(bytes input) returns() 207 | func (_IPaymasterFlow *IPaymasterFlowTransactor) General(opts *bind.TransactOpts, input []byte) (*types.Transaction, error) { 208 | return _IPaymasterFlow.contract.Transact(opts, "general", input) 209 | } 210 | 211 | // General is a paid mutator transaction binding the contract method 0x8c5a3445. 212 | // 213 | // Solidity: function general(bytes input) returns() 214 | func (_IPaymasterFlow *IPaymasterFlowSession) General(input []byte) (*types.Transaction, error) { 215 | return _IPaymasterFlow.Contract.General(&_IPaymasterFlow.TransactOpts, input) 216 | } 217 | 218 | // General is a paid mutator transaction binding the contract method 0x8c5a3445. 219 | // 220 | // Solidity: function general(bytes input) returns() 221 | func (_IPaymasterFlow *IPaymasterFlowTransactorSession) General(input []byte) (*types.Transaction, error) { 222 | return _IPaymasterFlow.Contract.General(&_IPaymasterFlow.TransactOpts, input) 223 | } 224 | -------------------------------------------------------------------------------- /contracts/testneterc20token/testnet_erc20_token.go: -------------------------------------------------------------------------------- 1 | // Code generated - DO NOT EDIT. 2 | // This file is a generated binding and any manual changes will be lost. 3 | 4 | package testneterc20token 5 | 6 | import ( 7 | "errors" 8 | "math/big" 9 | "strings" 10 | 11 | ethereum "github.com/ethereum/go-ethereum" 12 | "github.com/ethereum/go-ethereum/accounts/abi" 13 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 14 | "github.com/ethereum/go-ethereum/common" 15 | "github.com/ethereum/go-ethereum/core/types" 16 | "github.com/ethereum/go-ethereum/event" 17 | ) 18 | 19 | // Reference imports to suppress errors if they are not otherwise used. 20 | var ( 21 | _ = errors.New 22 | _ = big.NewInt 23 | _ = strings.NewReader 24 | _ = ethereum.NotFound 25 | _ = bind.Bind 26 | _ = common.Big1 27 | _ = types.BloomLookup 28 | _ = event.NewSubscription 29 | _ = abi.ConvertType 30 | ) 31 | 32 | // ITestnetERC20TokenMetaData contains all meta data concerning the ITestnetERC20Token contract. 33 | var ITestnetERC20TokenMetaData = &bind.MetaData{ 34 | ABI: "[{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", 35 | } 36 | 37 | // ITestnetERC20TokenABI is the input ABI used to generate the binding from. 38 | // Deprecated: Use ITestnetERC20TokenMetaData.ABI instead. 39 | var ITestnetERC20TokenABI = ITestnetERC20TokenMetaData.ABI 40 | 41 | // ITestnetERC20Token is an auto generated Go binding around an Ethereum contract. 42 | type ITestnetERC20Token struct { 43 | ITestnetERC20TokenCaller // Read-only binding to the contract 44 | ITestnetERC20TokenTransactor // Write-only binding to the contract 45 | ITestnetERC20TokenFilterer // Log filterer for contract events 46 | } 47 | 48 | // ITestnetERC20TokenCaller is an auto generated read-only Go binding around an Ethereum contract. 49 | type ITestnetERC20TokenCaller struct { 50 | contract *bind.BoundContract // Generic contract wrapper for the low level calls 51 | } 52 | 53 | // ITestnetERC20TokenTransactor is an auto generated write-only Go binding around an Ethereum contract. 54 | type ITestnetERC20TokenTransactor struct { 55 | contract *bind.BoundContract // Generic contract wrapper for the low level calls 56 | } 57 | 58 | // ITestnetERC20TokenFilterer is an auto generated log filtering Go binding around an Ethereum contract events. 59 | type ITestnetERC20TokenFilterer struct { 60 | contract *bind.BoundContract // Generic contract wrapper for the low level calls 61 | } 62 | 63 | // ITestnetERC20TokenSession is an auto generated Go binding around an Ethereum contract, 64 | // with pre-set call and transact options. 65 | type ITestnetERC20TokenSession struct { 66 | Contract *ITestnetERC20Token // Generic contract binding to set the session for 67 | CallOpts bind.CallOpts // Call options to use throughout this session 68 | TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session 69 | } 70 | 71 | // ITestnetERC20TokenCallerSession is an auto generated read-only Go binding around an Ethereum contract, 72 | // with pre-set call options. 73 | type ITestnetERC20TokenCallerSession struct { 74 | Contract *ITestnetERC20TokenCaller // Generic contract caller binding to set the session for 75 | CallOpts bind.CallOpts // Call options to use throughout this session 76 | } 77 | 78 | // ITestnetERC20TokenTransactorSession is an auto generated write-only Go binding around an Ethereum contract, 79 | // with pre-set transact options. 80 | type ITestnetERC20TokenTransactorSession struct { 81 | Contract *ITestnetERC20TokenTransactor // Generic contract transactor binding to set the session for 82 | TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session 83 | } 84 | 85 | // ITestnetERC20TokenRaw is an auto generated low-level Go binding around an Ethereum contract. 86 | type ITestnetERC20TokenRaw struct { 87 | Contract *ITestnetERC20Token // Generic contract binding to access the raw methods on 88 | } 89 | 90 | // ITestnetERC20TokenCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. 91 | type ITestnetERC20TokenCallerRaw struct { 92 | Contract *ITestnetERC20TokenCaller // Generic read-only contract binding to access the raw methods on 93 | } 94 | 95 | // ITestnetERC20TokenTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. 96 | type ITestnetERC20TokenTransactorRaw struct { 97 | Contract *ITestnetERC20TokenTransactor // Generic write-only contract binding to access the raw methods on 98 | } 99 | 100 | // NewITestnetERC20Token creates a new instance of ITestnetERC20Token, bound to a specific deployed contract. 101 | func NewITestnetERC20Token(address common.Address, backend bind.ContractBackend) (*ITestnetERC20Token, error) { 102 | contract, err := bindITestnetERC20Token(address, backend, backend, backend) 103 | if err != nil { 104 | return nil, err 105 | } 106 | return &ITestnetERC20Token{ITestnetERC20TokenCaller: ITestnetERC20TokenCaller{contract: contract}, ITestnetERC20TokenTransactor: ITestnetERC20TokenTransactor{contract: contract}, ITestnetERC20TokenFilterer: ITestnetERC20TokenFilterer{contract: contract}}, nil 107 | } 108 | 109 | // NewITestnetERC20TokenCaller creates a new read-only instance of ITestnetERC20Token, bound to a specific deployed contract. 110 | func NewITestnetERC20TokenCaller(address common.Address, caller bind.ContractCaller) (*ITestnetERC20TokenCaller, error) { 111 | contract, err := bindITestnetERC20Token(address, caller, nil, nil) 112 | if err != nil { 113 | return nil, err 114 | } 115 | return &ITestnetERC20TokenCaller{contract: contract}, nil 116 | } 117 | 118 | // NewITestnetERC20TokenTransactor creates a new write-only instance of ITestnetERC20Token, bound to a specific deployed contract. 119 | func NewITestnetERC20TokenTransactor(address common.Address, transactor bind.ContractTransactor) (*ITestnetERC20TokenTransactor, error) { 120 | contract, err := bindITestnetERC20Token(address, nil, transactor, nil) 121 | if err != nil { 122 | return nil, err 123 | } 124 | return &ITestnetERC20TokenTransactor{contract: contract}, nil 125 | } 126 | 127 | // NewITestnetERC20TokenFilterer creates a new log filterer instance of ITestnetERC20Token, bound to a specific deployed contract. 128 | func NewITestnetERC20TokenFilterer(address common.Address, filterer bind.ContractFilterer) (*ITestnetERC20TokenFilterer, error) { 129 | contract, err := bindITestnetERC20Token(address, nil, nil, filterer) 130 | if err != nil { 131 | return nil, err 132 | } 133 | return &ITestnetERC20TokenFilterer{contract: contract}, nil 134 | } 135 | 136 | // bindITestnetERC20Token binds a generic wrapper to an already deployed contract. 137 | func bindITestnetERC20Token(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { 138 | parsed, err := ITestnetERC20TokenMetaData.GetAbi() 139 | if err != nil { 140 | return nil, err 141 | } 142 | return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil 143 | } 144 | 145 | // Call invokes the (constant) contract method with params as input values and 146 | // sets the output to result. The result type might be a single field for simple 147 | // returns, a slice of interfaces for anonymous returns and a struct for named 148 | // returns. 149 | func (_ITestnetERC20Token *ITestnetERC20TokenRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { 150 | return _ITestnetERC20Token.Contract.ITestnetERC20TokenCaller.contract.Call(opts, result, method, params...) 151 | } 152 | 153 | // Transfer initiates a plain transaction to move funds to the contract, calling 154 | // its default method if one is available. 155 | func (_ITestnetERC20Token *ITestnetERC20TokenRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { 156 | return _ITestnetERC20Token.Contract.ITestnetERC20TokenTransactor.contract.Transfer(opts) 157 | } 158 | 159 | // Transact invokes the (paid) contract method with params as input values. 160 | func (_ITestnetERC20Token *ITestnetERC20TokenRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { 161 | return _ITestnetERC20Token.Contract.ITestnetERC20TokenTransactor.contract.Transact(opts, method, params...) 162 | } 163 | 164 | // Call invokes the (constant) contract method with params as input values and 165 | // sets the output to result. The result type might be a single field for simple 166 | // returns, a slice of interfaces for anonymous returns and a struct for named 167 | // returns. 168 | func (_ITestnetERC20Token *ITestnetERC20TokenCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { 169 | return _ITestnetERC20Token.Contract.contract.Call(opts, result, method, params...) 170 | } 171 | 172 | // Transfer initiates a plain transaction to move funds to the contract, calling 173 | // its default method if one is available. 174 | func (_ITestnetERC20Token *ITestnetERC20TokenTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { 175 | return _ITestnetERC20Token.Contract.contract.Transfer(opts) 176 | } 177 | 178 | // Transact invokes the (paid) contract method with params as input values. 179 | func (_ITestnetERC20Token *ITestnetERC20TokenTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { 180 | return _ITestnetERC20Token.Contract.contract.Transact(opts, method, params...) 181 | } 182 | 183 | // Decimals is a paid mutator transaction binding the contract method 0x313ce567. 184 | // 185 | // Solidity: function decimals() returns(uint8) 186 | func (_ITestnetERC20Token *ITestnetERC20TokenTransactor) Decimals(opts *bind.TransactOpts) (*types.Transaction, error) { 187 | return _ITestnetERC20Token.contract.Transact(opts, "decimals") 188 | } 189 | 190 | // Decimals is a paid mutator transaction binding the contract method 0x313ce567. 191 | // 192 | // Solidity: function decimals() returns(uint8) 193 | func (_ITestnetERC20Token *ITestnetERC20TokenSession) Decimals() (*types.Transaction, error) { 194 | return _ITestnetERC20Token.Contract.Decimals(&_ITestnetERC20Token.TransactOpts) 195 | } 196 | 197 | // Decimals is a paid mutator transaction binding the contract method 0x313ce567. 198 | // 199 | // Solidity: function decimals() returns(uint8) 200 | func (_ITestnetERC20Token *ITestnetERC20TokenTransactorSession) Decimals() (*types.Transaction, error) { 201 | return _ITestnetERC20Token.Contract.Decimals(&_ITestnetERC20Token.TransactOpts) 202 | } 203 | 204 | // Mint is a paid mutator transaction binding the contract method 0x40c10f19. 205 | // 206 | // Solidity: function mint(address _to, uint256 _amount) returns(bool) 207 | func (_ITestnetERC20Token *ITestnetERC20TokenTransactor) Mint(opts *bind.TransactOpts, _to common.Address, _amount *big.Int) (*types.Transaction, error) { 208 | return _ITestnetERC20Token.contract.Transact(opts, "mint", _to, _amount) 209 | } 210 | 211 | // Mint is a paid mutator transaction binding the contract method 0x40c10f19. 212 | // 213 | // Solidity: function mint(address _to, uint256 _amount) returns(bool) 214 | func (_ITestnetERC20Token *ITestnetERC20TokenSession) Mint(_to common.Address, _amount *big.Int) (*types.Transaction, error) { 215 | return _ITestnetERC20Token.Contract.Mint(&_ITestnetERC20Token.TransactOpts, _to, _amount) 216 | } 217 | 218 | // Mint is a paid mutator transaction binding the contract method 0x40c10f19. 219 | // 220 | // Solidity: function mint(address _to, uint256 _amount) returns(bool) 221 | func (_ITestnetERC20Token *ITestnetERC20TokenTransactorSession) Mint(_to common.Address, _amount *big.Int) (*types.Transaction, error) { 222 | return _ITestnetERC20Token.Contract.Mint(&_ITestnetERC20Token.TransactOpts, _to, _amount) 223 | } 224 | -------------------------------------------------------------------------------- /eip712/domain.go: -------------------------------------------------------------------------------- 1 | package eip712 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | "github.com/ethereum/go-ethereum/common/math" 6 | "github.com/ethereum/go-ethereum/signer/core/apitypes" 7 | "math/big" 8 | ) 9 | 10 | // Domain represents the domain parameters used for EIP-712 signing. 11 | type Domain struct { 12 | Name string `json:"name"` // Name of the domain. 13 | Version string `json:"version"` // Version of the domain. 14 | ChainId *big.Int `json:"chainId"` // Chain ID associated with the domain. 15 | VerifyingContract *common.Address `json:"verifyingContract"` // Address of the verifying contract for the domain. 16 | } 17 | 18 | // Type returns the name of the domain field. 19 | func (d *Domain) Type() string { 20 | return "EIP712Domain" 21 | } 22 | 23 | // Types returns the domain field types. 24 | func (d *Domain) Types() []apitypes.Type { 25 | types := []apitypes.Type{ 26 | {Name: "name", Type: "string"}, 27 | {Name: "version", Type: "string"}, 28 | {Name: "chainId", Type: "uint256"}, 29 | } 30 | if d.VerifyingContract != nil { 31 | types = append(types, apitypes.Type{Name: "verifyingContract", Type: "address"}) 32 | } 33 | return types 34 | } 35 | 36 | // TypedData returns domain typed data. 37 | func (d *Domain) TypedData() apitypes.TypedDataDomain { 38 | domain := apitypes.TypedDataDomain{ 39 | Name: d.Name, 40 | Version: d.Version, 41 | ChainId: math.NewHexOrDecimal256(d.ChainId.Int64()), 42 | } 43 | if d.VerifyingContract != nil { 44 | domain.VerifyingContract = d.VerifyingContract.String() 45 | } 46 | return domain 47 | } 48 | 49 | const ( 50 | DomainDefaultName = `zkSync` 51 | DomainDefaultVersion = `2` 52 | ) 53 | 54 | // ZkSyncEraEIP712Domain represents the EIP-712 domain for ZKsync Era. 55 | func ZkSyncEraEIP712Domain(chainId int64) *Domain { 56 | return &Domain{ 57 | Name: DomainDefaultName, 58 | Version: DomainDefaultVersion, 59 | ChainId: big.NewInt(chainId), 60 | VerifyingContract: nil, 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/zksync-sdk/zksync2-go 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/ethereum/go-ethereum v1.14.3 7 | github.com/pkg/errors v0.9.1 8 | github.com/stephenlacy/go-ethereum-hdwallet v0.0.0-20230913225845-a4fa94429863 9 | github.com/stretchr/testify v1.8.4 10 | ) 11 | 12 | require ( 13 | github.com/Microsoft/go-winio v0.6.1 // indirect 14 | github.com/StackExchange/wmi v1.2.1 // indirect 15 | github.com/bits-and-blooms/bitset v1.13.0 // indirect 16 | github.com/btcsuite/btcd v0.24.2 // indirect 17 | github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect 18 | github.com/btcsuite/btcd/btcutil v1.1.5 // indirect 19 | github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect 20 | github.com/consensys/bavard v0.1.13 // indirect 21 | github.com/consensys/gnark-crypto v0.12.1 // indirect 22 | github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect 23 | github.com/davecgh/go-spew v1.1.1 // indirect 24 | github.com/deckarep/golang-set/v2 v2.1.0 // indirect 25 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect 26 | github.com/ethereum/c-kzg-4844 v1.0.0 // indirect 27 | github.com/fsnotify/fsnotify v1.6.0 // indirect 28 | github.com/go-ole/go-ole v1.3.0 // indirect 29 | github.com/google/uuid v1.3.0 // indirect 30 | github.com/gorilla/websocket v1.5.0 // indirect 31 | github.com/holiman/uint256 v1.2.4 // indirect 32 | github.com/mmcloughlin/addchain v0.4.0 // indirect 33 | github.com/pmezard/go-difflib v1.0.0 // indirect 34 | github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect 35 | github.com/supranational/blst v0.3.11 // indirect 36 | github.com/tklauser/go-sysconf v0.3.12 // indirect 37 | github.com/tklauser/numcpus v0.6.1 // indirect 38 | github.com/tyler-smith/go-bip39 v1.1.0 // indirect 39 | golang.org/x/crypto v0.31.0 // indirect 40 | golang.org/x/exp v0.0.0-20240213143201-ec583247a57a // indirect 41 | golang.org/x/mod v0.17.0 // indirect 42 | golang.org/x/sync v0.7.0 // indirect 43 | golang.org/x/sys v0.28.0 // indirect 44 | golang.org/x/tools v0.20.0 // indirect 45 | gopkg.in/yaml.v3 v3.0.1 // indirect 46 | rsc.io/tmplfunc v0.0.3 // indirect 47 | ) 48 | -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /scripts/check-format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | fmt_output=$(gofmt -l ../.) 4 | 5 | if [ -n "$fmt_output" ]; then 6 | echo "Code not formatted correctly:" 7 | echo "$fmt_output" 8 | exit 1 9 | else 10 | echo "Code is formatted correctly" 11 | exit 0 12 | fi -------------------------------------------------------------------------------- /scripts/fetch-abi/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Install required tools" 4 | 5 | curl https://binaries.soliditylang.org/linux-amd64/solc-linux-amd64-v0.8.20+commit.a1b79de6 -o /usr/local/bin/solc 6 | chmod +x /usr/local/bin/solc 7 | apt-get update && apt-get install -y jq 8 | 9 | echo "Clone the matter-labs/era-contracts repository" 10 | git clone https://github.com/matter-labs/era-contracts.git era-contracts 11 | pushd era-contracts || exit 1 12 | 13 | echo "Install dependencies" 14 | yarn install 15 | 16 | 17 | echo "Generate ABIs" 18 | solc --base-path l1-contracts/ \ 19 | --include-path l1-contracts/node_modules/ \ 20 | -o l1-abi \ 21 | --abi \ 22 | l1-contracts/contracts/bridgehub/IBridgehub.sol \ 23 | l1-contracts/contracts/state-transition/chain-interfaces/IZkSyncStateTransition.sol \ 24 | l1-contracts/contracts/dev-contracts/interfaces/ITestnetERC20Token.sol \ 25 | l1-contracts/contracts/bridge/interfaces/IL1ERC20Bridge.sol \ 26 | l1-contracts/contracts/bridge/interfaces/IL1WethBridge.sol \ 27 | l1-contracts/contracts/bridge/interfaces/IL1Erc20Bridge.sol \ 28 | l1-contracts/contracts/bridge/interfaces/IL2Bridge.sol 29 | 30 | solc --base-path system-contracts \ 31 | -o system-contracts-abi \ 32 | --abi \ 33 | system-contracts/contracts/interfaces/IContractDeployer.sol \ 34 | system-contracts/contracts/interfaces/IEthToken.sol \ 35 | system-contracts/contracts/interfaces/IL1Messenger.sol \ 36 | system-contracts/contracts/interfaces/INonceHolder.sol \ 37 | system-contracts/contracts/interfaces/IPaymasterFlow.sol 38 | 39 | mkdir abi /abi 40 | mv l1-abi/* system-contracts-abi/* abi 41 | 42 | contracts="IBridgehub.abi IZkSyncStateTransition.abi IL1ERC20Bridge.abi IL1WethBridge.abi IL1Erc20Bridge.abi IL2Bridge.abi IContractDeployer.abi IEthToken.abi IL1Messenger.abi INonceHolder.abi IPaymasterFlow.abi ITestnetERC20Token.abi" 43 | 44 | for filename in $contracts; do 45 | jq '.' "abi/$filename" > "/abi/${filename%.abi}.json" 46 | done 47 | 48 | echo "Folder content" 49 | ls /abi 50 | -------------------------------------------------------------------------------- /scripts/fetch-abi/execute.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Fetch the contracts from https://github.com/matter-labs/era-contracts 4 | # and generates ABI json files and puts them in abi folder. 5 | 6 | docker create --name abi-gen --entrypoint /usr/local/bin/entrypoint.sh node:18 7 | docker cp entrypoint.sh abi-gen:/usr/local/bin/entrypoint.sh 8 | docker start -i abi-gen 9 | docker cp abi-gen:/abi/. ../../abi 10 | docker rm abi-gen -------------------------------------------------------------------------------- /scripts/generate-contracts/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/ash 2 | 3 | mkdir -p /contracts/bridgehub 4 | abigen \ 5 | --abi /abi/IBridgehub.json \ 6 | --out /contracts/bridgehub/bridgehub.go \ 7 | --pkg bridgehub \ 8 | --type IBridgehub 9 | 10 | mkdir -p /contracts/contractdeployer 11 | abigen \ 12 | --abi /abi/IContractDeployer.json \ 13 | --out /contracts/contractdeployer/contract_deployer.go \ 14 | --pkg contractdeployer \ 15 | --type IContractDeployer 16 | 17 | mkdir -p /contracts/ethtoken 18 | abigen \ 19 | --abi /abi/IEthToken.json \ 20 | --out /contracts/ethtoken/eth_token.go \ 21 | --pkg ethtoken \ 22 | --type IEthToken 23 | 24 | mkdir -p /contracts/l1bridge 25 | abigen \ 26 | --abi /abi/IL1Bridge.json \ 27 | --out /contracts/l1bridge/l1_bridge.go \ 28 | --pkg l1bridge \ 29 | --type IL1Bridge 30 | 31 | mkdir -p /contracts/l1erc20bridge 32 | abigen \ 33 | --abi /abi/IL1ERC20Bridge.json \ 34 | --out /contracts/l1erc20bridge/l1_erc20_bridge.go \ 35 | --pkg l1erc20bridge \ 36 | --type IL1ERC20Bridge 37 | 38 | mkdir -p /contracts/l1messenger 39 | abigen \ 40 | --abi /abi/IL1Messenger.json \ 41 | --out /contracts/l1messenger/l1_messenger.go \ 42 | --pkg l1messenger \ 43 | --type IL1Messenger 44 | 45 | mkdir -p /contracts/l1sharedbridge 46 | abigen \ 47 | --abi /abi/IL1SharedBridge.json \ 48 | --out /contracts/l1sharedbridge/l1_shared_bridge.go \ 49 | --pkg l1sharedbridge \ 50 | --type IL1SharedBridge 51 | 52 | mkdir -p /contracts/l2bridge 53 | abigen \ 54 | --abi /abi/IL2Bridge.json \ 55 | --out /contracts/l2bridge/l2_bridge.go \ 56 | --pkg l2bridge \ 57 | --type IL2Bridge 58 | 59 | mkdir -p /contracts/l2sharedbridge 60 | abigen \ 61 | --abi /abi/IL2SharedBridge.json \ 62 | --out /contracts/l2sharedbridge/l2_shared_bridge.go \ 63 | --pkg l2sharedbridge \ 64 | --type IL2SharedBridge 65 | 66 | mkdir -p /contracts/nonceholder 67 | abigen \ 68 | --abi /abi/INonceHolder.json \ 69 | --out /contracts/nonceholder/nonce_holder.go \ 70 | --pkg nonceholder \ 71 | --type INonceHolder 72 | 73 | mkdir -p /contracts/paymasterflow 74 | abigen \ 75 | --abi /abi/IPaymasterFlow.json \ 76 | --out /contracts/paymasterflow/paymaster_flow.go \ 77 | --pkg paymasterflow \ 78 | --type IPaymasterFlow 79 | 80 | mkdir -p /contracts/testneterc20token 81 | abigen \ 82 | --abi /abi/ITestnetERC20Token.json \ 83 | --out /contracts/testneterc20token/testnet_erc20_token.go \ 84 | --pkg testneterc20token \ 85 | --type ITestnetERC20Token 86 | 87 | mkdir -p /contracts/zksynchyperchain 88 | abigen \ 89 | --abi /abi/IZkSyncHyperchain.json \ 90 | --out /contracts/zksynchyperchain/zksync_hyperchain.go \ 91 | --pkg zksynchyperchain \ 92 | --type IZkSyncHyperchain 93 | 94 | echo "Folder content" 95 | ls -alhR /contracts 96 | -------------------------------------------------------------------------------- /scripts/generate-contracts/execute.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Generates go bindings from abi folder using abigen tool 4 | # and puts them in contracts folder. 5 | 6 | docker create -it --name abigen \ 7 | --entrypoint /usr/local/bin/entrypoint.sh \ 8 | ethereum/client-go:alltools-v1.13.13 9 | 10 | docker cp ../../abi abigen:/abi 11 | docker cp entrypoint.sh abigen:/usr/local/bin/entrypoint.sh 12 | docker start -i abigen 13 | 14 | docker cp abigen:/contracts/. ../../contracts 15 | docker rm abigen -------------------------------------------------------------------------------- /test/account_abstraction_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "context" 5 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 6 | "github.com/ethereum/go-ethereum/common" 7 | "github.com/ethereum/go-ethereum/ethclient" 8 | "github.com/stretchr/testify/assert" 9 | "github.com/zksync-sdk/zksync2-go/accounts" 10 | "github.com/zksync-sdk/zksync2-go/clients" 11 | "github.com/zksync-sdk/zksync2-go/types" 12 | "github.com/zksync-sdk/zksync2-go/utils" 13 | "math/big" 14 | "testing" 15 | ) 16 | 17 | func TestIntegration_ApprovalPaymaster(t *testing.T) { 18 | AirdropAmount := big.NewInt(10) 19 | MinimalAllowance := big.NewInt(1) 20 | MintAmount := big.NewInt(7) 21 | 22 | client, err := clients.Dial(L2ChainURL) 23 | defer client.Close() 24 | assert.NoError(t, err, "clients.Dial should not return an error") 25 | 26 | ethClient, err := ethclient.Dial(L1ChainURL) 27 | assert.NoError(t, err, "ethclient.Dial should not return an error") 28 | defer ethClient.Close() 29 | 30 | wallet, err := accounts.NewWallet(common.Hex2Bytes(PrivateKey1), client, ethClient) 31 | assert.NoError(t, err, "NewWallet should not return an error") 32 | 33 | // ====== Deploy Token ====== 34 | tokenAbi, err := TokenMetaData.GetAbi() 35 | assert.NoError(t, err, "GetAbi should not return an error") 36 | 37 | tokenConstructor, err := tokenAbi.Pack("", "Crown", "Crown", uint8(18)) 38 | assert.NoError(t, err, "Pack should not return an error") 39 | 40 | tokenDeployHash, err := wallet.DeployWithCreate(nil, accounts.CreateTransaction{ 41 | Bytecode: common.FromHex(TokenMetaData.Bin), 42 | Calldata: tokenConstructor, 43 | }) 44 | assert.NoError(t, err, "Deploy should not return an error") 45 | 46 | tokenDeployReceipt, err := client.WaitMined(context.Background(), tokenDeployHash) 47 | assert.NoError(t, err, "client.WaitMined should not return an error") 48 | 49 | tokenAddress := tokenDeployReceipt.ContractAddress 50 | assert.NotNil(t, tokenAddress, "Contract should be deployed") 51 | 52 | // ===== Mint tokens to wallet ===== 53 | token, err := NewToken(tokenAddress, client) 54 | assert.NoError(t, err, "NewToken should not return an error") 55 | 56 | opts, err := bind.NewKeyedTransactorWithChainID(wallet.Signer().PrivateKey(), wallet.Signer().ChainID()) 57 | assert.NoError(t, err, "NewKeyedTransactorWithChainID should not return an error") 58 | 59 | mint, err := token.Mint(opts, wallet.Address(), AirdropAmount) 60 | assert.NoError(t, err, "Mint should not return an error") 61 | 62 | _, err = client.WaitMined(context.Background(), mint.Hash()) 63 | assert.NoError(t, err, "client.WaitMined should not return an error") 64 | 65 | // ===== Deploy Paymaster ===== 66 | _, paymasterAbi, bytecode, err := utils.ReadStandardJson("./testdata/Paymaster.json") 67 | assert.NoError(t, err, "ReadStandardJson should not return an error") 68 | 69 | paymasterConstructor, err := paymasterAbi.Pack("", tokenAddress) 70 | assert.NoError(t, err, "Pack should not return an error") 71 | 72 | paymasterDeployHash, err := wallet.Deploy(nil, accounts.Create2Transaction{ 73 | Bytecode: bytecode, 74 | Calldata: paymasterConstructor, 75 | }) 76 | assert.NoError(t, err, "DeployWithCreate should not return an error") 77 | 78 | paymasterDeployReceipt, err := client.WaitMined(context.Background(), paymasterDeployHash) 79 | assert.NoError(t, err, "client.WaitMined should not return an error") 80 | 81 | paymasterAddress := paymasterDeployReceipt.ContractAddress 82 | assert.NotNil(t, paymasterAddress, "Contract should be deployed") 83 | 84 | // ===== Transfer some base token to paymaster, so it can pay fee with it ===== 85 | transferTxHash, err := wallet.Transfer(nil, accounts.TransferTransaction{ 86 | To: paymasterAddress, 87 | Amount: big.NewInt(1_000_000_000_000_000_000), 88 | Token: utils.L2BaseTokenAddress, 89 | }) 90 | assert.NoError(t, err, "Transfer should not return an error") 91 | 92 | _, err = client.WaitMined(context.Background(), transferTxHash) 93 | assert.NoError(t, err, "client.WaitMined should not return an error") 94 | 95 | // Read token and base token balances from user and paymaster accounts 96 | balanceBefore, err := wallet.Balance(nil, utils.L2BaseTokenAddress) 97 | assert.NoError(t, err, "Balance should not return an error") 98 | 99 | tokenBalanceBefore, err := token.BalanceOf(nil, wallet.Address()) 100 | assert.NoError(t, err, "BalanceOf should not return an error") 101 | 102 | paymasterBalanceBefore, err := client.BalanceAt(context.Background(), paymasterAddress, nil) 103 | assert.NoError(t, err, "BalanceAt should not return an error") 104 | 105 | paymasterTokenBalanceBefore, err := token.BalanceOf(nil, paymasterAddress) 106 | assert.NoError(t, err, "BalanceOf should not return an error") 107 | 108 | calldata, err := tokenAbi.Pack("mint", wallet.Address(), MintAmount) 109 | assert.NoError(t, err, "Pack should not return an error") 110 | 111 | paymasterParams, err := utils.GetPaymasterParams( 112 | paymasterAddress, 113 | &types.ApprovalBasedPaymasterInput{ 114 | Token: tokenAddress, 115 | MinimalAllowance: MinimalAllowance, 116 | InnerInput: []byte{}, 117 | }) 118 | assert.NoError(t, err, "GetPaymasterParams should not return an error") 119 | 120 | hash, err := wallet.SendTransaction(context.Background(), &accounts.Transaction{ 121 | To: &tokenAddress, 122 | Data: calldata, 123 | PaymasterParams: paymasterParams, 124 | }) 125 | assert.NoError(t, err, "SendTransaction should not return an error") 126 | 127 | _, err = client.WaitMined(context.Background(), hash) 128 | assert.NoError(t, err, "client.WaitMined should not return an error") 129 | 130 | balanceAfter, err := wallet.Balance(nil, utils.L2BaseTokenAddress) 131 | assert.NoError(t, err, "Balance should not return an error") 132 | 133 | tokenBalanceAfter, err := token.BalanceOf(nil, wallet.Address()) 134 | assert.NoError(t, err, "BalanceOf should not return an error") 135 | 136 | paymasterBalanceAfter, err := client.BalanceAt(context.Background(), paymasterAddress, nil) 137 | assert.NoError(t, err, "BalanceAt should not return an error") 138 | 139 | paymasterTokenBalanceAfter, err := token.BalanceOf(nil, paymasterAddress) 140 | assert.NoError(t, err, "BalanceOf should not return an error") 141 | 142 | assert.True(t, paymasterTokenBalanceBefore.Cmp(big.NewInt(0)) == 0, "Paymaster token balance before transaction should be 0") 143 | assert.True(t, tokenBalanceBefore.Cmp(AirdropAmount) == 0, "Wallet token balance before transaction should be equal to airdrop amount") 144 | assert.True(t, paymasterBalanceBefore.Cmp(paymasterBalanceAfter) > 0, "Paymaster balance after transaction should be decreased") 145 | assert.True(t, paymasterTokenBalanceAfter.Cmp(MinimalAllowance) == 0, "Paymaster token balance after transaction should be minimal allowance amount") 146 | assert.True(t, balanceBefore.Cmp(balanceAfter) == 0, "Balance should be the same") 147 | 148 | tmp := new(big.Int).Add(tokenBalanceBefore, MintAmount) 149 | assert.True(t, tokenBalanceAfter.Cmp(tmp.Sub(tmp, MinimalAllowance)) == 0, "Wallet token balance should be increased by difference between mint amount and allowance amount") 150 | } 151 | -------------------------------------------------------------------------------- /test/const.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | "reflect" 6 | ) 7 | 8 | var IsEthBasedChain = true 9 | 10 | const L1ChainURL = "http://localhost:15045" 11 | 12 | var L2ChainURL = "http://localhost:15100" 13 | 14 | const PrivateKey1 = "7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110" 15 | const PrivateKey2 = "ac1e735be8536c6534bb4f17f06f6afc73b2b5ba84ac2cfb12f7461b20c0bbe3" 16 | 17 | var Address1 = common.HexToAddress("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049") 18 | var Address2 = common.HexToAddress("0xa61464658AfeAf65CccaaFD3a512b69A83B77618") 19 | 20 | var Salt = common.Hex2Bytes("0x293328ad84b118194c65a0dc0defdb6483740d3163fd99b260907e15f2e2f642") 21 | 22 | // ApprovalToken is deployed using create2 and Salt 23 | var ApprovalToken = common.HexToAddress("0x834FF28392Ab0460f13286c389fEF4E3980e28F6") 24 | 25 | // Paymaster is an approval based paymaster for approval token deployed using create2 and Salt 26 | var Paymaster = common.HexToAddress("0xe1438081bF20c4C910266aa1229930473446b283") 27 | 28 | // MultisigAccount is an account that signs transaction using PrivateKey1 and PrivateKey2 29 | // and is deployed using create2 and Salt 30 | var MultisigAccount = common.HexToAddress("0x60222D60b22D3e2A6F459Dc7264aEbf9f8735363") 31 | 32 | // NtvAddress is the address where Native Token Vault is deployed on ZK chains 33 | var NtvAddress = common.HexToAddress("0x0000000000000000000000000000000000010004") 34 | 35 | var L1Tokens []TokenData 36 | var L2Dai common.Address 37 | var L1Dai = common.HexToAddress("0xDb6ca4Dd98d4F7248f7dEaE35204706e10492Ef7") 38 | var L2DepositTx common.Hash 39 | var L1DepositTx common.Hash 40 | 41 | type TokenData struct { 42 | Name string `json:"name"` 43 | Symbol string `json:"symbol"` 44 | Decimals int `json:"decimals"` 45 | Address common.Address `json:"address"` 46 | } 47 | 48 | func DeepEqualIgnore(s1, s2 interface{}, ignoreFields ...string) bool { 49 | // contains checks if a string slice contains a specific string. 50 | contains := func(slice []string, str string) bool { 51 | for _, s := range slice { 52 | if s == str { 53 | return true 54 | } 55 | } 56 | return false 57 | } 58 | 59 | v1 := reflect.ValueOf(s1) 60 | v2 := reflect.ValueOf(s2) 61 | 62 | for i := 0; i < v1.NumField(); i++ { 63 | field := v1.Type().Field(i) 64 | fieldName := field.Name 65 | 66 | if contains(ignoreFields, fieldName) { 67 | continue 68 | } 69 | 70 | val1 := v1.Field(i) 71 | val2 := v2.Field(i) 72 | 73 | if !reflect.DeepEqual(val1.Interface(), val2.Interface()) { 74 | return false 75 | } 76 | } 77 | 78 | return true 79 | } 80 | -------------------------------------------------------------------------------- /test/incrementer.go: -------------------------------------------------------------------------------- 1 | // Code generated - DO NOT EDIT. 2 | // This file is a generated binding and any manual changes will be lost. 3 | 4 | package test 5 | 6 | import ( 7 | "errors" 8 | "math/big" 9 | "strings" 10 | 11 | ethereum "github.com/ethereum/go-ethereum" 12 | "github.com/ethereum/go-ethereum/accounts/abi" 13 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 14 | "github.com/ethereum/go-ethereum/common" 15 | "github.com/ethereum/go-ethereum/core/types" 16 | "github.com/ethereum/go-ethereum/event" 17 | ) 18 | 19 | // Reference imports to suppress errors if they are not otherwise used. 20 | var ( 21 | _ = errors.New 22 | _ = big.NewInt 23 | _ = strings.NewReader 24 | _ = ethereum.NotFound 25 | _ = bind.Bind 26 | _ = common.Big1 27 | _ = types.BloomLookup 28 | _ = event.NewSubscription 29 | _ = abi.ConvertType 30 | ) 31 | 32 | // IncrementerMetaData contains all meta data concerning the Incrementer contract. 33 | var IncrementerMetaData = &bind.MetaData{ 34 | ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_incrementer\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"increment\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", 35 | } 36 | 37 | // IncrementerABI is the input ABI used to generate the binding from. 38 | // Deprecated: Use IncrementerMetaData.ABI instead. 39 | var IncrementerABI = IncrementerMetaData.ABI 40 | 41 | // Incrementer is an auto generated Go binding around an Ethereum contract. 42 | type Incrementer struct { 43 | IncrementerCaller // Read-only binding to the contract 44 | IncrementerTransactor // Write-only binding to the contract 45 | IncrementerFilterer // Log filterer for contract events 46 | } 47 | 48 | // IncrementerCaller is an auto generated read-only Go binding around an Ethereum contract. 49 | type IncrementerCaller struct { 50 | contract *bind.BoundContract // Generic contract wrapper for the low level calls 51 | } 52 | 53 | // IncrementerTransactor is an auto generated write-only Go binding around an Ethereum contract. 54 | type IncrementerTransactor struct { 55 | contract *bind.BoundContract // Generic contract wrapper for the low level calls 56 | } 57 | 58 | // IncrementerFilterer is an auto generated log filtering Go binding around an Ethereum contract events. 59 | type IncrementerFilterer struct { 60 | contract *bind.BoundContract // Generic contract wrapper for the low level calls 61 | } 62 | 63 | // IncrementerSession is an auto generated Go binding around an Ethereum contract, 64 | // with pre-set call and transact options. 65 | type IncrementerSession struct { 66 | Contract *Incrementer // Generic contract binding to set the session for 67 | CallOpts bind.CallOpts // Call options to use throughout this session 68 | TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session 69 | } 70 | 71 | // IncrementerCallerSession is an auto generated read-only Go binding around an Ethereum contract, 72 | // with pre-set call options. 73 | type IncrementerCallerSession struct { 74 | Contract *IncrementerCaller // Generic contract caller binding to set the session for 75 | CallOpts bind.CallOpts // Call options to use throughout this session 76 | } 77 | 78 | // IncrementerTransactorSession is an auto generated write-only Go binding around an Ethereum contract, 79 | // with pre-set transact options. 80 | type IncrementerTransactorSession struct { 81 | Contract *IncrementerTransactor // Generic contract transactor binding to set the session for 82 | TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session 83 | } 84 | 85 | // IncrementerRaw is an auto generated low-level Go binding around an Ethereum contract. 86 | type IncrementerRaw struct { 87 | Contract *Incrementer // Generic contract binding to access the raw methods on 88 | } 89 | 90 | // IncrementerCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. 91 | type IncrementerCallerRaw struct { 92 | Contract *IncrementerCaller // Generic read-only contract binding to access the raw methods on 93 | } 94 | 95 | // IncrementerTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. 96 | type IncrementerTransactorRaw struct { 97 | Contract *IncrementerTransactor // Generic write-only contract binding to access the raw methods on 98 | } 99 | 100 | // NewIncrementer creates a new instance of Incrementer, bound to a specific deployed contract. 101 | func NewIncrementer(address common.Address, backend bind.ContractBackend) (*Incrementer, error) { 102 | contract, err := bindIncrementer(address, backend, backend, backend) 103 | if err != nil { 104 | return nil, err 105 | } 106 | return &Incrementer{IncrementerCaller: IncrementerCaller{contract: contract}, IncrementerTransactor: IncrementerTransactor{contract: contract}, IncrementerFilterer: IncrementerFilterer{contract: contract}}, nil 107 | } 108 | 109 | // NewIncrementerCaller creates a new read-only instance of Incrementer, bound to a specific deployed contract. 110 | func NewIncrementerCaller(address common.Address, caller bind.ContractCaller) (*IncrementerCaller, error) { 111 | contract, err := bindIncrementer(address, caller, nil, nil) 112 | if err != nil { 113 | return nil, err 114 | } 115 | return &IncrementerCaller{contract: contract}, nil 116 | } 117 | 118 | // NewIncrementerTransactor creates a new write-only instance of Incrementer, bound to a specific deployed contract. 119 | func NewIncrementerTransactor(address common.Address, transactor bind.ContractTransactor) (*IncrementerTransactor, error) { 120 | contract, err := bindIncrementer(address, nil, transactor, nil) 121 | if err != nil { 122 | return nil, err 123 | } 124 | return &IncrementerTransactor{contract: contract}, nil 125 | } 126 | 127 | // NewIncrementerFilterer creates a new log filterer instance of Incrementer, bound to a specific deployed contract. 128 | func NewIncrementerFilterer(address common.Address, filterer bind.ContractFilterer) (*IncrementerFilterer, error) { 129 | contract, err := bindIncrementer(address, nil, nil, filterer) 130 | if err != nil { 131 | return nil, err 132 | } 133 | return &IncrementerFilterer{contract: contract}, nil 134 | } 135 | 136 | // bindIncrementer binds a generic wrapper to an already deployed contract. 137 | func bindIncrementer(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { 138 | parsed, err := IncrementerMetaData.GetAbi() 139 | if err != nil { 140 | return nil, err 141 | } 142 | return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil 143 | } 144 | 145 | // Call invokes the (constant) contract method with params as input values and 146 | // sets the output to result. The result type might be a single field for simple 147 | // returns, a slice of interfaces for anonymous returns and a struct for named 148 | // returns. 149 | func (_Incrementer *IncrementerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { 150 | return _Incrementer.Contract.IncrementerCaller.contract.Call(opts, result, method, params...) 151 | } 152 | 153 | // Transfer initiates a plain transaction to move funds to the contract, calling 154 | // its default method if one is available. 155 | func (_Incrementer *IncrementerRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { 156 | return _Incrementer.Contract.IncrementerTransactor.contract.Transfer(opts) 157 | } 158 | 159 | // Transact invokes the (paid) contract method with params as input values. 160 | func (_Incrementer *IncrementerRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { 161 | return _Incrementer.Contract.IncrementerTransactor.contract.Transact(opts, method, params...) 162 | } 163 | 164 | // Call invokes the (constant) contract method with params as input values and 165 | // sets the output to result. The result type might be a single field for simple 166 | // returns, a slice of interfaces for anonymous returns and a struct for named 167 | // returns. 168 | func (_Incrementer *IncrementerCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { 169 | return _Incrementer.Contract.contract.Call(opts, result, method, params...) 170 | } 171 | 172 | // Transfer initiates a plain transaction to move funds to the contract, calling 173 | // its default method if one is available. 174 | func (_Incrementer *IncrementerTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { 175 | return _Incrementer.Contract.contract.Transfer(opts) 176 | } 177 | 178 | // Transact invokes the (paid) contract method with params as input values. 179 | func (_Incrementer *IncrementerTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { 180 | return _Incrementer.Contract.contract.Transact(opts, method, params...) 181 | } 182 | 183 | // Get is a free data retrieval call binding the contract method 0x6d4ce63c. 184 | // 185 | // Solidity: function get() view returns(uint256) 186 | func (_Incrementer *IncrementerCaller) Get(opts *bind.CallOpts) (*big.Int, error) { 187 | var out []interface{} 188 | err := _Incrementer.contract.Call(opts, &out, "get") 189 | 190 | if err != nil { 191 | return *new(*big.Int), err 192 | } 193 | 194 | out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) 195 | 196 | return out0, err 197 | 198 | } 199 | 200 | // Get is a free data retrieval call binding the contract method 0x6d4ce63c. 201 | // 202 | // Solidity: function get() view returns(uint256) 203 | func (_Incrementer *IncrementerSession) Get() (*big.Int, error) { 204 | return _Incrementer.Contract.Get(&_Incrementer.CallOpts) 205 | } 206 | 207 | // Get is a free data retrieval call binding the contract method 0x6d4ce63c. 208 | // 209 | // Solidity: function get() view returns(uint256) 210 | func (_Incrementer *IncrementerCallerSession) Get() (*big.Int, error) { 211 | return _Incrementer.Contract.Get(&_Incrementer.CallOpts) 212 | } 213 | 214 | // Increment is a paid mutator transaction binding the contract method 0xd09de08a. 215 | // 216 | // Solidity: function increment() returns() 217 | func (_Incrementer *IncrementerTransactor) Increment(opts *bind.TransactOpts) (*types.Transaction, error) { 218 | return _Incrementer.contract.Transact(opts, "increment") 219 | } 220 | 221 | // Increment is a paid mutator transaction binding the contract method 0xd09de08a. 222 | // 223 | // Solidity: function increment() returns() 224 | func (_Incrementer *IncrementerSession) Increment() (*types.Transaction, error) { 225 | return _Incrementer.Contract.Increment(&_Incrementer.TransactOpts) 226 | } 227 | 228 | // Increment is a paid mutator transaction binding the contract method 0xd09de08a. 229 | // 230 | // Solidity: function increment() returns() 231 | func (_Incrementer *IncrementerTransactorSession) Increment() (*types.Transaction, error) { 232 | return _Incrementer.Contract.Increment(&_Incrementer.TransactOpts) 233 | } 234 | -------------------------------------------------------------------------------- /test/smart_account_utils_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "context" 5 | "github.com/stretchr/testify/assert" 6 | "github.com/zksync-sdk/zksync2-go/accounts" 7 | "github.com/zksync-sdk/zksync2-go/clients" 8 | "github.com/zksync-sdk/zksync2-go/types" 9 | "math/big" 10 | "testing" 11 | ) 12 | 13 | func TestIntegration_PopulateTransactionECDSA(t *testing.T) { 14 | client, err := clients.Dial(L2ChainURL) 15 | defer client.Close() 16 | assert.NoError(t, err, "clients.DialBase should not return an error") 17 | 18 | tx := types.Transaction{ 19 | To: &Address2, 20 | Value: big.NewInt(7_000_000_000), 21 | ChainID: big.NewInt(270), 22 | From: &Address1, 23 | } 24 | 25 | err = accounts.PopulateTransactionECDSA(context.Background(), &tx, PrivateKey1, client) 26 | assert.NoError(t, err, "PopulateTransactionECDSA should not return an error") 27 | 28 | assert.Equal(t, &Address2, tx.To, "To addresses must be the same") 29 | assert.Equal(t, &Address1, tx.From, "From addresses must be the same") 30 | assert.Equal(t, big.NewInt(7_000_000_000), tx.Value, "Values must be the same") 31 | assert.NotNil(t, tx.ChainID, "Chain IDs must not be nil") 32 | assert.NotNil(t, tx.Data, "Data must not be nil") 33 | assert.NotNil(t, tx.Gas, "Gas must not be nil") 34 | assert.NotNil(t, tx.GasFeeCap, "GasFeeCap must not be nil") 35 | assert.NotNil(t, tx.GasTipCap, "GasTipCap must not be nil") 36 | assert.NotNil(t, tx.GasPerPubdata, "GasPerPubdata must not be nil") 37 | } 38 | 39 | func TestIntegration_PopulateTransactionECDSA_ErrorNoClientProvided(t *testing.T) { 40 | tx := types.Transaction{ 41 | To: &Address2, 42 | Value: big.NewInt(7_000_000_000), 43 | ChainID: big.NewInt(270), 44 | From: &Address1, 45 | } 46 | 47 | err := accounts.PopulateTransactionECDSA(context.Background(), &tx, PrivateKey1, nil) 48 | assert.Error(t, err, "PopulateTransactionECDSA should return an error when client is not provided") 49 | } 50 | 51 | func TestIntegration_PopulateTransactionMultipleECDSA_ErrorNoMultipleKeysProvided(t *testing.T) { 52 | client, err := clients.Dial(L2ChainURL) 53 | defer client.Close() 54 | assert.NoError(t, err, "clients.DialBase should not return an error") 55 | 56 | err = accounts.PopulateTransactionMultipleECDSA(context.Background(), &types.Transaction{}, [1]string{PrivateKey1}, client) 57 | assert.Error(t, err, "PopulateTransactionMultipleECDSA should return an error when only one private key is provided") 58 | } 59 | -------------------------------------------------------------------------------- /test/testdata/Demo.zbin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zksync-sdk/zksync2-go/aa3a6ff6eeedfb4b745b8e4b7e9cc4e9d6713982/test/testdata/Demo.zbin -------------------------------------------------------------------------------- /test/testdata/Foo.zbin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zksync-sdk/zksync2-go/aa3a6ff6eeedfb4b745b8e4b7e9cc4e9d6713982/test/testdata/Foo.zbin -------------------------------------------------------------------------------- /test/testdata/Incrementer.zbin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zksync-sdk/zksync2-go/aa3a6ff6eeedfb4b745b8e4b7e9cc4e9d6713982/test/testdata/Incrementer.zbin -------------------------------------------------------------------------------- /test/testdata/Storage.zbin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zksync-sdk/zksync2-go/aa3a6ff6eeedfb4b745b8e4b7e9cc4e9d6713982/test/testdata/Storage.zbin -------------------------------------------------------------------------------- /test/testdata/tokens.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "DAI", 4 | "symbol": "DAI", 5 | "decimals": 18, 6 | "address": "0x70a0F165d6f8054d0d0CF8dFd4DD2005f0AF6B55" 7 | }, 8 | { 9 | "name": "wBTC", 10 | "symbol": "wBTC", 11 | "decimals": 8, 12 | "address": "0xAfb5167116e6B833889018916594aC8040DbC05F" 13 | }, 14 | { 15 | "name": "BAT", 16 | "symbol": "BAT", 17 | "decimals": 18, 18 | "address": "0x8E9C82509488eD471A83824d20Dd474b8F534a0b" 19 | }, 20 | { 21 | "name": "Wrapped Ether", 22 | "symbol": "WETH", 23 | "decimals": 18, 24 | "address": "0x54FCb2405EE4f574C4F09333d25c401E68aD3408" 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /types/block.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | "github.com/ethereum/go-ethereum/common/hexutil" 6 | "github.com/ethereum/go-ethereum/core/types" 7 | "math/big" 8 | "time" 9 | ) 10 | 11 | // Block represents a block. 12 | type Block struct { 13 | Header *types.Header 14 | Uncles []*types.Header 15 | Transactions []*TransactionResponse 16 | Withdrawals types.Withdrawals 17 | Hash common.Hash 18 | Size *big.Int 19 | TotalDifficulty *big.Int 20 | SealFields []interface{} 21 | ReceivedAt time.Time 22 | ReceivedFrom interface{} 23 | L1BatchNumber *big.Int // The batch number on L1. 24 | L1BatchTimestamp *big.Int // The timestamp of the batch on L1. 25 | } 26 | 27 | // BatchDetails contains batch information. 28 | type BatchDetails struct { 29 | // Hashes of the base system contracts involved in the batch. 30 | BaseSystemContractsHashes struct { 31 | Bootloader common.Hash `json:"bootloader"` 32 | DefaultAa common.Hash `json:"default_aa"` 33 | } `json:"baseSystemContractsHashes"` 34 | CommitTxHash *common.Hash `json:"commitTxHash"` // The transaction hash of the commit operation on L1. 35 | CommittedAt time.Time `json:"committedAt"` // The timestamp when the block was committed on L1. 36 | ExecuteTxHash *common.Hash `json:"executeTxHash"` // The transaction hash of the execution on L1. 37 | ExecutedAt time.Time `json:"executedAt"` // The timestamp when the block execution was completed on L1. 38 | L1GasPrice *big.Int `json:"l1GasPrice"` // L1 gas price at the time of the block's execution. 39 | L1TxCount uint64 `json:"l1TxCount"` // The number of L1 transactions included in the batch. 40 | L2FairGasPrice *big.Int `json:"l2FairGasPrice"` // Fair gas price on L2 at the time of the block's execution. 41 | L2TxCount uint64 `json:"l2TxCount"` // The number of L2 transactions associated with this batch. 42 | Number *big.Int `json:"number"` // L1 batch number. 43 | ProveTxHash *common.Hash `json:"proveTxHash"` // The transaction hash of the proof submission on L1. 44 | ProvenAt time.Time `json:"provenAt"` // The timestamp when the proof was submitted on L1. 45 | RootHash *common.Hash `json:"rootHash"` // Root hash of the state after processing the batch. 46 | Status string `json:"status"` // Current status of the batch (e.g., verified). 47 | Timestamp uint64 `json:"timestamp"` // Unix timestamp when the batch was processed. 48 | } 49 | 50 | // BlockDetails contains block details. 51 | type BlockDetails struct { 52 | Number *big.Int `json:"number"` // The number of the block. 53 | L1BatchNumber *big.Int `json:"l1BatchNumber"` // Corresponding L1 batch number. 54 | Timestamp uint64 `json:"timestamp"` // Unix timestamp when the block was committed. 55 | L1TxCount uint64 `json:"l1TxCount"` // The number of L1 transactions included in the block. 56 | L2TxCount uint64 `json:"l2TxCount"` // The number of L2 transactions included in the block. 57 | RootHash common.Hash `json:"rootHash"` // Root hash of the block's state after execution. 58 | Status string `json:"status"` // Current status of the block (e.g., verified, executed). 59 | CommitTxHash *common.Hash `json:"commitTxHash"` // The transaction hash of the commit operation on L1. 60 | CommittedAt time.Time `json:"committedAt"` // The timestamp when the block was committed on L1. 61 | ProveTxHash *common.Hash `json:"proveTxHash"` // The transaction hash of the proof submission on L1. 62 | ProvenAt time.Time `json:"provenAt"` // The timestamp when the proof was submitted on L1. 63 | ExecuteTxHash *common.Hash `json:"executeTxHash"` // The transaction hash of the execution on L1. 64 | ExecutedAt time.Time `json:"executedAt"` // The timestamp when the block execution was completed on L1. 65 | L1GasPrice *big.Int `json:"l1GasPrice"` // L1 gas price at the time of the block's execution. 66 | L2FairGasPrice *big.Int `json:"l2FairGasPrice"` // Fair gas price on L2 at the time of the block's execution. 67 | // A collection of hashes for the base system contracts involved in the block. 68 | BaseSystemContractsHashes struct { 69 | Bootloader common.Hash `json:"bootloader"` 70 | DefaultAa common.Hash `json:"default_aa"` 71 | } `json:"baseSystemContractsHashes"` 72 | OperatorAddress common.Address `json:"operatorAddress"` // Address of the operator who committed the block. 73 | ProtocolVersion string `json:"protocolVersion"` // Version of the ZKsync protocol the block was committed under. 74 | } 75 | 76 | // RawBlockTransaction represents a raw block transaction. 77 | type RawBlockTransaction struct { 78 | // General information about the L2 transaction. 79 | CommonData struct { 80 | L2 struct { 81 | Nonce uint64 `json:"nonce"` 82 | Fee struct { 83 | GasLimit hexutil.Big `json:"gas_limit"` 84 | MaxFeePerGas hexutil.Big `json:"max_fee_per_gas"` 85 | MaxPriorityFeePerGas hexutil.Big `json:"max_priority_fee_per_gas"` 86 | GasPerPubdataLimit hexutil.Big `json:"gas_per_pubdata_limit"` 87 | } `json:"fee"` 88 | InitiatorAddress common.Address `json:"initiatorAddress"` 89 | Signature []byte `json:"signature"` 90 | TransactionType string `json:"transactionType"` 91 | Input struct { 92 | Hash common.Hash `json:"hash"` 93 | Data []uint64 `json:"data"` 94 | } `json:"input"` 95 | PaymasterParams struct { 96 | Paymaster common.Address `json:"paymaster"` 97 | PaymasterInput []byte `json:"paymasterInput"` 98 | } `json:"paymasterParams"` 99 | } `json:"L2"` 100 | } `json:"common_data"` 101 | // Details regarding the execution of the transaction. 102 | Execute struct { 103 | ContractAddress common.Address `json:"contractAddress"` 104 | Calldata hexutil.Bytes `json:"calldata"` 105 | Value hexutil.Big `json:"value"` 106 | FactoryDeps []hexutil.Bytes `json:"factoryDeps"` 107 | } `json:"execute"` 108 | // Timestamp when the transaction was received, in milliseconds. 109 | ReceivedTimestampMs uint64 `json:"received_timestamp_ms"` 110 | // Raw bytes of the transaction as a hexadecimal string. 111 | RawBytes hexutil.Bytes `json:"raw_bytes"` 112 | } 113 | -------------------------------------------------------------------------------- /types/call_msg.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/ethereum/go-ethereum/common" 6 | "github.com/ethereum/go-ethereum/common/hexutil" 7 | "math/big" 8 | ) 9 | 10 | // CallMsg contains parameters for contract call using L2 transaction. 11 | type CallMsg struct { 12 | From common.Address // The sender of the 'transaction'. 13 | To *common.Address // The destination contract (nil for contract creation). 14 | Gas uint64 // If 0, the call executes with near-infinite gas. 15 | GasPrice *big.Int // Wei <-> gas exchange ratio 16 | GasFeeCap *big.Int // EIP-1559 fee cap per gas. 17 | GasTipCap *big.Int // EIP-1559 tip per gas. 18 | Value *big.Int // Amount of wei sent along with the call. 19 | Data []byte // Input data, usually an ABI-encoded contract method invocation. 20 | 21 | // GasPerPubdata denotes the maximum amount of gas the user is willing 22 | // to pay for a single byte of pubdata. 23 | GasPerPubdata *big.Int 24 | // CustomSignature is used for the cases in which the signer's account 25 | // is not an EOA. 26 | CustomSignature hexutil.Bytes 27 | // FactoryDeps is a non-empty array of bytes. For deployment transactions, 28 | // it should contain the bytecode of the contract being deployed. 29 | // If the contract is a factory contract, i.e. it can deploy other contracts, 30 | // the array should also contain the bytecodes of the contracts which it can deploy. 31 | FactoryDeps []hexutil.Bytes 32 | // PaymasterParams contains parameters for configuring the custom paymaster 33 | // for the transaction. 34 | PaymasterParams *PaymasterParams 35 | } 36 | 37 | func (m CallMsg) MarshalJSON() ([]byte, error) { 38 | arg := map[string]interface{}{ 39 | "type": "0x71", 40 | "from": m.From, 41 | "to": m.To, 42 | } 43 | if len(m.Data) > 0 { 44 | arg["data"] = hexutil.Bytes(m.Data) 45 | } 46 | if m.Value != nil { 47 | arg["value"] = (*hexutil.Big)(m.Value) 48 | } 49 | if m.Gas != 0 { 50 | arg["gas"] = hexutil.Uint64(m.Gas) 51 | } 52 | if m.GasPrice != nil { 53 | arg["gasPrice"] = (*hexutil.Big)(m.GasPrice) 54 | } 55 | if m.GasTipCap != nil { 56 | arg["maxPriorityFeePerGas"] = (*hexutil.Big)(m.GasTipCap) 57 | } 58 | if m.GasFeeCap != nil { 59 | arg["maxFeePerGas"] = (*hexutil.Big)(m.GasFeeCap) 60 | } 61 | 62 | eip712Meta := map[string]interface{}{} 63 | 64 | if m.GasPerPubdata != nil { 65 | eip712Meta["gasPerPubdata"] = (*hexutil.Big)(m.GasPerPubdata) 66 | } 67 | if len(m.CustomSignature) > 0 { 68 | eip712Meta["customSignature"] = m.CustomSignature 69 | } 70 | if len(m.FactoryDeps) > 0 { 71 | // Convert FactoryDeps into [][]uint format 72 | fdb := make([][]uint, len(m.FactoryDeps)) 73 | for i, v := range m.FactoryDeps { 74 | fdb[i] = make([]uint, len(v)) 75 | for j, b := range v { 76 | fdb[i][j] = uint(b) 77 | } 78 | } 79 | eip712Meta["factoryDeps"] = fdb 80 | } 81 | if m.PaymasterParams != nil { 82 | eip712Meta["paymasterParams"] = m.PaymasterParams 83 | } 84 | 85 | // Add eip712Meta to the main argument map 86 | arg["eip712Meta"] = eip712Meta 87 | return json.Marshal(arg) 88 | } 89 | -------------------------------------------------------------------------------- /types/config.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // StandardConfiguration presents the standard-json 4 | // configuration generated as output of zksolc compiler. 5 | type StandardConfiguration struct { 6 | Format string `json:"_format"` 7 | ContractName string `json:"contractName"` 8 | SourceName string `json:"sourceName"` 9 | Abi []struct { 10 | Inputs []struct { 11 | InternalType string `json:"internalType"` 12 | Name string `json:"name"` 13 | Type string `json:"type"` 14 | Indexed bool `json:"indexed,omitempty"` 15 | } `json:"inputs"` 16 | StateMutability string `json:"stateMutability,omitempty"` 17 | Type string `json:"type"` 18 | Anonymous bool `json:"anonymous,omitempty"` 19 | Name string `json:"name,omitempty"` 20 | Outputs []struct { 21 | InternalType string `json:"internalType"` 22 | Name string `json:"name"` 23 | Type string `json:"type"` 24 | } `json:"outputs,omitempty"` 25 | } `json:"abi"` 26 | Bytecode string `json:"bytecode"` 27 | DeployedBytecode string `json:"deployedBytecode"` 28 | LinkReferences struct { 29 | } `json:"linkReferences"` 30 | DeployedLinkReferences struct { 31 | } `json:"deployedLinkReferences"` 32 | FactoryDeps struct { 33 | } `json:"factoryDeps"` 34 | } 35 | -------------------------------------------------------------------------------- /types/contract.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | "github.com/zksync-sdk/zksync2-go/contracts/l1bridge" 6 | "github.com/zksync-sdk/zksync2-go/contracts/l1nativetokenvault" 7 | "github.com/zksync-sdk/zksync2-go/contracts/l1nullifier" 8 | "github.com/zksync-sdk/zksync2-go/contracts/l1sharedbridge" 9 | "github.com/zksync-sdk/zksync2-go/contracts/l2bridge" 10 | "github.com/zksync-sdk/zksync2-go/contracts/l2sharedbridge" 11 | ) 12 | 13 | // BridgeContracts represents the addresses of default bridge contracts for both L1 and L2. 14 | type BridgeContracts struct { 15 | L1Erc20Bridge common.Address `json:"l1Erc20DefaultBridge"` // Default L1Bridge contract address. 16 | L2Erc20Bridge common.Address `json:"l2Erc20DefaultBridge"` // Default L2Bridge contract address. 17 | L1WethBridge common.Address `json:"l1WethBridge"` // WETH L1Bridge contract address. 18 | L2WethBridge common.Address `json:"l2WethBridge"` // WETH L2Bridge contract address. 19 | L1SharedBridge common.Address `json:"l1SharedDefaultBridge"` // Default L1SharedBridge contract address. 20 | L2SharedBridge common.Address `json:"l2SharedDefaultBridge"` // Default L2SharedBridge contract address. 21 | L1Nullifier common.Address `json:"l1Nullifier"` // L1Nullifier contracts address. 22 | L1NativeTokenVault common.Address `json:"l1NativeTokenVault"` // L1 native token vault contract address. 23 | } 24 | 25 | // L1BridgeContracts represents the L1 bridge contracts. 26 | type L1BridgeContracts struct { 27 | Erc20 *l1bridge.IL1Bridge // Default L1Bridge contract. 28 | Shared *l1sharedbridge.IL1SharedBridge // L1SharedBridge contract. 29 | Nullifier *l1nullifier.IL1Nullifier // L1Nullifier contract. 30 | NativeTokenVault *l1nativetokenvault.IL1NativeTokenVault // L1NativeTokenVault contract 31 | } 32 | 33 | // L2BridgeContracts represents the L2 bridge contracts. 34 | type L2BridgeContracts struct { 35 | Erc20 *l2bridge.IL2Bridge // Default L2Bridge contract. 36 | Shared *l2sharedbridge.IL2SharedBridge // Shared L2Bridge contract. 37 | } 38 | 39 | // AccountAbstractionVersion represents an enumeration of account abstraction versions. 40 | type AccountAbstractionVersion uint8 41 | 42 | const ( 43 | None AccountAbstractionVersion = iota // Used for contracts that are not accounts. 44 | Version1 // Used for contracts that are accounts. 45 | ) 46 | 47 | // AccountNonceOrdering represents an enumeration of account nonce ordering formats. 48 | type AccountNonceOrdering uint8 49 | 50 | const ( 51 | // Sequential nonces should be ordered in the same way as in externally owned accounts (EOAs). 52 | // This means, for instance, that the operator will always wait for a transaction with nonce `X` 53 | // before processing a transaction with nonce `X+1`. 54 | Sequential AccountNonceOrdering = iota 55 | Arbitrary // Nonces can be ordered in arbitrary order. 56 | ) 57 | 58 | // ContractAccountInfo represent contract account information containing details on the supported account 59 | // abstraction version and nonce ordering format. 60 | type ContractAccountInfo struct { 61 | SupportedAAVersion AccountAbstractionVersion // The supported account abstraction version. 62 | NonceOrdering AccountNonceOrdering // The nonce ordering format. 63 | } 64 | -------------------------------------------------------------------------------- /types/fee.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common/hexutil" 5 | "math/big" 6 | ) 7 | 8 | // Fee represents the transaction fee parameters. 9 | type Fee struct { 10 | GasLimit *hexutil.Big `json:"gas_limit"` // Maximum amount of gas allowed for the transaction. 11 | // Maximum amount of gas the user is willing to pay for a single byte of pubdata. 12 | GasPerPubdataLimit *hexutil.Big `json:"gas_per_pubdata_limit"` 13 | MaxFeePerGas *hexutil.Big `json:"max_fee_per_gas"` // EIP-1559 fee cap per gas. 14 | MaxPriorityFeePerGas *hexutil.Big `json:"max_priority_fee_per_gas"` // EIP-1559 tip per gas. 15 | } 16 | 17 | // FeeParams represents the fee parameters configuration. 18 | type FeeParams struct { 19 | // Fee parameter configuration for the current version of the ZKsync protocol. 20 | V2 struct { 21 | // Settings related to transaction fee computation. 22 | Config struct { 23 | MinimalL2GasPrice *big.Int `json:"minimal_l2_gas_price"` // Minimal gas price on L2. 24 | ComputeOverheadPart float32 `json:"compute_overhead_part"` // Compute overhead part in fee calculation. 25 | PubdataOverheadPart float32 `json:"pubdata_overhead_part"` // Public data overhead part in fee calculation. 26 | BatchOverheadL1Gas *big.Int `json:"batch_overhead_l1_gas"` // Overhead in L1 gas for a batch of transactions. 27 | MaxGasPerBatch *big.Int `json:"max_gas_per_batch"` // Maximum gas allowed per batch. 28 | MaxPubdataPerBatch *big.Int `json:"max_pubdata_per_batch"` // Maximum amount of public data allowed per batch. 29 | } `json:"config"` 30 | // Represents the BaseToken<->ETH conversion ratio. 31 | ConversionRation struct { 32 | Denominator *big.Int `json:"denominator"` // Represents the denominator part of the conversion ratio. 33 | Numerator *big.Int `json:"numerator"` // Represents the numerator part of the conversion ratio. 34 | } `json:"conversion_ratio"` 35 | L1GasPrice *big.Int `json:"l1_gas_price"` // Current L1 gas price. 36 | L1PubdataPrice *big.Int `json:"l1_pubdata_price"` // Price of storing public data on L1. 37 | } `json:"V2"` 38 | } 39 | -------------------------------------------------------------------------------- /types/log.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/ethereum/go-ethereum/common" 6 | "github.com/ethereum/go-ethereum/common/hexutil" 7 | "github.com/ethereum/go-ethereum/core/types" 8 | ) 9 | 10 | // Log represents a log entry. 11 | type Log struct { 12 | types.Log 13 | L1BatchNumber *hexutil.Big `json:"l1BatchNumber"` // The batch number on L1. 14 | } 15 | 16 | func (l *Log) MarshalJSON() ([]byte, error) { 17 | // get json of embedded types.Log with its custom marshaller 18 | lj, err := json.Marshal(l.Log) 19 | if err != nil { 20 | return nil, err 21 | } 22 | // decode back to abstract struct 23 | var buf map[string]interface{} 24 | if err := json.Unmarshal(lj, &buf); err != nil { 25 | return nil, err 26 | } 27 | // mixin our fields 28 | buf["l1BatchNumber"] = l.L1BatchNumber 29 | // encode to json again all together 30 | return json.Marshal(&buf) 31 | } 32 | 33 | func (l *Log) UnmarshalJSON(input []byte) error { 34 | if err := l.Log.UnmarshalJSON(input); err != nil { 35 | return err 36 | } 37 | type Log struct { 38 | L1BatchNumber *hexutil.Big `json:"l1BatchNumber"` 39 | } 40 | var dec Log 41 | if err := json.Unmarshal(input, &dec); err != nil { 42 | return err 43 | } 44 | l.L1BatchNumber = dec.L1BatchNumber 45 | return nil 46 | } 47 | 48 | // L2ToL1Log represents a layer 2 to layer 1 transaction log. 49 | type L2ToL1Log struct { 50 | BlockNumber *hexutil.Big `json:"blockNumber"` // The block number. 51 | BlockHash common.Hash `json:"blockHash"` // The block hash. 52 | L1BatchNumber *hexutil.Big `json:"l1BatchNumber"` // The batch number on L1. 53 | TransactionIndex *hexutil.Uint `json:"transactionIndex"` // The L2 transaction number in a block, in which the log was sent. 54 | TransactionLogIndex *hexutil.Uint `json:"transactionLogIndex"` // The transaction log index. 55 | TxIndexInL1Batch *hexutil.Uint `json:"txIndexInL1Batch"` // The transaction index in L1 batch. 56 | ShardId *hexutil.Uint `json:"shardId"` // The shard identifier, 0 - rollup, 1 - porter. 57 | // A boolean flag that is part of the log along with `key`, `value`, and `sender` address. 58 | // This field is required formally but does not have any special meaning. 59 | IsService bool `json:"isService"` 60 | Sender common.Address `json:"sender"` // The L2 address which sent the log. 61 | Key string `json:"key"` // The 32 bytes of information that was sent in the log. 62 | Value string `json:"value"` // The 32 bytes of information that was sent in the log. 63 | TxHash common.Hash `json:"transactionHash"` // The transaction hash. 64 | Index *hexutil.Uint `json:"logIndex"` // The log index. 65 | } 66 | -------------------------------------------------------------------------------- /types/network.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/ethereum/go-ethereum/common" 6 | "github.com/ethereum/go-ethereum/common/hexutil" 7 | "github.com/ethereum/go-ethereum/core/types" 8 | "time" 9 | ) 10 | 11 | // Receipt represents the results of a transaction. 12 | type Receipt struct { 13 | types.Receipt 14 | 15 | From common.Address `json:"from"` 16 | To common.Address `json:"to"` 17 | EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice"` 18 | L1BatchNumber *hexutil.Big `json:"l1BatchNumber"` // The batch number on the L1 network. 19 | L1BatchTxIndex *hexutil.Big `json:"l1BatchTxIndex"` // The transaction index within the batch on the L1 network. 20 | Logs []*Log `json:"logs"` 21 | L2ToL1Logs []*L2ToL1Log `json:"l2ToL1Logs"` // The logs of L2 to L1 messages. 22 | } 23 | 24 | func (r *Receipt) MarshalJSON() ([]byte, error) { 25 | // get json of embedded types.Receipt with its custom marshaller 26 | rj, err := json.Marshal(r.Receipt) 27 | if err != nil { 28 | return nil, err 29 | } 30 | // decode back to abstract struct 31 | var buf map[string]interface{} 32 | if err := json.Unmarshal(rj, &buf); err != nil { 33 | return nil, err 34 | } 35 | // mixin our fields 36 | buf["from"] = r.From 37 | buf["to"] = r.To 38 | buf["effectiveGasPrice"] = r.EffectiveGasPrice 39 | buf["l1BatchNumber"] = r.L1BatchNumber 40 | buf["l1BatchTxIndex"] = r.L1BatchTxIndex 41 | buf["logs"] = r.Logs 42 | buf["l2ToL1Logs"] = r.L2ToL1Logs 43 | // encode to json again all together 44 | return json.Marshal(&buf) 45 | } 46 | 47 | func (r *Receipt) UnmarshalJSON(input []byte) error { 48 | if err := r.Receipt.UnmarshalJSON(input); err != nil { 49 | return err 50 | } 51 | type TransactionReceipt struct { 52 | From common.Address `json:"from"` 53 | To common.Address `json:"to"` 54 | EffectiveGasPrice *hexutil.Big `json:"effectiveGasPrice"` 55 | L1BatchNumber *hexutil.Big `json:"l1BatchNumber"` 56 | L1BatchTxIndex *hexutil.Big `json:"l1BatchTxIndex"` 57 | Logs []*Log `json:"logs"` 58 | L2ToL1Logs []*L2ToL1Log `json:"l2ToL1Logs"` 59 | } 60 | var dec TransactionReceipt 61 | if err := json.Unmarshal(input, &dec); err != nil { 62 | return err 63 | } 64 | r.From = dec.From 65 | r.To = dec.To 66 | r.EffectiveGasPrice = dec.EffectiveGasPrice 67 | r.L1BatchNumber = dec.L1BatchNumber 68 | r.L1BatchTxIndex = dec.L1BatchTxIndex 69 | r.Logs = dec.Logs 70 | r.L2ToL1Logs = dec.L2ToL1Logs 71 | return nil 72 | } 73 | 74 | // TransactionResponse includes all properties of a transaction 75 | // as well as several properties that are useful once it has been mined. 76 | type TransactionResponse struct { 77 | BlockHash *common.Hash `json:"blockHash"` 78 | BlockNumber *hexutil.Big `json:"blockNumber"` 79 | ChainID hexutil.Big `json:"chainId"` 80 | From common.Address `json:"from"` 81 | Gas hexutil.Uint64 `json:"gas"` 82 | GasPrice hexutil.Big `json:"gasPrice"` 83 | Hash common.Hash `json:"hash"` 84 | Data hexutil.Bytes `json:"input"` 85 | L1BatchNumber hexutil.Big `json:"l1BatchNumber"` // The batch number on the L1 network. 86 | L1BatchTxIndex hexutil.Big `json:"l1BatchTxIndex"` // The transaction index within the batch on the L1 network. 87 | MaxFeePerGas hexutil.Big `json:"maxFeePerGas"` 88 | MaxPriorityFeePerGas hexutil.Big `json:"maxPriorityFeePerGas"` 89 | Nonce hexutil.Uint64 `json:"nonce"` 90 | V *hexutil.Big `json:"v"` 91 | R *hexutil.Big `json:"r"` 92 | S *hexutil.Big `json:"s"` 93 | To common.Address `json:"to"` 94 | TransactionIndex hexutil.Uint `json:"transactionIndex"` 95 | Type hexutil.Uint64 `json:"type"` 96 | Value hexutil.Big `json:"value"` 97 | } 98 | 99 | // TransactionDetails contains transaction details. 100 | type TransactionDetails struct { 101 | EthCommitTxHash *common.Hash `json:"ethCommitTxHash"` // The transaction hash of the commit operation. 102 | EthExecuteTxHash *common.Hash `json:"ethExecuteTxHash"` // The transaction hash of the execution. 103 | EthProveTxHash *common.Hash `json:"ethProveTxHash"` // The transaction hash of the proof submission. 104 | Fee hexutil.Big `json:"fee"` // The transaction fee. 105 | GasPerPubdata hexutil.Big `json:"gasPerPubdata"` // Gas amount per unit of public data for this transaction. 106 | InitiatorAddress common.Address `json:"initiatorAddress"` // Address of the transaction initiator. 107 | IsL1Originated bool `json:"isL1Originated"` // Indicates whether the transaction originated on Layer 1. 108 | ReceivedAt time.Time `json:"receivedAt"` // Timestamp when the transaction was received. 109 | Status string `json:"status"` // Current status of the transaction (e.g., verified). 110 | } 111 | 112 | // ProtocolVersion represents the protocol version. 113 | type ProtocolVersion struct { 114 | VersionId uint8 `json:"version_id"` // Protocol version ID. 115 | Timestamp uint64 `json:"timestamp"` // Unix timestamp of the version's activation. 116 | // Contains the hashes of various verification keys used in the protocol. 117 | VerificationKeysHashes struct { 118 | Params struct { 119 | RecursionNodeLevelVkHash common.Hash `json:"recursion_node_level_vk_hash"` 120 | RecursionLeafLevelVkHash common.Hash `json:"recursion_leaf_level_vk_hash"` 121 | RecursionCircuitsSetVksHash common.Hash `json:"recursion_circuits_set_vks_hash"` 122 | } `json:"params"` 123 | RecursionSchedulerLevelVkHash common.Hash `json:"recursion_scheduler_level_vk_hash"` 124 | } `json:"verification_keys_hashes"` 125 | // Hashes of the base system contracts. 126 | BaseSystemContracts struct { 127 | Bootloader common.Hash `json:"bootloader"` 128 | DefaultAa common.Hash `json:"default_aa"` 129 | } `json:"base_system_contracts"` 130 | // Hash of the transaction used for the system upgrade, if any. 131 | L2SystemUpgradeTxHash *common.Hash `json:"l2_system_upgrade_tx_hash"` 132 | } 133 | 134 | // TransactionWithDetailedOutput represents the transaction with detailed output. 135 | type TransactionWithDetailedOutput struct { 136 | TransactionHash common.Hash `json:"transactionHash"` // The transaction hash. 137 | // Storage slots. 138 | StorageLogs []struct { 139 | Address common.Address `json:"address"` 140 | Key string `json:"key"` 141 | WrittenValue string `json:"writtenValue"` 142 | } `json:"storageLogs"` 143 | // Generated events. 144 | Events []struct { 145 | Address common.Address `json:"address"` 146 | Topics []common.Hash `json:"topics"` 147 | Data hexutil.Bytes `json:"data"` 148 | BlockHash *common.Hash `json:"blockHash"` 149 | BlockNumber *hexutil.Big `json:"blockNumber"` 150 | L1BatchNumber *hexutil.Big `json:"l1BatchNumber"` 151 | TransactionHash common.Hash `json:"transactionHash"` 152 | TransactionIndex hexutil.Uint `json:"transactionIndex"` 153 | LogIndex *hexutil.Uint `json:"logIndex"` 154 | TransactionLogIndex *hexutil.Uint `json:"transactionLogIndex"` 155 | LogType *hexutil.Bytes `json:"logType"` 156 | Removed bool `json:"removed"` 157 | } `json:"events"` 158 | } 159 | -------------------------------------------------------------------------------- /types/paymaster.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | "math/big" 6 | ) 7 | 8 | // PaymasterInput is an interface that represents input data for a paymaster. 9 | // Paymasters can implement this interface to provide specific data required 10 | // for payment processing. 11 | type PaymasterInput interface { 12 | // GetType returns the type of the paymaster input. It should provide a 13 | // unique identifier for the type of input data. 14 | GetType() string 15 | // GetInput returns the actual input data in the form of a byte slice. The 16 | // data format may vary depending on the type and implementation of the 17 | // paymaster. 18 | GetInput() []byte 19 | } 20 | 21 | // ApprovalBasedPaymasterInput contains approval-based paymaster input. 22 | // It should be used if the user is required to set a certain allowance to a token for the paymaster to operate. 23 | type ApprovalBasedPaymasterInput struct { 24 | Token common.Address // ERC20 token used to pay transaction fee. 25 | // Minimal allowance of Token towards the paymaster from the account that pays the fee with the token. 26 | MinimalAllowance *big.Int 27 | InnerInput []byte // Additional payload that can be sent to the paymaster to implement any logic. 28 | } 29 | 30 | func (a *ApprovalBasedPaymasterInput) GetType() string { 31 | return "ApprovalBased" 32 | } 33 | 34 | func (a *ApprovalBasedPaymasterInput) GetInput() []byte { 35 | return a.InnerInput 36 | } 37 | 38 | // GeneralPaymasterInput contains general paymaster input. 39 | // It should be used if no prior actions are required from the user for the paymaster to operate. 40 | type GeneralPaymasterInput []byte 41 | 42 | func (g *GeneralPaymasterInput) GetType() string { 43 | return "General" 44 | } 45 | 46 | func (g *GeneralPaymasterInput) GetInput() []byte { 47 | return *g 48 | } 49 | -------------------------------------------------------------------------------- /types/proof.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | "math/big" 6 | ) 7 | 8 | // LogProof represents a log proof for an L2 to L1 transaction. 9 | type LogProof struct { 10 | Id int32 `json:"id"` // Identifier of the log within the transaction. 11 | Proof []common.Hash `json:"proof"` // Each element represents a piece of the proof for the specified log. 12 | Root common.Hash `json:"root"` // Root hash of the proof, anchoring it to a specific state in the blockchain. 13 | } 14 | 15 | // MessageProof represents a log proof for an L2 to L1 transaction. 16 | type MessageProof struct { 17 | Id int32 `json:"id"` // Identifier of the log within the transaction. 18 | Proof []common.Hash `json:"proof"` // Each element represents a piece of the proof for the specified log. 19 | Root common.Hash `json:"root"` // Root hash of the proof, anchoring it to a specific state in the blockchain. 20 | } 21 | 22 | // StorageProof Merkle proofs for one or more storage values at the specified account. 23 | type StorageProof struct { 24 | Address string `json:"address"` // Account address associated with the storage proofs. 25 | // Array of objects, each representing a storage proof for the requested keys. 26 | Proofs []struct { 27 | Key string `json:"key"` // Storage key for which the proof is provided. 28 | // An array of 32-byte hashes that constitute the Merkle path from the leaf node 29 | // (representing the storage key-value pair) to the root of the Merkle tree. 30 | Proof []string `json:"proof"` 31 | Value string `json:"value"` // Value stored in the specified storage key at the time of the specified l1BatchNumber. 32 | // A 1-based index representing the position of the tree entry within the Merkle tree. 33 | // This index is used to help reconstruct the Merkle path during verification. 34 | Index int32 `json:"index"` 35 | } `json:"storageProof"` 36 | } 37 | 38 | // PriorityOpConfirmation represents confirmation data that is part of L2->L1 message 39 | type PriorityOpConfirmation struct { 40 | L1BatchNumber *big.Int 41 | L2MessageIndex int32 42 | L2TxNumberInBlock *big.Int 43 | Proof []common.Hash 44 | } 45 | -------------------------------------------------------------------------------- /types/token.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "bytes" 5 | "github.com/ethereum/go-ethereum/common" 6 | ) 7 | 8 | // Token represents a token with addresses on both L1 and L2 chains. 9 | type Token struct { 10 | L1Address common.Address `json:"l1Address"` // Token address on L1. 11 | L2Address common.Address `json:"l2Address"` // Token address on L2. 12 | Name string `json:"name"` // Token name. 13 | Symbol string `json:"symbol"` // Token symbol. 14 | Decimals uint8 `json:"decimals"` // Number of decimals for the token. 15 | } 16 | 17 | // IsETH checks weather the token is ETH. 18 | func (t *Token) IsETH() bool { 19 | return bytes.Equal(t.L2Address.Bytes(), common.Address{}.Bytes()) && t.Symbol == "ETH" 20 | } 21 | -------------------------------------------------------------------------------- /utils/bridge.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "context" 5 | "encoding/binary" 6 | "fmt" 7 | "github.com/ethereum/go-ethereum/accounts/abi" 8 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 9 | "github.com/ethereum/go-ethereum/common" 10 | "github.com/ethereum/go-ethereum/crypto" 11 | "github.com/zksync-sdk/zksync2-go/contracts/erc20" 12 | "github.com/zksync-sdk/zksync2-go/contracts/l1nativetokenvault" 13 | "github.com/zksync-sdk/zksync2-go/contracts/l2bridge" 14 | "github.com/zksync-sdk/zksync2-go/types" 15 | "math/big" 16 | ) 17 | 18 | // CreateETH creates ETH token with appropriate Name, Symbol and Decimals values. 19 | func CreateETH() *types.Token { 20 | return &types.Token{ 21 | L1Address: common.Address{}, 22 | L2Address: common.Address{}, 23 | Name: `ETH`, 24 | Symbol: `ETH`, 25 | Decimals: 18, 26 | } 27 | } 28 | 29 | // Erc20DefaultBridgeData Returns the data needed for correct initialization of an L1 token counterpart on L2. 30 | func Erc20DefaultBridgeData(l1TokenAddress common.Address, backend bind.ContractBackend) ([]byte, error) { 31 | var ( 32 | name = "Ether" 33 | symbol = "ETH" 34 | decimals = uint8(18) 35 | ) 36 | 37 | if l1TokenAddress != EthAddressInContracts { 38 | token, err := erc20.NewIERC20(l1TokenAddress, backend) 39 | if err != nil { 40 | return nil, fmt.Errorf("failed to load IERC20: %w", err) 41 | } 42 | name, err = token.Name(nil) 43 | if err != nil { 44 | return nil, err 45 | } 46 | symbol, err = token.Symbol(nil) 47 | if err != nil { 48 | return nil, err 49 | } 50 | decimals, err = token.Decimals(nil) 51 | if err != nil { 52 | return nil, err 53 | } 54 | } 55 | 56 | stringAbiType, err := abi.NewType("string", "", nil) 57 | if err != nil { 58 | return nil, err 59 | } 60 | uint256AbiType, err := abi.NewType("uint256", "", nil) 61 | if err != nil { 62 | return nil, err 63 | } 64 | bytesAbiType, err := abi.NewType("bytes", "", nil) 65 | if err != nil { 66 | return nil, err 67 | } 68 | 69 | nameEncoded, err := abi.Arguments{{Type: stringAbiType}}.Pack(name) 70 | if err != nil { 71 | return nil, err 72 | } 73 | symbolEncoded, err := abi.Arguments{{Type: stringAbiType}}.Pack(symbol) 74 | if err != nil { 75 | return nil, err 76 | } 77 | decimalsEncoded, err := abi.Arguments{{Type: uint256AbiType}}.Pack(big.NewInt(int64(decimals))) 78 | if err != nil { 79 | return nil, err 80 | } 81 | 82 | return abi.Arguments{ 83 | {Type: bytesAbiType}, 84 | {Type: bytesAbiType}, 85 | {Type: bytesAbiType}, 86 | }.Pack(nameEncoded, symbolEncoded, decimalsEncoded) 87 | } 88 | 89 | // Erc20BridgeCalldata returns the calldata that will be sent by an L1 ERC20 bridge to its L2 counterpart 90 | // during bridging of a token. 91 | func Erc20BridgeCalldata(l1TokenAddress, l1Sender, l2Receiver common.Address, amount *big.Int, bridgeData []byte) ([]byte, error) { 92 | l2BridgeAbi, err := l2bridge.IL2BridgeMetaData.GetAbi() 93 | if err != nil { 94 | return nil, fmt.Errorf("failed to load L2 bridge ABI: %w", err) 95 | } 96 | return l2BridgeAbi.Pack("finalizeDeposit", l1Sender, l2Receiver, l1TokenAddress, amount, bridgeData) 97 | } 98 | 99 | // HashedL2ToL1Msg returns a `keccak` encoded message with a given sender address and 100 | // block number from the L1 messenger contract. 101 | func HashedL2ToL1Msg(sender common.Address, msg []byte, txNumberInBlock uint16) common.Hash { 102 | txNumberInBlockBytes := make([]byte, 2) 103 | binary.BigEndian.PutUint16(txNumberInBlockBytes, txNumberInBlock) 104 | 105 | encodedMsg := append([]byte{0, 1}, txNumberInBlockBytes...) 106 | encodedMsg = append(encodedMsg, L1MessengerAddress.Bytes()...) 107 | encodedMsg = append(encodedMsg, common.LeftPadBytes(sender.Bytes(), 32)...) 108 | encodedMsg = append(encodedMsg, crypto.Keccak256(msg)...) 109 | 110 | return crypto.Keccak256Hash(encodedMsg) 111 | } 112 | 113 | // NativeTokenVaultAssetId returns the assetId for a token in the Native Token Vault with specific 114 | // origin chainId and address. 115 | func NativeTokenVaultAssetId(chainId *big.Int, address common.Address) (common.Hash, error) { 116 | uint256AbiType, err := abi.NewType("uint256", "", nil) 117 | if err != nil { 118 | return common.Hash{}, err 119 | } 120 | addressAbiType, err := abi.NewType("address", "", nil) 121 | if err != nil { 122 | return common.Hash{}, err 123 | } 124 | 125 | chainIdEncoded, err := abi.Arguments{{Type: uint256AbiType}}.Pack(chainId) 126 | if err != nil { 127 | return common.Hash{}, err 128 | } 129 | l2NativeTokenVaultAddressEncoded, err := abi.Arguments{{Type: addressAbiType}}.Pack(L2NativeTokenVaultAddress) 130 | if err != nil { 131 | return common.Hash{}, err 132 | } 133 | addressEncoded, err := abi.Arguments{{Type: addressAbiType}}.Pack(address) 134 | if err != nil { 135 | return common.Hash{}, err 136 | } 137 | 138 | encodedMsg := append(chainIdEncoded, l2NativeTokenVaultAddressEncoded...) 139 | encodedMsg = append(encodedMsg, addressEncoded...) 140 | return crypto.Keccak256Hash(encodedMsg), nil 141 | } 142 | 143 | // NativeTokenVaultTransferData encodes the data for a transfer of a token through the Native Token Vault. 144 | func NativeTokenVaultTransferData(amount *big.Int, receiver, token common.Address) ([]byte, error) { 145 | uint256AbiType, err := abi.NewType("uint256", "", nil) 146 | if err != nil { 147 | return nil, err 148 | } 149 | addressAbiType, err := abi.NewType("address", "", nil) 150 | if err != nil { 151 | return nil, err 152 | } 153 | 154 | amountEncoded, err := abi.Arguments{{Type: uint256AbiType}}.Pack(amount) 155 | if err != nil { 156 | return nil, err 157 | } 158 | receiverEncoded, err := abi.Arguments{{Type: addressAbiType}}.Pack(receiver) 159 | if err != nil { 160 | return nil, err 161 | } 162 | tokenEncoded, err := abi.Arguments{{Type: addressAbiType}}.Pack(token) 163 | if err != nil { 164 | return nil, err 165 | } 166 | 167 | encodedMsg := append(amountEncoded, receiverEncoded...) 168 | encodedMsg = append(encodedMsg, tokenEncoded...) 169 | return encodedMsg, nil 170 | } 171 | 172 | // SecondBridgeDataV1 encodes asset transfer data for BridgeHub contract, using v1 encoding scheme (introduced in v26 upgrade). 173 | // Can be utilized to encode deposit initiation data. 174 | func SecondBridgeDataV1(assetId [32]byte, transferData []byte) ([]byte, error) { 175 | bytes32AbiType, err := abi.NewType("bytes32", "", nil) 176 | if err != nil { 177 | return nil, err 178 | } 179 | bytesAbiType, err := abi.NewType("bytes", "", nil) 180 | if err != nil { 181 | return nil, err 182 | } 183 | 184 | data, err := abi.Arguments{ 185 | {Type: bytes32AbiType}, 186 | {Type: bytesAbiType}, 187 | }.Pack(assetId, transferData) 188 | if err != nil { 189 | return nil, err 190 | } 191 | encodedMsg := append(common.Hex2Bytes("01"), data...) 192 | return encodedMsg, nil 193 | } 194 | 195 | // ResolveAssetId resolves the assetId for a token. 196 | func ResolveAssetId(ctx context.Context, vault *l1nativetokenvault.IL1NativeTokenVault, token common.Address, chainID *big.Int) ([32]byte, error) { 197 | if token == LegacyEthAddress { 198 | token = EthAddressInContracts 199 | } 200 | 201 | // In case only token is provided, we expect that it is a token inside Native Token Vault 202 | assetIdFromNTV, err := vault.AssetId(&bind.CallOpts{Context: ctx}, token) 203 | if err != nil { 204 | return [32]byte{}, err 205 | } 206 | 207 | if assetIdFromNTV != (common.Hash{}) { 208 | return assetIdFromNTV, nil 209 | } 210 | 211 | // Okay, the token have not been registered within the Native token vault. 212 | // There are two cases when it is possible: 213 | // - The token is native to L1 (it may or may not be bridged), but it has not been 214 | // registered within NTV after the Gateway upgrade. We assume that this is not the case 215 | // as the SDK is expected to work only after the full migration is done. 216 | // - The token is native to the current chain, and it has never been bridged. 217 | return NativeTokenVaultAssetId(chainID, token) 218 | } 219 | -------------------------------------------------------------------------------- /utils/bridge_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | func TestHashedL2ToL1Msg(t *testing.T) { 10 | sender := common.HexToAddress("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049") 11 | withdrawETHMessage := common.FromHex("0x6c0960f936615cf349d7f6344891b1e7ca7c72883f5dc04900000000000000000000000000000000000000000000000000000001a13b8600") 12 | withdrawETHMessageHash := common.HexToHash("0x521bd25904766c83fe868d6a29cbffa011afd8a1754f6c9a52b053b693e42f18") 13 | expected := HashedL2ToL1Msg(sender, withdrawETHMessage, 0) 14 | assert.Equal(t, expected, withdrawETHMessageHash, "Hashes should be the same") 15 | } 16 | -------------------------------------------------------------------------------- /utils/config.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/ethereum/go-ethereum/accounts/abi" 7 | "github.com/ethereum/go-ethereum/common/hexutil" 8 | "github.com/zksync-sdk/zksync2-go/types" 9 | "log" 10 | "os" 11 | "strings" 12 | ) 13 | 14 | // ReadStandardJson reads standard-json file generated as output from zksolc. 15 | // Returns standard json configuration and extracted contracts abi and bytecode from config file. 16 | func ReadStandardJson(path string) (*types.StandardConfiguration, abi.ABI, []byte, error) { 17 | data, err := os.ReadFile(path) 18 | if err != nil { 19 | return nil, abi.ABI{}, nil, fmt.Errorf("error reading standard json file: %w", err) 20 | } 21 | 22 | var config types.StandardConfiguration 23 | err = json.Unmarshal(data, &config) 24 | if err != nil { 25 | return nil, abi.ABI{}, nil, fmt.Errorf("error decoding standard json file: %w", err) 26 | } 27 | 28 | bytecode, err := hexutil.Decode(config.Bytecode) 29 | if err != nil { 30 | return nil, abi.ABI{}, nil, fmt.Errorf("error decoding bytcode from standard json file: %w", err) 31 | } 32 | 33 | abiJson, err := json.Marshal(config.Abi) 34 | if err != nil { 35 | log.Panic(err) 36 | } 37 | 38 | contractAbi, err := abi.JSON(strings.NewReader(string(abiJson))) 39 | if err != nil { 40 | log.Panic(err) 41 | } 42 | 43 | return &config, contractAbi, bytecode, nil 44 | } 45 | -------------------------------------------------------------------------------- /utils/contract.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto/sha256" 5 | "errors" 6 | "fmt" 7 | "github.com/ethereum/go-ethereum/accounts/abi" 8 | "github.com/ethereum/go-ethereum/common" 9 | "github.com/ethereum/go-ethereum/crypto" 10 | "github.com/zksync-sdk/zksync2-go/contracts/contractdeployer" 11 | "github.com/zksync-sdk/zksync2-go/types" 12 | "math/big" 13 | "strings" 14 | ) 15 | 16 | var ( 17 | // EthAddress The address of the L1 ETH token. 18 | EthAddress = common.HexToAddress("0x0000000000000000000000000000000000000000") 19 | // LegacyEthAddress The address of the L1 ETH token. 20 | LegacyEthAddress = common.HexToAddress("0x0000000000000000000000000000000000000000") 21 | // EthAddressInContracts In the contracts the zero address can not be used, use one instead. 22 | EthAddressInContracts = common.HexToAddress("0x0000000000000000000000000000000000000001") 23 | // BootloaderFormalAddress The formal address for the Bootloader. 24 | BootloaderFormalAddress = common.HexToAddress("0x0000000000000000000000000000000000008001") 25 | // ContractDeployerAddress The address of the Contract deployer. 26 | ContractDeployerAddress = common.HexToAddress("0x0000000000000000000000000000000000008006") 27 | // L1MessengerAddress The address of the L1 messenger. 28 | L1MessengerAddress = common.HexToAddress("0x0000000000000000000000000000000000008008") 29 | // L2BaseTokenAddress The address of the base token. 30 | L2BaseTokenAddress = common.HexToAddress("0x000000000000000000000000000000000000800a") 31 | // NonceHolderAddress The address of the Nonce holder. 32 | NonceHolderAddress = common.HexToAddress("0x0000000000000000000000000000000000008003") 33 | // L2AssetRouterAddress The address of the L2 asset router. 34 | L2AssetRouterAddress = common.HexToAddress("0x0000000000000000000000000000000000010003") 35 | // L2NativeTokenVaultAddress The address of the L2 native token vault 36 | L2NativeTokenVaultAddress = common.HexToAddress("0x0000000000000000000000000000000000010004") 37 | 38 | // L1ToL2AliasOffset Used for applying and undoing aliases on contract addresses during bridging from L1 to L2. 39 | L1ToL2AliasOffset = common.HexToAddress("0x1111000000000000000000000000000000001111") 40 | AddressModulo = new(big.Int).Exp(big.NewInt(2), big.NewInt(160), nil) 41 | 42 | // Eip1271MagicValue indicates the signature used by EIP-1271 is valid. 43 | Eip1271MagicValue = [4]byte(common.FromHex("0x1626ba7e")) 44 | ) 45 | 46 | var contractDeployerABI *abi.ABI 47 | 48 | // ApplyL1ToL2Alias converts the address of smart contract that submitted a transaction to the inbox on L1 to the 49 | // `msg.sender` viewed on L2. 50 | func ApplyL1ToL2Alias(address common.Address) common.Address { 51 | result := new(big.Int).Add(new(big.Int).SetBytes(address.Bytes()), new(big.Int).SetBytes(L1ToL2AliasOffset.Bytes())) 52 | result.Mod(result, AddressModulo) 53 | return common.BytesToAddress(result.Bytes()) 54 | } 55 | 56 | // UndoL1ToL2Alias converts the address of smart contract that submitted a transaction to the inbox on L2 to the 57 | // `msg.sender` viewed on L1. 58 | func UndoL1ToL2Alias(address common.Address) common.Address { 59 | result := new(big.Int).Sub(new(big.Int).SetBytes(address.Bytes()), new(big.Int).SetBytes(L1ToL2AliasOffset.Bytes())) 60 | if result.Sign() < 0 { 61 | result = result.Add(result, AddressModulo) 62 | } 63 | return common.BytesToAddress(result.Bytes()) 64 | } 65 | 66 | func getContractDeployerABI() (*abi.ABI, error) { 67 | if contractDeployerABI == nil { 68 | cda, err := abi.JSON(strings.NewReader(contractdeployer.IContractDeployerMetaData.ABI)) 69 | if err != nil { 70 | return nil, fmt.Errorf("failed to load Deployer ABI: %w", err) 71 | } 72 | contractDeployerABI = &cda 73 | } 74 | return contractDeployerABI, nil 75 | } 76 | 77 | // Create2Address generates a future-proof contract address using salt plus bytecode which allows determination 78 | // of an address before deployment. 79 | func Create2Address(sender common.Address, bytecode, constructor, salt []byte) (common.Address, error) { 80 | if len(salt) == 0 { 81 | salt = make([]byte, 32) 82 | } else if len(salt) != 32 { 83 | return common.Address{}, errors.New("salt must be 32 bytes") 84 | } 85 | if constructor == nil { 86 | constructor = []byte{} 87 | } 88 | bytecodeHash, err := HashBytecode(bytecode) 89 | if err != nil { 90 | return common.Address{}, fmt.Errorf("failed to get hash of bytecode: %w", err) 91 | } 92 | bytes := make([]byte, 5*32) // concatenate five 32-bytes slices 93 | copy(bytes[0:32], crypto.Keccak256([]byte("zksyncCreate2"))) // 1 - prefix 94 | copy(bytes[44:64], sender[:]) // 2 - sender (20 bytes right padded to 32) 95 | copy(bytes[64:96], salt) // 3 - salt 96 | copy(bytes[96:128], bytecodeHash) // 4 - bytecode hash 97 | copy(bytes[128:160], crypto.Keccak256(constructor)) // 5 - constructor hash 98 | result := crypto.Keccak256(bytes) 99 | return common.BytesToAddress(result[12:]), nil 100 | } 101 | 102 | // CreateAddress generates a contract address from deployer's account and nonce. 103 | func CreateAddress(sender common.Address, nonce *big.Int) (common.Address, error) { 104 | nonceBytes := nonce.Bytes() 105 | bytes := make([]byte, 3*32) // concatenate three 32-bytes slices 106 | copy(bytes[0:32], crypto.Keccak256([]byte("zksyncCreate"))) // 1 - prefix 107 | copy(bytes[44:64], sender[:]) // 2 - sender (20 bytes right padded to 32) 108 | copy(bytes[64+(32-len(nonceBytes)):96], nonceBytes) // 3 - nonce (some bytes right padded to 32) 109 | result := crypto.Keccak256(bytes) 110 | return common.BytesToAddress(result[12:]), nil 111 | } 112 | 113 | // EncodeCreate2 returns the encoded constructor data for CREATE2 method used for smart contract deployment. 114 | func EncodeCreate2(bytecode, calldata, salt []byte) ([]byte, error) { 115 | cdABI, err := getContractDeployerABI() 116 | if err != nil { 117 | return nil, err 118 | } 119 | // prepare 120 | if len(salt) == 0 { 121 | salt = make([]byte, 32) 122 | } else if len(salt) != 32 { 123 | return nil, errors.New("salt must be 32 bytes") 124 | } 125 | hash, err := HashBytecode(bytecode) 126 | if err != nil { 127 | return nil, fmt.Errorf("failed to get hash of bytecode: %w", err) 128 | } 129 | salt32 := [32]byte{} 130 | copy(salt32[:], salt) 131 | hash32 := [32]byte{} 132 | copy(hash32[:], hash) 133 | 134 | res, err := cdABI.Pack("create2", salt32, hash32, calldata) 135 | if err != nil { 136 | return nil, fmt.Errorf("failed to pack create2 function: %w", err) 137 | } 138 | return res, nil 139 | } 140 | 141 | // EncodeCreate encodes the constructor data for CREATE method used for smart contract deployment. 142 | func EncodeCreate(bytecode, calldata []byte) ([]byte, error) { 143 | cdABI, err := getContractDeployerABI() 144 | if err != nil { 145 | return nil, err 146 | } 147 | hash, err := HashBytecode(bytecode) 148 | if err != nil { 149 | return nil, fmt.Errorf("failed to get hash of bytecode: %w", err) 150 | } 151 | salt32 := [32]byte{} // will be empty 152 | hash32 := [32]byte{} 153 | copy(hash32[:], hash) 154 | 155 | res, err := cdABI.Pack("create", salt32, hash32, calldata) 156 | if err != nil { 157 | return nil, fmt.Errorf("failed to pack create function: %w", err) 158 | } 159 | return res, nil 160 | } 161 | 162 | // EncodeCreate2Account encodes the constructor data for CREATE2 method used for smart account deployment. 163 | func EncodeCreate2Account(bytecode, calldata, salt []byte, version types.AccountAbstractionVersion) ([]byte, error) { 164 | cdABI, err := getContractDeployerABI() 165 | if err != nil { 166 | return nil, err 167 | } 168 | // prepare 169 | if len(salt) == 0 { 170 | salt = make([]byte, 32) 171 | } else if len(salt) != 32 { 172 | return nil, errors.New("salt must be 32 bytes") 173 | } 174 | hash, err := HashBytecode(bytecode) 175 | if err != nil { 176 | return nil, fmt.Errorf("failed to get hash of bytecode: %w", err) 177 | } 178 | salt32 := [32]byte{} 179 | copy(salt32[:], salt) 180 | hash32 := [32]byte{} 181 | copy(hash32[:], hash) 182 | 183 | res, err := cdABI.Pack("create2Account", salt32, hash32, calldata, version) 184 | if err != nil { 185 | return nil, fmt.Errorf("failed to pack create2Account function: %w", err) 186 | } 187 | return res, nil 188 | } 189 | 190 | // EncodeCreateAccount encodes the constructor data for CREATE method used for smart account deployment. 191 | func EncodeCreateAccount(bytecode, calldata []byte, version types.AccountAbstractionVersion) ([]byte, error) { 192 | cdABI, err := getContractDeployerABI() 193 | if err != nil { 194 | return nil, err 195 | } 196 | hash, err := HashBytecode(bytecode) 197 | if err != nil { 198 | return nil, fmt.Errorf("failed to get hash of bytecode: %w", err) 199 | } 200 | salt32 := [32]byte{} // will be empty 201 | hash32 := [32]byte{} 202 | copy(hash32[:], hash) 203 | 204 | res, err := cdABI.Pack("createAccount", salt32, hash32, calldata, version) 205 | if err != nil { 206 | return nil, fmt.Errorf("failed to pack createAccount function: %w", err) 207 | } 208 | return res, nil 209 | } 210 | 211 | // HashBytecode returns the hash of given bytecode. 212 | func HashBytecode(bytecode []byte) ([]byte, error) { 213 | if len(bytecode)%32 != 0 { 214 | return nil, errors.New("bytecode length in bytes must be divisible by 32") 215 | } 216 | bytecodeHash := sha256.Sum256(bytecode) 217 | // get real length of bytecode, which is presented as 32-byte words 218 | length := big.NewInt(int64(len(bytecode) / 32)) 219 | if length.BitLen() > 16 { 220 | return nil, errors.New("bytecode length must be less than 2^16 bytes") 221 | } 222 | // replace first 2 bytes of hash with version 223 | version := []byte{1, 0} 224 | copy(bytecodeHash[0:2], version) 225 | // replace second 2 bytes of hash with bytecode length 226 | length2b := make([]byte, 2) 227 | length2b = length.FillBytes(length2b) // 0-padded in 2 bytes 228 | copy(bytecodeHash[2:4], length2b) 229 | return bytecodeHash[:], nil 230 | } 231 | -------------------------------------------------------------------------------- /utils/gas.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | ) 7 | 8 | var ( 9 | // RequiredL1ToL2GasPerPubdataLimit It is possible to provide practically any gasPerPubdataByte for L1->L2 transactions, 10 | // since the cost per gas will be adjusted respectively. Use 800 as a relatively optimal value for now. 11 | RequiredL1ToL2GasPerPubdataLimit = big.NewInt(800) 12 | // DefaultGasPerPubdataLimit The large L2 gas per pubdata to sign. This gas is enough to ensure that 13 | // any reasonable limit will be accepted. Note, that the operator is NOT required to 14 | // use the honest value of gas per pubdata, and it can use any value up to the one signed by the user. 15 | // In the future releases, we will provide a way to estimate the current gasPerPubdata. 16 | DefaultGasPerPubdataLimit = big.NewInt(50_000) 17 | 18 | // MaxPriorityFeePerGas is fixed because L2 node does not support eth_maxPriorityFeePerGas method 19 | MaxPriorityFeePerGas = big.NewInt(1_000_000_000) 20 | 21 | // L1RecommendedMinErc20DepositGasLimit This gas limit will be used for displaying the error messages when the users do not have enough fee. 22 | L1RecommendedMinErc20DepositGasLimit = big.NewInt(400_000) 23 | // L1RecommendedMinEthDepositGasLimit This gas limit will be used for displaying the error messages when the users do not have enough fee. 24 | L1RecommendedMinEthDepositGasLimit = big.NewInt(200_000) 25 | ) 26 | 27 | // ScaleGasLimit scales the provided gas limit using a coefficient to ensure acceptance of L1->L2 transactions. 28 | func ScaleGasLimit(gasLimit *big.Int) *big.Int { 29 | // Currently, for some reason the SDK may return slightly smaller L1 gas limit than required for initiating L1->L2 30 | // transaction. We use a coefficient to ensure that the transaction will be accepted. 31 | L1FeeEstimationCoefNumerator := big.NewInt(12) 32 | L1FeeEstimationCoefDenominator := big.NewInt(10) 33 | gasLimit.Mul(L1FeeEstimationCoefNumerator, gasLimit) 34 | return gasLimit.Div(gasLimit, L1FeeEstimationCoefDenominator) 35 | } 36 | 37 | // CheckBaseCost checks if the provided base cost is greater than the provided value. 38 | // If it is, return an error indicating that there are not enough funds. 39 | func CheckBaseCost(baseCost, value *big.Int) error { 40 | if baseCost.Cmp(value) > 0 { 41 | return fmt.Errorf( 42 | "the base cost of performing the priority operation is higher than the provided value parameter for the transaction: baseCost: %d, provided value: %d", baseCost, value) 43 | } 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /utils/gas_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "math/big" 6 | "testing" 7 | ) 8 | 9 | func TestCheckBaseCost(t *testing.T) { 10 | err := CheckBaseCost(big.NewInt(100), big.NewInt(99)) 11 | assert.Error(t, err, "CheckBaseCost Should return error when base cost is greater than value") 12 | assert.ErrorContains(t, err, "the base cost of performing the priority operation is higher") 13 | } 14 | 15 | func TestScaleGasLimit(t *testing.T) { 16 | actual := ScaleGasLimit(big.NewInt(100)) 17 | assert.True(t, big.NewInt(120).Cmp(actual) == 0, "Amounts should be the same") 18 | } 19 | -------------------------------------------------------------------------------- /utils/mics.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum" 5 | "github.com/ethereum/go-ethereum/common/hexutil" 6 | "github.com/zksync-sdk/zksync2-go/types" 7 | "math/big" 8 | ) 9 | 10 | func NewBigZero() *hexutil.Big { 11 | return (*hexutil.Big)(new(big.Int)) 12 | } 13 | 14 | func NewBig(n int64) *hexutil.Big { 15 | return (*hexutil.Big)(big.NewInt(n)) 16 | } 17 | 18 | // NewCallMsg converts ethereum call message to L2 message. 19 | func NewCallMsg(call ethereum.CallMsg) *types.CallMsg { 20 | return &types.CallMsg{ 21 | From: call.From, 22 | To: call.To, 23 | Gas: call.Gas, 24 | GasPrice: call.GasPrice, 25 | GasFeeCap: call.GasFeeCap, 26 | GasTipCap: call.GasTipCap, 27 | Value: call.Value, 28 | Data: call.Data, 29 | GasPerPubdata: DefaultGasPerPubdataLimit, 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /utils/paymaster.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/ethereum/go-ethereum/accounts/abi" 7 | "github.com/ethereum/go-ethereum/common" 8 | "github.com/zksync-sdk/zksync2-go/contracts/paymasterflow" 9 | "github.com/zksync-sdk/zksync2-go/types" 10 | "log" 11 | "strings" 12 | ) 13 | 14 | var paymasterFlowAbi abi.ABI 15 | 16 | func init() { 17 | var err error 18 | paymasterFlowAbi, err = abi.JSON(strings.NewReader(paymasterflow.IPaymasterFlowMetaData.ABI)) 19 | if err != nil { 20 | log.Fatal("failed to load paymasterFlowAbi: %w", err) 21 | } 22 | } 23 | 24 | // GetApprovalBasedPaymasterInput returns encoded input for an approval-based paymaster. 25 | func GetApprovalBasedPaymasterInput(paymasterInput types.ApprovalBasedPaymasterInput) ([]byte, error) { 26 | return paymasterFlowAbi.Pack("approvalBased", 27 | paymasterInput.Token, 28 | paymasterInput.MinimalAllowance, 29 | paymasterInput.InnerInput) 30 | } 31 | 32 | // GetGeneralPaymasterInput returns encoded input for a general-based paymaster. 33 | func GetGeneralPaymasterInput(paymasterInput types.GeneralPaymasterInput) ([]byte, error) { 34 | return paymasterFlowAbi.Pack("general", paymasterInput.GetInput()) 35 | } 36 | 37 | // GetPaymasterParams returns a correctly-formed paymaster parameters for common paymaster flows. 38 | func GetPaymasterParams(paymasterAddress common.Address, paymasterInput types.PaymasterInput) (*types.PaymasterParams, error) { 39 | if paymasterInput.GetType() == "General" { 40 | generalPaymasterInput, ok := paymasterInput.(*types.GeneralPaymasterInput) 41 | if !ok { 42 | return &types.PaymasterParams{}, errors.New("cannot convert PaymasterInput to GeneralPaymasterInput type") 43 | } 44 | input, err := GetGeneralPaymasterInput(*generalPaymasterInput) 45 | if err != nil { 46 | return &types.PaymasterParams{}, err 47 | } 48 | return &types.PaymasterParams{ 49 | Paymaster: paymasterAddress, 50 | PaymasterInput: input, 51 | }, nil 52 | } else if paymasterInput.GetType() == "ApprovalBased" { 53 | approvalBasedPaymasterInput, ok := paymasterInput.(*types.ApprovalBasedPaymasterInput) 54 | if !ok { 55 | return &types.PaymasterParams{}, errors.New("cannot convert PaymasterInput to ApprovalBasedPaymasterInput type") 56 | } 57 | input, err := GetApprovalBasedPaymasterInput(*approvalBasedPaymasterInput) 58 | if err != nil { 59 | return &types.PaymasterParams{}, err 60 | } 61 | return &types.PaymasterParams{ 62 | Paymaster: paymasterAddress, 63 | PaymasterInput: input, 64 | }, nil 65 | } else { 66 | return &types.PaymasterParams{}, fmt.Errorf("cannot recognize given paymaster input type: %s", paymasterInput.GetType()) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /utils/paymaster_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | "github.com/stretchr/testify/assert" 6 | "github.com/zksync-sdk/zksync2-go/types" 7 | "math/big" 8 | "testing" 9 | ) 10 | 11 | func TestGetPaymasterParamsGeneral(t *testing.T) { 12 | expected := &types.PaymasterParams{ 13 | Paymaster: common.HexToAddress("0x0a67078A35745947A37A552174aFe724D8180c25"), 14 | PaymasterInput: common.Hex2Bytes("8c5a344500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000"), 15 | } 16 | 17 | params, err := GetPaymasterParams( 18 | common.HexToAddress("0x0a67078A35745947A37A552174aFe724D8180c25"), 19 | &types.GeneralPaymasterInput{}, 20 | ) 21 | 22 | assert.NoError(t, err, "GetPaymasterParams should not return error") 23 | assert.Equal(t, expected, params, "Parameter should be the same") 24 | } 25 | 26 | func TestGetPaymasterParamsApprovalBased(t *testing.T) { 27 | expected := &types.PaymasterParams{ 28 | Paymaster: common.HexToAddress("0x0a67078A35745947A37A552174aFe724D8180c25"), 29 | PaymasterInput: common.Hex2Bytes("949431dc00000000000000000000000065c899b5fb8eb9ae4da51d67e1fc417c7cb7e964000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000"), 30 | } 31 | 32 | params, err := GetPaymasterParams( 33 | common.HexToAddress("0x0a67078A35745947A37A552174aFe724D8180c25"), 34 | &types.ApprovalBasedPaymasterInput{ 35 | Token: common.HexToAddress("0x65C899B5fb8Eb9ae4da51D67E1fc417c7CB7e964"), 36 | MinimalAllowance: big.NewInt(1), 37 | InnerInput: []byte{}, 38 | }, 39 | ) 40 | 41 | assert.NoError(t, err, "GetPaymasterParams should not return error") 42 | assert.Equal(t, expected, params, "Parameter should be the same") 43 | } 44 | -------------------------------------------------------------------------------- /utils/signature.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/accounts" 5 | "github.com/ethereum/go-ethereum/common" 6 | "github.com/ethereum/go-ethereum/crypto" 7 | "github.com/ethereum/go-ethereum/signer/core/apitypes" 8 | ) 9 | 10 | // IsMessageSignatureCorrect checks whether the message ECDSA signature is correct. 11 | func IsMessageSignatureCorrect(address common.Address, msg, sig []byte) (bool, error) { 12 | signature := common.CopyBytes(sig) 13 | if signature[64] > 4 { 14 | signature[64] = signature[64] - 27 15 | } 16 | publicKey, err := crypto.SigToPub(accounts.TextHash(msg), signature) 17 | if err != nil { 18 | return false, err 19 | } 20 | return address == crypto.PubkeyToAddress(*publicKey), nil 21 | } 22 | 23 | // IsTypedDataSignatureCorrect checks whether the typed data ECDSA signature is correct. 24 | func IsTypedDataSignatureCorrect(address common.Address, typedData apitypes.TypedData, sig []byte) (bool, error) { 25 | signature := common.CopyBytes(sig) 26 | if signature[64] > 4 { 27 | signature[64] = signature[64] - 27 28 | } 29 | hash, _, err := apitypes.TypedDataAndHash(typedData) 30 | if err != nil { 31 | return false, err 32 | } 33 | publicKey, err := crypto.SigToPub(hash, signature) 34 | if err != nil { 35 | return false, err 36 | } 37 | return address == crypto.PubkeyToAddress(*publicKey), nil 38 | } 39 | -------------------------------------------------------------------------------- /utils/signature_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | "github.com/ethereum/go-ethereum/common/hexutil" 6 | "github.com/ethereum/go-ethereum/common/math" 7 | "github.com/ethereum/go-ethereum/signer/core/apitypes" 8 | "github.com/stretchr/testify/assert" 9 | "testing" 10 | ) 11 | 12 | var Address1 = common.HexToAddress("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049") 13 | 14 | func TestIsMessageSignatureCorrect(t *testing.T) { 15 | const message = "Hello, world!" 16 | const signature = "0xb04f825363596418c630425916f73447d636094a75e47b45e2eb59d8a6c7d5035355f03b903b84700376f0efa23f3b095815776c5c6daf2b371a0a61b5f703451b" 17 | address := common.HexToAddress("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049") 18 | actual, err := IsMessageSignatureCorrect(address, []byte(message), common.FromHex(signature)) 19 | assert.NoError(t, err, "IsMessageSignatureCorrect should not return error for valid message signature") 20 | assert.True(t, actual, "Message signature should be valid") 21 | } 22 | 23 | func TestIsMessageSignatureCorrectInvalidSignature(t *testing.T) { 24 | const message = "Hello world" 25 | const invalidSignature = "0xb04f825363596418c630425916f73447d636094a75e47b45e2eb59d8a6c7d5035355f03b903b84700376f0efa23f3b095815776c5c6daf2b371a0a61b5f703451b" 26 | address := common.HexToAddress("0x36615Cf349d7F6344891B1e7CA7C72883F5dc049") 27 | actual, err := IsMessageSignatureCorrect(address, []byte(message), common.FromHex(invalidSignature)) 28 | assert.NoError(t, err, "IsMessageSignatureCorrect should not return error for invalid message signature") 29 | assert.False(t, actual, "Message signature should be invalid") 30 | } 31 | 32 | func TestIsTypedDataSignatureCorrect(t *testing.T) { 33 | typedData := apitypes.TypedData{ 34 | Domain: apitypes.TypedDataDomain{ 35 | Name: "Example", 36 | Version: "1", 37 | ChainId: math.NewHexOrDecimal256(270), 38 | }, 39 | Types: apitypes.Types{ 40 | "Person": []apitypes.Type{ 41 | {Name: "name", Type: "string"}, 42 | {Name: "age", Type: "uint8"}, 43 | }, 44 | "EIP712Domain": []apitypes.Type{ 45 | {Name: "name", Type: "string"}, 46 | {Name: "version", Type: "string"}, 47 | {Name: "chainId", Type: "uint256"}, 48 | }, 49 | }, 50 | PrimaryType: "Person", 51 | Message: apitypes.TypedDataMessage{ 52 | "name": "John", 53 | "age": hexutil.EncodeUint64(30), 54 | }, 55 | } 56 | 57 | const invalidSignature = "0xbcaf0673c0c2b0e120165d207d42281d0c6e85f0a7f6b8044b0578a91cf5bda66b4aeb62aca4ae17012a38d71c9943e27285792fa7d788d848f849e3ea2e614b1b" 58 | actual, err := IsTypedDataSignatureCorrect(Address1, typedData, common.FromHex(invalidSignature)) 59 | assert.NoError(t, err, "IsTypedDataSignatureCorrect should not return an error") 60 | assert.True(t, actual, "Typed data signature should be valid") 61 | } 62 | 63 | func TestIsTypedDataSignatureCorrectInvalidSignature(t *testing.T) { 64 | typedData := apitypes.TypedData{ 65 | Domain: apitypes.TypedDataDomain{ 66 | Name: "Example", 67 | Version: "1", 68 | ChainId: math.NewHexOrDecimal256(270), 69 | }, 70 | Types: apitypes.Types{ 71 | "Person": []apitypes.Type{ 72 | {Name: "name", Type: "string"}, 73 | {Name: "age", Type: "uint8"}, 74 | }, 75 | "EIP712Domain": []apitypes.Type{ 76 | {Name: "name", Type: "string"}, 77 | {Name: "version", Type: "string"}, 78 | {Name: "chainId", Type: "uint256"}, 79 | }, 80 | }, 81 | PrimaryType: "Person", 82 | Message: apitypes.TypedDataMessage{ 83 | "name": "Bob", // instead of John 84 | "age": hexutil.EncodeUint64(30), 85 | }, 86 | } 87 | 88 | const signature = "0xbcaf0673c0c2b0e120165d207d42281d0c6e85f0a7f6b8044b0578a91cf5bda66b4aeb62aca4ae17012a38d71c9943e27285792fa7d788d848f849e3ea2e614b1b" 89 | actual, err := IsTypedDataSignatureCorrect(Address1, typedData, common.FromHex(signature)) 90 | assert.NoError(t, err, "IsTypedDataSignatureCorrect should not return an error") 91 | assert.False(t, actual, "Typed data signature should be valid") 92 | } 93 | --------------------------------------------------------------------------------