This file is a merged representation of the entire codebase, combined into a single document by Repomix. The content has been processed where content has been compressed (code blocks are separated by ⋮---- delimiter). # File Summary ## Purpose This file contains a packed representation of the entire repository's contents. It is designed to be easily consumable by AI systems for analysis, code review, or other automated processes. ## File Format The content is organized as follows: 1. This summary section 2. Repository information 3. Directory structure 4. Repository files (if enabled) 5. Multiple file entries, each consisting of: a. A header with the file path (## File: path/to/file) b. The full contents of the file in a code block ## Usage Guidelines - This file should be treated as read-only. Any changes should be made to the original repository files, not this packed version. - When processing this file, use the file path to distinguish between different files in the repository. - Be aware that this file may contain sensitive information. Handle it with the same level of security as you would the original repository. ## Notes - Some files may have been excluded based on .gitignore rules and Repomix's configuration - Binary files are not included in this packed representation. Please refer to the Repository Structure section for a complete list of file paths, including binary files - Files matching patterns in .gitignore are excluded - Files matching default ignore patterns are excluded - Content has been compressed - code blocks are separated by ⋮---- delimiter - Files are sorted by Git change count (files with more changes are at the bottom) # Directory Structure ``` .github/ ISSUE_TEMPLATE/ BUG_REPORT.yaml FEATURE_IMPROVEMENT.yaml workflows/ pre-commit.yaml test.yaml trufflehog.yml CODEOWNERS PULL_REQUEST_TEMPLATE.md network-scripts/ deploy.sh deployer.Dockerfile genesis-generator.sh sidecar-start.sh sum-node-start.sh off-chain/ abis/ SumTask.abi.json cmd/ benchmark/ main.go node/ main.go internal/ contracts/ sumTask.go utils/ util.go .dockerignore Dockerfile go.mod script/ mocks/ MockERC20.sol utils/ BN254G2.sol sort_errors.py sort_imports.py my-relay-deploy.toml MyRelayDeploy.sol snapshots/ gas.txt sizes.txt src/ symbiotic/ Driver.sol KeyRegistry.sol Settlement.sol VotingPowers.sol SumTask.sol test/ mock/ SettlementMock.sol SumTask.t.sol .gitignore .gitmodules .pre-commit-config.yaml .prettierignore .prettierrc CONTRIBUTING.md foundry.lock foundry.toml generate_network.sh package.json README.md remappings.txt ``` # Files ## File: .github/ISSUE_TEMPLATE/BUG_REPORT.yaml ````yaml name: Bug report description: File a bug report to help us improve the code title: "[Bug]: " labels: ["bug"] body: - type: markdown attributes: value: | Please check that the bug is not already being tracked. - type: textarea attributes: label: Describe the bug description: Provide a clear and concise description of what the bug is and which contracts it affects. validations: required: true - type: textarea attributes: label: Expected Behavior description: Provide a clear and concise description of the desired fix. validations: required: true - type: textarea attributes: label: To Reproduce description: If you have written tests to showcase the bug, what can we run to reproduce the issue? placeholder: "git checkout / forge test --isolate --mt " - type: textarea attributes: label: Additional context description: If there is any additional context needed like a dependency or integrating contract that is affected please describe it below. ```` ## File: .github/ISSUE_TEMPLATE/FEATURE_IMPROVEMENT.yaml ````yaml name: Feature Improvement description: Suggest an improvement. labels: ["triage"] body: - type: markdown attributes: value: | Please ensure that the feature has not already been requested. - type: dropdown attributes: label: Component description: Which area of code does your idea improve? multiple: true options: - Gas Optimization - General design optimization (improving efficiency, cleanliness, or developer experience) - Testing - Documentation - type: textarea attributes: label: Describe the suggested feature and problem it solves. description: Provide a clear and concise description of what feature you would like to see, and what problems it solves. validations: required: true - type: textarea attributes: label: Describe the desired implementation. description: If possible, provide a suggested architecture change or implementation. - type: textarea attributes: label: Describe alternatives. description: If possible, describe the alternatives you've considered, or describe the current functionality and how it may be sub-optimal. - type: textarea attributes: label: Additional context. description: Please list any additional dependencies or integrating contacts that are affected. ```` ## File: .github/workflows/pre-commit.yaml ````yaml # checks that pre-commit hooks pass before allowing to merge a PR name: pre-commit on: pull_request: branches: [main, master, staging, dev, feat/**, fix/**] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: FOUNDRY_PROFILE: ${{ github.event_name == 'push' && 'ci' || 'pr' }} ETH_RPC_URL: ${{ secrets.ETH_RPC_URL }} jobs: pre-commit: runs-on: ubuntu-latest timeout-minutes: 45 steps: - uses: bullfrogsec/bullfrog@1831f79cce8ad602eef14d2163873f27081ebfb3 # v0.8.4 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: submodules: recursive - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: "3.11" - name: Install Foundry uses: foundry-rs/foundry-toolchain@82dee4ba654bd2146511f85f0d013af94670c4de # v1.4.0 with: version: stable - name: Install dependencies run: npm install - name: Install pre-commit run: python -m pip install --upgrade pip pre-commit - name: Run pre-commit run: pre-commit run --all-files --color always --show-diff-on-failure env: SKIP: forge-snapshots ```` ## File: .github/workflows/test.yaml ````yaml name: test on: pull_request: branches: [main, master, staging, dev, feat/**, fix/**] push: branches: [main, master, staging, dev] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: FOUNDRY_PROFILE: ${{ github.event_name == 'push' && 'ci' || 'pr' }} ETH_RPC_URL: ${{ secrets.ETH_RPC_URL }} jobs: forge-test: name: Foundry project runs-on: ubuntu-latest timeout-minutes: 45 steps: - uses: bullfrogsec/bullfrog@1831f79cce8ad602eef14d2163873f27081ebfb3 # v0.8.4 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: submodules: recursive - name: Install Foundry uses: foundry-rs/foundry-toolchain@82dee4ba654bd2146511f85f0d013af94670c4de # v1.4.0 with: version: stable - name: Install npm dependencies run: npm install - name: Run Forge fmt check run: forge fmt --check - name: Run Forge tests run: forge test --isolate ```` ## File: .github/workflows/trufflehog.yml ````yaml name: TruffleHog on: pull_request: types: [opened, synchronize, reopened] permissions: contents: read pull-requests: write id-token: write issues: write concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: scan: runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: bullfrogsec/bullfrog@1831f79cce8ad602eef14d2163873f27081ebfb3 # v0.8.4 - name: Checkout code uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 - name: TruffleHog OSS id: trufflehog uses: trufflesecurity/trufflehog@0f58ae7c5036094a1e3e750d18772af92821b503 # v3.90.5 with: path: . base: ${{ github.event.pull_request.base.sha }} head: ${{ github.event.pull_request.head.sha }} extra_args: --results=verified,unknown ```` ## File: .github/CODEOWNERS ```` * @symbioticfi/contracts * @symbioticfi/relay ```` ## File: .github/PULL_REQUEST_TEMPLATE.md ````markdown # Pull Request ## Description Please include a summary of the change and which feature was implemented or which issue was fixed. Also, include relevant motivation and context. List any dependencies that are required for this change. Fixes # (issue) ### How Has This Been Tested? Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. # Checklist: - [ ] 100% test and branch coverage - [ ] check slither or other analyzer for severe issues - [ ] fuzz and invariant tests (when applicable) --- ### Considerations - I have followed the [contributing guidelines](../CONTRIBUTING.md). - My code follows the style guidelines of this project and I have run `forge fmt` and prettier to ensure the code style is valid - I have performed a self-review of my own code - I have commented my code, particularly in hard-to-understand areas - I have made corresponding changes to the documentation - I have added tests that prove my fix is effective or that my feature works - New and existing unit tests pass locally with my changes ### Additional context Add any other context about the pull request here. ```` ## File: network-scripts/deploy.sh ````bash #!/bin/sh set -e ANVIL_RPC_URL=${ANVIL_RPC_URL:-http://anvil:8545} SETTLEMENT_RPC_URL=${SETTLEMENT_RPC_URL:-http://anvil-settlement:8546} MULTICALL_ADDRESS=0x05f32b3cc3888453ff71b01135b34ff8e41263f2 MULTICALL_BALANCE_HEX=0xde0b6b3a7640000 # 1 ether echo "Waiting for anvil to be ready..." until cast client --rpc-url "$ANVIL_RPC_URL" > /dev/null 2>&1; do sleep 1; done until cast client --rpc-url "$SETTLEMENT_RPC_URL" > /dev/null 2>&1; do sleep 1; done echo "Deploying Multicall3 contracts..." cast rpc --rpc-url "$ANVIL_RPC_URL" anvil_setBalance "$MULTICALL_ADDRESS" "$MULTICALL_BALANCE_HEX" cast rpc --rpc-url "$ANVIL_RPC_URL" anvil_setNonce "$MULTICALL_ADDRESS" 0x0 cast publish 0xf90f538085174876e800830f42408080b90f00608060405234801561001057600080fd5b50610ee0806100206000396000f3fe6080604052600436106100f35760003560e01c80634d2301cc1161008a578063a8b0574e11610059578063a8b0574e1461025a578063bce38bd714610275578063c3077fa914610288578063ee82ac5e1461029b57600080fd5b80634d2301cc146101ec57806372425d9d1461022157806382ad56cb1461023457806386d516e81461024757600080fd5b80633408e470116100c65780633408e47014610191578063399542e9146101a45780633e64a696146101c657806342cbb15c146101d957600080fd5b80630f28c97d146100f8578063174dea711461011a578063252dba421461013a57806327e86d6e1461015b575b600080fd5b34801561010457600080fd5b50425b6040519081526020015b60405180910390f35b61012d610128366004610a85565b6102ba565b6040516101119190610bbe565b61014d610148366004610a85565b6104ef565b604051610111929190610bd8565b34801561016757600080fd5b50437fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0140610107565b34801561019d57600080fd5b5046610107565b6101b76101b2366004610c60565b610690565b60405161011193929190610cba565b3480156101d257600080fd5b5048610107565b3480156101e557600080fd5b5043610107565b3480156101f857600080fd5b50610107610207366004610ce2565b73ffffffffffffffffffffffffffffffffffffffff163190565b34801561022d57600080fd5b5044610107565b61012d610242366004610a85565b6106ab565b34801561025357600080fd5b5045610107565b34801561026657600080fd5b50604051418152602001610111565b61012d610283366004610c60565b61085a565b6101b7610296366004610a85565b610a1a565b3480156102a757600080fd5b506101076102b6366004610d18565b4090565b60606000828067ffffffffffffffff8111156102d8576102d8610d31565b60405190808252806020026020018201604052801561031e57816020015b6040805180820190915260008152606060208201528152602001906001900390816102f65790505b5092503660005b8281101561047757600085828151811061034157610341610d60565b6020026020010151905087878381811061035d5761035d610d60565b905060200281019061036f9190610d8f565b6040810135958601959093506103886020850185610ce2565b73ffffffffffffffffffffffffffffffffffffffff16816103ac6060870187610dcd565b6040516103ba929190610e32565b60006040518083038185875af1925050503d80600081146103f7576040519150601f19603f3d011682016040523d82523d6000602084013e6103fc565b606091505b50602080850191909152901515808452908501351761046d577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260846000fd5b5050600101610325565b508234146104e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4d756c746963616c6c333a2076616c7565206d69736d6174636800000000000060448201526064015b60405180910390fd5b50505092915050565b436060828067ffffffffffffffff81111561050c5761050c610d31565b60405190808252806020026020018201604052801561053f57816020015b606081526020019060019003908161052a5790505b5091503660005b8281101561068657600087878381811061056257610562610d60565b90506020028101906105749190610e42565b92506105836020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166105a66020850185610dcd565b6040516105b4929190610e32565b6000604051808303816000865af19150503d80600081146105f1576040519150601f19603f3d011682016040523d82523d6000602084013e6105f6565b606091505b5086848151811061060957610609610d60565b602090810291909101015290508061067d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b50600101610546565b5050509250929050565b43804060606106a086868661085a565b905093509350939050565b6060818067ffffffffffffffff8111156106c7576106c7610d31565b60405190808252806020026020018201604052801561070d57816020015b6040805180820190915260008152606060208201528152602001906001900390816106e55790505b5091503660005b828110156104e657600084828151811061073057610730610d60565b6020026020010151905086868381811061074c5761074c610d60565b905060200281019061075e9190610e76565b925061076d6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166107906040850185610dcd565b60405161079e929190610e32565b6000604051808303816000865af19150503d80600081146107db576040519150601f19603f3d011682016040523d82523d6000602084013e6107e0565b606091505b506020808401919091529015158083529084013517610851577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260646000fd5b50600101610714565b6060818067ffffffffffffffff81111561087657610876610d31565b6040519080825280602002602001820160405280156108bc57816020015b6040805180820190915260008152606060208201528152602001906001900390816108945790505b5091503660005b82811015610a105760008482815181106108df576108df610d60565b602002602001015190508686838181106108fb576108fb610d60565b905060200281019061090d9190610e42565b925061091c6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff1661093f6020850185610dcd565b60405161094d929190610e32565b6000604051808303816000865af19150503d806000811461098a576040519150601f19603f3d011682016040523d82523d6000602084013e61098f565b606091505b506020830152151581528715610a07578051610a07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b506001016108c3565b5050509392505050565b6000806060610a2b60018686610690565b919790965090945092505050565b60008083601f840112610a4b57600080fd5b50813567ffffffffffffffff811115610a6357600080fd5b6020830191508360208260051b8501011115610a7e57600080fd5b9250929050565b60008060208385031215610a9857600080fd5b823567ffffffffffffffff811115610aaf57600080fd5b610abb85828601610a39565b90969095509350505050565b6000815180845260005b81811015610aed57602081850181015186830182015201610ad1565b81811115610aff576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600082825180855260208086019550808260051b84010181860160005b84811015610bb1578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001895281518051151584528401516040858501819052610b9d81860183610ac7565b9a86019a9450505090830190600101610b4f565b5090979650505050505050565b602081526000610bd16020830184610b32565b9392505050565b600060408201848352602060408185015281855180845260608601915060608160051b870101935082870160005b82811015610c52577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0888703018452610c40868351610ac7565b95509284019290840190600101610c06565b509398975050505050505050565b600080600060408486031215610c7557600080fd5b83358015158114610c8557600080fd5b9250602084013567ffffffffffffffff811115610ca157600080fd5b610cad86828701610a39565b9497909650939450505050565b838152826020820152606060408201526000610cd96060830184610b32565b95945050505050565b600060208284031215610cf457600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610bd157600080fd5b600060208284031215610d2a57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112610dc357600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610e0257600080fd5b83018035915067ffffffffffffffff821115610e1d57600080fd5b602001915036819003821315610a7e57600080fd5b8183823760009101908152919050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112610dc357600080fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1833603018112610dc357600080fdfea2646970667358221220bb2b5c71a328032f97c676ae39a1ec2148d3e5d6f73d95e9b17910152d61f16264736f6c634300080c00331ca0edce47092c0f398cebf3ffc267f05c8e7076e3b89445e0fe50f6332273d4569ba01b0b9d000e19b24c5869b0fc3b22b0d6fa47cd63316875cbbd577d76e6fde086 --rpc-url "$ANVIL_RPC_URL" cast rpc --rpc-url "$SETTLEMENT_RPC_URL" anvil_setBalance "$MULTICALL_ADDRESS" "$MULTICALL_BALANCE_HEX" cast rpc --rpc-url "$SETTLEMENT_RPC_URL" anvil_setNonce "$MULTICALL_ADDRESS" 0x0 cast publish 0xf90f538085174876e800830f42408080b90f00608060405234801561001057600080fd5b50610ee0806100206000396000f3fe6080604052600436106100f35760003560e01c80634d2301cc1161008a578063a8b0574e11610059578063a8b0574e1461025a578063bce38bd714610275578063c3077fa914610288578063ee82ac5e1461029b57600080fd5b80634d2301cc146101ec57806372425d9d1461022157806382ad56cb1461023457806386d516e81461024757600080fd5b80633408e470116100c65780633408e47014610191578063399542e9146101a45780633e64a696146101c657806342cbb15c146101d957600080fd5b80630f28c97d146100f8578063174dea711461011a578063252dba421461013a57806327e86d6e1461015b575b600080fd5b34801561010457600080fd5b50425b6040519081526020015b60405180910390f35b61012d610128366004610a85565b6102ba565b6040516101119190610bbe565b61014d610148366004610a85565b6104ef565b604051610111929190610bd8565b34801561016757600080fd5b50437fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0140610107565b34801561019d57600080fd5b5046610107565b6101b76101b2366004610c60565b610690565b60405161011193929190610cba565b3480156101d257600080fd5b5048610107565b3480156101e557600080fd5b5043610107565b3480156101f857600080fd5b50610107610207366004610ce2565b73ffffffffffffffffffffffffffffffffffffffff163190565b34801561022d57600080fd5b5044610107565b61012d610242366004610a85565b6106ab565b34801561025357600080fd5b5045610107565b34801561026657600080fd5b50604051418152602001610111565b61012d610283366004610c60565b61085a565b6101b7610296366004610a85565b610a1a565b3480156102a757600080fd5b506101076102b6366004610d18565b4090565b60606000828067ffffffffffffffff8111156102d8576102d8610d31565b60405190808252806020026020018201604052801561031e57816020015b6040805180820190915260008152606060208201528152602001906001900390816102f65790505b5092503660005b8281101561047757600085828151811061034157610341610d60565b6020026020010151905087878381811061035d5761035d610d60565b905060200281019061036f9190610d8f565b6040810135958601959093506103886020850185610ce2565b73ffffffffffffffffffffffffffffffffffffffff16816103ac6060870187610dcd565b6040516103ba929190610e32565b60006040518083038185875af1925050503d80600081146103f7576040519150601f19603f3d011682016040523d82523d6000602084013e6103fc565b606091505b50602080850191909152901515808452908501351761046d577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260846000fd5b5050600101610325565b508234146104e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f4d756c746963616c6c333a2076616c7565206d69736d6174636800000000000060448201526064015b60405180910390fd5b50505092915050565b436060828067ffffffffffffffff81111561050c5761050c610d31565b60405190808252806020026020018201604052801561053f57816020015b606081526020019060019003908161052a5790505b5091503660005b8281101561068657600087878381811061056257610562610d60565b90506020028101906105749190610e42565b92506105836020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166105a66020850185610dcd565b6040516105b4929190610e32565b6000604051808303816000865af19150503d80600081146105f1576040519150601f19603f3d011682016040523d82523d6000602084013e6105f6565b606091505b5086848151811061060957610609610d60565b602090810291909101015290508061067d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b50600101610546565b5050509250929050565b43804060606106a086868661085a565b905093509350939050565b6060818067ffffffffffffffff8111156106c7576106c7610d31565b60405190808252806020026020018201604052801561070d57816020015b6040805180820190915260008152606060208201528152602001906001900390816106e55790505b5091503660005b828110156104e657600084828151811061073057610730610d60565b6020026020010151905086868381811061074c5761074c610d60565b905060200281019061075e9190610e76565b925061076d6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff166107906040850185610dcd565b60405161079e929190610e32565b6000604051808303816000865af19150503d80600081146107db576040519150601f19603f3d011682016040523d82523d6000602084013e6107e0565b606091505b506020808401919091529015158083529084013517610851577f08c379a000000000000000000000000000000000000000000000000000000000600052602060045260176024527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060445260646000fd5b50600101610714565b6060818067ffffffffffffffff81111561087657610876610d31565b6040519080825280602002602001820160405280156108bc57816020015b6040805180820190915260008152606060208201528152602001906001900390816108945790505b5091503660005b82811015610a105760008482815181106108df576108df610d60565b602002602001015190508686838181106108fb576108fb610d60565b905060200281019061090d9190610e42565b925061091c6020840184610ce2565b73ffffffffffffffffffffffffffffffffffffffff1661093f6020850185610dcd565b60405161094d929190610e32565b6000604051808303816000865af19150503d806000811461098a576040519150601f19603f3d011682016040523d82523d6000602084013e61098f565b606091505b506020830152151581528715610a07578051610a07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f4d756c746963616c6c333a2063616c6c206661696c656400000000000000000060448201526064016104dd565b506001016108c3565b5050509392505050565b6000806060610a2b60018686610690565b919790965090945092505050565b60008083601f840112610a4b57600080fd5b50813567ffffffffffffffff811115610a6357600080fd5b6020830191508360208260051b8501011115610a7e57600080fd5b9250929050565b60008060208385031215610a9857600080fd5b823567ffffffffffffffff811115610aaf57600080fd5b610abb85828601610a39565b90969095509350505050565b6000815180845260005b81811015610aed57602081850181015186830182015201610ad1565b81811115610aff576000602083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600082825180855260208086019550808260051b84010181860160005b84811015610bb1578583037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001895281518051151584528401516040858501819052610b9d81860183610ac7565b9a86019a9450505090830190600101610b4f565b5090979650505050505050565b602081526000610bd16020830184610b32565b9392505050565b600060408201848352602060408185015281855180845260608601915060608160051b870101935082870160005b82811015610c52577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0888703018452610c40868351610ac7565b95509284019290840190600101610c06565b509398975050505050505050565b600080600060408486031215610c7557600080fd5b83358015158114610c8557600080fd5b9250602084013567ffffffffffffffff811115610ca157600080fd5b610cad86828701610a39565b9497909650939450505050565b838152826020820152606060408201526000610cd96060830184610b32565b95945050505050565b600060208284031215610cf457600080fd5b813573ffffffffffffffffffffffffffffffffffffffff81168114610bd157600080fd5b600060208284031215610d2a57600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81833603018112610dc357600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1843603018112610e0257600080fd5b83018035915067ffffffffffffffff821115610e1d57600080fd5b602001915036819003821315610a7e57600080fd5b8183823760009101908152919050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc1833603018112610dc357600080fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa1833603018112610dc357600080fdfea2646970667358221220bb2b5c71a328032f97c676ae39a1ec2148d3e5d6f73d95e9b17910152d61f16264736f6c634300080c00331ca0edce47092c0f398cebf3ffc267f05c8e7076e3b89445e0fe50f6332273d4569ba01b0b9d000e19b24c5869b0fc3b22b0d6fa47cd63316875cbbd577d76e6fde086 --rpc-url "$SETTLEMENT_RPC_URL" echo "Deploying contracts..." ./node_modules/@symbioticfi/relay-contracts/script/relay-deploy.sh script/MyRelayDeploy.sol script/my-relay-deploy.toml --broadcast --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 -vvvvv echo 'Waiting for deployment completion...' until [ -f /deploy-data/deployment-completed.json ]; do sleep 2; done echo "Setting interval mining..." cast rpc --rpc-url "$ANVIL_RPC_URL" evm_setIntervalMining 1 cast rpc --rpc-url "$SETTLEMENT_RPC_URL" evm_setIntervalMining 1 echo "Mine a single block to finalize the deployment..." cast rpc --rpc-url "$ANVIL_RPC_URL" evm_mine cast rpc --rpc-url "$SETTLEMENT_RPC_URL" evm_mine echo "Deployment completed successfully!" # Create deployment completion marker echo "$(date): Deployment completed successfully" > /deploy-data/deployment-complete.marker echo "Deployment completion marker created" ```` ## File: network-scripts/deployer.Dockerfile ````dockerfile FROM ghcr.io/foundry-rs/foundry:v1.4.3 USER root # Install Python (needed by relay deployment helper scripts) together with tomli fallback. RUN set -eux; \ if command -v apt-get >/dev/null 2>&1; then \ apt-get update; \ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ python3 \ python3-venv \ python3-pip; \ rm -rf /var/lib/apt/lists/*; \ elif command -v apk >/dev/null 2>&1; then \ apk add --no-cache \ python3 \ py3-virtualenv \ py3-pip; \ else \ echo "Unable to determine package manager for Python installation." >&2; \ exit 1; \ fi; \ python3 -m pip install --no-cache-dir tomli; \ python3 --version USER foundry ```` ## File: network-scripts/genesis-generator.sh ````bash #!/bin/sh echo 'Waiting for deployment completion...' until [ -f /deploy-data/deployment-complete.marker ]; do sleep 2; done DRIVER_ADDRESS=0x43C27243F96591892976FFf886511807B65a33d5 MAX_RETRIES=50 RETRY_DELAY=2 attempt=1 while [ $attempt -le $MAX_RETRIES ]; do echo "Attempt $attempt of $MAX_RETRIES: Generating network genesis..." if /app/relay_utils network \ --chains http://anvil:8545,http://anvil-settlement:8546 \ --driver.address "$DRIVER_ADDRESS" \ --driver.chainid 31337 \ generate-genesis \ --commit \ --secret-keys 31337:0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80,31338:0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80; then echo 'Genesis generation completed successfully!' # Create genesis completion marker echo "$(date): Genesis generation completed successfully" > /deploy-data/genesis-complete.marker echo "Genesis completion marker created" echo "Waiting few seconds before exiting..." sleep 5 exit 0 else echo "Genesis generation failed on attempt $attempt" if [ $attempt -lt $MAX_RETRIES ]; then echo "Waiting $RETRY_DELAY seconds before retry..." sleep $RETRY_DELAY else echo "All $MAX_RETRIES attempts failed. Exiting with error." exit 1 fi attempt=$((attempt + 1)) fi done ```` ## File: network-scripts/sidecar-start.sh ````bash #!/bin/sh DRIVER_ADDRESS=0x43C27243F96591892976FFf886511807B65a33d5 cat > /tmp/sidecar.yaml << EOFCONFIG # Logging log: level: "debug" mode: "pretty" # API Server Configuration api: listen: ":8080" # Metrics Configuration metrics: pprof: true # Driver Contract driver: chain-id: 31337 address: "$DRIVER_ADDRESS" # P2P Configuration p2p: listen: "/ip4/0.0.0.0/tcp/8880" bootnodes: - /dns4/relay-sidecar-1/tcp/8880/p2p/16Uiu2HAmFUiPYAJ7bE88Q8d7Kznrw5ifrje2e5QFyt7uFPk2G3iR dht-mode: "server" mdns: true # EVM Configuration evm: chains: - "http://anvil:8545" - "http://anvil-settlement:8546" max-calls: 30 EOFCONFIG exec /app/relay_sidecar --config /tmp/sidecar.yaml --secret-keys "$1" --storage-dir "$2" ```` ## File: network-scripts/sum-node-start.sh ````bash #!/bin/sh SUMTASK_ADDRESS=0xDf12251aD82BF1eb0E0951AD15d37AE5ED3Ac1dF SETTLEMENT_SUMTASK_ADDRESS=0xDf12251aD82BF1eb0E0951AD15d37AE5ED3Ac1dF exec /app/sum-node --evm-rpc-urls http://anvil:8545,http://anvil-settlement:8546 --relay-api-url "$1" --contract-addresses "$SUMTASK_ADDRESS,$SETTLEMENT_SUMTASK_ADDRESS" --private-key "$2" --log-level info ```` ## File: off-chain/abis/SumTask.abi.json ````json [ { "type": "constructor", "inputs": [{ "name": "_settlement", "type": "address", "internalType": "address" }], "stateMutability": "nonpayable" }, { "type": "function", "name": "TASK_EXPIRY", "inputs": [], "outputs": [{ "name": "", "type": "uint32", "internalType": "uint32" }], "stateMutability": "view" }, { "type": "function", "name": "createTask", "inputs": [ { "name": "numberA", "type": "uint256", "internalType": "uint256" }, { "name": "numberB", "type": "uint256", "internalType": "uint256" } ], "outputs": [{ "name": "taskId", "type": "bytes32", "internalType": "bytes32" }], "stateMutability": "nonpayable" }, { "type": "function", "name": "getTaskStatus", "inputs": [{ "name": "taskId", "type": "bytes32", "internalType": "bytes32" }], "outputs": [ { "name": "", "type": "uint8", "internalType": "enum SumTask.TaskStatus" } ], "stateMutability": "view" }, { "type": "function", "name": "nonce", "inputs": [], "outputs": [{ "name": "", "type": "uint256", "internalType": "uint256" }], "stateMutability": "view" }, { "type": "function", "name": "respondTask", "inputs": [ { "name": "taskId", "type": "bytes32", "internalType": "bytes32" }, { "name": "result", "type": "uint256", "internalType": "uint256" }, { "name": "epoch", "type": "uint48", "internalType": "uint48" }, { "name": "proof", "type": "bytes", "internalType": "bytes" } ], "outputs": [], "stateMutability": "nonpayable" }, { "type": "function", "name": "responses", "inputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], "outputs": [ { "name": "answeredAt", "type": "uint48", "internalType": "uint48" }, { "name": "answer", "type": "uint256", "internalType": "uint256" } ], "stateMutability": "view" }, { "type": "function", "name": "settlement", "inputs": [], "outputs": [ { "name": "", "type": "address", "internalType": "contract ISettlement" } ], "stateMutability": "view" }, { "type": "function", "name": "tasks", "inputs": [{ "name": "", "type": "bytes32", "internalType": "bytes32" }], "outputs": [ { "name": "numberA", "type": "uint256", "internalType": "uint256" }, { "name": "numberB", "type": "uint256", "internalType": "uint256" }, { "name": "nonce", "type": "uint256", "internalType": "uint256" }, { "name": "createdAt", "type": "uint48", "internalType": "uint48" } ], "stateMutability": "view" }, { "type": "event", "name": "CreateTask", "inputs": [ { "name": "taskId", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "task", "type": "tuple", "indexed": false, "internalType": "struct SumTask.Task", "components": [ { "name": "numberA", "type": "uint256", "internalType": "uint256" }, { "name": "numberB", "type": "uint256", "internalType": "uint256" }, { "name": "nonce", "type": "uint256", "internalType": "uint256" }, { "name": "createdAt", "type": "uint48", "internalType": "uint48" } ] } ], "anonymous": false }, { "type": "event", "name": "RespondTask", "inputs": [ { "name": "taskId", "type": "bytes32", "indexed": true, "internalType": "bytes32" }, { "name": "response", "type": "tuple", "indexed": false, "internalType": "struct SumTask.Response", "components": [ { "name": "answeredAt", "type": "uint48", "internalType": "uint48" }, { "name": "answer", "type": "uint256", "internalType": "uint256" } ] } ], "anonymous": false }, { "type": "error", "name": "AlreadyResponded", "inputs": [] }, { "type": "error", "name": "InvalidQuorumSignature", "inputs": [] }, { "type": "error", "name": "InvalidVerifyingEpoch", "inputs": [] } ] ```` ## File: off-chain/cmd/benchmark/main.go ````go package main ⋮---- import ( "bytes" "context" "crypto/rand" "fmt" "log/slog" "math/big" "os" "os/exec" "strings" "time" "github.com/samber/lo" "sum/internal/utils" "github.com/ethereum/go-ethereum/common" "github.com/go-errors/errors" v1 "github.com/symbioticfi/relay/api/client/v1" "golang.org/x/sync/errgroup" ) ⋮---- "bytes" "context" "crypto/rand" "fmt" "log/slog" "math/big" "os" "os/exec" "strings" "time" ⋮---- "github.com/samber/lo" ⋮---- "sum/internal/utils" ⋮---- "github.com/ethereum/go-ethereum/common" "github.com/go-errors/errors" v1 "github.com/symbioticfi/relay/api/client/v1" "golang.org/x/sync/errgroup" ⋮---- type processes []process ⋮---- type process struct { args []string cmd *exec.Cmd stdOut *bytes.Buffer stdErr *bytes.Buffer apiAddr string relayClient *v1.SymbioticClient runSeparately bool } ⋮---- const ( operatorsCount = 3 numberOfSignRequests = 1000 sizeOfMessageBytes = 320 ) ⋮---- func main() ⋮---- func run(ctx context.Context) error ⋮---- func sendRequestAndWait(ctx context.Context, prs processes, nRequests int) *errors.Error ⋮---- func runProcesses(ctx context.Context, runSeparately bool) (processes, error) ⋮---- var prs processes ⋮---- func (prs processes) waitServerStarted(ctx context.Context) ⋮---- var startedCound int ⋮---- func (prs processes) stopProcesses() ⋮---- // Send an interrupt signal for a graceful shutdown, that is equivalent to pressing Ctrl+C. ⋮---- // If signaling fails, you can resort to killing the process forcefully. ⋮---- func (prs processes) sendMessageToAllRelays(ctx context.Context, epoch uint64) (string, error) ⋮---- var requestID string ⋮---- func sendSignMessageRequest(ctx context.Context, pr process, message []byte, epoch uint64) (string, error) ⋮---- func randomMessage(n int) []byte ⋮---- func (prs processes) printErrLogs() ```` ## File: off-chain/cmd/node/main.go ````go package main ⋮---- import ( "context" "encoding/json" "fmt" "log/slog" "math/big" "os" "os/signal" "syscall" "time" "github.com/ethereum/go-ethereum/core/types" "sum/internal/utils" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" v1 "github.com/symbioticfi/relay/api/client/v1" "sum/internal/contracts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" "github.com/go-errors/errors" "github.com/spf13/cobra" ) ⋮---- "context" "encoding/json" "fmt" "log/slog" "math/big" "os" "os/signal" "syscall" "time" ⋮---- "github.com/ethereum/go-ethereum/core/types" ⋮---- "sum/internal/utils" ⋮---- "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" v1 "github.com/symbioticfi/relay/api/client/v1" ⋮---- "sum/internal/contracts" ⋮---- "github.com/ethereum/go-ethereum/common" ⋮---- "github.com/ethereum/go-ethereum/ethclient" "github.com/go-errors/errors" "github.com/spf13/cobra" ⋮---- const ( TaskCreated uint8 = iota TaskResponded TaskExpired TaskNotFound ) ⋮---- type config struct { relayApiURL string evmRpcURLs []string contractAddresses []string privateKey string logLevel string } ⋮---- var relayClient *v1.SymbioticClient var evmClients map[int64]*ethclient.Client var sumContracts map[int64]*contracts.SumTask var lastBlocks map[int64]uint64 ⋮---- func main() ⋮---- func run() error ⋮---- var cfg config ⋮---- type TaskState struct { ChainID int64 Task contracts.SumTaskTask Result *big.Int SigEpoch int64 SigRequestID string AggProof []byte Statuses map[int64]uint8 } ⋮---- var tasks map[common.Hash]TaskState ⋮---- // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ Use: "sum-node", SilenceUsage: true, SilenceErrors: true, RunE: func(cmd *cobra.Command, args []string) error { switch cfg.logLevel { case "debug": slog.SetLogLoggerLevel(slog.LevelDebug) case "info": slog.SetLogLoggerLevel(slog.LevelInfo) case "warn": slog.SetLogLoggerLevel(slog.LevelWarn) case "error": slog.SetLogLoggerLevel(slog.LevelError) } ctx := signalContext(context.Background()) var err error conn, err := utils.GetGRPCConnection(cfg.relayApiURL) if err != nil { return errors.Errorf("failed to create relay client: %w", err) } relayClient = v1.NewSymbioticClient(conn) if len(cfg.evmRpcURLs) == 0 { return errors.Errorf("no RPC URLs provided") } if len(cfg.contractAddresses) != len(cfg.evmRpcURLs) { return errors.Errorf("mismatched lengths: evm-rpc-urls=%d, contract-addresses=%d", len(cfg.evmRpcURLs), len(cfg.contractAddresses)) } evmClients = make(map[int64]*ethclient.Client) sumContracts = make(map[int64]*contracts.SumTask) tasks = make(map[common.Hash]TaskState) lastBlocks = make(map[int64]uint64) for i, evmRpcURL := range cfg.evmRpcURLs { evmClient, err := ethclient.DialContext(ctx, evmRpcURL) if err != nil { return errors.Errorf("failed to connect to RPC URL '%s': %w", evmRpcURL, err) } chainID, err := evmClient.ChainID(ctx) if err != nil { return errors.Errorf("failed to get chain ID from RPC URL '%s': %w", evmRpcURL, err) } addr := common.HexToAddress(cfg.contractAddresses[i]) sumContract, err := contracts.NewSumTask(addr, evmClient) if err != nil { return errors.Errorf("failed to create sum contract for %s on chain %d: %w", addr.Hex(), chainID, err) } evmClients[chainID.Int64()] = evmClient sumContracts[chainID.Int64()] = sumContract finalizedBlockNumber, err := getFinalizedBlockNumber(ctx, evmClient) if err != nil { return errors.Errorf("failed to get finalized block number for chain %d: %w", chainID, err) } lastBlocks[chainID.Int64()] = finalizedBlockNumber slog.InfoContext(ctx, "Initialized chain", "chainID", chainID, "finalizedBlock", finalizedBlockNumber, "startBlock", lastBlocks[chainID.Int64()]) } ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() for { select { case <-ticker.C: for chainID, evmClient := range evmClients { endBlockNumber, err := getFinalizedBlockNumber(ctx, evmClient) if err != nil { return errors.Errorf("failed to get finalized block number for chain %d: %w", chainID, err) } lastBlock := lastBlocks[chainID] if endBlockNumber < lastBlock { slog.DebugContext(ctx, "Finalized block number is behind last processed block, skipping", "chainID", chainID, "finalizedBlock", endBlockNumber, "lastProcessedBlock", lastBlock) continue } slog.DebugContext(ctx, "Fetching events", "chainID", chainID, "fromBlock", lastBlock, "toBlock", endBlockNumber) events, err := sumContracts[chainID].FilterCreateTask(&bind.FilterOpts{ Context: ctx, Start: lastBlock, End: &endBlockNumber, }, [][32]byte{}) if err != nil { return errors.Errorf("failed to filter new task created events: %w", err) } lastBlocks[chainID] = endBlockNumber + 1 err = processNewTasks(ctx, chainID, events) if err != nil { fmt.Printf("Error processing new task event: %v\n", err) } } err = fetchResults(ctx) if err != nil { fmt.Printf("Error fetching results: %v\n", err) } case <-ctx.Done(): return nil } } }, } ⋮---- var err error ⋮---- func getFinalizedBlockNumber(ctx context.Context, evmClient *ethclient.Client) (uint64, error) ⋮---- // Get finalized block and set starting point to 24 hours ago var raw json.RawMessage ⋮---- var head *types.Header ⋮---- func fetchResults(ctx context.Context) error ⋮---- // slog.InfoContext(ctx, "Failed to fetch aggregation proof", "err", err) ⋮---- func processProof(ctx context.Context, taskID common.Hash) error ⋮---- func processNewTasks(ctx context.Context, chainID int64, iter *contracts.SumTaskCreateTaskIterator) error ⋮---- // skip if task is not in created state ⋮---- func signalContext(ctx context.Context) context.Context ```` ## File: off-chain/internal/contracts/sumTask.go ````go // Code generated - DO NOT EDIT. // This file is a generated binding and any manual changes will be lost. ⋮---- package contracts ⋮---- import ( "errors" "math/big" "strings" ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/event" ) ⋮---- "errors" "math/big" "strings" ⋮---- ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/event" ⋮---- // Reference imports to suppress errors if they are not otherwise used. var ( _ = errors.New _ = big.NewInt _ = strings.NewReader _ = ethereum.NotFound _ = bind.Bind _ = common.Big1 _ = types.BloomLookup _ = event.NewSubscription _ = abi.ConvertType ) ⋮---- // SumTaskResponse is an auto generated low-level Go binding around an user-defined struct. type SumTaskResponse struct { AnsweredAt *big.Int Answer *big.Int } ⋮---- // SumTaskTask is an auto generated low-level Go binding around an user-defined struct. type SumTaskTask struct { NumberA *big.Int NumberB *big.Int Nonce *big.Int CreatedAt *big.Int } ⋮---- // SumTaskMetaData contains all meta data concerning the SumTask contract. var SumTaskMetaData = &bind.MetaData{ ABI: "[{\"type\":\"constructor\",\"inputs\":[{\"name\":\"_settlement\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"TASK_EXPIRY\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"createTask\",\"inputs\":[{\"name\":\"numberA\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"numberB\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"taskId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"getTaskStatus\",\"inputs\":[{\"name\":\"taskId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint8\",\"internalType\":\"enumSumTask.TaskStatus\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"nonce\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"respondTask\",\"inputs\":[{\"name\":\"taskId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"result\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"epoch\",\"type\":\"uint48\",\"internalType\":\"uint48\"},{\"name\":\"proof\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"responses\",\"inputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"answeredAt\",\"type\":\"uint48\",\"internalType\":\"uint48\"},{\"name\":\"answer\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"settlement\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"contractISettlement\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"tasks\",\"inputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"numberA\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"numberB\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"nonce\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"createdAt\",\"type\":\"uint48\",\"internalType\":\"uint48\"}],\"stateMutability\":\"view\"},{\"type\":\"event\",\"name\":\"CreateTask\",\"inputs\":[{\"name\":\"taskId\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"task\",\"type\":\"tuple\",\"indexed\":false,\"internalType\":\"structSumTask.Task\",\"components\":[{\"name\":\"numberA\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"numberB\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"nonce\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"createdAt\",\"type\":\"uint48\",\"internalType\":\"uint48\"}]}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"RespondTask\",\"inputs\":[{\"name\":\"taskId\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"response\",\"type\":\"tuple\",\"indexed\":false,\"internalType\":\"structSumTask.Response\",\"components\":[{\"name\":\"answeredAt\",\"type\":\"uint48\",\"internalType\":\"uint48\"},{\"name\":\"answer\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]}],\"anonymous\":false},{\"type\":\"error\",\"name\":\"AlreadyResponded\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidQuorumSignature\",\"inputs\":[]},{\"type\":\"error\",\"name\":\"InvalidVerifyingEpoch\",\"inputs\":[]}]", } ⋮---- // SumTaskABI is the input ABI used to generate the binding from. // Deprecated: Use SumTaskMetaData.ABI instead. var SumTaskABI = SumTaskMetaData.ABI ⋮---- // SumTask is an auto generated Go binding around an Ethereum contract. type SumTask struct { SumTaskCaller // Read-only binding to the contract SumTaskTransactor // Write-only binding to the contract SumTaskFilterer // Log filterer for contract events } ⋮---- SumTaskCaller // Read-only binding to the contract SumTaskTransactor // Write-only binding to the contract SumTaskFilterer // Log filterer for contract events ⋮---- // SumTaskCaller is an auto generated read-only Go binding around an Ethereum contract. type SumTaskCaller struct { contract *bind.BoundContract // Generic contract wrapper for the low level calls } ⋮---- contract *bind.BoundContract // Generic contract wrapper for the low level calls ⋮---- // SumTaskTransactor is an auto generated write-only Go binding around an Ethereum contract. type SumTaskTransactor struct { contract *bind.BoundContract // Generic contract wrapper for the low level calls } ⋮---- // SumTaskFilterer is an auto generated log filtering Go binding around an Ethereum contract events. type SumTaskFilterer struct { contract *bind.BoundContract // Generic contract wrapper for the low level calls } ⋮---- // SumTaskSession is an auto generated Go binding around an Ethereum contract, // with pre-set call and transact options. type SumTaskSession struct { Contract *SumTask // Generic contract binding to set the session for CallOpts bind.CallOpts // Call options to use throughout this session TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session } ⋮---- Contract *SumTask // Generic contract binding to set the session for CallOpts bind.CallOpts // Call options to use throughout this session TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session ⋮---- // SumTaskCallerSession is an auto generated read-only Go binding around an Ethereum contract, // with pre-set call options. type SumTaskCallerSession struct { Contract *SumTaskCaller // Generic contract caller binding to set the session for CallOpts bind.CallOpts // Call options to use throughout this session } ⋮---- Contract *SumTaskCaller // Generic contract caller binding to set the session for CallOpts bind.CallOpts // Call options to use throughout this session ⋮---- // SumTaskTransactorSession is an auto generated write-only Go binding around an Ethereum contract, // with pre-set transact options. type SumTaskTransactorSession struct { Contract *SumTaskTransactor // Generic contract transactor binding to set the session for TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session } ⋮---- Contract *SumTaskTransactor // Generic contract transactor binding to set the session for TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session ⋮---- // SumTaskRaw is an auto generated low-level Go binding around an Ethereum contract. type SumTaskRaw struct { Contract *SumTask // Generic contract binding to access the raw methods on } ⋮---- Contract *SumTask // Generic contract binding to access the raw methods on ⋮---- // SumTaskCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. type SumTaskCallerRaw struct { Contract *SumTaskCaller // Generic read-only contract binding to access the raw methods on } ⋮---- Contract *SumTaskCaller // Generic read-only contract binding to access the raw methods on ⋮---- // SumTaskTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. type SumTaskTransactorRaw struct { Contract *SumTaskTransactor // Generic write-only contract binding to access the raw methods on } ⋮---- Contract *SumTaskTransactor // Generic write-only contract binding to access the raw methods on ⋮---- // NewSumTask creates a new instance of SumTask, bound to a specific deployed contract. func NewSumTask(address common.Address, backend bind.ContractBackend) (*SumTask, error) ⋮---- // NewSumTaskCaller creates a new read-only instance of SumTask, bound to a specific deployed contract. func NewSumTaskCaller(address common.Address, caller bind.ContractCaller) (*SumTaskCaller, error) ⋮---- // NewSumTaskTransactor creates a new write-only instance of SumTask, bound to a specific deployed contract. func NewSumTaskTransactor(address common.Address, transactor bind.ContractTransactor) (*SumTaskTransactor, error) ⋮---- // NewSumTaskFilterer creates a new log filterer instance of SumTask, bound to a specific deployed contract. func NewSumTaskFilterer(address common.Address, filterer bind.ContractFilterer) (*SumTaskFilterer, error) ⋮---- // bindSumTask binds a generic wrapper to an already deployed contract. func bindSumTask(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) ⋮---- // Call invokes the (constant) contract method with params as input values and // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. func (_SumTask *SumTaskRaw) Call(opts *bind.CallOpts, result *[]interface ⋮---- // Transfer initiates a plain transaction to move funds to the contract, calling // its default method if one is available. func (_SumTask *SumTaskRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) ⋮---- // Transact invokes the (paid) contract method with params as input values. func (_SumTask *SumTaskRaw) Transact(opts *bind.TransactOpts, method string, params ...interface ⋮---- // TASKEXPIRY is a free data retrieval call binding the contract method 0x240697b6. // // Solidity: function TASK_EXPIRY() view returns(uint32) func (_SumTask *SumTaskCaller) TASKEXPIRY(opts *bind.CallOpts) (uint32, error) ⋮---- var out []interface{} ⋮---- // GetTaskStatus is a free data retrieval call binding the contract method 0x2bf6cc79. ⋮---- // Solidity: function getTaskStatus(bytes32 taskId) view returns(uint8) func (_SumTask *SumTaskCaller) GetTaskStatus(opts *bind.CallOpts, taskId [32]byte) (uint8, error) ⋮---- // Nonce is a free data retrieval call binding the contract method 0xaffed0e0. ⋮---- // Solidity: function nonce() view returns(uint256) func (_SumTask *SumTaskCaller) Nonce(opts *bind.CallOpts) (*big.Int, error) ⋮---- // Responses is a free data retrieval call binding the contract method 0x72164a6c. ⋮---- // Solidity: function responses(bytes32 ) view returns(uint48 answeredAt, uint256 answer) func (_SumTask *SumTaskCaller) Responses(opts *bind.CallOpts, arg0 [32]byte) (struct ⋮---- // Settlement is a free data retrieval call binding the contract method 0x51160630. ⋮---- // Solidity: function settlement() view returns(address) func (_SumTask *SumTaskCaller) Settlement(opts *bind.CallOpts) (common.Address, error) ⋮---- // Tasks is a free data retrieval call binding the contract method 0xe579f500. ⋮---- // Solidity: function tasks(bytes32 ) view returns(uint256 numberA, uint256 numberB, uint256 nonce, uint48 createdAt) func (_SumTask *SumTaskCaller) Tasks(opts *bind.CallOpts, arg0 [32]byte) (struct ⋮---- // CreateTask is a paid mutator transaction binding the contract method 0xe75b2378. ⋮---- // Solidity: function createTask(uint256 numberA, uint256 numberB) returns(bytes32 taskId) func (_SumTask *SumTaskTransactor) CreateTask(opts *bind.TransactOpts, numberA *big.Int, numberB *big.Int) (*types.Transaction, error) ⋮---- // RespondTask is a paid mutator transaction binding the contract method 0xc92914cd. ⋮---- // Solidity: function respondTask(bytes32 taskId, uint256 result, uint48 epoch, bytes proof) returns() func (_SumTask *SumTaskTransactor) RespondTask(opts *bind.TransactOpts, taskId [32]byte, result *big.Int, epoch *big.Int, proof []byte) (*types.Transaction, error) ⋮---- // SumTaskCreateTaskIterator is returned from FilterCreateTask and is used to iterate over the raw logs and unpacked data for CreateTask events raised by the SumTask contract. type SumTaskCreateTaskIterator struct { Event *SumTaskCreateTask // Event containing the contract specifics and raw log contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data logs chan types.Log // Log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration } ⋮---- Event *SumTaskCreateTask // Event containing the contract specifics and raw log ⋮---- contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data ⋮---- logs chan types.Log // Log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration ⋮---- // Next advances the iterator to the subsequent event, returning whether there // are any more events found. In case of a retrieval or parsing error, false is // returned and Error() can be queried for the exact failure. func (it *SumTaskCreateTaskIterator) Next() bool ⋮---- // If the iterator failed, stop iterating ⋮---- // If the iterator completed, deliver directly whatever's available ⋮---- // Iterator still in progress, wait for either a data or an error event ⋮---- // Error returns any retrieval or parsing error occurred during filtering. func (it *SumTaskCreateTaskIterator) Error() error ⋮---- // Close terminates the iteration process, releasing any pending underlying // resources. func (it *SumTaskCreateTaskIterator) Close() error ⋮---- // SumTaskCreateTask represents a CreateTask event raised by the SumTask contract. type SumTaskCreateTask struct { TaskId [32]byte Task SumTaskTask Raw types.Log // Blockchain specific contextual infos } ⋮---- Raw types.Log // Blockchain specific contextual infos ⋮---- // FilterCreateTask is a free log retrieval operation binding the contract event 0x091f91724daf92df11de4c7b494af095ec9c1325865974e9750636fbe5373677. ⋮---- // Solidity: event CreateTask(bytes32 indexed taskId, (uint256,uint256,uint256,uint48) task) func (_SumTask *SumTaskFilterer) FilterCreateTask(opts *bind.FilterOpts, taskId [][32]byte) (*SumTaskCreateTaskIterator, error) ⋮---- var taskIdRule []interface{} ⋮---- // WatchCreateTask is a free log subscription operation binding the contract event 0x091f91724daf92df11de4c7b494af095ec9c1325865974e9750636fbe5373677. ⋮---- func (_SumTask *SumTaskFilterer) WatchCreateTask(opts *bind.WatchOpts, sink chan<- *SumTaskCreateTask, taskId [][32]byte) (event.Subscription, error) ⋮---- // New log arrived, parse the event and forward to the user ⋮---- // ParseCreateTask is a log parse operation binding the contract event 0x091f91724daf92df11de4c7b494af095ec9c1325865974e9750636fbe5373677. ⋮---- func (_SumTask *SumTaskFilterer) ParseCreateTask(log types.Log) (*SumTaskCreateTask, error) ⋮---- // SumTaskRespondTaskIterator is returned from FilterRespondTask and is used to iterate over the raw logs and unpacked data for RespondTask events raised by the SumTask contract. type SumTaskRespondTaskIterator struct { Event *SumTaskRespondTask // Event containing the contract specifics and raw log contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data logs chan types.Log // Log channel receiving the found contract events sub ethereum.Subscription // Subscription for errors, completion and termination done bool // Whether the subscription completed delivering logs fail error // Occurred error to stop iteration } ⋮---- Event *SumTaskRespondTask // Event containing the contract specifics and raw log ⋮---- // SumTaskRespondTask represents a RespondTask event raised by the SumTask contract. type SumTaskRespondTask struct { TaskId [32]byte Response SumTaskResponse Raw types.Log // Blockchain specific contextual infos } ⋮---- Raw types.Log // Blockchain specific contextual infos ⋮---- // FilterRespondTask is a free log retrieval operation binding the contract event 0xfc4f6d5b11d191a9cdec60e7a5819b4695dba8d8365e7afb1f24b159ceb7e287. ⋮---- // Solidity: event RespondTask(bytes32 indexed taskId, (uint48,uint256) response) func (_SumTask *SumTaskFilterer) FilterRespondTask(opts *bind.FilterOpts, taskId [][32]byte) (*SumTaskRespondTaskIterator, error) ⋮---- // WatchRespondTask is a free log subscription operation binding the contract event 0xfc4f6d5b11d191a9cdec60e7a5819b4695dba8d8365e7afb1f24b159ceb7e287. ⋮---- func (_SumTask *SumTaskFilterer) WatchRespondTask(opts *bind.WatchOpts, sink chan<- *SumTaskRespondTask, taskId [][32]byte) (event.Subscription, error) ⋮---- // ParseRespondTask is a log parse operation binding the contract event 0xfc4f6d5b11d191a9cdec60e7a5819b4695dba8d8365e7afb1f24b159ceb7e287. ⋮---- func (_SumTask *SumTaskFilterer) ParseRespondTask(log types.Log) (*SumTaskRespondTask, error) ```` ## File: off-chain/internal/utils/util.go ````go package utils ⋮---- import ( "time" grpcmiddleware "github.com/grpc-ecosystem/go-grpc-middleware" "github.com/grpc-ecosystem/go-grpc-middleware/retry" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) ⋮---- "time" ⋮---- grpcmiddleware "github.com/grpc-ecosystem/go-grpc-middleware" "github.com/grpc-ecosystem/go-grpc-middleware/retry" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ⋮---- func GetGRPCConnection(address string) (*grpc.ClientConn, error) ```` ## File: off-chain/.dockerignore ```` # Git files .git .gitignore # Docker files Dockerfile .dockerignore # Documentation README.md *.md # IDE files .vscode/ .idea/ *.swp *.swo # OS files .DS_Store Thumbs.db # Logs *.log # Temporary files *.tmp *.temp # Test files *_test.go test/ # Build artifacts bin/ dist/ # Coverage files *.out coverage.txt # Go specific vendor/ ```` ## File: off-chain/Dockerfile ```` # Multi-stage build for the off-chain Go application # Build stage FROM golang:1.24.5-alpine AS builder # Install build dependencies RUN apk add --no-cache git ca-certificates tzdata # Set working directory WORKDIR /app # Copy go mod files COPY go.mod go.sum ./ # Download dependencies RUN go mod download # Copy source code COPY . . # Build the application with optimizations for production RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ -ldflags="-w -s" \ -o /app/sum-node \ ./cmd/node # Runtime stage FROM alpine:3.19 # Install runtime dependencies RUN apk add --no-cache ca-certificates tzdata # Set working directory WORKDIR /app # Copy binary from builder stage COPY --from=builder /app/sum-node . # Expose port if needed (adjust based on your application) # EXPOSE 8080 # Set the entrypoint ENTRYPOINT ["./sum-node"] # Default command (can be overridden) CMD ["--help"] ```` ## File: off-chain/go.mod ```` module sum go 1.24.5 require ( github.com/ethereum/go-ethereum v1.16.3 github.com/go-errors/errors v1.5.1 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/samber/lo v1.51.0 github.com/spf13/cobra v1.10.1 github.com/symbioticfi/relay v0.2.1-0.20250929084906-8a36673e5ad5 golang.org/x/sync v0.17.0 google.golang.org/grpc v1.75.1 ) require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/bits-and-blooms/bitset v1.24.0 // indirect github.com/consensys/gnark-crypto v0.19.0 // indirect github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect github.com/deckarep/golang-set/v2 v2.8.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/ethereum/c-kzg-4844/v2 v2.1.1 // indirect github.com/ethereum/go-verkle v0.2.2 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/holiman/uint256 v1.3.2 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/shirou/gopsutil v3.21.11+incompatible // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/supranational/blst v0.3.15 // indirect github.com/tklauser/go-sysconf v0.3.15 // indirect github.com/tklauser/numcpus v0.10.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect golang.org/x/crypto v0.42.0 // indirect golang.org/x/net v0.44.0 // indirect golang.org/x/sys v0.36.0 // indirect golang.org/x/text v0.29.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250908214217-97024824d090 // indirect google.golang.org/protobuf v1.36.9 // indirect ) ```` ## File: script/mocks/MockERC20.sol ```` // SPDX-License-Identifier: MIT ⋮---- import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; ⋮---- contract MockERC20 is ERC20 { ⋮---- function mint(address to, uint256 amount) public { ```` ## File: script/utils/BN254G2.sol ```` /** * @title Elliptic curve operations on twist points for alt_bn128 * @author Mustafa Al-Bassam (mus@musalbas.com) * @dev Homepage: https://github.com/musalbas/solidity-BN256G2 */ ⋮---- // WARNING: this code is used ONLY for testing purposes, DO NOT USE IN PRODUCTION ⋮---- /** * @notice Add two twist points * @param pt1xx Coefficient 1 of x on point 1 * @param pt1xy Coefficient 2 of x on point 1 * @param pt1yx Coefficient 1 of y on point 1 * @param pt1yy Coefficient 2 of y on point 1 * @param pt2xx Coefficient 1 of x on point 2 * @param pt2xy Coefficient 2 of x on point 2 * @param pt2yx Coefficient 1 of y on point 2 * @param pt2yy Coefficient 2 of y on point 2 * @return (pt3xx, pt3xy, pt3yx, pt3yy) */ function ECTwistAdd( ⋮---- /** * @notice Multiply a twist point by a scalar * @param s Scalar to multiply by * @param pt1xx Coefficient 1 of x * @param pt1xy Coefficient 2 of x * @param pt1yx Coefficient 1 of y * @param pt1yy Coefficient 2 of y * @return (pt2xx, pt2xy, pt2yx, pt2yy) */ function ECTwistMul(uint256 s, uint256 pt1xx, uint256 pt1xy, uint256 pt1yx, uint256 pt1yy) ⋮---- /** * @notice Get the field modulus * @return The field modulus */ function GetFieldModulus() internal pure returns (uint256) { ⋮---- function submod(uint256 a, uint256 b, uint256 n) internal pure returns (uint256) { ⋮---- function _FQ2Mul(uint256 xx, uint256 xy, uint256 yx, uint256 yy) internal pure returns (uint256, uint256) { ⋮---- function _FQ2Muc(uint256 xx, uint256 xy, uint256 c) internal pure returns (uint256, uint256) { ⋮---- function _FQ2Add(uint256 xx, uint256 xy, uint256 yx, uint256 yy) internal pure returns (uint256, uint256) { ⋮---- function _FQ2Sub(uint256 xx, uint256 xy, uint256 yx, uint256 yy) internal pure returns (uint256 rx, uint256 ry) { ⋮---- function _FQ2Div(uint256 xx, uint256 xy, uint256 yx, uint256 yy) internal view returns (uint256, uint256) { ⋮---- function _FQ2Inv(uint256 x, uint256 y) internal view returns (uint256, uint256) { ⋮---- function _isOnCurve(uint256 xx, uint256 xy, uint256 yx, uint256 yy) internal pure returns (bool) { ⋮---- function _modInv(uint256 a, uint256 n) internal view returns (uint256 result) { ⋮---- function _fromJacobian(uint256 pt1xx, uint256 pt1xy, uint256 pt1yx, uint256 pt1yy, uint256 pt1zx, uint256 pt1zy) ⋮---- function _ECTwistAddJacobian(_ECTwistAddJacobianArgs memory $) internal pure returns (uint256[6] memory pt3) { ⋮---- ($.pt2yx, $.pt2yy) = _FQ2Mul($.pt2yx, $.pt2yy, $.pt1zx, $.pt1zy); // U1 = y2 * z1 (pt3[PTYX], pt3[PTYY]) = _FQ2Mul($.pt1yx, $.pt1yy, $.pt2zx, $.pt2zy); // U2 = y1 * z2 ($.pt2xx, $.pt2xy) = _FQ2Mul($.pt2xx, $.pt2xy, $.pt1zx, $.pt1zy); // V1 = x2 * z1 (pt3[PTZX], pt3[PTZY]) = _FQ2Mul($.pt1xx, $.pt1xy, $.pt2zx, $.pt2zy); // V2 = x1 * z2 ⋮---- ($.pt2zx, $.pt2zy) = _FQ2Mul($.pt1zx, $.pt1zy, $.pt2zx, $.pt2zy); // W = z1 * z2 ($.pt1xx, $.pt1xy) = _FQ2Sub($.pt2yx, $.pt2yy, pt3[PTYX], pt3[PTYY]); // U = U1 - U2 ($.pt1yx, $.pt1yy) = _FQ2Sub($.pt2xx, $.pt2xy, pt3[PTZX], pt3[PTZY]); // V = V1 - V2 ($.pt1zx, $.pt1zy) = _FQ2Mul($.pt1yx, $.pt1yy, $.pt1yx, $.pt1yy); // V_squared = V * V ($.pt2yx, $.pt2yy) = _FQ2Mul($.pt1zx, $.pt1zy, pt3[PTZX], pt3[PTZY]); // V_squared_times_V2 = V_squared * V2 ($.pt1zx, $.pt1zy) = _FQ2Mul($.pt1zx, $.pt1zy, $.pt1yx, $.pt1yy); // V_cubed = V * V_squared (pt3[PTZX], pt3[PTZY]) = _FQ2Mul($.pt1zx, $.pt1zy, $.pt2zx, $.pt2zy); // newz = V_cubed * W ($.pt2xx, $.pt2xy) = _FQ2Mul($.pt1xx, $.pt1xy, $.pt1xx, $.pt1xy); // U * U ($.pt2xx, $.pt2xy) = _FQ2Mul($.pt2xx, $.pt2xy, $.pt2zx, $.pt2zy); // U * U * W ($.pt2xx, $.pt2xy) = _FQ2Sub($.pt2xx, $.pt2xy, $.pt1zx, $.pt1zy); // U * U * W - V_cubed ($.pt2zx, $.pt2zy) = _FQ2Muc($.pt2yx, $.pt2yy, 2); // 2 * V_squared_times_V2 ($.pt2xx, $.pt2xy) = _FQ2Sub($.pt2xx, $.pt2xy, $.pt2zx, $.pt2zy); // A = U * U * W - V_cubed - 2 * V_squared_times_V2 (pt3[PTXX], pt3[PTXY]) = _FQ2Mul($.pt1yx, $.pt1yy, $.pt2xx, $.pt2xy); // newx = V * A ($.pt1yx, $.pt1yy) = _FQ2Sub($.pt2yx, $.pt2yy, $.pt2xx, $.pt2xy); // V_squared_times_V2 - A ($.pt1yx, $.pt1yy) = _FQ2Mul($.pt1xx, $.pt1xy, $.pt1yx, $.pt1yy); // U * (V_squared_times_V2 - A) ($.pt1xx, $.pt1xy) = _FQ2Mul($.pt1zx, $.pt1zy, pt3[PTYX], pt3[PTYY]); // V_cubed * U2 (pt3[PTYX], pt3[PTYY]) = _FQ2Sub($.pt1yx, $.pt1yy, $.pt1xx, $.pt1xy); // newy = U * (V_squared_times_V2 - A) - V_cubed * U2 ⋮---- function _ECTwistDoubleJacobian( ⋮---- (pt2xx, pt2xy) = _FQ2Muc(pt1xx, pt1xy, 3); // 3 * x (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1xx, pt1xy); // W = 3 * x * x (pt1zx, pt1zy) = _FQ2Mul(pt1yx, pt1yy, pt1zx, pt1zy); // S = y * z (pt2yx, pt2yy) = _FQ2Mul(pt1xx, pt1xy, pt1yx, pt1yy); // x * y (pt2yx, pt2yy) = _FQ2Mul(pt2yx, pt2yy, pt1zx, pt1zy); // B = x * y * S (pt1xx, pt1xy) = _FQ2Mul(pt2xx, pt2xy, pt2xx, pt2xy); // W * W (pt2zx, pt2zy) = _FQ2Muc(pt2yx, pt2yy, 8); // 8 * B (pt1xx, pt1xy) = _FQ2Sub(pt1xx, pt1xy, pt2zx, pt2zy); // H = W * W - 8 * B (pt2zx, pt2zy) = _FQ2Mul(pt1zx, pt1zy, pt1zx, pt1zy); // S_squared = S * S (pt2yx, pt2yy) = _FQ2Muc(pt2yx, pt2yy, 4); // 4 * B (pt2yx, pt2yy) = _FQ2Sub(pt2yx, pt2yy, pt1xx, pt1xy); // 4 * B - H (pt2yx, pt2yy) = _FQ2Mul(pt2yx, pt2yy, pt2xx, pt2xy); // W * (4 * B - H) (pt2xx, pt2xy) = _FQ2Muc(pt1yx, pt1yy, 8); // 8 * y (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1yx, pt1yy); // 8 * y * y (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt2zx, pt2zy); // 8 * y * y * S_squared (pt2yx, pt2yy) = _FQ2Sub(pt2yx, pt2yy, pt2xx, pt2xy); // newy = W * (4 * B - H) - 8 * y * y * S_squared (pt2xx, pt2xy) = _FQ2Muc(pt1xx, pt1xy, 2); // 2 * H (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1zx, pt1zy); // newx = 2 * H * S (pt2zx, pt2zy) = _FQ2Mul(pt1zx, pt1zy, pt2zx, pt2zy); // S * S_squared (pt2zx, pt2zy) = _FQ2Muc(pt2zx, pt2zy, 8); // newz = 8 * S * S_squared ⋮---- function _ECTwistMulJacobian( ```` ## File: script/utils/sort_errors.py ````python #!/usr/bin/env python3 ⋮---- def _error_sort_key(error_line: str) -> str ⋮---- signature = error_line.strip()[len("error ") :] name = signature.split("(", 1)[0].strip() ⋮---- def _extract_entries(block_text: str) -> List[Tuple[int, int, str, int]] ⋮---- entries: List[Tuple[int, int, str, int]] = [] offset = 0 entry_start: Optional[int] = None last_content_end: Optional[int] = None current_error: Optional[str] = None ⋮---- def finalize() -> None ⋮---- entry_start = None last_content_end = None current_error = None ⋮---- newline_idx = block_text.find("\n", offset) ⋮---- line_end = len(block_text) ⋮---- line_end = newline_idx + 1 line = block_text[offset:line_end] stripped = line.strip() ⋮---- last_content_end = line_end offset = line_end ⋮---- entry_start = offset ⋮---- current_error = line.strip() ⋮---- def _sort_error_block(text: str) -> Tuple[str, bool] ⋮---- marker = "/* ERRORS */" search_pos = 0 changed = False ⋮---- marker_idx = text.find(marker, search_pos) ⋮---- block_start = text.find("\n", marker_idx) ⋮---- cursor = block_start first_block_idx: Optional[int] = None last_block_idx = block_start ⋮---- newline_idx = text.find("\n", cursor) ⋮---- line_end = len(text) ⋮---- line = text[cursor:line_end] ⋮---- cursor = line_end ⋮---- first_block_idx = cursor last_block_idx = line_end ⋮---- search_pos = cursor ⋮---- block_text = text[first_block_idx:last_block_idx] entries = _extract_entries(block_text) ⋮---- search_pos = last_block_idx ⋮---- prefix = block_text[:entries[0][0]] suffix = block_text[entries[-1][1]:] sorted_entries = sorted(entries, key=lambda item: (item[2], item[3])) sorted_chunks = [block_text[start:end].rstrip("\n") for start, end, _, _ in sorted_entries] ⋮---- new_block = prefix + "\n\n".join(sorted_chunks) ⋮---- stripped_suffix = suffix.lstrip("\n") ⋮---- text = text[:first_block_idx] + new_block + text[last_block_idx:] changed = True search_pos = first_block_idx + len(new_block) ⋮---- def sort_errors_in_file(path: Path) ⋮---- original = path.read_text(encoding="utf-8") ⋮---- def iter_solidity_files(targets: Iterable[Path]) -> Iterable[Path] ⋮---- def resolve_targets(path_args: Iterable[str]) -> List[Path] ⋮---- defaults = [Path(name) for name in ("src", "examples") if Path(name).exists()] ⋮---- def sort_from_stdin() -> None ⋮---- errors = sys.stdin.read() sorted_errors = sorted(x.strip() for x in errors.splitlines()) ⋮---- def main() -> int ⋮---- parser = argparse.ArgumentParser( ⋮---- args = parser.parse_args() ⋮---- targets = resolve_targets(args.paths) ```` ## File: script/utils/sort_imports.py ````python #!/usr/bin/env python3 ⋮---- FROM_IMPORT_RE = re.compile(r'from\s+["\']([^"\']+)["\']', re.IGNORECASE | re.MULTILINE) DIRECT_IMPORT_RE = re.compile(r'import\s+["\']([^"\']+)["\']', re.IGNORECASE | re.MULTILINE) WHITESPACE_RE = re.compile(r"\s+") ⋮---- @dataclass class ImportEntry ⋮---- start: int end: int text: str path: str order: int ⋮---- def extract_path(import_text: str) -> str ⋮---- match = FROM_IMPORT_RE.search(import_text) ⋮---- match = DIRECT_IMPORT_RE.search(import_text) ⋮---- def collect_import_entries(lines: Sequence[str]) -> List[ImportEntry] ⋮---- entries: List[ImportEntry] = [] index = 0 ⋮---- stripped = lines[index].lstrip() ⋮---- start = index statement_lines = [lines[index].rstrip()] ⋮---- end = index text = "\n".join(statement_lines) path = extract_path(text) ⋮---- def normalize_import_text(import_text: str) -> str ⋮---- """Flatten whitespace so multi-line imports sort identically to single-line ones.""" compact = WHITESPACE_RE.sub(" ", import_text.strip()) compact = compact.replace("{ ", "{").replace(" }", "}") compact = compact.replace("( ", "(").replace(" )", ")") ⋮---- def classify_import(path: str) -> str ⋮---- normalized = path.lstrip("./") lower = normalized.lower() ⋮---- library = normalized.split("/", 1)[0] ⋮---- def build_sorted_block(entries: Sequence[ImportEntry]) -> List[str] ⋮---- grouped: Dict[str, List[ImportEntry]] = defaultdict(list) ⋮---- ordered_groups: List[List[ImportEntry]] = [] ⋮---- external_groups = sorted( ⋮---- block_text = "\n\n".join("\n".join(entry.text for entry in group) for group in ordered_groups) ⋮---- def sort_imports_in_file(path: Path) -> None ⋮---- original_text = path.read_text(encoding="utf-8") has_trailing_newline = original_text.endswith("\n") lines = original_text.splitlines() ⋮---- entries = collect_import_entries(lines) ⋮---- block_start = entries[0].start block_end = entries[-1].end covered_indexes = set() ⋮---- new_block_lines = build_sorted_block(entries) new_lines = lines[:block_start] + new_block_lines + lines[block_end + 1 :] new_text = "\n".join(new_lines) ⋮---- def iter_solidity_files(targets: Iterable[Path]) -> Iterable[Path] ⋮---- def resolve_targets(path_args: Iterable[str]) -> List[Path] ⋮---- defaults = [Path(name) for name in ("src", "examples") if Path(name).exists()] ⋮---- def main() -> int ⋮---- parser = argparse.ArgumentParser( ⋮---- args = parser.parse_args() ⋮---- targets = resolve_targets(args.paths) ```` ## File: script/my-relay-deploy.toml ````toml [31337] endpoint_url = "http://anvil:8545" [31338] endpoint_url = "http://anvil-settlement:8546" [1234567890] endpoint_url = "" keyRegistry = 31337 votingPowerProvider = [31337] settlement = [ 31337, 31338, ] valSetDriver = 31337 ```` ## File: script/MyRelayDeploy.sol ```` // SPDX-License-Identifier: MIT ⋮---- import {Vm, VmSafe} from "forge-std/Vm.sol"; ⋮---- import {VotingPowers} from "../src/symbiotic/VotingPowers.sol"; import {KeyRegistry} from "../src/symbiotic/KeyRegistry.sol"; import {Driver} from "../src/symbiotic/Driver.sol"; import {Settlement} from "../src/symbiotic/Settlement.sol"; import {SumTask} from "../src/SumTask.sol"; import {MockERC20} from "./mocks/MockERC20.sol"; import {BN254G2} from "./utils/BN254G2.sol"; ⋮---- import {RelayDeploy} from "@symbioticfi/relay-contracts/script/RelayDeploy.sol"; ⋮---- import {BN254} from "@symbioticfi/relay-contracts/src/libraries/utils/BN254.sol"; import {KeyTags} from "@symbioticfi/relay-contracts/src/libraries/utils/KeyTags.sol"; import {KeyEcdsaSecp256k1} from "@symbioticfi/relay-contracts/src/libraries/keys/KeyEcdsaSecp256k1.sol"; import {KeyBlsBn254} from "@symbioticfi/relay-contracts/src/libraries/keys/KeyBlsBn254.sol"; import { SigVerifierBlsBn254Simple } from "@symbioticfi/relay-contracts/src/modules/settlement/sig-verifiers/SigVerifierBlsBn254Simple.sol"; import { SigVerifierBlsBn254ZK } from "@symbioticfi/relay-contracts/src/modules/settlement/sig-verifiers/SigVerifierBlsBn254ZK.sol"; import { IVotingPowerProvider } from "@symbioticfi/relay-contracts/src/interfaces/modules/voting-power/IVotingPowerProvider.sol"; import { IOpNetVaultAutoDeploy } from "@symbioticfi/relay-contracts/src/interfaces/modules/voting-power/extensions/IOpNetVaultAutoDeploy.sol"; import {INetworkManager} from "@symbioticfi/relay-contracts/src/interfaces/modules/base/INetworkManager.sol"; import {IOzEIP712} from "@symbioticfi/relay-contracts/src/interfaces/modules/base/IOzEIP712.sol"; import {IOzOwnable} from "@symbioticfi/relay-contracts/src/interfaces/modules/common/permissions/IOzOwnable.sol"; import { IKeyRegistry, KEY_TYPE_BLS_BN254, KEY_TYPE_ECDSA_SECP256K1 } from "@symbioticfi/relay-contracts/src/interfaces/modules/key-registry/IKeyRegistry.sol"; import {IValSetDriver} from "@symbioticfi/relay-contracts/src/interfaces/modules/valset-driver/IValSetDriver.sol"; import {IEpochManager} from "@symbioticfi/relay-contracts/src/interfaces/modules/valset-driver/IEpochManager.sol"; import {ISettlement} from "@symbioticfi/relay-contracts/src/interfaces/modules/settlement/ISettlement.sol"; ⋮---- import {Network, INetwork} from "@symbioticfi/network/src/Network.sol"; ⋮---- import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IVault} from "@symbioticfi/core/src/interfaces/vault/IVault.sol"; import {INetworkMiddlewareService} from "@symbioticfi/core/src/interfaces/service/INetworkMiddlewareService.sol"; import {Logs} from "@symbioticfi/core/script/utils/Logs.sol"; import {SymbioticCoreConstants} from "@symbioticfi/core/test/integration/SymbioticCoreConstants.sol"; ⋮---- contract MyRelayDeploy is RelayDeploy { ⋮---- // Configurable constants ⋮---- uint48 internal constant SLASHING_WINDOW = 1 days; // 1 day uint208 internal constant MAX_VALIDATORS_COUNT = 1000; // 1000 validators uint256 internal constant MAX_VOTING_POWER = 2 ** 247; // no max limit uint256 internal constant MIN_INCLUSION_VOTING_POWER = 0; // include anyone uint248 internal constant QUORUM_THRESHOLD = (uint248(1e18) * 2) / 3 + 1; // 2/3 + 1 uint8 internal constant REQUIRED_KEY_TAG = 15; // 15 is the default key tag (BLS-BN254/15) ⋮---- uint8 internal constant REQUIRED_KEY_TAG_ECDSA = 16; // 16 is the default key tag for ecdsa keys (ECDSA-SECP256K1/0) ⋮---- // CREATE3 salts ⋮---- function getDeployerAddress() internal returns (address deployer) { ⋮---- function getStakingToken() internal returns (address) { ⋮---- function getNetwork() internal returns (address) { ⋮---- function _keyRegistryParams() internal override returns (address implementation, bytes memory initData) { ⋮---- function _votingPowerProviderParams() internal override returns (address implementation, bytes memory initData) { ⋮---- function _settlementParams() internal override returns (address implementation, bytes memory initData) { ⋮---- function _valSetDriverParams() internal override returns (address implementation, bytes memory initData) { ⋮---- function runDeployKeyRegistry() public override { ⋮---- function runDeployVotingPowerProvider() public override { ⋮---- function runDeploySettlement() public override { ⋮---- function runDeployValSetDriver() public override { ⋮---- function configureOperatorKeys(uint256 index) public { ⋮---- // Register BLS-BN254 key with tag 11, not related to header key tag ⋮---- // Register ECDSA key ⋮---- function registerOperator(uint256 index, uint256 stakeAmount) public { ⋮---- function fundOperators() public { ⋮---- function getOperator(uint256 index) public returns (VmSafe.Wallet memory operator) { // deterministic operator private key ⋮---- function getBLSKeys(uint256 privateKey) public returns (BN254.G1Point memory, BN254.G2Point memory) { ⋮---- function printOperatorsInfo() public { ```` ## File: snapshots/gas.txt ```` No files changed, compilation skipped Ran 2 tests for test/SumTask.t.sol:SumTaskTest [PASS] test_CreateTask() (gas: 123340) [PASS] test_RespondToTask() (gas: 205638) Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 3.73ms (1.36ms CPU time) ╭----------------------------------+-----------------+--------+--------+--------+---------╮ | src/SumTask.sol:SumTask Contract | | | | | | +=========================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------+-----------------+--------+--------+--------+---------| | 0 | 2587 | | | | | |----------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |----------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------+-----------------+--------+--------+--------+---------| | createTask | 115256 | 115256 | 115256 | 115256 | 2 | |----------------------------------+-----------------+--------+--------+--------+---------| | nonce | 2294 | 2294 | 2294 | 2294 | 1 | |----------------------------------+-----------------+--------+--------+--------+---------| | respondTask | 78984 | 78984 | 78984 | 78984 | 1 | |----------------------------------+-----------------+--------+--------+--------+---------| | responses | 4665 | 4665 | 4665 | 4665 | 1 | ╰----------------------------------+-----------------+--------+--------+--------+---------╯ ╭------------------------------------------------------+-----------------+------+--------+------+---------╮ | test/mock/SettlementMock.sol:SettlementMock Contract | | | | | | +=========================================================================================================+ | Deployment Cost | Deployment Size | | | | | |------------------------------------------------------+-----------------+------+--------+------+---------| | 226020 | 827 | | | | | |------------------------------------------------------+-----------------+------+--------+------+---------| | | | | | | | |------------------------------------------------------+-----------------+------+--------+------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |------------------------------------------------------+-----------------+------+--------+------+---------| | getCaptureTimestampFromValSetHeaderAt | 372 | 372 | 372 | 372 | 1 | |------------------------------------------------------+-----------------+------+--------+------+---------| | getQuorumThresholdFromValSetHeaderAt | 308 | 308 | 308 | 308 | 1 | |------------------------------------------------------+-----------------+------+--------+------+---------| | getRequiredKeyTagFromValSetHeaderAt | 438 | 438 | 438 | 438 | 1 | |------------------------------------------------------+-----------------+------+--------+------+---------| | verifyQuorumSigAt | 1249 | 1249 | 1249 | 1249 | 1 | ╰------------------------------------------------------+-----------------+------+--------+------+---------╯ Ran 1 test suite in 6.23ms (3.73ms CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests) ```` ## File: snapshots/sizes.txt ```` No files changed, compilation skipped ╭--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------╮ | Contract | Runtime Size (B) | Initcode Size (B) | Runtime Margin (B) | Initcode Margin (B) | +====================================================================================================================================================================================+ | Address | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | BN254 | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | BN254G2 | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Bytes | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Checkpoints (node_modules/@openzeppelin/contracts/utils/structs/Checkpoints.sol) | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Checkpoints (node_modules/@symbioticfi/core/src/contracts/libraries/Checkpoints.sol) | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Checkpoints (node_modules/@symbioticfi/relay-contracts/src/libraries/structs/Checkpoints.sol) | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Create2 | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | CreateXWrapper | 2,764 | 2,792 | 21,812 | 46,360 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Driver | 17,519 | 17,547 | 7,057 | 31,605 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | ECDSA | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | ERC1967Proxy | 122 | 934 | 24,454 | 48,218 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | ERC1967Utils | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | ERC4626Math | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Errors | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | ExtraDataStorageHelper | 123 | 173 | 24,453 | 48,979 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | FeeOnTransferToken | 1,785 | 3,197 | 22,791 | 45,955 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | InputNormalizer | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | KeyBlsBn254 | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | KeyEcdsaSecp256k1 | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | KeyRegistry (node_modules/@symbioticfi/relay-contracts/src/modules/key-registry/KeyRegistry.sol) | 14,998 | 15,026 | 9,578 | 34,126 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | KeyRegistry (src/symbiotic/KeyRegistry.sol) | 15,267 | 15,295 | 9,309 | 33,857 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | KeyTags | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Logs | 4,290 | 4,342 | 20,286 | 44,810 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Math | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | MessageHashUtils | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | MockERC20 | 1,821 | 2,911 | 22,755 | 46,241 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Network | 12,358 | 12,550 | 12,218 | 36,602 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | OpNetVaultAutoDeployLogic | 5,693 | 5,745 | 18,883 | 43,407 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Panic | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | PersistentSet | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | ProxyAdmin | 977 | 1,213 | 23,599 | 47,939 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | SafeCast | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | SafeERC20 | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Settlement (src/symbiotic/Settlement.sol) | 11,798 | 11,826 | 12,778 | 37,326 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | SettlementMock | 799 | 827 | 23,777 | 48,325 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | SigBlsBn254 | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | SigEcdsaSecp256k1 | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | SigVerifierBlsBn254Simple | 4,946 | 4,974 | 19,630 | 44,178 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | SigVerifierBlsBn254ZK | 2,694 | 3,774 | 21,882 | 45,378 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | SignatureChecker | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | SignedMath | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Simulation | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | StorageSlot | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Strings | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Subnetwork | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | SumTask | 2,421 | 2,555 | 22,155 | 46,597 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | SymbioticCoreBytecode | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | SymbioticCoreConstants | 44 | 94 | 24,532 | 49,058 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | SymbioticUtils | 80 | 109 | 24,496 | 49,043 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | TimelockControllerUpgradeable | 7,690 | 7,718 | 16,886 | 41,434 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | Token | 1,723 | 3,065 | 22,853 | 46,087 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | TransparentUpgradeableProxy | 1,073 | 3,445 | 23,503 | 45,707 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | VotingPowerProviderLogic | 15,628 | 15,680 | 8,948 | 33,472 | |--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------| | VotingPowers | 14,815 | 15,023 | 9,761 | 34,129 | ╰--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------╯ ```` ## File: src/symbiotic/Driver.sol ```` // SPDX-License-Identifier: MIT ⋮---- import {OzAccessControl} from "@symbioticfi/relay-contracts/src/modules/common/permissions/OzAccessControl.sol"; import {ValSetDriver} from "@symbioticfi/relay-contracts/src/modules/valset-driver/ValSetDriver.sol"; ⋮---- contract Driver is ValSetDriver, OzAccessControl { function initialize(ValSetDriverInitParams memory valSetDriverInitParams, address defaultAdmin) ```` ## File: src/symbiotic/KeyRegistry.sol ```` // SPDX-License-Identifier: MIT ⋮---- import { KeyRegistry as SymbioticKeyRegistry } from "@symbioticfi/relay-contracts/src/modules/key-registry/KeyRegistry.sol"; ⋮---- contract KeyRegistry is SymbioticKeyRegistry { function initialize(KeyRegistryInitParams memory keyRegistryInitParams) public virtual initializer { ```` ## File: src/symbiotic/Settlement.sol ```` // SPDX-License-Identifier: MIT ⋮---- import {OzAccessControl} from "@symbioticfi/relay-contracts/src/modules/common/permissions/OzAccessControl.sol"; import {Settlement as SymbioticSettlement} from "@symbioticfi/relay-contracts/src/modules/settlement/Settlement.sol"; ⋮---- contract Settlement is SymbioticSettlement, OzAccessControl { function initialize(SettlementInitParams memory settlementInitParams, address defaultAdmin) ```` ## File: src/symbiotic/VotingPowers.sol ```` // SPDX-License-Identifier: MIT ⋮---- import { EqualStakeVPCalc } from "@symbioticfi/relay-contracts/src/modules/voting-power/common/voting-power-calc/EqualStakeVPCalc.sol"; import { OpNetVaultAutoDeploy } from "@symbioticfi/relay-contracts/src/modules/voting-power/extensions/OpNetVaultAutoDeploy.sol"; import {OzOwnable} from "@symbioticfi/relay-contracts/src/modules/common/permissions/OzOwnable.sol"; import {VotingPowerProvider} from "@symbioticfi/relay-contracts/src/modules/voting-power/VotingPowerProvider.sol"; ⋮---- contract VotingPowers is VotingPowerProvider, OzOwnable, EqualStakeVPCalc, OpNetVaultAutoDeploy { ⋮---- function initialize( ⋮---- function _registerOperatorImpl(address operator) internal override(OpNetVaultAutoDeploy, VotingPowerProvider) { ⋮---- function _unregisterOperatorVaultImpl(address operator, address vault) ```` ## File: src/SumTask.sol ```` // SPDX-License-Identifier: MIT ⋮---- import {ISettlement} from "@symbioticfi/relay-contracts/src/interfaces/modules/settlement/ISettlement.sol"; ⋮---- contract SumTask { ⋮---- event CreateTask(bytes32 indexed taskId, Task task); ⋮---- event RespondTask(bytes32 indexed taskId, Response response); ⋮---- function getTaskStatus(bytes32 taskId) public view returns (TaskStatus) { ⋮---- function createTask(uint256 numberA, uint256 numberB) public returns (bytes32 taskId) { ⋮---- function respondTask(bytes32 taskId, uint256 result, uint48 epoch, bytes calldata proof) public { // check if the task is not responded yet ⋮---- // verify that the verifying epoch is not stale ⋮---- // verify the quorum signature ```` ## File: test/mock/SettlementMock.sol ```` // SPDX-License-Identifier: MIT ⋮---- contract SettlementMock { function getRequiredKeyTagFromValSetHeaderAt(uint48) public pure returns (uint8) { ⋮---- function getQuorumThresholdFromValSetHeaderAt(uint48) public pure returns (uint256) { ⋮---- function getCaptureTimestampFromValSetHeaderAt(uint48) public pure returns (uint48) { ⋮---- function getLastCommittedHeaderEpoch() public pure returns (uint48) { ⋮---- function verifyQuorumSigAt(bytes calldata, uint8, uint256, bytes calldata, uint48, bytes calldata) ⋮---- function verifyQuorumSig(bytes calldata, uint8, uint256, bytes calldata) public pure returns (bool) { ```` ## File: test/SumTask.t.sol ```` // SPDX-License-Identifier: MIT ⋮---- import {Test} from "forge-std/Test.sol"; import {SumTask} from "../src/SumTask.sol"; import {SettlementMock} from "./mock/SettlementMock.sol"; ⋮---- contract SumTaskTest is Test { ⋮---- function setUp() public { ⋮---- function test_CreateTask() public { ⋮---- function test_RespondToTask() public { ```` ## File: .gitignore ```` # macOS .DS_Store # Ignore node_modules and dependencies node_modules *.log yarn-error.log # Ignore build outputs dist out **/__pycache__ # Ignore environment files .env # Ignore version control files docs/autogen/.git docs/autogen/.gitignore # Ignore editor and OS-specific files *.swp *.DS_Store Thumbs.db # Ignore IDE-specific files .idea/ .vscode/ cache/ out/ broadcast/ deployments/ script/logs.txt lcov.info /target /coverage /report .password # Network generated files temp-network/ ```` ## File: .gitmodules ```` [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std ```` ## File: .pre-commit-config.yaml ````yaml repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: - id: mixed-line-ending args: ["--fix=lf"] exclude: "^(docs/autogen|snapshots/)" - id: trailing-whitespace exclude: "^(docs/autogen|snapshots/)" - id: end-of-file-fixer exclude: "^(docs/autogen|snapshots/)" - id: check-merge-conflict - id: check-json exclude: "^(docs/autogen|snapshots/)" - id: check-yaml - repo: local hooks: - id: sort-imports name: Sort imports description: Normalize and sort import statements in Solidity files language: system entry: python3 script/utils/sort_imports.py files: "^(src|examples)/.*\\.sol$" pass_filenames: true - id: sort-errors name: Sort solidity errors description: Alphabetize error declarations in Solidity files language: system entry: python3 script/utils/sort_errors.py files: "^(src|examples)/.*\\.sol$" pass_filenames: true - id: format name: Format solidity code description: Format solidity code with `forge fmt` language: system entry: forge fmt files: '\.sol$' exclude: "^lib/" pass_filenames: true - id: forge-snapshots name: Update forge snapshots language: system entry: bash -lc 'mkdir -p snapshots; forge build; forge build --sizes | tee snapshots/sizes.txt >/dev/null; forge test --isolate --gas-report | tee snapshots/gas.txt >/dev/null' pass_filenames: false - repo: https://github.com/pre-commit/mirrors-prettier rev: v3.1.0 hooks: - id: prettier name: Format non-solidity files with prettier exclude: "^(docs/autogen|snapshots/)" language_version: system ```` ## File: .prettierignore ```` foundry.toml out lib/ cache/ docs/autogenerated *.sol deployments/ snapshots/ ```` ## File: .prettierrc ```` { "printWidth": 120, "tabWidth": 2, "useTabs": false, "singleQuote": false, "bracketSpacing": true, "trailingComma": "all", "overrides": [ { "files": "*.sol", "options": { "printWidth": 120, "tabWidth": 4, "useTabs": false, "singleQuote": false, "bracketSpacing": false } }, { "files": "*.json", "options": { "tabWidth": 4 } } ] } ```` ## File: CONTRIBUTING.md ````markdown # Contributing - [Install](#install) - [Pre-commit Hooks](#pre-commit-hooks) - [Requirements for merge](#requirements-for-merge) - [Branching](#branching) - [Main](#main) - [Audit](#audit) - [Code Practices](#code-practices) - [Code Style](#code-style) - [Solidity Versioning](#solidity-versioning) - [Interfaces](#interfaces) - [NatSpec \& Comments](#natspec--comments) - [Testing](#testing) - [Best Practices](#best-practices) - [IR Compilation](#ir-compilation) - [Gas Metering](#gas-metering) - [Deployment](#deployment) - [Bytecode Hash](#bytecode-hash) - [Dependency Management](#dependency-management) - [Releases](#releases) ## Install Follow these steps to set up your local environment for development: - [Install foundry](https://book.getfoundry.sh/getting-started/installation) - Install dependencies: `forge install` - [Install pre-commit](https://pre-commit.com/#installation) - Install pre commit hooks: `pre-commit install` ## Pre-commit Hooks Follow the [installation steps](#install) to enable pre-commit hooks. To ensure consistency in our formatting `pre-commit` is used to check whether code was formatted properly and the documentation is up to date. Whenever a commit does not meet the checks implemented by pre-commit, the commit will fail and the pre-commit checks will modify the files to make the commits pass. Include these changes in your commit for the next commit attempt to succeed. On pull requests the CI checks whether all pre-commit hooks were run correctly. This repo includes the following pre-commit hooks that are defined in the `.pre-commit-config.yaml`: - `mixed-line-ending`: This hook ensures that all files have the same line endings (LF). - `trailing-whitespace`: Strips trailing spaces from lines so that diffs remain clean and editors don't introduce noise. - `end-of-file-fixer`: Ensures every file ends with a single newline and removes extra blank lines at the end of files. - `check-merge-conflict`: Fails when Git merge conflict markers are present to avoid committing unresolved conflicts. - `check-json`: Validates JSON files and fails fast on malformed syntax. - `check-yaml`: Parses YAML files to verify they are syntactically valid. - `sort-imports`: Normalises and sorts imports according to the rules mentioned in the [Code Style](#code-style) below. - `sort-errors`: Sorts errors according to the rules mentioned in the [Code Style](#code-style) below. - `format`: This hook uses `forge fmt` to format all Solidity files. - `doc`: This hook uses `forge doc` to generate the Solidity documentation. Commit the generated files whenever the documentation changes. - `prettier`: All remaining files are formatted using prettier. ## Requirements for merge In order for a PR to be merged, it must pass the following requirements: - All commits within the PR must be signed - CI must pass (tests, linting, etc.) - New features must be merged with associated tests - Bug fixes must have a corresponding test that fails without the fix - The PR must be approved by at least one maintainer ## Branching This section outlines the branching strategy of this repo. ### Main The main branch is supposed to reflect the deployed state on all networks, if not indicated otherwise inside the README. Only audited code should be merged into main. Сommits from dev branches should be merged into the main branch using a regular merge strategy. The commit messages should follow [the Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/). ### Audit Before an audit, the code should be frozen on a branch dedicated to the audit with the naming convention `audit/`. Each fix in response to an audit finding should be developed as a separate commit. The commit message should look similar to `fix: - `. ## Code Practices ### Code Style The repo follows the official [Solidity Style Guide](https://docs.soliditylang.org/en/latest/style-guide.html). In addition to that, this repo also borrows the following rules from [OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/GUIDELINES.md#solidity-conventions): - Internal or private state variables or functions should have an underscore prefix. ```solidity contract TestContract { uint256 private _privateVar; uint256 internal _internalVar; function _testInternal() internal { ... } function _testPrivate() private { ... } } ``` - Naming collisions should be avoided using a single trailing underscore. ```solidity contract TestContract { uint256 public foo; constructor(uint256 foo_) { foo = foo_; } } ``` - Interface names should have a capital I prefix. ```solidity interface IERC777 { ``` - Contracts not intended to be used standalone should be marked abstract, so they are required to be inherited by other contracts. ```solidity abstract contract AccessControl is ..., { ``` - Unchecked arithmetic blocks should contain comments explaining why overflow is guaranteed not to happen or is permissible. If the reason is immediately apparent from the line above the unchecked block, the comment may be omitted. Also, such exceptions/additions exist: - Functions should be grouped according to their visibility and ordered: 1. constructor 2. external 3. public 4. internal 5. private 6. receive function (if exists) 7. fallback function (if exists) - Each contract should be virtually divided into sections by using such separators: 1. /\* CONSTANTS \*/ 2. /\* IMMUTABLES \*/ 3. /\* STATE VARIABLES \*/ 4. /\* MODIFIERS \*/ 5. /\* CONSTRUCTOR \*/ 6. /\* EXTERNAL FUNCTIONS \*/ 7. /\* PUBLIC FUNCTIONS \*/ 8. /\* INTERNAL FUNCTIONS \*/ 9. /\* PRIVATE FUNCTIONS \*/ 10. /\* RECEIVE FUNCTION \*/ 11. /\* FALLBACK FUNCTION \*/ - Each interface should be virtually divided into sections by using such separators: 1. /\* ERRORS \*/ 2. /\* STRUCTS \*/ 3. /\* EVENTS \*/ 4. /\* FUNCTIONS \*/ - Do not use external and private visibilities in most cases. - Events should generally be emitted immediately after the state change that they represent, and should be named the same as the function's name. Some exceptions may be made for gas efficiency if the result doesn't affect the observable ordering of events. ```solidity function _burn(address who, uint256 value) internal { super._burn(who, value); emit Burn(who, value); } ``` - Custom errors should be used whenever possible. The naming should be concise and easy to read. - Imports should be divided into separate groups and ordered alphabetically ascending inside each group: 1. contracts 2. libraries 3. interfaces 4. external files separately ```solidity import {NetworkManager} from "../base/NetworkManager.sol"; import {OzEIP712} from "../base/OzEIP712.sol"; import {PermissionManager} from "../base/PermissionManager.sol"; import {Checkpoints} from "../../libraries/structs/Checkpoints.sol"; import {KeyTags} from "../../libraries/utils/KeyTags.sol"; import {ISettlement} from "../interfaces/modules/settlement/ISettlement.sol"; import {ISigVerifier} from "../interfaces/modules/settlement/sig-verifiers/ISigVerifier.sol"; import {StaticDelegateCallable} from "@symbioticfi/core/src/contracts/common/StaticDelegateCallable.sol"; import {Subnetwork} from "@symbioticfi/core/src/contracts/libraries/Subnetwork.sol"; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; ``` - In case of comparison with `msg.sender` or `tx.origin`, these keywords should be on the right side of the inequality. ```solidity modifier onlyOwner() internal { if (owner != msg.sender) { revert NotOwner(); } } ``` - Errors should be ordered alphabetically ascending. ```solidity error InsufficientFunds(); error NoAccess(); error NotOwner(); ``` ### Solidity Versioning Contracts that are meant to be deployed should have an explicit version set in the `pragma` statement. ```solidity pragma solidity 0.8.X; ``` Abstract contracts, libraries and interfaces should use the caret (`^`) range operator to specify the version range to ensure better compatibility. ```solidity pragma solidity ^0.X.0; ``` Libraries and abstract contracts using functionality introduced in newer versions of Solidity can use caret range operators with higher path versions (e.g., `^0.8.24` when using transient storage opcodes). For interfaces, it should be considered to use the greater than or equal to (`>=`) range operator to ensure better compatibility with future versions of Solidity. ### Interfaces Every contract MUST implement its corresponding interface that includes all externally callable functions, errors and events. ### NatSpec & Comments Interfaces should be the entry point for all contracts. When exploring a contract within the repository, the interface MUST contain all relevant information to understand the functionality of the contract in the form of NatSpec comments. This includes all externally callable functions, structs, errors and events. The NatSpec documentation MUST be added to the functions, structs, errors and events within the interface. This allows a reader to understand the functionality of a function before moving on to the implementation. The implementing functions MUST point to the NatSpec documentation in the interface using `@inheritdoc`. Internal and private functions shouldn't have NatSpec documentation except for `@dev` comments, whenever more context is needed. Additional comments within a function should only be used to give more context to more complex operations; otherwise, the code should be kept readable and self-explanatory. NatSpec comments in contracts should use a triple slash (`///`) to bring less noise to the implementation, while libraries and interfaces should use `/* */` wrappers. The comments should respect the following rules: - For read functions: `@notice Returns <...>` - For write functions: `@notice ` - For structs: `@notice ` - For errors: `@notice Raised when <...>` - For events: `@notice Emitted when <...>` Each contract/library/interface should have a title comment that should follow such a structure: 1. `@title ` (e.g., `Vault`) 2. `@notice Contract/Library/Interface for <...>.` - also, other variations are possible, e.g.: - `@notice Interface for the Vault contract.` - `@notice Base contract for <...>.` - `@notice Library-logic for <...>.` 3. `@dev <...>` (optional) ## Testing The following testing practices should be followed when writing unit tests for new code. All functions, lines and branches should be tested to result in 100% testing coverage. Fuzz parameters and conditions whenever possible. Extremes should be tested in dedicated edge case and corner case tests. Invariants should be tested in dedicated invariant tests. Differential testing should be used to compare assembly implementations with implementations in Solidity or testing alternative implementations against existing Solidity or non-Solidity code using ffi. New features must be merged with associated tests. Bug fixes should have a corresponding test that fails without the bug fix. ### Best Practices Best practices and naming conventions should be followed as outlined in the [Foundry Book](https://getfoundry.sh/forge/tests/overview). ### IR Compilation All contracts and tests should be compilable without IR whenever possible. ### Gas Metering Gas for function calls should be metered using the built-in `vm.snapshotGasLastCall` function in forge. To meter across multiple calls `vm.startSnapshotGas` and `vm.stopSnapshotGas` can be used. Tests that measure gas should be annotated with `/// forge-config: default.isolate = true` and not be fuzzed to ensure that the gas snapshot is accurate and consistent for CI verification. All external functions should have a gas snapshot test, and diverging paths within a function should have appropriate gas snapshot tests. For more information on gas metering, see the [Forge cheatcodes reference](https://getfoundry.sh/reference/cheatcodes/gas-snapshots/#snapshotgas-cheatcodes). ### Bytecode Hash Bytecode hash should be set to `none` in the `foundry.toml` file to ensure that the bytecode is consistent. ## Dependency Management The preferred way to manage dependencies is using [`forge install`](https://book.getfoundry.sh/forge/dependencies). This ensures that your project uses the correct versions and structure for all external libraries. Also, `npm` and `soldeer` packages should be published to increase coverage of different use-cases. ## Releases Every deployment and change made to contracts after deployment should be accompanied by a tag and release on GitHub. ```` ## File: foundry.lock ```` { "circuits": { "rev": "338c1b1faadf409d9f9fb7f64e7e2d1df4da9d16" }, "lib/forge-std": { "tag": { "name": "v1.11.0", "rev": "8e40513d678f392f398620b3ef2b418648b33e89" } } } ```` ## File: foundry.toml ````toml [profile.default] evm_version = "prague" solc = "0.8.28" optimizer = true optimizer_runs = 200 via_ir = false bytecode_hash = "none" src = "src" out = "out" libs = ["lib"] fs_permissions = [{ access = "read-write", path = "./"}] gas_reports = ["*"] gas_limit = "18446744073709551615" dynamic_test_linking = true ignored_warnings_from = ["test/","script/"] ffi = true [rpc_endpoints] mainnet = "${ETH_RPC_URL}" hoodi = "${ETH_RPC_URL_HOODI}" holesky = "${ETH_RPC_URL_HOLESKY}" sepolia = "${ETH_RPC_URL_SEPOLIA}" [fmt] bracket_spacing = false int_types = "long" line_length = 120 multiline_func_header = "attributes_first" number_underscore = "thousands" quote_style = "double" tab_width = 4 single_line_statement_blocks = "preserve" sort_imports = false contract_new_lines = false override_spacing = false hex_underscore = "preserve" wrap_comments = false [lint] lint_on_build = false exclude_lints = ["asm-keccak256","mixed-case-function","mixed-case-variable","pascal-case-struct","screaming-snake-case-const"] ignore = ["test/**/*.sol","script/**/*.sol","src/interfaces/**/*.sol"] additional_compiler_profiles = [ { name = "test", via_ir = false, optimizer = false }, ] compilation_restrictions = [ { paths = "test/**", via_ir = false, optimizer = false }, ] [profile.default.fuzz] runs = 1000 max_test_rejects = 262144 [profile.pr.fuzz] runs = 10000 max_test_rejects = 262144 [profile.ci.fuzz] runs = 100000 max_test_rejects = 262144 [profile.debug] via_ir = false optimizer_runs = 200 fuzz.runs = 100 # See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options ```` ## File: generate_network.sh ````bash #!/bin/bash # Symbiotic Network Generator # This script generates a Docker Compose setup for a local blockchain network # with configurable number of operators, commiters, and aggregators set -e # Define the image tag for the relay service, that the current sum node is compatible with RELAY_IMAGE_TAG="0.3.0" RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # Default values DEFAULT_OPERATORS=4 DEFAULT_COMMITERS=1 DEFAULT_AGGREGATORS=1 MAX_OPERATORS=999 print_status() { echo -e "${GREEN}[INFO]${NC} $1" } print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" } print_error() { echo -e "${RED}[ERROR]${NC} $1" } print_header() { echo -e "${BLUE}================================${NC}" echo -e "${BLUE}$1${NC}" echo -e "${BLUE}================================${NC}" } validate_number() { local num=$1 local name=$2 if ! [[ "$num" =~ ^[0-9]+$ ]] || [ "$num" -lt 1 ]; then print_error "$name must be a positive integer" exit 1 fi } get_user_input() { echo print_header "Symbiotic Network Configuration" echo read -p "Enter number of operators (default: $DEFAULT_OPERATORS, max: $MAX_OPERATORS): " operators operators=${operators:-$DEFAULT_OPERATORS} validate_number "$operators" "Number of operators" read -p "Enter number of commiters (default: $DEFAULT_COMMITERS): " commiters commiters=${commiters:-$DEFAULT_COMMITERS} validate_number "$commiters" "Number of commiters" read -p "Enter number of aggregators (default: $DEFAULT_AGGREGATORS): " aggregators aggregators=${aggregators:-$DEFAULT_AGGREGATORS} validate_number "$aggregators" "Number of aggregators" # Validate that commiters + aggregators <= operators total_special_roles=$((commiters + aggregators)) if [ "$total_special_roles" -gt "$operators" ]; then print_error "Total commiters ($commiters) + aggregators ($aggregators) cannot exceed total operators ($operators)" exit 1 fi if [ "$operators" -gt $MAX_OPERATORS ]; then print_error "Maximum $MAX_OPERATORS operators supported. Requested: $operators" exit 1 fi print_status "Configuration:" print_status " Operators: $operators" print_status " Committers: $commiters" print_status " Aggregators: $aggregators" print_status " Regular signers: $((operators - total_special_roles))" } # Function to generate Docker Compose file generate_docker_compose() { local operators=$1 local commiters=$2 local aggregators=$3 local network_dir="temp-network" if [ -d "$network_dir" ]; then print_status "Cleaning up existing $network_dir directory..." rm -rf "$network_dir" fi mkdir -p "$network_dir/deploy-data" chmod 777 "$network_dir/deploy-data" # Create cache and broadcast directories with proper permissions print_status "Creating out, cache and broadcast directories..." mkdir -p "$network_dir/out" "$network_dir/cache" "$network_dir/broadcast" chmod 777 "$network_dir/out" "$network_dir/cache" "$network_dir/broadcast" local deploy_config_src="script/my-relay-deploy.toml" local deploy_config_dst="$network_dir/my-relay-deploy.toml" if [ ! -f "$deploy_config_src" ]; then print_error "Deployment config not found at $deploy_config_src" exit 1 fi print_status "Copying deployment config to $deploy_config_dst" cp "$deploy_config_src" "$deploy_config_dst" for i in $(seq 1 $operators); do local storage_dir="$network_dir/data-$(printf "%02d" $i)" mkdir -p "$storage_dir" # Make sure the directory is writable chmod 777 "$storage_dir" done local anvil_port=8545 local anvil_settlement_port=8546 local relay_start_port=8081 local sum_start_port=9091 cat > "$network_dir/docker-compose.yml" << EOF services: # Main Anvil local Ethereum network (Chain ID: 31337) anvil: image: ghcr.io/foundry-rs/foundry:v1.4.3 container_name: symbiotic-anvil entrypoint: ["anvil"] command: "--port 8545 --chain-id 31337 --timestamp 1754051800 --auto-impersonate --slots-in-an-epoch 1 --accounts 10 --balance 10000 --gas-limit 30000000 --gas-price 10000000" environment: - ANVIL_IP_ADDR=0.0.0.0 ports: - "8545:8545" networks: - symbiotic-network healthcheck: test: ["CMD", "cast", "client", "--rpc-url", "http://localhost:8545"] interval: 2s timeout: 1s retries: 10 # Settlement Anvil local Ethereum network (Chain ID: 31338) anvil-settlement: image: ghcr.io/foundry-rs/foundry:v1.4.3 container_name: symbiotic-anvil-settlement entrypoint: ["anvil"] command: "--port 8546 --chain-id 31338 --timestamp 1754051800 --auto-impersonate --slots-in-an-epoch 1 --accounts 10 --balance 10000 --gas-limit 30000000 --gas-price 10000000" environment: - ANVIL_IP_ADDR=0.0.0.0 ports: - "8546:8546" networks: - symbiotic-network healthcheck: test: ["CMD", "cast", "client", "--rpc-url", "http://localhost:8546"] interval: 2s timeout: 1s retries: 10 # Contract deployment service for main chain deployer: build: context: .. dockerfile: network-scripts/deployer.Dockerfile image: symbiotic-deployer container_name: symbiotic-deployer user: "1000:1000" volumes: - ../:/app - ./cache:/app/cache - ./broadcast:/app/broadcast - ./out:/app/out - ./deploy-data:/deploy-data - ./my-relay-deploy.toml:/my-relay-deploy.toml working_dir: /app command: ./network-scripts/deploy.sh depends_on: anvil: condition: service_healthy anvil-settlement: condition: service_healthy networks: - symbiotic-network environment: - OPERATOR_COUNT=$operators - NUM_AGGREGATORS=$aggregators - NUM_COMMITTERS=$commiters # Genesis generation service genesis-generator: image: symbioticfi/relay:$RELAY_IMAGE_TAG container_name: symbiotic-genesis-generator volumes: - ../:/workspace - ./deploy-data:/deploy-data working_dir: /workspace command: ./network-scripts/genesis-generator.sh depends_on: deployer: condition: service_completed_successfully networks: - symbiotic-network EOF local committer_count=0 local aggregator_count=0 local signer_count=0 # Calculate symb private key properly # ECDSA secp256k1 private keys must be 32 bytes (64 hex chars) and within range [1, n-1] # where n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 BASE_PRIVATE_KEY=1000000000000000000 SWARM_KEY=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140 for i in $(seq 1 $operators); do local port=$((relay_start_port + i - 1)) local storage_dir="data-$(printf "%02d" $i)" local key_index=$((i - 1)) SYMB_PRIVATE_KEY_DECIMAL=$(($BASE_PRIVATE_KEY + $key_index)) SYMB_SECONDARY_PRIVATE_KEY_DECIMAL=$(($BASE_PRIVATE_KEY + $key_index + 10000)) SYMB_PRIVATE_KEY_HEX=$(printf "%064x" $SYMB_PRIVATE_KEY_DECIMAL) SYMB_SECONDARY_PRIVATE_KEY_HEX=$(printf "%064x" $SYMB_SECONDARY_PRIVATE_KEY_DECIMAL) # Validate ECDSA secp256k1 private key range (must be between 1 and n-1) # Maximum valid key: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140 if [ $SYMB_PRIVATE_KEY_DECIMAL -eq 0 ]; then echo "ERROR: Generated private key is zero (invalid for ECDSA)" exit 1 fi cat >> "$network_dir/docker-compose.yml" << EOF # Relay sidecar $i ($role_name) relay-sidecar-$i: image: symbioticfi/relay:$RELAY_IMAGE_TAG container_name: symbiotic-relay-$i command: - /workspace/network-scripts/sidecar-start.sh - symb/0/15/0x$SYMB_PRIVATE_KEY_HEX,symb/0/11/0x$SYMB_SECONDARY_PRIVATE_KEY_HEX,symb/1/0/0x$SYMB_PRIVATE_KEY_HEX,evm/1/31337/0x$SYMB_PRIVATE_KEY_HEX,evm/1/31338/0x$SYMB_PRIVATE_KEY_HEX,p2p/1/0/$SWARM_KEY,p2p/1/1/$SYMB_PRIVATE_KEY_HEX - /app/$storage_dir ports: - "$port:8080" volumes: - ../:/workspace - ./$storage_dir:/app/$storage_dir - ./deploy-data:/deploy-data depends_on: genesis-generator: condition: service_completed_successfully networks: - symbiotic-network restart: unless-stopped EOF local relay_port=$((relay_start_port + i - 1)) local sum_port=$((sum_start_port + i - 1)) cat >> "$network_dir/docker-compose.yml" << EOF # Sum node $i sum-node-$i: build: context: ../off-chain dockerfile: Dockerfile container_name: symbiotic-sum-node-$i entrypoint: ["/workspace/network-scripts/sum-node-start.sh"] command: ["relay-sidecar-$i:8080", "$SYMB_PRIVATE_KEY_HEX"] volumes: - ../:/workspace - ./deploy-data:/deploy-data ports: - "$sum_port:8080" depends_on: relay-sidecar-$i: condition: service_started networks: - symbiotic-network restart: unless-stopped EOF done cat >> "$network_dir/docker-compose.yml" << EOF networks: symbiotic-network: driver: bridge EOF } # Main execution main() { print_header "Symbiotic Network Generator" # Check if required tools are available if ! command -v docker &> /dev/null; then print_error "Docker is not installed or not in PATH" exit 1 fi if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then print_error "Docker Compose is not installed or not in PATH" exit 1 fi get_user_input print_status "Generating Docker Compose configuration..." print_status "Creating $operators new operator accounts..." generate_docker_compose "$operators" "$commiters" "$aggregators" print_header "Setup Complete!" echo print_status "Files generated in temp-network/ directory:" echo " - temp-network/docker-compose.yml" echo " - temp-network/data-* (storage directories)" echo print_status "To start the network, run:" echo " docker compose --project-directory temp-network up -d" echo print_status "To check the status, run:" echo " docker compose --project-directory temp-network ps" echo print_status "To view logs, run:" echo " docker compose --project-directory temp-network logs -f" echo print_warning "Note: The first startup may take several minutes(2-4mins) as it needs to:" echo " 1. Download Docker images" echo " 2. Build the sum-node image" echo " 3. Deploy contracts" echo " 4. Generate network genesis and fund operators" echo } main "$@" ```` ## File: package.json ````json { "name": "@symbioticfi/symbiotic-super-sum", "version": "1.0.0", "description": "Simple task-based network built with Symbiotic Relay", "homepage": "https://symbiotic.fi/symbiotic-super-sum", "bugs": "https://github.com/symbioticfi/symbiotic-super-sum/issues", "license": "MIT", "author": "Symbiotic Team", "files": [ "examples/**/*", "src/**/*", "script/**/*", "test/mocks/**/*" ], "repository": { "type": "git", "url": "https://github.com/symbioticfi/symbiotic-super-sum.git" }, "keywords": [ "solidity", "ethereum", "smart", "contracts", "security" ], "dependencies": { "@openzeppelin/contracts": "5.4.0", "@openzeppelin/contracts-upgradeable": "5.4.0", "@symbioticfi/core": "1.0.3", "@symbioticfi/relay-contracts": "^1.0.0-rc.1", "@symbioticfi/network": "1.0.0-rc.1" } } ```` ## File: README.md ````markdown # Sum task network example [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/symbioticfi/symbiotic-super-sum) ## Prerequisites ### Clone the repository ```bash git clone https://github.com/symbioticfi/symbiotic-super-sum.git ``` Update submodules ```bash git submodule update --init --recursive ``` Install dependencies ```bash npm install ``` ## Running in Docker ### Dependencies - Docker ### Quick Start 1. **Generate the network configuration:** ```bash ./generate_network.sh ``` 2. **Start the network:** ```bash docker compose --project-directory temp-network up -d ``` 3. **Check status:** ```bash docker compose --project-directory temp-network ps ``` ### Services #### Core Services - **anvil**: Local Ethereum network (port 8545) - **anvil-settlement**: Local Ethereum network (port 8546) - **deployer**: Contract deployment service - **genesis-generator**: Network genesis generation service - **network-validator**: intermediary service to mark network setup completion for all nodes #### Relay Sidecars - **relay-sidecar-1**: First relay sidecar (port 8081) - **relay-sidecar-2**: Second relay sidecar (port 8082) - **relay-sidecar-N**: Nth relay sidecar (port 808N) #### Sum Nodes - **sum-node-1**: First sum node (port 9091) - **sum-node-2**: Second sum node (port 9092) - **sum-node-N**: Nth sum node (port 909N) ### Start the network ```bash docker compose --project-directory temp-network up -d ``` ### Check status ```bash docker compose --project-directory temp-network ps ``` ### View logs ```bash # View all logs docker compose --project-directory temp-network logs -f # View specific service logs docker compose --project-directory temp-network logs -f anvil docker compose --project-directory temp-network logs -f anvil-settlement docker compose --project-directory temp-network logs -f deployer docker compose --project-directory temp-network logs -f genesis-generator docker compose --project-directory temp-network logs -f relay-sidecar-1 docker compose --project-directory temp-network logs -f sum-node-1 ``` ### Stop the network ```bash docker compose --project-directory temp-network down ``` ### Clean up data ```bash docker compose --project-directory temp-network down -v rm -rf temp-network ``` ### Create a task ```bash taskID=$(cast send --rpc-url http://127.0.0.1:8545 --json \ --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ 0xDf12251aD82BF1eb0E0951AD15d37AE5ED3Ac1dF \ "createTask(uint256,uint256)" 33 9 | jq -r '.logs[0].topics[1]') ``` or ```bash taskID=$(cast send --rpc-url http://127.0.0.1:8546 --json \ --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ 0xDf12251aD82BF1eb0E0951AD15d37AE5ED3Ac1dF \ "createTask(uint256,uint256)" 33 9 | jq -r '.logs[0].topics[1]') ``` ### Check task result ```bash result=$(cast call --rpc-url http://127.0.0.1:8545 \ 0xDf12251aD82BF1eb0E0951AD15d37AE5ED3Ac1dF \ "responses(bytes32)" $taskID) cast decode-abi --json "data()(uint48,uint256)" $result ``` or ```bash result=$(cast call --rpc-url http://127.0.0.1:8546 \ 0xDf12251aD82BF1eb0E0951AD15d37AE5ED3Ac1dF \ "responses(bytes32)" $taskID) cast decode-abi --json "data()(uint48,uint256)" $result ``` ### Troubleshooting 1. **Services not starting**: Check logs with `docker compose --project-directory temp-network logs [service-name]` 2. **Port conflicts**: Ensure ports 8545-8546 8081-8099, 9091-9099 are available 3. **Build issues**: Rebuild with `docker compose --project-directory temp-network build` 4. **Reset everything**: `docker compose --project-directory temp-network down -v && rm -rf temp-network && ./generate_network.sh && docker compose --project-directory temp-network up -d` ### Service Endpoints - **Anvil RPC**: http://localhost:8545 - **Anvil Settlement RPC**: http://localhost:8546 - **Relay sidecar 1**: http://localhost:8081 - **Relay sidecar 2**: http://localhost:8082 - **Sum node 1**: http://localhost:9091 - **Sum node 2**: http://localhost:9092 ### Network Configuration The network supports: - **Up to 999 operators** (configurable via `generate_network.sh`) - **Committers**: Operators that commit to the network - **Aggregators**: Operators that aggregate results - **Signers**: Regular operators that sign messages ### Debugging ```bash # Access container shell docker compose --project-directory temp-network exec anvil sh docker compose --project-directory temp-network exec relay-sidecar-1 sh docker compose --project-directory temp-network exec sum-node-1 sh # View real-time logs docker compose --project-directory temp-network logs -f --tail=100 ``` ### Performance Monitoring ```bash # Check resource usage docker stats # Monitor specific container docker stats symbiotic-anvil symbiotic-relay-1 symbiotic-sum-node-1 ``` ## Local Deployments http://anvil:8545: - `ValSetDriver`: 0x43C27243F96591892976FFf886511807B65a33d5 - `SumTask`: 0xDf12251aD82BF1eb0E0951AD15d37AE5ED3Ac1dF - `VotingPowerProvider`: 0x369c72C823A4Fc8d2A3A5C3B15082fb34A342878 - `KeyRegistry`: 0xe1557A820E1f50dC962c3392b875Fe0449eb184F - `Settlement`: 0x882B9439598239d9626164f7578F812Ef324F5Cb - `Network`: 0xfdc4b2cA12dD7b1463CC01D8022a49BDcf5cFa24 http://anvil-settlement:8546: - `SumTask`: 0xDf12251aD82BF1eb0E0951AD15d37AE5ED3Ac1dF - `Settlement`: 0x882B9439598239d9626164f7578F812Ef324F5Cb ```` ## File: remappings.txt ```` node_modules/@symbioticfi/relay-contracts:forge-std/=lib/forge-std/src/ @openzeppelin/contracts/=node_modules/@openzeppelin/contracts/ @openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/ @symbioticfi/relay-contracts/=node_modules/@symbioticfi/relay-contracts/ @symbioticfi/network/=node_modules/@symbioticfi/network/ @symbioticfi/core/=node_modules/@symbioticfi/core/ ````