# AI Pack: symbiotic-super-sum-example

- Description: Example dApp: Symbiotic Super Sum.
- Remote: https://github.com/symbioticfi/symbiotic-super-sum
- Remote ref: HEAD
- Commit: 03ed86c7447ba49d4c390e17cbe756329ab5c243
- Generated (UTC): 2026-06-02T10:22:21.999Z
- Repomix: 1.14.1
- Preset: contract
- Ignore patterns: .gitignore, .dockerignore, .prettierignore, .npmignore, .nvmrc, .editorconfig, .gitattributes, **/.github/**, **/.husky/**, **/.vscode/**, **/.idea/**, **/.DS_Store, node_modules/, dist/, build/, .turbo/, .next/, out/, cache/, broadcast/, bin/, coverage*, *.test, target/, docs/, abis/, bindings/, test/**, !test/integration/**, script/utils/**, package-lock.json, pnpm-lock.yaml, yarn.lock, bun.lockb, .pre-commit-config.yaml
- Flags: none

---

This file is a merged representation of a subset of the codebase, containing files not matching ignore patterns, combined into a single document by Repomix.

# File Summary

## Purpose

This file contains a packed representation of a subset of the repository's contents that is considered the most important context.
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 these patterns are excluded: .gitignore, .dockerignore, .prettierignore, .npmignore, .nvmrc, .editorconfig, .gitattributes, **/.github/**, **/.husky/**, **/.vscode/**, **/.idea/**, **/.DS_Store, node_modules/, dist/, build/, .turbo/, .next/, out/, cache/, broadcast/, bin/, coverage*, *.test, target/, docs/, abis/, bindings/, test/**, !test/integration/**, script/utils/**, package-lock.json, pnpm-lock.yaml, yarn.lock, bun.lockb, .pre-commit-config.yaml
- Files matching patterns in .gitignore are excluded
- Files matching default ignore patterns are excluded
- Files are sorted by Git change count (files with more changes are at the bottom)

# Directory Structure

```
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
  my-relay-deploy.toml
  MyRelayDeploy.sol
snapshots/
  gas.txt
  sizes.txt
src/
  symbiotic/
    Driver.sol
    KeyRegistry.sol
    Settlement.sol
    VotingPowers.sol
  SumTask.sol
.gitmodules
.prettierrc
CONTRIBUTING.md
foundry.lock
foundry.toml
generate_network.sh
package.json
README.md
remappings.txt
```

# Files

## 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"
)

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() {
	slog.SetLogLoggerLevel(slog.LevelDebug)

	ctx := context.Background()

	if err := run(ctx); err != nil {
		panic(err)
	}
}

func run(ctx context.Context) error {
	prs, err := runProcesses(ctx, false)
	if err != nil {
		return errors.Errorf("failed to run processes: %w", err)
	}
	defer prs.stopProcesses()

	if err := sendRequestAndWait(ctx, prs, numberOfSignRequests); err != nil {
		return errors.Errorf("failed to send request and wait: %w", err)
	}
	if errors.Is(err, context.Canceled) {
		slog.Info("Context was canceled, stopping processes")
	}

	select {
	case <-ctx.Done():
		slog.Info("Context done, stopping processes")
		break
	}

	return nil
}

func sendRequestAndWait(ctx context.Context, prs processes, nRequests int) *errors.Error {
	lastAllCommitted, err := prs[0].relayClient.GetLastAllCommitted(ctx, &v1.GetLastAllCommittedRequest{})
	if err != nil {
		return errors.Errorf("failed to get current epoch: %w", err)
	}

	lastCommittedEpoch := lo.Min(
		lo.Map(
			lo.Values(lastAllCommitted.EpochInfos),
			func(item *v1.ChainEpochInfo, _ int) uint64 {
				return item.LastCommittedEpoch
			}),
	)

	requestIDsSent := make([]string, nRequests)
	eg, egCtx := errgroup.WithContext(ctx)
	eg.SetLimit(20)
	for i := range nRequests {
		eg.Go(func() error {
			requestID, err := prs.sendMessageToAllRelays(egCtx, lastCommittedEpoch)
			if err != nil {
				return errors.Errorf("failed to send messages to all processes: %w", err)
			}
			requestIDsSent[i] = requestID
			return nil
		})
	}
	if err := eg.Wait(); err != nil {
		return errors.Errorf("failed to send messages to all processes: %w", err)
	}

	sentTime := time.Now()
	slog.Info("Sent all requesst", "nRequests", nRequests, "time", sentTime)

	timer := time.NewTimer(0)
	defer timer.Stop()

	requestIDsAggregated := make(map[string]struct{})

cycle:
	for {
		select {
		case <-timer.C:
			for i := range requestIDsSent {
				requestID := requestIDsSent[i]
				if _, ok := requestIDsAggregated[requestID]; ok {
					continue
				}

				resp, err := prs[0].relayClient.GetAggregationProof(ctx, &v1.GetAggregationProofRequest{
					RequestId: requestID,
				})
				if err != nil {
					slog.Debug("Failed to get aggregation status", "error", err)
					continue
				}

				slog.Debug("Received aggregation proof", "requestID", requestID, "proof", common.Bytes2Hex(resp.AggregationProof.MessageHash), "elapsed", time.Since(sentTime))

				requestIDsAggregated[requestID] = struct{}{}
			}
			if len(requestIDsAggregated) == nRequests {
				slog.Info("All requests aggregated", "count", len(requestIDsAggregated), "elapsed", time.Since(sentTime))
				break cycle
			}
			timer.Reset(time.Second)
		case <-ctx.Done():
			slog.Info("Context done, stopping processes")
			break cycle
		}
	}
	return nil
}

func runProcesses(ctx context.Context, runSeparately bool) (processes, error) {
	pathToExec := "bin/symbiotic_relay"
	commonArgs := []string{"--config", "sidecar.common.yaml", "--log-mode", "text"}
	firstArgs := []string{
		"--aggregator", "true",
		"--committer", "true",
	}
	secretKeyTemplate := "symb/0/15/%s,evm/1/31337/0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
	firstKey := uint64(1e18)
	firstPort := 8080
	firstData := 0

	var prs processes

	for i := 0; i < operatorsCount; i++ {
		secretKey := fmt.Sprintf(secretKeyTemplate, common.Bytes2Hex(new(big.Int).SetUint64(firstKey+uint64(i)).Bytes()))
		apiAddr := fmt.Sprintf(":%d", firstPort+i)
		args := append(commonArgs, "--secret-keys", secretKey, "--http-listen", apiAddr, "--storage-dir", fmt.Sprintf(".data/%03d", firstData+i))
		if i == 0 {
			args = append(args, firstArgs...)
		}

		pr := process{
			args:          args,
			apiAddr:       apiAddr,
			stdOut:        &bytes.Buffer{},
			stdErr:        &bytes.Buffer{},
			runSeparately: runSeparately,
		}

		if !runSeparately {
			pr.cmd = exec.Command(pathToExec, args...)
			pr.cmd.Stdout = pr.stdOut
			pr.cmd.Stderr = pr.stdErr
			err := pr.cmd.Start()
			if err != nil {
				return nil, errors.Errorf("failed to start process: %w", err)
			}
		}

		conn, err := utils.GetGRPCConnection("localhost" + apiAddr + "/api/v1")
		if err != nil {
			return nil, errors.Errorf("failed to create relay client: %w", err)
		}
		pr.relayClient = v1.NewSymbioticClient(conn)

		slog.Debug("Started process", "args", pr.args)

		prs = append(prs, pr)
	}

	if !runSeparately {
		prs.waitServerStarted(ctx)
	}

	return prs, nil
}

func (prs processes) waitServerStarted(ctx context.Context) {
	var startedCound int

	for i := range 100 {
		startedCound = 0
		for _, pr := range prs {
			if strings.Contains(pr.stdOut.String(), "All missing epochs loaded") {
				startedCound++
			}
		}
		if startedCound == len(prs) {
			break
		}
		slog.Info("Not all processes started successfully, retrying...", "attempt", i+1, "startedCount", startedCound, "totalCount", len(prs))

		time.Sleep(time.Second)
	}
	if startedCound != len(prs) {
		prs.printErrLogs()
		panic("Not all processes started successfully. Check logs for details.")
	}
	slog.InfoContext(ctx, "All processes started", "count", len(prs))
}

func (prs processes) stopProcesses() {
	for _, pr := range prs {
		if pr.runSeparately {
			continue
		}
		// Send an interrupt signal for a graceful shutdown, that is equivalent to pressing Ctrl+C.
		slog.Debug("Stopping process...", "pid", pr.cmd.Process.Pid)
		if err := pr.cmd.Process.Signal(os.Interrupt); err != nil {
			slog.Warn("Failed to send interrupt signal to process. Trying to kill.", "pid", pr.cmd.Process.Pid, "error", err)
			// If signaling fails, you can resort to killing the process forcefully.
			if killErr := pr.cmd.Process.Kill(); killErr != nil {
				slog.Error("Failed to kill process.", "pid", pr.cmd.Process.Pid, "error", killErr)
			}
		}

		if err := pr.cmd.Wait(); err != nil {
			slog.Warn("Process exited with error.", "pid", pr.cmd.Process.Pid, "error", err, "stderr", pr.stdErr.String(), "stdout", pr.stdOut.String())
		} else {
			slog.Info("Process stopped successfully.", "pid", pr.cmd.Process.Pid)
		}
	}
}

func (prs processes) sendMessageToAllRelays(ctx context.Context, epoch uint64) (string, error) {
	message := randomMessage(sizeOfMessageBytes)
	var requestID string
	for _, pr := range prs {
		rID, err := sendSignMessageRequest(ctx, pr, message, epoch)
		if err != nil {
			return "", errors.Errorf("failed to send sign message request: %w", err)
		}
		requestID = rID
	}
	return requestID, nil
}

func sendSignMessageRequest(ctx context.Context, pr process, message []byte, epoch uint64) (string, error) {
	resp, err := pr.relayClient.SignMessage(ctx, &v1.SignMessageRequest{
		KeyTag:        15,
		Message:       message,
		RequiredEpoch: &epoch,
	})
	if err != nil {
		return "", errors.Errorf("failed to send sign message request: %w", err)
	}
	slog.Debug("Sign message request sent", "message", common.Bytes2Hex(message))

	return resp.RequestId, nil
}

func randomMessage(n int) []byte {
	b := make([]byte, n)
	_, err := rand.Read(b)
	if err != nil {
		panic(fmt.Sprintf("failed to generate random bytes: %v", err))
	}
	return b
}

func (prs processes) printErrLogs() {
	for _, pr := range prs {
		if pr.stdErr.Len() > 0 {
			slog.Error("Process stderr", "pid", pr.cmd.Process.Pid, "stderr", pr.stdErr.String())
		}
		if pr.stdOut.Len() > 0 {
			slog.Info("Process stdout", "pid", pr.cmd.Process.Pid, "stdout", pr.stdOut.String())
		}
	}
}
```

## 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"
	"google.golang.org/grpc/codes"
	grpcstatus "google.golang.org/grpc/status"

	"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

	SumTaskSeed         = "sum-task"
	SlotDurationSeconds = 10
)

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() {
	slog.Info("Running sum task off-chain client", "args", os.Args)

	if err := run(); err != nil && !errors.Is(err, context.Canceled) {
		slog.Error("Error executing command", "error", err)
		os.Exit(1)
	}
	slog.Info("Sum task off-chain client completed successfully")
}

func run() error {
	rootCmd.PersistentFlags().StringVarP(&cfg.relayApiURL, "relay-api-url", "r", "", "Relay API URL")
	rootCmd.PersistentFlags().StringSliceVarP(&cfg.evmRpcURLs, "evm-rpc-urls", "e", []string{}, "EVM RPC URLs separated by comma (e.g., 'https://mainnet.infura.io/v3/,...')")
	rootCmd.PersistentFlags().StringSliceVarP(&cfg.contractAddresses, "contract-addresses", "a", []string{}, "SumTask contracts' addresses corresponding to the RPC URLs separated by comma (e.g., '0x4826533B4897376654Bb4d4AD88B7faFD0C98528,...')")
	rootCmd.PersistentFlags().StringVarP(&cfg.privateKey, "private-key", "p", "", "Task response private key")
	rootCmd.PersistentFlags().StringVarP(&cfg.logLevel, "log-level", "l", "info", "Log level")

	if err := rootCmd.MarkPersistentFlagRequired("relay-api-url"); err != nil {
		return errors.Errorf("failed to mark relay-api-url as required: %w", err)
	}
	if err := rootCmd.MarkPersistentFlagRequired("evm-rpc-urls"); err != nil {
		return errors.Errorf("failed to mark evm-rpc-urls as required: %w", err)
	}
	if err := rootCmd.MarkPersistentFlagRequired("contract-addresses"); err != nil {
		return errors.Errorf("failed to mark contract-addresses as required: %w", err)
	}
	if err := rootCmd.MarkPersistentFlagRequired("private-key"); err != nil {
		return errors.Errorf("failed to mark private-key as required: %w", err)
	}

	return rootCmd.Execute()
}

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
			}
		}
	},
}

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
	err := evmClient.Client().CallContext(ctx, &raw, "eth_getBlockByNumber", "finalized", true)
	if err != nil {
		return 0, errors.Errorf("failed to get finalized block number: %w", err)
	}
	var head *types.Header
	if err := json.Unmarshal(raw, &head); err != nil {
		return 0, errors.Errorf("failed to unmarshal finalized block: %w", err)
	}

	return head.Number.Uint64(), nil
}

func fetchResults(ctx context.Context) error {
	var schedule *v1.GetCustomScheduleNodeStatusResponse
	for taskID, state := range tasks {
		needProcessing := false
		for chainID := range sumContracts {
			if state.Statuses[chainID] == TaskResponded {
				continue
			}
			status, err := sumContracts[chainID].GetTaskStatus(&bind.CallOpts{
				Context: ctx,
			}, taskID)
			if err != nil {
				return err
			}
			state.Statuses[chainID] = status
			if status == TaskCreated {
				needProcessing = true
			}
		}
		slog.InfoContext(ctx, "Task statuses", "taskID", taskID.Hex(), "statuses", state.Statuses)
		if !needProcessing {
			delete(tasks, taskID)
			continue
		}
		if state.AggProof == nil {
			resp, err := relayClient.GetAggregationProof(ctx, &v1.GetAggregationProofRequest{
				RequestId: state.SigRequestID,
			})
			if err != nil {
				if grpcstatus.Code(err) != codes.NotFound {
					slog.ErrorContext(ctx, "Failed to fetch aggregation proof for task, skipping", "err", err, "taskID", taskID.Hex())
				}
				continue
			}
			state.AggProof = resp.AggregationProof.Proof
			slog.InfoContext(ctx, "Got aggregation proof", "taskID", taskID.Hex(), "proof", hexutil.Encode(resp.AggregationProof.Proof))
		}

		tasks[taskID] = state
		if schedule == nil || time.Now().After(schedule.CurrentSlotEndTime.AsTime()) {
			// check schedule
			epochInfos, err := relayClient.GetLastAllCommitted(ctx, &v1.GetLastAllCommittedRequest{})
			if err != nil {
				return err
			}
			schedule, err = relayClient.GetCustomScheduleNodeStatus(ctx, &v1.GetCustomScheduleNodeStatusRequest{
				Epoch:               &epochInfos.SuggestedEpochInfo.LastCommittedEpoch,
				Seed:                []byte(SumTaskSeed),
				SlotDurationSeconds: SlotDurationSeconds,
				// NOTE: for demo purposes we set both min and max participants to 1
				MaxParticipantsPerSlot: 1,
				MinParticipantsPerSlot: 1,
			})
			if err != nil {
				return err
			}
		}

		if !schedule.IsActive {
			slog.InfoContext(ctx, "Not scheduled to respond in this epoch", "taskID", taskID.Hex())
			continue
		}
		if err := processProof(ctx, taskID); err != nil {
			fmt.Printf("Error processing proof: %v\n", err)
		}
	}
	return nil
}

func processProof(ctx context.Context, taskID common.Hash) error {
	pk, err := crypto.HexToECDSA(cfg.privateKey)
	if err != nil {
		return errors.Errorf("failed to parse private key: %w", err)
	}
	task := tasks[taskID]
	for chainID, status := range task.Statuses {
		if status == TaskResponded {
			continue
		}
		txOpts, err := bind.NewKeyedTransactorWithChainID(pk, big.NewInt(chainID))
		if err != nil {
			return errors.Errorf("failed to create transactor: %w", err)
		}
		txOpts.Context = ctx

		tx, err := sumContracts[chainID].RespondTask(txOpts, taskID, task.Result, big.NewInt(task.SigEpoch), task.AggProof)
		if err != nil {
			return errors.Errorf("failed to respond task: %w", err)
		}

		slog.InfoContext(ctx, "Submitted response tx", "taskID", taskID.Hex(), "tx", tx.Hash().String(), "gas", tx.Gas())
	}
	return nil
}

func processNewTasks(ctx context.Context, chainID int64, iter *contracts.SumTaskCreateTaskIterator) error {
	for iter.Next() {
		evt := iter.Event
		status, err := sumContracts[chainID].GetTaskStatus(&bind.CallOpts{
			Context: ctx,
		}, evt.TaskId)
		if err != nil {
			return err
		}

		if status != TaskCreated {
			// skip if task is not in created state
			continue
		}

		slog.InfoContext(ctx, "Received new task", "taskID", common.Hash(evt.TaskId).Hex(), "task", evt.Task)

		bytes32T, _ := abi.NewType("bytes32", "", nil)
		uint256T, _ := abi.NewType("uint256", "", nil)

		args := abi.Arguments{
			{Type: bytes32T},
			{Type: uint256T},
		}

		taskResult := new(big.Int).Add(evt.Task.NumberA, evt.Task.NumberB)

		slog.InfoContext(ctx, "New task result", "result", taskResult.String())

		msg, err := args.Pack(evt.TaskId, taskResult)
		if err != nil {
			return err
		}

		slog.InfoContext(ctx, "New task result to sign", "message", hexutil.Encode(msg))

		epochInfos, err := relayClient.GetLastAllCommitted(ctx, &v1.GetLastAllCommittedRequest{})
		if err != nil {
			return err
		}
		suggestedEpoch := epochInfos.SuggestedEpochInfo.LastCommittedEpoch

		resp, err := relayClient.SignMessage(ctx, &v1.SignMessageRequest{
			KeyTag:        15,
			Message:       msg,
			RequiredEpoch: &suggestedEpoch,
		})
		if err != nil {
			return err
		}

		tasks[evt.TaskId] = TaskState{
			ChainID:      chainID,
			Task:         evt.Task,
			Result:       taskResult,
			SigEpoch:     int64(resp.Epoch),
			SigRequestID: resp.RequestId,
			AggProof:     nil,
			Statuses:     map[int64]uint8{},
		}

		slog.InfoContext(ctx, "New task result signed", "resp", resp)
	}
	return nil
}

func signalContext(ctx context.Context) context.Context {
	cnCtx, cancel := context.WithCancel(ctx)

	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGTERM, syscall.SIGINT)

	go func() {
		sig := <-c
		slog.WarnContext(ctx, "Received signal", "signal", sig)
		cancel()
	}()

	return cnCtx
}
```

## 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"
)

// 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 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
}

// 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
}

// 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
}

// 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
}

// 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
}

// 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
}

// 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
}

// NewSumTask creates a new instance of SumTask, bound to a specific deployed contract.
func NewSumTask(address common.Address, backend bind.ContractBackend) (*SumTask, error) {
	contract, err := bindSumTask(address, backend, backend, backend)
	if err != nil {
		return nil, err
	}
	return &SumTask{SumTaskCaller: SumTaskCaller{contract: contract}, SumTaskTransactor: SumTaskTransactor{contract: contract}, SumTaskFilterer: SumTaskFilterer{contract: contract}}, nil
}

// 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) {
	contract, err := bindSumTask(address, caller, nil, nil)
	if err != nil {
		return nil, err
	}
	return &SumTaskCaller{contract: contract}, nil
}

// 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) {
	contract, err := bindSumTask(address, nil, transactor, nil)
	if err != nil {
		return nil, err
	}
	return &SumTaskTransactor{contract: contract}, nil
}

// 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) {
	contract, err := bindSumTask(address, nil, nil, filterer)
	if err != nil {
		return nil, err
	}
	return &SumTaskFilterer{contract: contract}, nil
}

// 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) {
	parsed, err := SumTaskMetaData.GetAbi()
	if err != nil {
		return nil, err
	}
	return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil
}

// 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{}, method string, params ...interface{}) error {
	return _SumTask.Contract.SumTaskCaller.contract.Call(opts, result, method, params...)
}

// 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) {
	return _SumTask.Contract.SumTaskTransactor.contract.Transfer(opts)
}

// Transact invokes the (paid) contract method with params as input values.
func (_SumTask *SumTaskRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
	return _SumTask.Contract.SumTaskTransactor.contract.Transact(opts, method, params...)
}

// 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 *SumTaskCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
	return _SumTask.Contract.contract.Call(opts, result, method, params...)
}

// Transfer initiates a plain transaction to move funds to the contract, calling
// its default method if one is available.
func (_SumTask *SumTaskTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
	return _SumTask.Contract.contract.Transfer(opts)
}

// Transact invokes the (paid) contract method with params as input values.
func (_SumTask *SumTaskTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
	return _SumTask.Contract.contract.Transact(opts, method, params...)
}

// 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{}
	err := _SumTask.contract.Call(opts, &out, "TASK_EXPIRY")

	if err != nil {
		return *new(uint32), err
	}

	out0 := *abi.ConvertType(out[0], new(uint32)).(*uint32)

	return out0, err

}

// TASKEXPIRY is a free data retrieval call binding the contract method 0x240697b6.
//
// Solidity: function TASK_EXPIRY() view returns(uint32)
func (_SumTask *SumTaskSession) TASKEXPIRY() (uint32, error) {
	return _SumTask.Contract.TASKEXPIRY(&_SumTask.CallOpts)
}

// TASKEXPIRY is a free data retrieval call binding the contract method 0x240697b6.
//
// Solidity: function TASK_EXPIRY() view returns(uint32)
func (_SumTask *SumTaskCallerSession) TASKEXPIRY() (uint32, error) {
	return _SumTask.Contract.TASKEXPIRY(&_SumTask.CallOpts)
}

// 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) {
	var out []interface{}
	err := _SumTask.contract.Call(opts, &out, "getTaskStatus", taskId)

	if err != nil {
		return *new(uint8), err
	}

	out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8)

	return out0, err

}

// GetTaskStatus is a free data retrieval call binding the contract method 0x2bf6cc79.
//
// Solidity: function getTaskStatus(bytes32 taskId) view returns(uint8)
func (_SumTask *SumTaskSession) GetTaskStatus(taskId [32]byte) (uint8, error) {
	return _SumTask.Contract.GetTaskStatus(&_SumTask.CallOpts, taskId)
}

// GetTaskStatus is a free data retrieval call binding the contract method 0x2bf6cc79.
//
// Solidity: function getTaskStatus(bytes32 taskId) view returns(uint8)
func (_SumTask *SumTaskCallerSession) GetTaskStatus(taskId [32]byte) (uint8, error) {
	return _SumTask.Contract.GetTaskStatus(&_SumTask.CallOpts, taskId)
}

// 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) {
	var out []interface{}
	err := _SumTask.contract.Call(opts, &out, "nonce")

	if err != nil {
		return *new(*big.Int), err
	}

	out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)

	return out0, err

}

// Nonce is a free data retrieval call binding the contract method 0xaffed0e0.
//
// Solidity: function nonce() view returns(uint256)
func (_SumTask *SumTaskSession) Nonce() (*big.Int, error) {
	return _SumTask.Contract.Nonce(&_SumTask.CallOpts)
}

// Nonce is a free data retrieval call binding the contract method 0xaffed0e0.
//
// Solidity: function nonce() view returns(uint256)
func (_SumTask *SumTaskCallerSession) Nonce() (*big.Int, error) {
	return _SumTask.Contract.Nonce(&_SumTask.CallOpts)
}

// 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 {
	AnsweredAt *big.Int
	Answer     *big.Int
}, error) {
	var out []interface{}
	err := _SumTask.contract.Call(opts, &out, "responses", arg0)

	outstruct := new(struct {
		AnsweredAt *big.Int
		Answer     *big.Int
	})
	if err != nil {
		return *outstruct, err
	}

	outstruct.AnsweredAt = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
	outstruct.Answer = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int)

	return *outstruct, err

}

// Responses is a free data retrieval call binding the contract method 0x72164a6c.
//
// Solidity: function responses(bytes32 ) view returns(uint48 answeredAt, uint256 answer)
func (_SumTask *SumTaskSession) Responses(arg0 [32]byte) (struct {
	AnsweredAt *big.Int
	Answer     *big.Int
}, error) {
	return _SumTask.Contract.Responses(&_SumTask.CallOpts, arg0)
}

// Responses is a free data retrieval call binding the contract method 0x72164a6c.
//
// Solidity: function responses(bytes32 ) view returns(uint48 answeredAt, uint256 answer)
func (_SumTask *SumTaskCallerSession) Responses(arg0 [32]byte) (struct {
	AnsweredAt *big.Int
	Answer     *big.Int
}, error) {
	return _SumTask.Contract.Responses(&_SumTask.CallOpts, arg0)
}

// 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) {
	var out []interface{}
	err := _SumTask.contract.Call(opts, &out, "settlement")

	if err != nil {
		return *new(common.Address), err
	}

	out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)

	return out0, err

}

// Settlement is a free data retrieval call binding the contract method 0x51160630.
//
// Solidity: function settlement() view returns(address)
func (_SumTask *SumTaskSession) Settlement() (common.Address, error) {
	return _SumTask.Contract.Settlement(&_SumTask.CallOpts)
}

// Settlement is a free data retrieval call binding the contract method 0x51160630.
//
// Solidity: function settlement() view returns(address)
func (_SumTask *SumTaskCallerSession) Settlement() (common.Address, error) {
	return _SumTask.Contract.Settlement(&_SumTask.CallOpts)
}

// 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 {
	NumberA   *big.Int
	NumberB   *big.Int
	Nonce     *big.Int
	CreatedAt *big.Int
}, error) {
	var out []interface{}
	err := _SumTask.contract.Call(opts, &out, "tasks", arg0)

	outstruct := new(struct {
		NumberA   *big.Int
		NumberB   *big.Int
		Nonce     *big.Int
		CreatedAt *big.Int
	})
	if err != nil {
		return *outstruct, err
	}

	outstruct.NumberA = *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
	outstruct.NumberB = *abi.ConvertType(out[1], new(*big.Int)).(**big.Int)
	outstruct.Nonce = *abi.ConvertType(out[2], new(*big.Int)).(**big.Int)
	outstruct.CreatedAt = *abi.ConvertType(out[3], new(*big.Int)).(**big.Int)

	return *outstruct, err

}

// 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 *SumTaskSession) Tasks(arg0 [32]byte) (struct {
	NumberA   *big.Int
	NumberB   *big.Int
	Nonce     *big.Int
	CreatedAt *big.Int
}, error) {
	return _SumTask.Contract.Tasks(&_SumTask.CallOpts, arg0)
}

// 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 *SumTaskCallerSession) Tasks(arg0 [32]byte) (struct {
	NumberA   *big.Int
	NumberB   *big.Int
	Nonce     *big.Int
	CreatedAt *big.Int
}, error) {
	return _SumTask.Contract.Tasks(&_SumTask.CallOpts, arg0)
}

// 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) {
	return _SumTask.contract.Transact(opts, "createTask", numberA, numberB)
}

// CreateTask is a paid mutator transaction binding the contract method 0xe75b2378.
//
// Solidity: function createTask(uint256 numberA, uint256 numberB) returns(bytes32 taskId)
func (_SumTask *SumTaskSession) CreateTask(numberA *big.Int, numberB *big.Int) (*types.Transaction, error) {
	return _SumTask.Contract.CreateTask(&_SumTask.TransactOpts, numberA, numberB)
}

// CreateTask is a paid mutator transaction binding the contract method 0xe75b2378.
//
// Solidity: function createTask(uint256 numberA, uint256 numberB) returns(bytes32 taskId)
func (_SumTask *SumTaskTransactorSession) CreateTask(numberA *big.Int, numberB *big.Int) (*types.Transaction, error) {
	return _SumTask.Contract.CreateTask(&_SumTask.TransactOpts, numberA, numberB)
}

// 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) {
	return _SumTask.contract.Transact(opts, "respondTask", taskId, result, epoch, proof)
}

// 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 *SumTaskSession) RespondTask(taskId [32]byte, result *big.Int, epoch *big.Int, proof []byte) (*types.Transaction, error) {
	return _SumTask.Contract.RespondTask(&_SumTask.TransactOpts, taskId, result, epoch, proof)
}

// 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 *SumTaskTransactorSession) RespondTask(taskId [32]byte, result *big.Int, epoch *big.Int, proof []byte) (*types.Transaction, error) {
	return _SumTask.Contract.RespondTask(&_SumTask.TransactOpts, taskId, result, epoch, proof)
}

// 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
}

// 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 it.fail != nil {
		return false
	}
	// If the iterator completed, deliver directly whatever's available
	if it.done {
		select {
		case log := <-it.logs:
			it.Event = new(SumTaskCreateTask)
			if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
				it.fail = err
				return false
			}
			it.Event.Raw = log
			return true

		default:
			return false
		}
	}
	// Iterator still in progress, wait for either a data or an error event
	select {
	case log := <-it.logs:
		it.Event = new(SumTaskCreateTask)
		if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
			it.fail = err
			return false
		}
		it.Event.Raw = log
		return true

	case err := <-it.sub.Err():
		it.done = true
		it.fail = err
		return it.Next()
	}
}

// Error returns any retrieval or parsing error occurred during filtering.
func (it *SumTaskCreateTaskIterator) Error() error {
	return it.fail
}

// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *SumTaskCreateTaskIterator) Close() error {
	it.sub.Unsubscribe()
	return nil
}

// 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
}

// 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{}
	for _, taskIdItem := range taskId {
		taskIdRule = append(taskIdRule, taskIdItem)
	}

	logs, sub, err := _SumTask.contract.FilterLogs(opts, "CreateTask", taskIdRule)
	if err != nil {
		return nil, err
	}
	return &SumTaskCreateTaskIterator{contract: _SumTask.contract, event: "CreateTask", logs: logs, sub: sub}, nil
}

// WatchCreateTask is a free log subscription operation binding the contract event 0x091f91724daf92df11de4c7b494af095ec9c1325865974e9750636fbe5373677.
//
// Solidity: event CreateTask(bytes32 indexed taskId, (uint256,uint256,uint256,uint48) task)
func (_SumTask *SumTaskFilterer) WatchCreateTask(opts *bind.WatchOpts, sink chan<- *SumTaskCreateTask, taskId [][32]byte) (event.Subscription, error) {

	var taskIdRule []interface{}
	for _, taskIdItem := range taskId {
		taskIdRule = append(taskIdRule, taskIdItem)
	}

	logs, sub, err := _SumTask.contract.WatchLogs(opts, "CreateTask", taskIdRule)
	if err != nil {
		return nil, err
	}
	return event.NewSubscription(func(quit <-chan struct{}) error {
		defer sub.Unsubscribe()
		for {
			select {
			case log := <-logs:
				// New log arrived, parse the event and forward to the user
				event := new(SumTaskCreateTask)
				if err := _SumTask.contract.UnpackLog(event, "CreateTask", log); err != nil {
					return err
				}
				event.Raw = log

				select {
				case sink <- event:
				case err := <-sub.Err():
					return err
				case <-quit:
					return nil
				}
			case err := <-sub.Err():
				return err
			case <-quit:
				return nil
			}
		}
	}), nil
}

// ParseCreateTask is a log parse operation binding the contract event 0x091f91724daf92df11de4c7b494af095ec9c1325865974e9750636fbe5373677.
//
// Solidity: event CreateTask(bytes32 indexed taskId, (uint256,uint256,uint256,uint48) task)
func (_SumTask *SumTaskFilterer) ParseCreateTask(log types.Log) (*SumTaskCreateTask, error) {
	event := new(SumTaskCreateTask)
	if err := _SumTask.contract.UnpackLog(event, "CreateTask", log); err != nil {
		return nil, err
	}
	event.Raw = log
	return event, nil
}

// 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
}

// 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 *SumTaskRespondTaskIterator) Next() bool {
	// If the iterator failed, stop iterating
	if it.fail != nil {
		return false
	}
	// If the iterator completed, deliver directly whatever's available
	if it.done {
		select {
		case log := <-it.logs:
			it.Event = new(SumTaskRespondTask)
			if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
				it.fail = err
				return false
			}
			it.Event.Raw = log
			return true

		default:
			return false
		}
	}
	// Iterator still in progress, wait for either a data or an error event
	select {
	case log := <-it.logs:
		it.Event = new(SumTaskRespondTask)
		if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
			it.fail = err
			return false
		}
		it.Event.Raw = log
		return true

	case err := <-it.sub.Err():
		it.done = true
		it.fail = err
		return it.Next()
	}
}

// Error returns any retrieval or parsing error occurred during filtering.
func (it *SumTaskRespondTaskIterator) Error() error {
	return it.fail
}

// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *SumTaskRespondTaskIterator) Close() error {
	it.sub.Unsubscribe()
	return nil
}

// 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
}

// 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) {

	var taskIdRule []interface{}
	for _, taskIdItem := range taskId {
		taskIdRule = append(taskIdRule, taskIdItem)
	}

	logs, sub, err := _SumTask.contract.FilterLogs(opts, "RespondTask", taskIdRule)
	if err != nil {
		return nil, err
	}
	return &SumTaskRespondTaskIterator{contract: _SumTask.contract, event: "RespondTask", logs: logs, sub: sub}, nil
}

// WatchRespondTask is a free log subscription operation binding the contract event 0xfc4f6d5b11d191a9cdec60e7a5819b4695dba8d8365e7afb1f24b159ceb7e287.
//
// Solidity: event RespondTask(bytes32 indexed taskId, (uint48,uint256) response)
func (_SumTask *SumTaskFilterer) WatchRespondTask(opts *bind.WatchOpts, sink chan<- *SumTaskRespondTask, taskId [][32]byte) (event.Subscription, error) {

	var taskIdRule []interface{}
	for _, taskIdItem := range taskId {
		taskIdRule = append(taskIdRule, taskIdItem)
	}

	logs, sub, err := _SumTask.contract.WatchLogs(opts, "RespondTask", taskIdRule)
	if err != nil {
		return nil, err
	}
	return event.NewSubscription(func(quit <-chan struct{}) error {
		defer sub.Unsubscribe()
		for {
			select {
			case log := <-logs:
				// New log arrived, parse the event and forward to the user
				event := new(SumTaskRespondTask)
				if err := _SumTask.contract.UnpackLog(event, "RespondTask", log); err != nil {
					return err
				}
				event.Raw = log

				select {
				case sink <- event:
				case err := <-sub.Err():
					return err
				case <-quit:
					return nil
				}
			case err := <-sub.Err():
				return err
			case <-quit:
				return nil
			}
		}
	}), nil
}

// ParseRespondTask is a log parse operation binding the contract event 0xfc4f6d5b11d191a9cdec60e7a5819b4695dba8d8365e7afb1f24b159ceb7e287.
//
// Solidity: event RespondTask(bytes32 indexed taskId, (uint48,uint256) response)
func (_SumTask *SumTaskFilterer) ParseRespondTask(log types.Log) (*SumTaskRespondTask, error) {
	event := new(SumTaskRespondTask)
	if err := _SumTask.contract.UnpackLog(event, "RespondTask", log); err != nil {
		return nil, err
	}
	event.Raw = log
	return event, nil
}
```

## File: off-chain/internal/utils/util.go

```go
package utils

import (
	"time"

	grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/retry"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

func GetGRPCConnection(address string) (*grpc.ClientConn, error) {
	retryOpts := []grpc_retry.CallOption{
		grpc_retry.WithMax(3),
		grpc_retry.WithBackoff(grpc_retry.BackoffLinear(time.Second)),
	}
	unaryInterceptors := []grpc.UnaryClientInterceptor{grpc_retry.UnaryClientInterceptor(retryOpts...)}
	opts := []grpc.DialOption{
		grpc.WithStreamInterceptor(grpc_retry.StreamClientInterceptor(retryOpts...)),
		grpc.WithChainUnaryInterceptor(unaryInterceptors...),
		grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(100*1024*1024), grpc.MaxCallSendMsgSize(100*1024*1024)),
	}

	opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))

	return grpc.NewClient(address, opts...)
}
```

## 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.25.3-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.25.3

require (
	github.com/ethereum/go-ethereum v1.16.5
	github.com/go-errors/errors v1.5.1
	github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3
	github.com/samber/lo v1.52.0
	github.com/spf13/cobra v1.10.1
	github.com/symbioticfi/relay v0.3.1-0.20260101063025-eb92509700df
	golang.org/x/sync v0.18.0
	google.golang.org/grpc v1.76.0
)

require (
	github.com/Microsoft/go-winio v0.6.2 // indirect
	github.com/StackExchange/wmi v1.2.1 // indirect
	github.com/bits-and-blooms/bitset v1.24.3 // indirect
	github.com/consensys/gnark-crypto v0.19.2 // 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.6.0 // indirect
	github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
	github.com/ethereum/c-kzg-4844/v2 v2.1.5 // 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.4-0.20250319132907-e064f32e3674 // indirect
	github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect
	github.com/holiman/uint256 v1.3.2 // indirect
	github.com/inconshreveable/mousetrap v1.1.0 // indirect
	github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
	github.com/spf13/pflag v1.0.10 // indirect
	github.com/supranational/blst v0.3.16 // indirect
	github.com/tklauser/go-sysconf v0.3.15 // indirect
	github.com/tklauser/numcpus v0.10.0 // indirect
	golang.org/x/crypto v0.45.0 // indirect
	golang.org/x/net v0.47.0 // indirect
	golang.org/x/sys v0.38.0 // indirect
	golang.org/x/text v0.31.0 // indirect
	google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda // indirect
	google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda // indirect
	google.golang.org/protobuf v1.36.10 // indirect
)

// pinned versions
replace (
	github.com/consensys/gnark => github.com/consensys/gnark v0.14.0
	github.com/consensys/gnark-crypto => github.com/consensys/gnark-crypto v0.19.0
)
```

## File: script/mocks/MockERC20.sol

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MockERC20 is ERC20 {
    constructor(string memory name, string memory symbol) ERC20(name, symbol) {
        _mint(msg.sender, 1_000_000_000_000_000_000_000_000);
    }

    function mint(address to, uint256 amount) public {
        _mint(to, amount);
    }
}
```

## 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

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

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 {BLS12381G2} from "./utils/BLS12381G2.sol";

import {RelayDeploy} from "@symbioticfi/relay-contracts/script/RelayDeploy.sol";

import {BN254} from "@symbioticfi/relay-contracts/src/libraries/utils/BN254.sol";
import {BLS12381} from "@symbioticfi/relay-contracts/src/libraries/utils/BLS12381.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 {KeyBlsBls12381} from "@symbioticfi/relay-contracts/src/libraries/keys/KeyBlsBls12381.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_BLS_BLS12381,
    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 {
    using KeyTags for uint8;
    using KeyBlsBn254 for BN254.G1Point;
    using KeyBlsBls12381 for BLS12381.G1Point;
    using KeyEcdsaSecp256k1 for address;
    using KeyEcdsaSecp256k1 for KeyEcdsaSecp256k1.KEY_ECDSA_SECP256K1;
    using KeyEcdsaSecp256k1 for bytes;
    using BN254 for BN254.G1Point;
    using BLS12381 for BLS12381.G1Point;
    using KeyBlsBn254 for KeyBlsBn254.KEY_BLS_BN254;
    using KeyBlsBls12381 for KeyBlsBls12381.KEY_BLS_BLS12381;

    bytes32 internal constant KEY_OWNERSHIP_TYPEHASH = keccak256("KeyOwnership(address operator,bytes key)");

    // Configurable constants
    uint48 internal immutable EPOCH_DURATION = uint48(vm.envOr("EPOCH_TIME", uint256(60)));
    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)
    uint256 internal constant OPERATOR_STAKE_AMOUNT = 100_000;
    uint8 internal constant REQUIRED_KEY_TAG_ECDSA = 16; // 16 is the default key tag for ecdsa keys (ECDSA-SECP256K1/0)
    uint8 internal constant REQUIRED_KEY_TAG_SECONDARY_BLS = 11;
    uint8 internal constant REQUIRED_KEY_TAG_BLS12381 = 33; // 33 is the default key tag (BLS12-381/1)
    uint256 internal immutable OPERATOR_COUNT = vm.envOr("OPERATOR_COUNT", uint256(4));
    uint8 internal immutable VERIFICATION_TYPE = uint8(vm.envOr("VERIFICATION_TYPE", uint256(1)));
    uint208 internal immutable NUM_AGGREGATORS = uint208(vm.envOr("NUM_AGGREGATORS", uint256(1)));
    uint208 internal immutable NUM_COMMITTERS = uint208(vm.envOr("NUM_COMMITTERS", uint256(1)));
    uint48 internal immutable COMMITTER_SLOT_DURATION = uint48(vm.envOr("COMMITTER_SLOT_DURATION", uint256(10)));

    // CREATE3 salts
    bytes11 public constant NETWORK_SALT = bytes11("Network");
    bytes11 public constant SUM_TASK_SALT = bytes11("SumTask");
    bytes11 public constant KEY_REGISTRY_SALT = bytes11("KeyRegistry");
    bytes11 public constant VOTING_POWER_PROVIDER_SALT = bytes11("VPProvider");
    bytes11 public constant SETTLEMENT_SALT = bytes11("Settlement");
    bytes11 public constant VALSET_DRIVER_SALT = bytes11("VSDriver");

    constructor() RelayDeploy("./temp-network/my-relay-deploy.toml") {}

    function getDeployerAddress() internal returns (address deployer) {
        (,, deployer) = vm.readCallers();
    }

    function getStakingToken() internal returns (address) {
        if (config.get("staking_token").data.length == 0) {
            vm.broadcast();
            config.set("staking_token", address(new MockERC20("StakingToken", "STK")));
        }
        return config.get("staking_token").toAddress();
    }

    function getNetwork() internal returns (address) {
        if (config.get("network").data.length == 0) {
            address[] memory proposersAndExecutors = new address[](1);
            proposersAndExecutors[0] = getDeployerAddress();
            SymbioticCoreConstants.Core memory core = getCore();
            vm.broadcast();
            address networkImpl =
                address(new Network(address(core.networkRegistry), address(core.networkMiddlewareService)));
            config.set(
                "network",
                _deployContract(
                    NETWORK_SALT,
                    networkImpl,
                    abi.encodeCall(
                        INetwork.initialize,
                        (INetwork.NetworkInitParams({
                                globalMinDelay: 0,
                                delayParams: new INetwork.DelayParams[](0),
                                proposers: proposersAndExecutors,
                                executors: proposersAndExecutors,
                                name: "Example Network",
                                metadataURI: "https://example.network",
                                defaultAdminRoleHolder: getDeployerAddress(),
                                nameUpdateRoleHolder: getDeployerAddress(),
                                metadataURIUpdateRoleHolder: getDeployerAddress()
                            }))
                    ),
                    getDeployerAddress(),
                    false
                )
            );
        }
        return config.get("network").toAddress();
    }

    function _keyRegistryParams() internal override returns (address implementation, bytes memory initData) {
        vm.broadcast();
        implementation = address(new KeyRegistry());

        initData = abi.encodeCall(
            KeyRegistry.initialize,
            (IKeyRegistry.KeyRegistryInitParams({
                    ozEip712InitParams: IOzEIP712.OzEIP712InitParams({name: "KeyRegistry", version: "1"})
                }))
        );
    }

    function _votingPowerProviderParams() internal override returns (address implementation, bytes memory initData) {
        vm.startBroadcast();
        implementation = address(
            new VotingPowers(
                address(getCore().operatorRegistry),
                address(getCore().vaultFactory),
                address(getCore().vaultConfigurator)
            )
        );
        vm.stopBroadcast();

        initData = abi.encodeCall(
            VotingPowers.initialize,
            (
                IVotingPowerProvider.VotingPowerProviderInitParams({
                    networkManagerInitParams: INetworkManager.NetworkManagerInitParams({
                        network: getNetwork(), subnetworkId: 0
                    }),
                    ozEip712InitParams: IOzEIP712.OzEIP712InitParams({name: "VotingPowers", version: "1"}),
                    requireSlasher: false,
                    minVaultEpochDuration: SLASHING_WINDOW,
                    token: getStakingToken()
                }),
                IOpNetVaultAutoDeploy.OpNetVaultAutoDeployInitParams({
                    isAutoDeployEnabled: true,
                    config: IOpNetVaultAutoDeploy.AutoDeployConfig({
                        epochDuration: SLASHING_WINDOW,
                        collateral: getStakingToken(),
                        burner: address(0),
                        withSlasher: true,
                        isBurnerHook: false
                    }),
                    isSetMaxNetworkLimitHookEnabled: true
                }),
                IOzOwnable.OzOwnableInitParams({owner: getDeployerAddress()})
            )
        );
    }

    function _settlementParams() internal override returns (address implementation, bytes memory initData) {
        vm.startBroadcast();
        implementation = address(new Settlement());

        address verifier;
        if (VERIFICATION_TYPE == 0) {
            address[] memory verifiers = new address[](3);
            verifiers[0] = deployCode("out/Verifier_10.sol/Verifier.json");
            verifiers[1] = deployCode("out/Verifier_100.sol/Verifier.json");
            verifiers[2] = deployCode("out/Verifier_1000.sol/Verifier.json");
            uint256[] memory maxValidators = new uint256[](verifiers.length);
            maxValidators[0] = 10;
            maxValidators[1] = 100;
            maxValidators[2] = 1000;
            verifier = address(new SigVerifierBlsBn254ZK(verifiers, maxValidators));
        } else if (VERIFICATION_TYPE == 1) {
            verifier = address(new SigVerifierBlsBn254Simple());
        } else {
            revert("Invalid verification type");
        }
        vm.stopBroadcast();
        initData = abi.encodeCall(
            Settlement.initialize,
            (
                ISettlement.SettlementInitParams({
                    networkManagerInitParams: INetworkManager.NetworkManagerInitParams({
                        network: getNetwork(), subnetworkId: 0
                    }),
                    ozEip712InitParams: IOzEIP712.OzEIP712InitParams({name: "Settlement", version: "1"}),
                    sigVerifier: verifier
                }),
                getDeployerAddress()
            )
        );
    }

    function _valSetDriverParams() internal override returns (address implementation, bytes memory initData) {
        vm.broadcast();
        implementation = address(new Driver());

        IValSetDriver.QuorumThreshold[] memory quorumThresholds = new IValSetDriver.QuorumThreshold[](4);
        quorumThresholds[0] =
            IValSetDriver.QuorumThreshold({keyTag: REQUIRED_KEY_TAG, quorumThreshold: QUORUM_THRESHOLD});
        quorumThresholds[1] =
            IValSetDriver.QuorumThreshold({keyTag: REQUIRED_KEY_TAG_ECDSA, quorumThreshold: QUORUM_THRESHOLD});
        quorumThresholds[2] =
            IValSetDriver.QuorumThreshold({keyTag: REQUIRED_KEY_TAG_SECONDARY_BLS, quorumThreshold: QUORUM_THRESHOLD});
        quorumThresholds[3] =
            IValSetDriver.QuorumThreshold({keyTag: REQUIRED_KEY_TAG_BLS12381, quorumThreshold: QUORUM_THRESHOLD});
        uint8[] memory requiredKeyTags = new uint8[](4);
        requiredKeyTags[0] = REQUIRED_KEY_TAG;
        requiredKeyTags[1] = REQUIRED_KEY_TAG_ECDSA;
        requiredKeyTags[2] = REQUIRED_KEY_TAG_SECONDARY_BLS;
        requiredKeyTags[3] = REQUIRED_KEY_TAG_BLS12381;
        initData = abi.encodeCall(
            Driver.initialize,
            (
                IValSetDriver.ValSetDriverInitParams({
                    networkManagerInitParams: INetworkManager.NetworkManagerInitParams({
                        network: getNetwork(), subnetworkId: 0
                    }),
                    epochManagerInitParams: IEpochManager.EpochManagerInitParams({
                        epochDuration: EPOCH_DURATION, epochDurationTimestamp: 0
                    }),
                    numAggregators: NUM_AGGREGATORS,
                    numCommitters: NUM_COMMITTERS,
                    committerSlotDuration: COMMITTER_SLOT_DURATION,
                    votingPowerProviders: getVotingPowerProviders(),
                    keysProvider: getKeyRegistry(),
                    settlements: getSettlements(),
                    maxVotingPower: MAX_VOTING_POWER,
                    minInclusionVotingPower: MIN_INCLUSION_VOTING_POWER,
                    maxValidatorsCount: MAX_VALIDATORS_COUNT,
                    requiredKeyTags: requiredKeyTags,
                    quorumThresholds: quorumThresholds,
                    requiredHeaderKeyTag: REQUIRED_KEY_TAG,
                    verificationType: VERIFICATION_TYPE
                }),
                getDeployerAddress()
            )
        );
    }

    function runDeployKeyRegistry() public override {
        deployKeyRegistry({proxyOwner: getDeployerAddress(), isDeployerGuarded: false, salt: KEY_REGISTRY_SALT});

        fundOperators();
        for (uint256 i; i < OPERATOR_COUNT; ++i) {
            configureOperatorKeys(i);
        }
    }

    function runDeployVotingPowerProvider() public override {
        address votingPowerProvider = deployVotingPowerProvider({
            proxyOwner: getDeployerAddress(), isDeployerGuarded: false, salt: VOTING_POWER_PROVIDER_SALT
        });
        address network = getNetwork();
        SymbioticCoreConstants.Core memory core = getCore();
        vm.startBroadcast(getDeployerAddress());
        Network(payable(network))
            .schedule(
                address(core.networkMiddlewareService),
                0,
                abi.encodeWithSelector(INetworkMiddlewareService.setMiddleware.selector, votingPowerProvider),
                bytes32(0),
                bytes32(0),
                0
            );
        Network(payable(network))
            .execute(
                address(core.networkMiddlewareService),
                0,
                abi.encodeWithSelector(INetworkMiddlewareService.setMiddleware.selector, votingPowerProvider),
                bytes32(0),
                bytes32(0)
            );
        vm.stopBroadcast();

        fundOperators();
        for (uint256 i; i < OPERATOR_COUNT; ++i) {
            registerOperator(i, OPERATOR_STAKE_AMOUNT);
        }
        printOperatorsInfo();
    }

    function runDeploySettlement() public override {
        address settlement =
            deploySettlement({proxyOwner: getDeployerAddress(), isDeployerGuarded: false, salt: SETTLEMENT_SALT});
        vm.broadcast();
        address sumTask =
            deployCreate3(bytes32(SUM_TASK_SALT), abi.encodePacked(type(SumTask).creationCode, abi.encode(settlement)));
        config.set("sum_task", sumTask);

        fundOperators();
    }

    function runDeployValSetDriver() public override {
        deployValSetDriver({proxyOwner: getDeployerAddress(), isDeployerGuarded: false, salt: VALSET_DRIVER_SALT});
        vm.writeJson("", "temp-network/deploy-data/deployment-completed.json");
    }

    function configureOperatorKeys(uint256 index) public {
        Vm.Wallet memory operator = getOperator(index);
        KeyRegistry keyRegistry = KeyRegistry(getKeyRegistry().addr);

        vm.startBroadcast(operator.privateKey);

        // Register primary BLS-BN254 key with tag 15
        _registerBlsBn254Key(keyRegistry, operator, operator.privateKey, 15);

        // Register secondary BLS-BN254 key with tag 11
        _registerBlsBn254Key(keyRegistry, operator, operator.privateKey + 10_000, 11);

        // Register ECDSA key
        _registerEcdsaKey(keyRegistry, operator);

        // Register BLS12-381 key with tag 1
        _registerBls12381Key(keyRegistry, operator, operator.privateKey + 20_000, 1);

        vm.stopBroadcast();
    }

    function _registerBlsBn254Key(KeyRegistry keyRegistry, Vm.Wallet memory operator, uint256 privateKey, uint8 keyTag)
        internal
    {
        (BN254.G1Point memory g1Key, BN254.G2Point memory g2Key) = getBLSKeys(privateKey);
        bytes memory keyBytes = KeyBlsBn254.wrap(g1Key).toBytes();
        bytes32 messageHash = keyRegistry.hashTypedDataV4(
            keccak256(abi.encode(KEY_OWNERSHIP_TYPEHASH, operator.addr, keccak256(keyBytes)))
        );
        BN254.G1Point memory messageG1 = BN254.hashToG1(messageHash);
        BN254.G1Point memory sigG1 = messageG1.scalar_mul(privateKey);
        keyRegistry.setKey(KEY_TYPE_BLS_BN254.getKeyTag(keyTag), keyBytes, abi.encode(sigG1), abi.encode(g2Key));
    }

    function _registerEcdsaKey(KeyRegistry keyRegistry, Vm.Wallet memory operator) internal {
        bytes memory keyBytes = KeyEcdsaSecp256k1.wrap(operator.addr).toBytes();
        bytes32 messageHash = keyRegistry.hashTypedDataV4(
            keccak256(abi.encode(KEY_OWNERSHIP_TYPEHASH, operator.addr, keccak256(keyBytes)))
        );
        (uint8 v, bytes32 r, bytes32 s) = vm.sign(operator.privateKey, messageHash);
        bytes memory signature = abi.encodePacked(r, s, v);
        keyRegistry.setKey(KEY_TYPE_ECDSA_SECP256K1.getKeyTag(0), keyBytes, signature, new bytes(0));
    }

    function _registerBls12381Key(KeyRegistry keyRegistry, Vm.Wallet memory operator, uint256 privateKey, uint8 keyTag)
        internal
    {
        (BLS12381.G1Point memory g1Key, BLS12381.G2Point memory g2Key) = getBLS12381Keys(privateKey);
        bytes memory keyBytes = KeyBlsBls12381.wrap(g1Key).toBytes();
        bytes32 messageHash = keyRegistry.hashTypedDataV4(
            keccak256(abi.encode(KEY_OWNERSHIP_TYPEHASH, operator.addr, keccak256(keyBytes)))
        );
        BLS12381.G1Point memory messageG1 = BLS12381.hashToG1(abi.encodePacked(messageHash));
        BLS12381.G1Point memory sigG1 = messageG1.scalar_mul(privateKey);
        keyRegistry.setKey(KEY_TYPE_BLS_BLS12381.getKeyTag(keyTag), keyBytes, abi.encode(sigG1), abi.encode(g2Key));
    }

    function registerOperator(uint256 index, uint256 stakeAmount) public {
        Vm.Wallet memory operator = getOperator(index);
        IERC20 stakingToken = IERC20(getStakingToken());
        VotingPowers votingPowers = VotingPowers(getVotingPowerProvider());

        vm.broadcast();
        stakingToken.transfer(operator.addr, stakeAmount);

        vm.startBroadcast(operator.privateKey);
        getCore().operatorRegistry.registerOperator();
        getCore().operatorNetworkOptInService.optIn(address(getNetwork()));
        votingPowers.registerOperator();
        IVault vault = IVault(votingPowers.getAutoDeployedVault(operator.addr));
        getCore().operatorVaultOptInService.optIn(address(vault));

        stakingToken.approve(address(vault), stakeAmount);
        vault.deposit(address(stakingToken), stakeAmount);
        vm.stopBroadcast();
    }

    function fundOperators() public {
        for (uint256 i; i < OPERATOR_COUNT; ++i) {
            Vm.Wallet memory operator = getOperator(i);
            vm.broadcast();
            payable(operator.addr).transfer(1 ether);
        }
    }

    function getOperator(uint256 index) public returns (VmSafe.Wallet memory operator) {
        // deterministic operator private key
        operator = vm.createWallet(1e18 + index);
        vm.rememberKey(operator.privateKey);
        return operator;
    }

    function getBLSKeys(uint256 privateKey) public returns (BN254.G1Point memory, BN254.G2Point memory) {
        BN254.G1Point memory G1Key = BN254.generatorG1().scalar_mul(privateKey);
        BN254.G2Point memory G2 = BN254.generatorG2();
        (uint256 x1, uint256 x2, uint256 y1, uint256 y2) =
            BN254G2.ECTwistMul(privateKey, G2.X[1], G2.X[0], G2.Y[1], G2.Y[0]);
        return (G1Key, BN254.G2Point([x2, x1], [y2, y1]));
    }

    function getBLS12381Keys(uint256 privateKey)
        public
        view
        returns (BLS12381.G1Point memory, BLS12381.G2Point memory)
    {
        BLS12381.G1Point memory G1Key = BLS12381.generatorG1().scalar_mul(privateKey);
        BLS12381.G2Point memory G2Key = BLS12381G2.scalarMul(privateKey, BLS12381.generatorG2());
        return (G1Key, G2Key);
    }

    function printOperatorsInfo() public {
        VotingPowers votingPowers = VotingPowers(getVotingPowerProvider());
        address[] memory operators = votingPowers.getOperators();
        VotingPowers.OperatorVotingPower[] memory operatorVPs = votingPowers.getVotingPowers(new bytes[](0));

        string memory logMessage =
            string.concat("Operators total: ", vm.toString(operators.length), "\n", "Operators:\n");

        for (uint256 i; i < operatorVPs.length; ++i) {
            uint256 totalVotingPower;
            logMessage =
                string.concat(logMessage, "   Address: ", vm.toString(operatorVPs[i].operator), "\n", "   Vaults:\n");
            for (uint256 j; j < operatorVPs[i].vaults.length; ++j) {
                logMessage = string.concat(
                    logMessage,
                    "       Address: ",
                    vm.toString(operatorVPs[i].vaults[j].vault),
                    "\n",
                    "       Voting power: ",
                    vm.toString(operatorVPs[i].vaults[j].value),
                    "\n"
                );
                totalVotingPower += operatorVPs[i].vaults[j].value;
            }
            logMessage = string.concat(logMessage, "   Total voting power: ", vm.toString(totalVotingPower), "\n");
        }

        Logs.log(logMessage);
    }
}
```

## 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 549.46µs (160.96µs 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 1.99ms (549.46µs 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              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| BLS12381                                                                                         | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| BLS12381G2                                                                                       | 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,793           | 17,821            | 6,783              | 31,331              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| ECDSA                                                                                            | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| ERC1967Proxy                                                                                     | 122              | 878               | 24,454             | 48,274              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| 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              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| KeyBlsBls12381                                                                                   | 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) | 21,199           | 21,227            | 3,377              | 27,925              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| KeyRegistry (src/symbiotic/KeyRegistry.sol)                                                      | 21,468           | 21,496            | 3,108              | 27,656              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| KeyTags                                                                                          | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| Logs                                                                                             | 4,290            | 4,342             | 20,286             | 44,810              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| LowLevelCall                                                                                     | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| 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,320           | 12,512            | 12,256             | 36,640              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| 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)                                                        | 12,596           | 12,624            | 11,980             | 36,528              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| SettlementMock                                                                                   | 799              | 827               | 23,777             | 48,325              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| SigBlsBls12381                                                                                   | 44               | 94                | 24,532             | 49,058              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| 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,655            | 7,683             | 16,921             | 41,469              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| Token                                                                                            | 1,723            | 3,065             | 22,853             | 46,087              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| TransparentUpgradeableProxy                                                                      | 1,018            | 3,334             | 23,558             | 45,818              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| VotingPowerProviderLogic                                                                         | 15,628           | 15,680            | 8,948              | 33,472              |
|--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------|
| VotingPowers                                                                                     | 14,602           | 14,810            | 9,974              | 34,342              |
╰--------------------------------------------------------------------------------------------------+------------------+-------------------+--------------------+---------------------╯
```

## File: src/symbiotic/Driver.sol

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

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)
        public
        virtual
        initializer
    {
        __ValSetDriver_init(valSetDriverInitParams);
        __OzAccessControl_init();

        _grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin);
    }
}
```

## File: src/symbiotic/KeyRegistry.sol

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

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 {
        __KeyRegistry_init(keyRegistryInitParams);
    }
}
```

## File: src/symbiotic/Settlement.sol

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

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)
        public
        virtual
        initializer
    {
        __Settlement_init(settlementInitParams);
        __OzAccessControl_init();

        _grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin);
    }
}
```

## File: src/symbiotic/VotingPowers.sol

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

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 {
    constructor(address operatorRegistry, address vaultFactory, address vaultConfigurator)
        VotingPowerProvider(operatorRegistry, vaultFactory)
        OpNetVaultAutoDeploy(vaultConfigurator)
    {}

    function initialize(
        VotingPowerProviderInitParams memory votingPowerProviderInitParams,
        OpNetVaultAutoDeployInitParams memory opNetVaultAutoDeployInitParams,
        OzOwnableInitParams memory ozOwnableInitParams
    ) public virtual initializer {
        __VotingPowerProvider_init(votingPowerProviderInitParams);
        __OpNetVaultAutoDeploy_init(opNetVaultAutoDeployInitParams);
        __OzOwnable_init(ozOwnableInitParams);
        __EqualStakeVPCalc_init();
    }

    function _registerOperatorImpl(address operator) internal override(OpNetVaultAutoDeploy, VotingPowerProvider) {
        super._registerOperatorImpl(operator);
    }

    function _unregisterOperatorVaultImpl(address operator, address vault)
        internal
        override(OpNetVaultAutoDeploy, VotingPowerProvider)
    {
        super._unregisterOperatorVaultImpl(operator, vault);
    }
}
```

## File: src/SumTask.sol

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {ISettlement} from "@symbioticfi/relay-contracts/src/interfaces/modules/settlement/ISettlement.sol";

contract SumTask {
    error AlreadyResponded();
    error InvalidQuorumSignature();
    error InvalidVerifyingEpoch();

    enum TaskStatus {
        CREATED,
        RESPONDED,
        EXPIRED,
        NOT_FOUND
    }

    struct Task {
        uint256 numberA;
        uint256 numberB;
        uint256 nonce;
        uint48 createdAt;
    }

    struct Response {
        uint48 answeredAt;
        uint256 answer;
    }

    event CreateTask(bytes32 indexed taskId, Task task);

    event RespondTask(bytes32 indexed taskId, Response response);

    uint32 public constant TASK_EXPIRY = 12_000;

    ISettlement public settlement;

    uint256 public nonce;

    mapping(bytes32 => Task) public tasks;

    mapping(bytes32 => Response) public responses;

    constructor(address _settlement) {
        settlement = ISettlement(_settlement);
    }

    function getTaskStatus(bytes32 taskId) public view returns (TaskStatus) {
        if (responses[taskId].answeredAt > 0) {
            return TaskStatus.RESPONDED;
        }

        if (tasks[taskId].createdAt == 0) {
            return TaskStatus.NOT_FOUND;
        }

        if (block.timestamp > tasks[taskId].createdAt + TASK_EXPIRY) {
            return TaskStatus.EXPIRED;
        }

        return TaskStatus.CREATED;
    }

    function createTask(uint256 numberA, uint256 numberB) public returns (bytes32 taskId) {
        uint256 nonce_ = nonce++;
        Task memory task = Task({numberA: numberA, numberB: numberB, nonce: nonce_, createdAt: uint48(block.timestamp)});
        taskId = keccak256(abi.encode(block.chainid, numberA, numberB, nonce_));
        tasks[taskId] = task;

        emit CreateTask(taskId, task);
    }

    function respondTask(bytes32 taskId, uint256 result, uint48 epoch, bytes calldata proof) public {
        // check if the task is not responded yet
        if (responses[taskId].answeredAt > 0) {
            revert AlreadyResponded();
        }

        // verify that the verifying epoch is not stale
        uint48 nextEpochCaptureTimestamp = settlement.getCaptureTimestampFromValSetHeaderAt(epoch + 1);
        if (nextEpochCaptureTimestamp > 0 && block.timestamp >= nextEpochCaptureTimestamp + TASK_EXPIRY) {
            revert InvalidVerifyingEpoch();
        }

        // verify the quorum signature
        if (!settlement.verifyQuorumSigAt(
                abi.encode(keccak256(abi.encode(taskId, result))),
                settlement.getRequiredKeyTagFromValSetHeaderAt(epoch),
                settlement.getQuorumThresholdFromValSetHeaderAt(epoch),
                proof,
                epoch,
                new bytes(0)
            )) {
            revert InvalidQuorumSignature();
        }

        Response memory response = Response({answeredAt: uint48(block.timestamp), answer: result});
        responses[taskId] = response;

        emit RespondTask(taskId, response);
    }
}
```

## File: .gitmodules

```
[submodule "lib/forge-std"]
	path = lib/forge-std
	url = https://github.com/foundry-rs/forge-std
```

## 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/<provider>`. Each fix in response to an audit finding should be developed as a separate commit. The commit message should look similar to `fix: <provider> - <issue title>`.

## 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 <What it does, starts with verb>`
- For structs: `@notice <What it is>`
- 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 <Name>` (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

```
{
  "lib/forge-std": {
    "tag": {
      "name": "v1.14.0",
      "rev": "1801b0541f4fda118a10798fd3486bb7051c5dd6"
    }
  }
}
```

## File: foundry.toml

```toml
[profile.default]
evm_version = "osaka"
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="1.1.1"

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)
        SYMB_BLS12381_PRIVATE_KEY_DECIMAL=$(($BASE_PRIVATE_KEY + $key_index + 20000))
        SYMB_BLS12381_PRIVATE_KEY_HEX=$(printf "%064x" $SYMB_BLS12381_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/2/1/0x$SYMB_BLS12381_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.5.0",
        "@openzeppelin/contracts-upgradeable": "5.5.0",
        "@symbioticfi/core": "1.0.3",
        "@symbioticfi/relay-contracts": "^1.0.0-rc.4",
        "@symbioticfi/network": "1.0.0-rc.2"
    }
}
```

## 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/
```
