Latest Update: Enhanced with structured error system (ErrorCode enum) and improved operator reconnection logic for better reliability.
The Ava Protocol AVS can be compiled directly using Go version 1.22+. Ensure you have the appropriate version of Go installed on your development environment.
Check Go version:
go version
Compile Ava Protocol AVS:
go build -o ap-avs
Then you can run ap-avs binary. We make an effort to use pure Go so you can also cross compile for any supported architecture that the Go compiler support.
Check how to run an operator docs
To run the aggregator, use the following command:
ap-avs aggregator
Note: The Ava Protocol team currently manages the aggregator, and the communication IP address between the operator and the aggregator is hardcoded in the operator.
See docs/changes/20260406-fee-estimation.md for the fee pricing model, configuration, and implementation details.
- Dynamic Pricing: Change rates without code deployments
- Environment-Specific: Different rates for dev/staging/production
- A/B Testing: Test different pricing models
- Backward Compatible: Works without any configuration changes
- Partial Overrides: Only specify rates you want to change
- Zero Disruption: Existing configurations continue to work
![]() |
For each owner we deploy a ERC6900 wallet to schedule task and approve spending to user wallet.
Each task type has its equivalent modular code to re-present its condition and their actual execution.
Aggregator accepts RPC request from client to submit Task Payload. Currently, aggregator is managed and run by Ava Protocol team.
Periodically, aggregator combine the task submission, update our internal storage and a zkSNARK proof will be write back to our TaskManager contract.
Aggregator also accept task condition check result from operator, perform quorum and consensus check, then write the result back and flag that a task is good to run.
The aggregator is run and managed by the Ava Protocol team. Point your operator at it in the operator config file:
aggregator_server_ip_port_address: "aggregator.avaprotocol.org:57376"One endpoint serves every chain. The gateway is a single deployment that routes per-chain internally, so there is no longer a separate testnet and mainnet address — Ethereum, Sepolia, Base, Base Sepolia and BNB all arrive over the same gRPC connection.
The REST API for workflows lives at https://api.avaprotocol.org/api/v1, and
the operator dashboard at
https://api.avaprotocol.org/telemetry.
Operators communicates with aggregators through RPC. It requests task data from aggregator, it performs condition execution to check whether a task can be trigger. The result is then sent back to aggregator.
For task is ok to run, the operator will executed them. The detail of how task is triggering through our ERC6900 modular wallet will come soon.
Currently, Ava Protocol has deployed our operator on the testnet. Community members can run their own operator and register for Ava Protocol AVS service, or they can delegate their tokens to the Ava Protocol operator.
- Ava Protocol's operator: 0x997e5d40a32c44a3d93e59fc55c4fd20b7d2d49d.
- Ava Protocol's operator: 0xc6B87cc9e85b07365b6aBEfff061F237F7cf7Dc3
An operator connected to the Ava Protocol gateway can see itself on the telemetry dashboard:
https://api.avaprotocol.org/telemetry
One dashboard covers every chain. The gateway is unified — a single deployment
serves all chains and routes per-chain internally — so the previous per-chain
aggregator-*.avaprotocol.org/telemetry hosts no longer exist.
All feature branches and pull requests must target the staging branch. The main branch is only updated by merging staging → main after migration checks pass. Never open a PR directly against main.
Before merging changes from staging to main, ensure any storage structure changes are properly migrated:
-
Check for Storage Changes
# First checkout staging branch git checkout staging # Compare with main to detect storage changes go run scripts/compare_storage_structure.go main
-
When Migration is Needed
- Storage key format changes
- Non-backward-compatible data structure changes
- Required field additions (without
omitempty) - Field type changes
- Field removals
- Protobuf message structure changes (manual analysis required)
- Trigger/node output format changes (manual analysis required)
-
Migration Process
For Go Struct Changes (Automated):
# First checkout staging branch git checkout staging # Compare with main to detect storage changes go run scripts/compare_storage_structure.go main # If changes are detected, generate a migration file go run scripts/migration/create_migration.go main
For Protobuf Changes (Manual Analysis Required):
# Compare protobuf changes between branches git diff origin/main..staging protobuf/ # Analyze for breaking changes in: # - Trigger output structures (e.g., timestamp -> data field) # - Node input/output formats (e.g., input field removal) # - Manual trigger structure changes (boolean -> ManualTrigger) # - Contract ABI format changes (string -> array)
-
Migration Types
Automated Migration (Go Structs): The migration script will:
- Create a timestamped migration file in
./migrations - Include detected changes as comments
- Provide example migration code
- Add the migration to
Migrationsslice in./migrations/migrations.go
Manual Migration (Protobuf Changes):
- Create migration file manually following existing patterns
- Focus on data cleanup rather than backward compatibility
- Cancel/remove incompatible workflows and executions
- Clear cached data that needs regeneration
- Create a timestamped migration file in
-
No Migration Needed For
- Adding fields with
omitemptyJSON tags - Runtime-only changes
- Backward-compatible modifications
- New optional protobuf fields
- Adding fields with
-
Active Migrations
Current active migrations that will run on deployment:
20250603-183034-token-metadata-fields- TokenMetadata struct field additions20250128-120000-protobuf-structure-cleanup- v1.9.6 protobuf structure cleanup- Cancels workflows with incompatible trigger structures
- Removes executions with old trigger output formats
- Cleans cached data for regeneration
- Impact: Workflows with old manual triggers or loop/filter nodes will be canceled
Important: Always run migrations before merging to
main. For protobuf changes, manual analysis is required as the automated scripts may not detect all breaking changes.
v1.9.6 Note: The protobuf structure cleanup migration will cancel workflows with incompatible structures. Ensure critical workflows are backed up before deployment.
See the development guide.
How to run the test suite locally — unit vs. integration tiers, the
config/test.yaml fixture and OWNER_EOA setup, the security notes, and
formatted output — lives in the development guide:
Testing.
# Install golangci-lint
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.55.2
# Run the linter
golangci-lint run ./...
# Or use the Makefile target
make audit- Run linters before committing code to catch issues early
- Configure your IDE to run linters on save for immediate feedback
- Include linting in CI/CD pipelines to enforce code quality standards
- Fix linting issues as they arise rather than letting them accumulate
The script is configured in Makefile, so run the below command to retrieve failed tests.
make cicd-failed RUN_ID=16632516768
Example output:
make cicd-failed RUN_ID=16632516768
--- FAIL: TestEventTriggerQueriesBasedMultipleContracts (0.07s)
--- FAIL: TestEventTriggerQueriesBasedMultipleContracts/Transfer_FROM_target_address_(any_token) (0.00s)
--- FAIL: TestEventTriggerQueriesBasedMultipleContracts/Transfer_TO_target_address_(any_token) (0.00s)
When you modify any .proto files (primarily protobuf/avs.proto or protobuf/node.proto), you need to regenerate the corresponding Go bindings.
-
Install
protoc(the protobuf compiler): If you don't have it, download it from the protobuf releases page and ensure it's in your system'sPATH. -
Install Go plugins for
protoc: These commands will install the necessary Go code generators for protobuf messages and gRPC services. Ensure your$GOPATH/binor$HOME/go/binis in your systemPATH.go install google.golang.org/protobuf/cmd/protoc-gen-go@latest go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
After installing the prerequisites, run the following Make target from the project root:
make protoc-gen- Proto source files: Reside in the
protobuf/directory (e.g.,protobuf/avs.proto,protobuf/node.proto). go_packageoption: Bothavs.protoandnode.protouseoption go_package = "github.com/AvaProtocol/EigenLayer-AVS/protobuf;avsproto";.- This directs
protoc-gen-goto generate files that will be part of the Go package aliased asavsproto. - The import path for this package in your Go code will be
github.com/AvaProtocol/EigenLayer-AVS/protobuf(assuminggithub.com/AvaProtocol/EigenLayer-AVSis your module name fromgo.mod).
- This directs
- Generated Go files: The
make protoc-gencommand is configured (via--go_out=.and thego_packageoption) to place the generated*.pb.goand*_grpc.pb.gofiles directly into theprotobuf/directory.- Example:
protobuf/avs.pb.go,protobuf/node.pb.go. - All these generated files will contain the Go package declaration:
package avsproto.
- Example:
To use the generated types and gRPC clients/servers in your Go code, import the package as follows:
import (
// ... other imports
avspb "github.com/AvaProtocol/EigenLayer-AVS/protobuf" // Use a suitable alias like avspb
)
func main() {
req := &avspb.IdReq{Id: "test-id"}
// ... use other types like avspb.Task, avspb.AggregatorClient, etc.
}Important: If you change the go_package option in the .proto files or the output paths in the Makefile, you will likely need to update the import paths in your Go codebase accordingly.
Production and local development use Alchemy's ERC-4337 bundler. With
bundler_provider: alchemy, the endpoint is derived as
https://<network>.g.alchemy.com/v2/<ALCHEMY_API_KEY> (e.g. eth-sepolia,
base-sepolia, eth-mainnet). Set ALCHEMY_API_KEY in .env.local.
export ALCHEMY_API_KEY=your_key
curl -X POST "https://eth-sepolia.g.alchemy.com/v2/${ALCHEMY_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'Expected Response:
{"jsonrpc": "2.0", "id": 1, "result": "0xaa36a7"}curl -X POST "https://eth-sepolia.g.alchemy.com/v2/${ALCHEMY_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_supportedEntryPoints","params":[],"id":1}'Expected Response (includes EntryPoint v0.7):
{"jsonrpc": "2.0", "id": 1, "result": ["0x0000000071727De22E5E9d8BAf0edAc6f37da032"]}# Point the helper scripts at Alchemy (or any v0.7 bundler URL)
export SEPOLIA_BUNDLER_RPC="https://eth-sepolia.g.alchemy.com/v2/${ALCHEMY_API_KEY}"
# Query specific user operation by hash
./scripts/query_userop.sh 0x1234567890abcdef... [status|receipt|both]
# Debug bundler connectivity and health
./scripts/bundler_debug.sh
# Search recent user operations (requires SEPOLIA_RPC)
export SEPOLIA_RPC=https://sepolia.infura.io/v3/YOUR_KEY
./scripts/search_recent_userops.sh [sender_address] [blocks_back]- Connection refused: Check network connectivity and firewall settings
- 401 Unauthorized: Verify the API key in the bundler URL
- Parse error: Check JSON formatting and Content-Type header
- AA20 account not deployed: Normal for testing with random addresses
- Method not found: Bundler may not support that specific ERC-4337 method
Coming soon
| Name | Address |
|---|---|
| ProxyAdmin | 0x26CF7A7DF7d1E00D83A5Ca24385f697a3ca4577d |
| ServiceManager | 0xEA3E82F9Ae371A6a372A6DCffB1a9bD17e0608eF |
| RegistryCoordinator | 0x90c6d6f2A78d5Ce22AB8631Ddb142C03AC87De7a |
| BLSApkRegistry | 0x6752F8BeeE5BF45c9d11FDBC4F8aFfF879925585 |
| IndexRegistry | 0x298a5d3C8F8Db30E8292C9e2BF92292de469C8FF |
| OperatorStateRetriever | 0xb7bb920538e038DFFEfcB55caBf713652ED2031F |
| PauserRegistry | 0x3A8ea6e4202CdDe4a9e0cCE19c4Dc1739ba2cF0b |
| StakeRegistry | 0x7BacD5dd5A7C3acf8bf1a3c88fB0D00B68EE626A |
| ApConfig | 0xb8abbb082ecaae8d1cd68378cf3b060f6f0e07eb |
| Name | Address |
|---|---|
| ProxyAdmin | 0x122c2D0Af2042ec0a6676d4dFd4d0c66dc40DDcf |
| ServiceManager | 0x80D492e1002F9C24b26179a024B8aA871fEbBC9D |
| RegistryCoordinator | 0xcA95381802FD1398d5BF2D01243210cFb3a2b3BD |
| BLSApkRegistry | 0xC7fe896a9f0529b0e2B8193EBaf911BcD9d84A3F |
| IndexRegistry | 0xdb8771619A7c2b9050CE66321A0652BC889399c2 |
| OperatorStateRetriever | 0x070E0ABE8407eb4727fC7C5284bdc1e5E5CBf605 |
| PauserRegistry | 0x3b36CE97b7fdf2571354679130a80F9E1450679b |
| StakeRegistry | 0x6dFAdB7E0ff7CAC8682f9C733890150210206E05 |
| ApConfig | 0xFf2E98967A8607F9Acd5CC9024cC5dE5DF0D60a3 |
| Name | Address |
|---|---|
| ProxyAdmin | 0x5989934D31f7f397511f105B7E4175a06B7A517F |
| ServiceManager | 0x18343Aa10e3D2F3A861e5649627324aEAD987Adf |
| RegistryCoordinator | 0x8DE3Ee0dE880161Aa0CD8Bf9F8F6a7AfEeB9A44B |
| BLSApkRegistry | 0xB58687fF303C8e92C28a484342755d3228081d45 |
| IndexRegistry | 0xc6A464e39d4fA5013D61295501c7cCd050d76612 |
| OperatorStateRetriever | 0xb3af70D5f72C04D1f490ff49e5aB189fA7122713 |
| PauserRegistry | 0xeec585186c37c517030ba371deac5c17e728c135 |
| StakeRegistry | 0x363b3604fE8c2323a98c00906115c8b87a512a12 |
| TaskManager | 0x940f62f75cbbbd723d37c9171dc681dfba653b49 |
| ApConfig | 0x9c02dfc92eea988902a98919bf4f035e4aaefced |
