Skip to content

feat: ciphernode economic contracts [skip-line-limit]#766

Merged
hmzakhalid merged 99 commits into
devfrom
hmza/economics
Oct 31, 2025
Merged

feat: ciphernode economic contracts [skip-line-limit]#766
hmzakhalid merged 99 commits into
devfrom
hmza/economics

Conversation

@hmzakhalid

@hmzakhalid hmzakhalid commented Sep 30, 2025

Copy link
Copy Markdown
Collaborator
  • New Features
    • USDC-based payments for E3 requests, with on-chain fee quotes.
    • Reward distribution to registered operators via a new Bonding Registry.
    • Operator lifecycle and eligibility managed through bonding, tickets, and licenses.
    • Slashing governance with proposals, verification, appeals, and bans.
    • New tokens: EnclaveToken (governance) and EnclaveTicketToken (non-transferable tickets).
    • Expanded registry APIs for committees, filters, and node management.
  • Chores
    • Added deployment scripts and Ignition modules for new contracts.
    • New CLI tasks: admin add, mint tokens; updated package scripts.

The Enclave system uses a score-based sortition mechanism to select a committee of ciphernodes to perform threshold homomorphic encryption operations. The flow involves:

  1. Operator Setup - Bonding license tokens and ticket balance
  2. Registration - Registering as a ciphernode operator
  3. E3 Request - A computation request triggers sortition
  4. Score Sortition - Nodes are selected based on ticket balances
  5. Committee Finalization - Selected nodes form a committee
  6. Keyshare Generation - Committee nodes generate threshold keyshares
  7. Public Key Aggregation - Keyshares are aggregated into a public key
  8. Encryption & Decryption - Data is encrypted and threshold-decrypted

Complete System Flow

sequenceDiagram
    participant Operator
    participant BondingRegistry
    participant CiphernodeRegistry
    participant EventBus
    participant NodeStateManager
    participant Sortition
    participant CiphernodeSelector
    participant Keyshare
    participant PublicKeyAggregator
    participant PlaintextAggregator

    Note over Operator,BondingRegistry: Phase 1: Operator Setup & Registration

    Operator->>BondingRegistry: bondLicense(amount)
    BondingRegistry->>BondingRegistry: Transfer ENCL tokens
    BondingRegistry->>EventBus: LicenseBondUpdated

    Operator->>BondingRegistry: registerOperator()
    BondingRegistry->>BondingRegistry: Check isLicensed()
    BondingRegistry->>CiphernodeRegistry: addCiphernode(operator)
    CiphernodeRegistry->>EventBus: CiphernodeAdded(operator, index, numNodes, chainId)
    EventBus->>NodeStateManager: CiphernodeAdded
    NodeStateManager->>NodeStateManager: Register operator in nodes HashMap

    Operator->>BondingRegistry: addTicketBalance(amount)
    BondingRegistry->>BondingRegistry: Mint ticket tokens
    BondingRegistry->>EventBus: TicketBalanceUpdated(operator, delta, newBalance, chainId)
    EventBus->>NodeStateManager: TicketBalanceUpdated
    NodeStateManager->>NodeStateManager: Update operator ticket balance

    BondingRegistry->>BondingRegistry: Check if balance >= minTicketBalance
    BondingRegistry->>EventBus: OperatorActivationChanged(operator, active=true, chainId)
    EventBus->>NodeStateManager: OperatorActivationChanged
    NodeStateManager->>NodeStateManager: Set operator active status

    Note over Operator,PlaintextAggregator: Phase 2: E3 Request & Sortition

    Operator->>EventBus: E3Requested(e3Id, thresholdM, thresholdN, seed, params, chainId)
    EventBus->>Sortition: E3Requested
    Sortition->>NodeStateManager: GetNodeState(chainId)
    NodeStateManager-->>Sortition: NodeStateStore { nodes, ticketPrice }
    Sortition->>Sortition: Build sortition list from active nodes
    Sortition->>Sortition: Run score sortition algorithm
    Sortition->>Sortition: Generate tickets for selected nodes

    loop For each selected node
        Sortition->>EventBus: TicketGenerated(e3Id, node, ticketId, chainId)
    end

    Note over CiphernodeRegistry,EventBus: Phase 3: On-Chain Ticket Submission

    EventBus->>CiphernodeRegistry: TicketGenerated (if ticketId != 0)
    CiphernodeRegistry->>CiphernodeRegistry: Submit ticket to contract
    CiphernodeRegistry->>CiphernodeRegistry: Wait for threshold tickets
    CiphernodeRegistry->>CiphernodeRegistry: Call finalizeCommittee()
    CiphernodeRegistry->>EventBus: CommitteeFinalized(e3Id, committee[], chainId)

    Note over EventBus,Sortition: Phase 4: Committee Storage

    EventBus->>Sortition: CommitteeFinalized
    Sortition->>Sortition: Store committee in finalized_committees HashMap
    Sortition->>Sortition: Persist to disk

    Note over EventBus,CiphernodeSelector: Phase 5: Node Selection

    EventBus->>CiphernodeSelector: CommitteeFinalized
    CiphernodeSelector->>CiphernodeSelector: Check if node address in committee
    alt Node is in committee
        CiphernodeSelector->>EventBus: CiphernodeSelected(e3Id, node, chainId)
    end

    Note over EventBus,Keyshare: Phase 6: Keyshare Generation

    EventBus->>Keyshare: CiphernodeSelected
    Keyshare->>Keyshare: fhe.generate_keyshare()
    Keyshare->>Keyshare: Generate secret key (random)
    Keyshare->>Keyshare: Generate public key share (sk + CRP)
    Keyshare->>Keyshare: Persist secret key
    Keyshare->>EventBus: KeyshareCreated(e3Id, node, pubkey, chainId)

    Note over EventBus,PublicKeyAggregator: Phase 7: Public Key Aggregation

    EventBus->>PublicKeyAggregator: KeyshareCreated
    PublicKeyAggregator->>Sortition: GetNodesForE3(e3Id, chainId)
    Sortition-->>PublicKeyAggregator: committee[]
    PublicKeyAggregator->>PublicKeyAggregator: Verify node in committee
    PublicKeyAggregator->>PublicKeyAggregator: Add keyshare to OrderedSet

    alt Threshold reached (all keyshares collected)
        PublicKeyAggregator->>PublicKeyAggregator: fhe.get_aggregate_public_key(keyshares)
        PublicKeyAggregator->>PublicKeyAggregator: Aggregate public key shares
        PublicKeyAggregator->>EventBus: PublicKeyAggregated(e3Id, pubkey, nodes, chainId)
        PublicKeyAggregator->>CiphernodeRegistry: publishPublicKey(e3Id, pubkey, nodes)
    end

    Note over Operator,PlaintextAggregator: Phase 8: Encryption & Computation

    Operator->>Operator: Encrypt input data with aggregated pubkey
    Operator->>EventBus: CiphertextOutputPublished(e3Id, ciphertext, chainId)

    Note over EventBus,PlaintextAggregator: Phase 9: Threshold Decryption

    EventBus->>Keyshare: CiphertextOutputPublished
    Keyshare->>Keyshare: Load secret key from storage
    Keyshare->>Keyshare: fhe.decrypt_ciphertext(secret, ciphertext)
    Keyshare->>Keyshare: Generate decryption share
    Keyshare->>EventBus: DecryptionshareCreated(e3Id, node, decryptionShare, chainId)

    EventBus->>PlaintextAggregator: DecryptionshareCreated
    PlaintextAggregator->>Sortition: GetNodesForE3(e3Id, chainId)
    Sortition-->>PlaintextAggregator: committee[]
    PlaintextAggregator->>PlaintextAggregator: Verify node in committee
    PlaintextAggregator->>PlaintextAggregator: Add decryption share to OrderedSet

    alt Threshold reached (all shares collected)
        PlaintextAggregator->>PlaintextAggregator: fhe.get_aggregate_plaintext(shares, ciphertext)
        PlaintextAggregator->>PlaintextAggregator: Aggregate decryption shares
        PlaintextAggregator->>PlaintextAggregator: Decode plaintext
        PlaintextAggregator->>EventBus: PlaintextAggregated(e3Id, plaintext, nodes, chainId)
    end
Loading

State Diagram: Node Lifecycle

stateDiagram-v2
    [*] --> Unbonded
    Unbonded --> Licensed: bondLicense(amount >= requiredBond)
    Licensed --> Registered: registerOperator()
    Registered --> Active: addTicketBalance(balance >= minBalance)
    Active --> Inactive: removeTicketBalance() OR unbondLicense()
    Inactive --> Active: addTicketBalance() OR bondLicense()
    Active --> ExitPending: deregisterOperator()
    Inactive --> ExitPending: deregisterOperator()
    Registered --> ExitPending: deregisterOperator()
    ExitPending --> [*]: claimExits() after exitDelay
    ExitPending --> Registered: registerOperator() (cancels exit)
Loading

Sortition Data Flow

flowchart TD
    A[E3Requested Event] --> B{Chain ID Match?}
    B -->|No| Z[Ignore]
    B -->|Yes| C[NodeStateManager: Get Active Nodes]
    C --> D[Filter: ticket_balance > 0 AND active=true]
    D --> E[Score Sortition: Build Weighted List]
    E --> F[Calculate Total Ticket Weight]
    F --> G{threshold_n nodes available?}
    G -->|No| H[Error: Insufficient Nodes]
    G -->|Yes| I[Select Top N Nodes by Weight]
    I --> J[Generate Ticket IDs]
    J --> K[Emit TicketGenerated Events]
    K --> L[EVM: Submit Tickets On-Chain]
    L --> M{Threshold Tickets Submitted?}
    M -->|No| L
    M -->|Yes| N[Contract: finalizeCommittee]
    N --> O[Emit CommitteeFinalized Event]
    O --> P[Sortition: Store Committee]
    P --> Q[CiphernodeSelector: Check Membership]
    Q --> R[Emit CiphernodeSelected]
    R --> S[Keyshare Generation Starts]
Loading

Committee Finalization Flow

flowchart LR
    A[TicketGenerated] --> B{ticket_id == 0?}
    B -->|Yes| C[Distance Sortition - Skip Contract]
    B -->|No| D[Score Sortition - Submit to Contract]
    D --> E[Contract: Collect Tickets]
    E --> F{Threshold Met?}
    F -->|No| E
    F -->|Yes| G[Contract: finalizeCommittee]
    G --> H[Freeze Committee List]
    H --> I[Emit CommitteeFinalized Event]
    C --> J[Manual CommitteeFinalized Emission]
    I --> K[All Nodes: Store Committee]
    J --> K
    K --> L[CiphernodeSelector: Process]
Loading

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced ERC20-based payment system for E3 requests (replacing ETH-based payments)
    • Added operator bonding and licensing framework with ticket balance management
    • Implemented committee finalization workflow with sortition-based node selection
    • Added slashing and governance mechanisms for operator management
    • Introduced reward distribution system for node operators
  • Bug Fixes & Improvements

    • Simplified E3 request interface by removing filter parameter
    • Enhanced committee lifecycle tracking and event emissions
    • Improved operator state management and activation/deactivation logic
  • Configuration Changes

    • Updated deployment configurations to replace filter registry with bonding registry
    • Added fee token configuration requirements for E3 systems
    • Updated network and contract address mappings across environments

@vercel

vercel Bot commented Sep 30, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
crisp Ready Ready Preview Comment Oct 31, 2025 3:36pm
1 Skipped Deployment
Project Deployment Preview Comments Updated (UTC)
enclave-docs Skipped Skipped Oct 31, 2025 3:36pm

@coderabbitai

coderabbitai Bot commented Sep 30, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This PR introduces a comprehensive bonding registry system that replaces the naive registry filter with sophisticated operator lifecycle management. It adds ERC20 fee-based payments, implements operator licensing with time-locked exit queues, introduces ticket-based sortition mechanics, expands the event system for committee and operator state tracking, and integrates these components across Solidity contracts, Rust actors, deployment infrastructure, and client SDKs.

Changes

Cohort / File(s) Summary
Solidity Interfaces & Contracts
packages/enclave-contracts/contracts/interfaces/IBondingRegistry.sol, IBondingRegistry.json, ISlashingManager.sol, ISlashVerifier.sol, IRegistryFilter.sol, IEnclave.sol, ICiphernodeRegistry.sol, Enclave.sol, registry/BondingRegistry.sol, registry/CiphernodeRegistryOwnable.sol, registry/NaiveRegistryFilter.sol, slashing/SlashingManager.sol, token/EnclaveTicketToken.sol, token/EnclaveToken.sol, lib/ExitQueueLib.sol
New bonding registry with operator licensing, ticket/license bonding, exit queues, and time-locked claims. New slashing manager with proposal/appeal workflows and node banning. Token wrappers (EnclaveTicketToken, EnclaveToken) with delegation controls. Exit queue library managing time-based asset transitions. Updated Enclave to support ERC20 fees, bonding registry integration, reward distribution.
Contract Artifacts
packages/enclave-contracts/artifacts/contracts/interfaces/*.json, packages/enclave-contracts/artifacts/contracts/registry/*.json
Updated ABI and metadata for ICiphernodeRegistry, IEnclave, IBondingRegistry, NaiveRegistryFilter reflecting new function signatures, events (CommitteeRequested, CommitteeFinalizeRequested, CommitteeFinalized, TicketSubmitted, etc.), and updated buildInfoIds.
Mock & Test Contracts
packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol, MockRegistryFilter.sol, MockSlashingVerifier.sol, MockStableToken.sol
Mock implementations updated to support new interface signatures; MockStableToken added as ERC20 mock for testing fee token flows.
Ignition Deployment Modules
packages/enclave-contracts/ignition/modules/bondingRegistry.ts, enclaveTicketToken.ts, enclaveToken.ts, mockSlashingVerifier.ts, mockStableToken.ts, slashingManager.ts
New Ignition modules for deploying bonding registry, token contracts, slashing manager, and mock tokens.
Deployment Scripts
packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts, enclave.ts, enclaveTicketToken.ts, enclaveToken.ts, mockStableToken.ts, slashingManager.ts, ciphernodeRegistryOwnable.ts, deployEnclave.ts
Deployment utilities with contract reuse/redeploy logic, fee token approval flows, cross-contract wiring, and persistence of deployment metadata.
Hardhat & Build Config
packages/enclave-contracts/hardhat.config.ts, package.json, README.md
Task registration for new ciphernode admin add/mint token tasks; updated npmFilesToBuild to include new contracts; documentation for ciphernode registration and configuration flows.
Enclave Contracts CLI Tasks
packages/enclave-contracts/tasks/ciphernode.ts, enclave.ts
Refactored ciphernode add/remove to use bonding registry flows; new mint-tokens and admin-add tasks for operator setup; enclave request task updated to approve and use ERC20 fee tokens.
Test Suites
packages/enclave-contracts/test/Enclave.spec.ts, Registry/BondingRegistry.spec.ts, Registry/CiphernodeRegistryOwnable.spec.ts, Slashing/SlashingManager.spec.ts
Comprehensive test coverage for new registry, bonding, and slashing workflows; operator lifecycle, exit queues, license bonding, activation/deactivation, slashing with appeals, and node banning.
Sortition System
crates/sortition/src/sortition.rs, ciphernode_selector.rs, backends.rs, distance.rs (deleted), repo.rs, Cargo.toml, Readme.md
New backend abstraction (SortitionBackend with ScoreBackend); NodeStateStore tracking operator state across chains; CommitteeFinalized event handling; ticket-based sortition; removal of distance-based sorting.
Event System Expansion
crates/events/src/enclave_event/mod.rs, committee_finalized.rs, committee_published.rs, committee_requested.rs, committee_finalize_requested.rs, configuration_updated.rs, operator_activation_changed.rs, plaintext_output_published.rs, ticket_balance_updated.rs, ticket_generated.rs, ticket_submitted.rs
New event types for operator lifecycle, committee state transitions, ticket generation/submission, configuration changes, and plaintext output publication.
EVM Integration
crates/evm/src/bonding_registry_sol.rs, ciphernode_registry_sol.rs, registry_filter_sol.rs (deleted), lib.rs, repo.rs, helpers.rs, events.rs
New BondingRegistrySol reader for event decoding; expanded CiphernodeRegistrySolWriter with finalization and publication workflows; removed RegistryFilterSol.
Aggregator & Ciphernode
crates/aggregator/src/committee_finalizer.rs, publickey_aggregator.rs, threshold_plaintext_aggregator.rs, lib.rs, crates/ciphernode-builder/src/ciphernode_builder.rs, crates/entrypoint/src/start/aggregator_start.rs, start/start.rs
New CommitteeFinalizer actor scheduling committee finalization; updated aggregators to use GetNodesForE3 instead of GetNodes; bonding registry integration; sortition backend configuration.
Request & Router
crates/request/src/meta.rs, router.rs
Extended E3Meta with esi_per_ct and error_size fields; router store injection refactor.
Config & Store Keys
crates/config/src/app_config.rs, contract.rs, store_keys.rs, crates/cli/src/print_env.rs, crates/entrypoint/src/config_set/mod.rs
Replaced filter_registry with bonding_registry; added fee_token address; new store keys for bonding registry reader, node state, finalized committees.
Documentation
crates/docs/user_guide.md, crates/sortition/Readme.md
Updated config examples to use bonding_registry; comprehensive sortition documentation with flow diagrams.
TypeScript SDK & React
packages/enclave-sdk/src/contract-client.ts, enclave-sdk.ts, types.ts, utils.ts, packages/enclave-react/README.md, src/useEnclaveSDK.ts
Added feeToken support; approveFeeToken method; updated E3RequestParams to remove filter; CommitteeFinalizedData event type; adjusted default E3 thresholds; mock compute provider params mode.
Rust EVM Helpers
crates/evm-helpers/src/contracts.rs
Added getE3Quote read-only function; updated request signature from payable to nonpayable; added trait methods for fee quoting.
CRISP Example
examples/CRISP/server/src/cli/approve.rs, commands.rs, main.rs, config.rs, server/indexer.rs, Readme.md, client/.env.example, enclave.config.yaml, .env.example, hardhat.config.ts, package.json, scripts/dev_cipher.sh, playwright.config.ts
ERC20 approval flows; dynamic timestamp retrieval from RPC; updated config for bonding registry and fee token; expanded test setup with multiple private keys.
Templates
templates/default/enclave.config.yaml, hardhat.config.ts, tests/integration.spec.ts, deployed_contracts.json, scripts/dev_ciphernodes.sh, package.json, client/src/pages/steps/RequestComputation.tsx, src/utils/env-config.ts, server/index.ts, server/utils.ts
Updated contract configs; added sortition scoring; removed filter parameters; integrated fee token approval; new ciphernode admin task invocations.
Integration Tests
tests/integration/enclave.config.yaml, fns.sh, base.sh, persist.sh, crates/tests/tests/integration.rs, integration_legacy.rs
Updated config structures; multi-key setup (ag, cn1-3); new event emissions for sortition workflow; extended TakeEvents counts for committee finalization steps.
Deployment Scripts
deploy/local/contracts.sh, nodes.sh, start.sh, .env.example, agg.yaml, cn1.yaml, cn2.yaml, cn3.yaml, docker-compose.yml, swarm_deployment.md
Pnpm-based CRISP deployment; new ciphernode addresses; bonding registry config; wallet setup for multiple nodes.
Root & Misc
.gitmodules, README.md, crates/aggregator/Cargo.toml
Re-indentation in .gitmodules; documentation diagram update; workspace dependency addition.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Enclave as Enclave Contract
    participant BondingReg as BondingRegistry
    participant TicketToken as EnclaveTicketToken
    participant CiphernodeReg as CiphernodeRegistry
    participant Sortition as Sortition Actor

    User->>Enclave: requestE3(E3RequestParams)
    Enclave->>Enclave: getE3Quote() → fee
    Enclave->>TicketToken: transferFrom(user, fee)
    Enclave->>CiphernodeReg: requestCommittee(e3Id, seed, threshold)
    CiphernodeReg->>CiphernodeReg: store request + deadline
    CiphernodeReg->>CiphernodeReg: emit CommitteeRequested

    Sortition->>Sortition: listen for CommitteeRequested
    Sortition->>Sortition: schedule finalization after deadline
    
    rect Committee Formation Window
        Note over Sortition: submitTicket (multiple nodes)
        Note over Sortition: compute scores, finalize top-N
    end

    Sortition->>CiphernodeReg: emit CommitteeFinalized
    CiphernodeReg->>CiphernodeReg: store finalized committee
    
    Enclave->>Enclave: on E3 completion
    Enclave->>BondingReg: distributeRewards(nodes, amounts)
    BondingReg->>TicketToken: transfer rewards
Loading
sequenceDiagram
    actor Operator
    participant BondingReg as BondingRegistry
    participant TicketToken as EnclaveTicketToken
    participant LicenseToken as IERC20 License
    participant ExitQueue as ExitQueueLib

    Operator->>BondingReg: registerOperator()
    BondingReg->>BondingReg: check license bond met
    BondingReg->>CiphernodeReg: add to registry

    Operator->>BondingReg: bondLicense(amount)
    BondingReg->>LicenseToken: transferFrom(operator, amount)
    BondingReg->>BondingReg: update operator status

    Operator->>BondingReg: deregisterOperator()
    BondingReg->>ExitQueue: queueAssetsForExit(exitDelay)
    ExitQueue->>ExitQueue: compute unlockTimestamp
    
    rect Time-locked Exit
        Note over ExitQueue: operator waits exitDelay seconds
    end

    Operator->>BondingReg: claimExits(maxTicket, maxLicense)
    ExitQueue->>ExitQueue: check unlockTimestamp passed
    BondingReg->>TicketToken: payout(operator, amount)
    BondingReg->>LicenseToken: transfer(operator, amount)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Areas requiring extra attention:

  • BondingRegistry.sol: Complex operator lifecycle with exit queues, license bonding, slashing integration, and reward distribution—verify state transitions, reentrancy, and authorization checks across all operator paths.
  • ExitQueueLib.sol: Time-locked asset queue management with merge-on-match logic and cleanup—verify correctness of queue ordering, unlock timestamp calculations, and pending amount tracking.
  • CiphernodeRegistryOwnable.sol: Substantial refactor from filter-based to sortition-based committee selection; verify IMT operations, committee finalization logic, seed-based scoring, and node eligibility checks with bonding registry integration.
  • SlashingManager.sol: Slashing proposal/appeal workflow with governance overrides and node banning—verify role-based access control, appeal window constraints, and interaction with bonding registry.
  • Sortition actor changes (sortition.rs, ciphernode_selector.rs): New NodeStateStore tracking, CommitteeFinalized event handling, GetNodesForE3 message routing, and metadata-driven CiphernodeSelected emission—verify state consistency, event ordering, and multi-chain scenarios.
  • EVM integration (bonding_registry_sol.rs, ciphernode_registry_sol.rs): New event decoding, writer attachment, and registry method invocations (publish_committee, finalize_committee, submit_ticket)—verify event log parsing, contract call ordering, and error propagation.
  • Fee token integration: Payment flow changes from payable to ERC20-based (getE3Quote, transferFrom, approve workflows)—verify fee calculation determinism, allowance checks, and edge cases around zero fees or insufficient balances.
  • Configuration & deployment: Multiple new parameters (ticketPrice, licenseRequiredBond, exitDelay, etc.) wired through deployment scripts and environment configs—verify consistency across environments and proper initialization order.

Possibly related PRs

Suggested reviewers

  • cedoor
  • ctrlc03

Poem

🐇 A bonding registry hops into view,
With tickets and licenses, all shiny and new,
Exit queues queue up with time-locked delays,
While sortition scores nodes through sortie-filled days,
Slashing and appeals keep order in place,
A rewarding refactor that picks up the pace! 🌟

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.03% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "feat: ciphernode economic contracts" is directly related to the primary changes in this changeset. The title accurately describes a major component of the work, which introduces several new economic-focused contracts including BondingRegistry, EnclaveToken, EnclaveTicketToken, and SlashingManager. These contracts form the foundation for operator bonding, license management, ticket tracking, reward distribution, and governance. The title is concise, specific, and avoids vague or generic phrasing. While the changeset also includes significant updates to sortition, committee management, and event systems, the economic contracts represent a core and cohesive theme that appropriately characterizes the main effort.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch hmza/economics

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

Copy link
Copy Markdown
Contributor

License Header Check Failed

Some files are missing the required SPDX license header. Please add the following header to the beginning of all .js, .jsx, .nr, .rs, .sol, .ts, and .tsx files:

// SPDX-License-Identifier: LGPL-3.0-only
//
// This file is provided WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE.

You can run ./scripts/check-license-headers.sh --fix locally to automatically add missing headers, then commit the changes.

Or run ./scripts/check-license-headers.sh to see which files need headers.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/enclave-contracts/scripts/deployAndSave/enclave.ts (1)

46-68: Critical validation logic flaw.

The validation condition at lines 46-59 has a logic error. The current condition is:

if (!params || !owner || !maxDuration || !registry || !bondingRegistry || !usdcToken || (preDeployedArgs equality checks))

This means if any required parameter is missing, the condition is true, and the code proceeds to the pre-deployed contract reuse block. However, if the pre-deployed contract doesn't exist (line 60), it throws an error. If it does exist, it reuses it even though the caller didn't provide all required parameters.

The correct logic should validate that all parameters are provided before attempting to reuse or deploy:

Apply this fix:

-  if (
-    !params ||
-    !owner ||
-    !maxDuration ||
-    !registry ||
-    !bondingRegistry ||
-    !usdcToken ||
-    (preDeployedArgs?.constructorArgs?.params === params &&
-      preDeployedArgs?.constructorArgs?.owner === owner &&
-      preDeployedArgs?.constructorArgs?.maxDuration === maxDuration &&
-      preDeployedArgs?.constructorArgs?.registry === registry &&
-      preDeployedArgs?.constructorArgs?.bondingRegistry === bondingRegistry &&
-      preDeployedArgs?.constructorArgs?.usdcToken === usdcToken)
-  ) {
+  // Validate all required parameters are provided
+  if (!params || !owner || !maxDuration || !registry || !bondingRegistry || !usdcToken) {
+    throw new Error("All deployment parameters (params, owner, maxDuration, registry, bondingRegistry, usdcToken) are required");
+  }
+
+  // Check if we can reuse pre-deployed contract
+  if (
+    preDeployedArgs?.constructorArgs?.params === params &&
+    preDeployedArgs?.constructorArgs?.owner === owner &&
+    preDeployedArgs?.constructorArgs?.maxDuration === maxDuration &&
+    preDeployedArgs?.constructorArgs?.registry === registry &&
+    preDeployedArgs?.constructorArgs?.bondingRegistry === bondingRegistry &&
+    preDeployedArgs?.constructorArgs?.usdcToken === usdcToken
+  ) {
     if (!preDeployedArgs?.address) {
       throw new Error("Enclave address not found, it must be deployed first");
     }
packages/enclave-contracts/contracts/registry/NaiveRegistryFilter.sol (1)

90-104: Use calldata for external params and fix unused modifier

  • Change parameters to address[] calldata nodes and bytes calldata publicKey in publishCommittee to avoid gas-costly memory copies (alchemy.com)
  • The onlyOwnerOrCiphernode modifier (defined at line 50) is never applied; replace onlyOwner with onlyOwnerOrCiphernode on publishCommittee if ciphernode actors should be permitted, or remove the unused modifier.
🧹 Nitpick comments (23)
packages/enclave-contracts/ignition/modules/mockSlashingVerifier.ts (1)

7-14: Confirm consistent pattern for as any. All Hardhat Ignition modules in packages/enclave-contracts/ignition/modules end with }) as any;, so this isn’t unique to mockSlashingVerifier.ts. No change required here—if you’d like to remove these assertions, consider an optional global refactor to define and apply proper return types for all modules.

.gitmodules (1)

6-6: Normalize indentation in .gitmodules

Line 6 has extra spaces before the tab; change it to a single tab for consistency:

-  	url = https://github.com/gnosisguild/risc0-ethereum
+	url = https://github.com/gnosisguild/risc0-ethereum

Git already resolves this to https://github.com/gnosisguild/risc0-ethereum. After merging, run git submodule sync -- templates/default/lib/risc0-ethereum to update local config.

packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts (7)

6-6: Prefer stable HRE import path

Import from "hardhat/types" to avoid relying on internal subpaths that may change.

-import type { HardhatRuntimeEnvironment } from "hardhat/types/hre";
+import type { HardhatRuntimeEnvironment } from "hardhat/types";

Based on learnings.


37-41: Checksum and reuse the validated owner address

Normalize owner once and use consistently to avoid mismatches with stored args. Also prevents accidental reuse when the same address is passed with different casing.

   const chain = (await signer.provider?.getNetwork())?.name ?? "localhost";
 
   const preDeployedArgs = readDeploymentArgs("EnclaveToken", chain);
 
-  if (!owner || preDeployedArgs?.constructorArgs?.owner === owner) {
+  const ownerChecksummed = owner ? ethers.getAddress(owner) : undefined;
+
+  if (!ownerChecksummed || preDeployedArgs?.constructorArgs?.owner === ownerChecksummed) {
@@
-      EnclaveToken: {
-        owner,
-      },
+      EnclaveToken: { owner: ownerChecksummed },
@@
-      constructorArgs: {
-        owner,
-      },
+      constructorArgs: { owner: ownerChecksummed },

Also applies to: 55-57, 69-71


42-45: Clarify error message for missing predeploy

Message should hint at how to proceed (pass owner to deploy) to reduce friction.

-      throw new Error(
-        "EnclaveToken address not found, it must be deployed first",
-      );
+      throw new Error(
+        "EnclaveToken not found in deployments. Pass a valid `owner` to deploy a new instance for this chain key."
+      );

61-66: Record the actual deployment block number from the receipt

Using the provider’s current block can drift if other blocks mine before you read. Prefer the deployment receipt’s blockNumber.

-  await enclaveToken.enclaveToken.waitForDeployment();
-
-  const blockNumber = await ethers.provider.getBlockNumber();
-
-  const enclaveTokenAddress = await enclaveToken.enclaveToken.getAddress();
+  const deployed = await enclaveToken.enclaveToken.waitForDeployment();
+  const tx = deployed.deploymentTransaction?.();
+  const receipt = tx ? await tx.wait() : undefined;
+  const blockNumber = receipt?.blockNumber ?? (await ethers.provider.getBlockNumber());
+  const enclaveTokenAddress = await deployed.getAddress();

46-51: Optional: sanity‑check that the predeployed address has code

Defend against stale/corrupted deployments.json by verifying code exists at the address before returning the connected instance.

   const enclaveTokenContract = EnclaveTokenFactory.connect(
     preDeployedArgs.address,
     signer,
   );
+  const code = await signer.provider!.getCode(preDeployedArgs.address);
+  if (!code || code === "0x") {
+    throw new Error(`No contract code at ${preDeployedArgs.address} for EnclaveToken on chain ${chain}`);
+  }
   return { enclaveToken: enclaveTokenContract };

79-85: Avoid redundant reconnect; you already have a connected instance

You can return the enclaveToken.enclaveToken instance directly after deployment. Re‑connecting is harmless but unnecessary.

-  const enclaveTokenContract = EnclaveTokenFactory.connect(
-    enclaveTokenAddress,
-    signer,
-  );
-
-  return { enclaveToken: enclaveTokenContract };
+  return { enclaveToken: enclaveToken.enclaveToken };

23-27: Docstring: reflect “get or deploy” behavior

The function reuses an existing deployment when owner is omitted or matches stored args. Update the summary to reduce confusion.

- * Deploys the EnclaveToken contract and saves the deployment arguments
+ * Connects to an existing EnclaveToken (if args match) or deploys a new one via Ignition, then saves deployment args
packages/enclave-contracts/ignition/modules/enclaveToken.ts (1)

7-7: Consider narrowing the linting exception.

The @typescript-eslint/no-explicit-any rule is disabled for the entire file, but the only usage of any is the type assertion on line 16 (as any). Disabling at the file level reduces type safety for any future additions to this module.

Consider using an inline disable comment instead:

-/* eslint-disable @typescript-eslint/no-explicit-any */
 import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
 
 export default buildModule("EnclaveToken", (m) => {
   const owner = m.getParameter("owner");
 
   const enclaveToken = m.contract("EnclaveToken", [owner]);
 
   return { enclaveToken };
-}) as any;
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+}) as any;

This keeps type checking enabled for any future code additions to this file.

packages/enclave-contracts/ignition/modules/mockStableToken.ts (2)

7-7: Document why the lint rule is disabled.

The @typescript-eslint/no-explicit-any rule is disabled for the entire file. Consider adding a comment explaining why this is necessary (likely due to Ignition module typing constraints).


16-16: Avoid as any cast if possible.

The as any cast weakens type safety. If this is required due to Ignition module typing limitations, consider adding a comment explaining why. Otherwise, investigate if a more specific type can be used.

Apply this approach:

-}) as any;
+}); // Note: Cast required due to Ignition module type inference limitations

Or better yet, if the Ignition types support it, use a proper type annotation.

packages/enclave-contracts/contracts/test/MockSlashingVerifier.sol (1)

11-18: Simplify unused parameter handling.

Line 15 (data;) appears to be an attempt to mark the parameter as used. In Solidity, you can omit the parameter name for unused parameters or use a comment to suppress warnings.

Apply this diff to improve clarity:

 function verify(
-    uint256,
+    uint256 /* proposalId */,
     bytes memory data
 ) external pure returns (bool success) {
-    data;
-
-    if (data.length > 0) success = true;
+    return data.length > 0;
 }

This is cleaner and more idiomatic Solidity.

packages/enclave-contracts/ignition/modules/slashingManager.ts (1)

7-7: Type safety bypassed with 'any' cast.

The any cast on line 20 and the eslint-disable on line 7 eliminate type safety for this module. While this may be necessary for Ignition module compatibility, consider whether stricter typing is possible or document why the escape hatch is required.

Also applies to: 20-20

packages/enclave-contracts/ignition/modules/bondingRegistry.ts (1)

7-7: Type safety bypassed with 'any' cast.

The module uses any typing, eliminating compile-time checks. Document why this is necessary or explore stricter typing options.

Also applies to: 34-34

packages/enclave-contracts/ignition/modules/enclaveTicketToken.ts (1)

7-7: Consider removing the any escape hatch if TypeScript types can be refined.

The eslint-disable comment and the as any cast on line 22 suggest the ignition module type definitions may be incomplete. If the hardhat-ignition types have been updated to properly type buildModule return values, these workarounds could be removed.

packages/enclave-contracts/contracts/registry/NaiveRegistryFilter.sol (1)

50-57: Consider gas optimization for repeated external calls.

The modifier calls ICiphernodeRegistry(registry).isCiphernodeEligible(msg.sender) on every invocation, which incurs an external call. If this modifier is used frequently, consider caching eligibility status or using events to maintain an internal mapping.

packages/enclave-contracts/contracts/lib/ExitQueueLib.sol (2)

88-94: Tranche creation pattern works but could be more explicit.

The code pushes an empty tranche (line 89), then immediately accesses and populates it (lines 90-93). While this works correctly, it's slightly unconventional. Consider using operatorQueue.push(ExitTranche({...})) for clarity.

Apply this diff for a more explicit pattern:

-        if (!merged) {
-            operatorQueue.push();
-            ExitTranche storage t = operatorQueue[len];
-            t.unlockTimestamp = unlockTimestamp;
-            t.ticketAmount = ticketAmount;
-            t.licenseAmount = licenseAmount;
-        }
+        if (!merged) {
+            operatorQueue.push(ExitTranche({
+                unlockTimestamp: unlockTimestamp,
+                ticketAmount: ticketAmount,
+                licenseAmount: licenseAmount
+            }));
+        }

138-155: Preview function has unbounded loop.

The previewClaimableAmounts function iterates through all tranches from the head index. If an operator has accumulated many tranches, this could consume significant gas. Since this is a view function, it won't fail transactions but could time out for off-chain callers.

Consider documenting this limitation or providing a paginated version for operators with many tranches.

packages/enclave-contracts/contracts/Enclave.sol (2)

154-155: Fee calculation is placeholder.

The getE3Quote function (line 511-515) returns a hardcoded fee of 1 USDC. Consider implementing dynamic fee calculation based on computation requirements (duration, threshold, etc.).


370-373: Reward distribution truncates remainder.

When dividing the payment equally (line 370), any remainder is lost due to integer division. The comment at line 381 acknowledges this "dust" but doesn't specify where it goes.

Consider implementing a dust collection mechanism (treasury address) or distributing the remainder to the first node to avoid permanent loss of funds.

packages/enclave-contracts/contracts/slashing/SlashingManager.sol (1)

49-71: Consider removing unused onlyVerifier modifier.

The onlyVerifier modifier (lines 58-61) is defined but doesn't appear to be used in this contract. If it's not part of a required interface or planned for future use, consider removing it.

Run the following script to confirm usage:

#!/bin/bash
# Description: Check if onlyVerifier modifier is used

# Search for onlyVerifier usage in function declarations
rg -n 'function.*onlyVerifier' --type=sol
packages/enclave-contracts/contracts/token/EnclaveToken.sol (1)

116-143: Consider consolidating validations in the first loop.

The function is correct, but you could optimize gas by checking for zero addresses and amounts during the first loop (lines 127-129) rather than during minting (lines 134-137). This avoids processing the entire batch if any item is invalid.

Apply this diff to consolidate validations:

         uint256 totalAmount = 0;
         for (uint256 i = 0; i < amounts.length; i++) {
+            if (recipients[i] == address(0)) revert ZeroAddress();
+            if (amounts[i] == 0) revert ZeroAmount();
             totalAmount += amounts[i];
         }
         if (totalMinted + totalAmount > TOTAL_SUPPLY)
             revert ExceedsTotalSupply();
 
         for (uint256 i = 0; i < recipients.length; i++) {
             address rec = recipients[i];
             uint256 amt = amounts[i];
-            if (rec == address(0)) revert ZeroAddress();
-            if (amt == 0) revert ZeroAmount();
 
             _mint(rec, amt);
             emit AllocationMinted(rec, amt, allocations[i]);
         }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 00e2f6d and 513c843.

📒 Files selected for processing (43)
  • .gitmodules (1 hunks)
  • packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json (6 hunks)
  • packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json (4 hunks)
  • packages/enclave-contracts/artifacts/contracts/registry/NaiveRegistryFilter.sol/NaiveRegistryFilter.json (2 hunks)
  • packages/enclave-contracts/contracts/Enclave.sol (12 hunks)
  • packages/enclave-contracts/contracts/interfaces/IBondingRegistry.sol (1 hunks)
  • packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol (4 hunks)
  • packages/enclave-contracts/contracts/interfaces/IEnclave.sol (3 hunks)
  • packages/enclave-contracts/contracts/interfaces/IRegistryFilter.sol (1 hunks)
  • packages/enclave-contracts/contracts/interfaces/ISlashVerifier.sol (1 hunks)
  • packages/enclave-contracts/contracts/interfaces/ISlashingManager.sol (1 hunks)
  • packages/enclave-contracts/contracts/lib/ExitQueueLib.sol (1 hunks)
  • packages/enclave-contracts/contracts/registry/BondingRegistry.sol (1 hunks)
  • packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol (10 hunks)
  • packages/enclave-contracts/contracts/registry/NaiveRegistryFilter.sol (4 hunks)
  • packages/enclave-contracts/contracts/slashing/SlashingManager.sol (1 hunks)
  • packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol (4 hunks)
  • packages/enclave-contracts/contracts/test/MockRegistryFilter.sol (4 hunks)
  • packages/enclave-contracts/contracts/test/MockSlashingVerifier.sol (1 hunks)
  • packages/enclave-contracts/contracts/test/MockStableToken.sol (1 hunks)
  • packages/enclave-contracts/contracts/token/EnclaveTicketToken.sol (1 hunks)
  • packages/enclave-contracts/contracts/token/EnclaveToken.sol (1 hunks)
  • packages/enclave-contracts/ignition/modules/bondingRegistry.ts (1 hunks)
  • packages/enclave-contracts/ignition/modules/enclave.ts (1 hunks)
  • packages/enclave-contracts/ignition/modules/enclaveTicketToken.ts (1 hunks)
  • packages/enclave-contracts/ignition/modules/enclaveToken.ts (1 hunks)
  • packages/enclave-contracts/ignition/modules/mockSlashingVerifier.ts (1 hunks)
  • packages/enclave-contracts/ignition/modules/mockStableToken.ts (1 hunks)
  • packages/enclave-contracts/ignition/modules/slashingManager.ts (1 hunks)
  • packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts (1 hunks)
  • packages/enclave-contracts/scripts/deployAndSave/ciphernodeRegistryOwnable.ts (1 hunks)
  • packages/enclave-contracts/scripts/deployAndSave/enclave.ts (5 hunks)
  • packages/enclave-contracts/scripts/deployAndSave/enclaveTicketToken.ts (1 hunks)
  • packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts (1 hunks)
  • packages/enclave-contracts/scripts/deployAndSave/mockStableToken.ts (1 hunks)
  • packages/enclave-contracts/scripts/deployAndSave/slashingManager.ts (1 hunks)
  • packages/enclave-contracts/scripts/deployEnclave.ts (3 hunks)
  • packages/enclave-contracts/scripts/index.ts (1 hunks)
  • packages/enclave-contracts/test/BondingRegistry.spec.ts (1 hunks)
  • packages/enclave-contracts/test/CiphernodeRegistry/CiphernodeRegistryOwnable.spec.ts (3 hunks)
  • packages/enclave-contracts/test/CiphernodeRegistry/NaiveRegistryFilter.spec.ts (1 hunks)
  • packages/enclave-contracts/test/Enclave.spec.ts (24 hunks)
  • packages/enclave-contracts/test/SlashingManager.spec.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (6)
packages/enclave-contracts/scripts/deployAndSave/mockStableToken.ts (1)
packages/enclave-contracts/scripts/utils.ts (2)
  • readDeploymentArgs (74-88)
  • storeDeploymentArgs (34-66)
packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts (1)
packages/enclave-contracts/scripts/utils.ts (2)
  • readDeploymentArgs (74-88)
  • storeDeploymentArgs (34-66)
packages/enclave-contracts/scripts/deployAndSave/slashingManager.ts (1)
packages/enclave-contracts/scripts/utils.ts (2)
  • readDeploymentArgs (74-88)
  • storeDeploymentArgs (34-66)
packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts (1)
packages/enclave-contracts/scripts/utils.ts (2)
  • readDeploymentArgs (74-88)
  • storeDeploymentArgs (34-66)
packages/enclave-contracts/scripts/deployAndSave/enclaveTicketToken.ts (1)
packages/enclave-contracts/scripts/utils.ts (2)
  • readDeploymentArgs (74-88)
  • storeDeploymentArgs (34-66)
packages/enclave-contracts/scripts/deployEnclave.ts (8)
packages/enclave-contracts/scripts/deployAndSave/mockStableToken.ts (1)
  • deployAndSaveMockStableToken (25-84)
packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts (1)
  • deployAndSaveEnclaveToken (28-85)
packages/enclave-contracts/scripts/deployAndSave/enclaveTicketToken.ts (1)
  • deployAndSaveEnclaveTicketToken (30-101)
packages/enclave-contracts/scripts/deployAndSave/slashingManager.ts (1)
  • deployAndSaveSlashingManager (29-95)
packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts (1)
  • deployAndSaveBondingRegistry (36-140)
packages/enclave-contracts/scripts/deployAndSave/ciphernodeRegistryOwnable.ts (1)
  • deployAndSaveCiphernodeRegistryOwnable (29-92)
packages/enclave-contracts/scripts/deployAndSave/enclave.ts (1)
  • deployAndSaveEnclave (30-108)
packages/enclave-contracts/scripts/deployAndSave/naiveRegistryFilter.ts (1)
  • deployAndSaveNaiveRegistryFilter (21-87)
🪛 GitHub Actions: License Header Check
packages/enclave-contracts/test/SlashingManager.spec.ts

[error] 1-1: Missing license header.

🪛 Gitleaks (8.28.0)
packages/enclave-contracts/test/Enclave.spec.ts

[high] 745-745: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: build_sdk
  • GitHub Check: test_contracts
  • GitHub Check: integration_prebuild
  • GitHub Check: build_e3_support_dev
  • GitHub Check: build_enclave_cli
  • GitHub Check: build_e3_support_risc0
  • GitHub Check: test_net
  • GitHub Check: rust_unit
  • GitHub Check: Build & Push Image
🔇 Additional comments (141)
packages/enclave-contracts/ignition/modules/mockSlashingVerifier.ts (1)

10-13: LGTM! Standard ignition module structure.

The module correctly wires the MockSlashingVerifier contract deployment using Hardhat Ignition's buildModule API. The structure is clean and follows the declarative pattern expected for test mock deployments.

packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts (1)

53-59: Parameters shape verified The EnclaveToken module’s ID and its owner parameter match the deployment call, so the parameters object is correct.

packages/enclave-contracts/contracts/test/MockStableToken.sol (3)

11-19: LGTM!

The contract structure and initialization are correct. The constructor properly scales the initial supply by 10^6 to match USDC's 6 decimal places, and ownership is correctly assigned to the deployer.


21-23: LGTM!

The decimals override correctly returns 6, matching the standard USDC token specification.


25-27: LGTM!

The owner-restricted mint function is appropriate for a test token, allowing flexible token provisioning in test scenarios.

packages/enclave-contracts/contracts/interfaces/IEnclave.sol (3)

73-80: LGTM!

The new events follow the existing naming and documentation patterns established in the interface. They correctly emit when bonding registry and USDC token addresses are configured.


136-136: Good gas optimization.

Changing the parameter location from memory to calldata for this external function reduces gas costs since the parameter is read-only and doesn't need to be copied to memory.


232-238: LGTM!

The new getE3Quote function provides a valuable query mechanism for clients to estimate fees before submitting an E3 request. The view function signature and calldata parameter are appropriate, and the documentation correctly indicates validation behavior.

packages/enclave-contracts/scripts/deployAndSave/enclave.ts (3)

20-22: LGTM!

The new optional parameters follow the existing pattern in the EnclaveArgs interface and correctly represent address strings for the bonding registry and USDC token.


35-37: LGTM!

Function parameters correctly extended to include the new bonding registry and USDC token addresses.


70-108: LGTM after validation fix.

The deployment and storage logic correctly includes the new bondingRegistry and usdcToken parameters. Once the validation logic issue (flagged above) is fixed, this segment will function correctly.

packages/enclave-contracts/contracts/interfaces/ISlashingManager.sol (1)

1-286: LGTM! Well-structured slashing governance interface.

The interface is comprehensive and well-documented. The separation of concerns (policies, proposals, appeals, bans) is clear, and the event/error definitions provide good observability. The two-phase slash workflow (propose → appeal window → execute) with proof verification support demonstrates thoughtful design.

packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts (1)

56-78: Verify the conditional logic for cache reuse.

The condition checks if any required parameter is missing OR if all parameters match the cached deployment. However, the logic appears inverted: if parameters are missing, the function should not proceed to reuse a cached deployment without validating completeness.

The current structure suggests: "reuse cached deployment if (any param missing) OR (all params match)". This could lead to returning a cached contract when required parameters are undefined.

Review the conditional flow:

  • Lines 56-65: Check if any required param is undefined
  • Lines 66-77: Check if all params match cached args
  • Line 78: If either condition is true, attempt to reuse cached deployment
  • Lines 79-83: Throw error if cached address is missing

This logic seems to conflate two distinct scenarios:

  1. Missing parameters during invocation (should error or prompt for values)
  2. All parameters match cached deployment (safe to reuse)

Consider refactoring to separate these cases:

+  // First, validate all required parameters are provided
+  if (
+    !owner ||
+    !ticketToken ||
+    !licenseToken ||
+    !registry ||
+    !slashedFundsTreasury ||
+    !ticketPrice ||
+    !licenseRequiredBond ||
+    minTicketBalance === undefined ||
+    exitDelay === undefined
+  ) {
+    throw new Error("All BondingRegistry parameters are required");
+  }
+
+  // Then, check if cached deployment matches current parameters
   if (
-    !owner ||
-    !ticketToken ||
-    !licenseToken ||
-    !registry ||
-    !slashedFundsTreasury ||
-    !ticketPrice ||
-    !licenseRequiredBond ||
-    minTicketBalance === undefined ||
-    exitDelay === undefined ||
-    (preDeployedArgs?.constructorArgs?.owner === owner &&
+    preDeployedArgs?.constructorArgs?.owner === owner &&
      preDeployedArgs?.constructorArgs?.ticketToken === ticketToken &&

This makes the intent clearer: require all parameters first, then reuse cached deployment only if they match.

packages/enclave-contracts/scripts/deployAndSave/ciphernodeRegistryOwnable.ts (1)

86-89: LGTM! Adds signer to contract connection.

The change ensures the BondingRegistry contract instance returned from new deployments is properly connected with a signer, matching the pattern already used in the cached deployment path (line 55). This enables the contract to send transactions.

packages/enclave-contracts/ignition/modules/enclaveToken.ts (1)

10-16: LGTM! Standard ignition module pattern.

The module correctly retrieves the owner parameter and deploys the EnclaveToken contract with it. The structure follows Hardhat Ignition conventions.

packages/enclave-contracts/test/CiphernodeRegistry/CiphernodeRegistryOwnable.spec.ts (3)

240-242: LGTM! Test updated for new access control.

The error expectation correctly reflects the updated access control in CiphernodeRegistryOwnable, where addCiphernode now allows both the owner and the bonding registry to add nodes.


247-247: LGTM! Test updated for renamed method.

The test correctly uses the new isEnabled() method name, which replaces the previous isCiphernodeEligible() API.


276-277: LGTM! Test updated for new access control.

The error expectation correctly reflects the updated access control for removeCiphernode, consistent with the change in addCiphernode.

packages/enclave-contracts/contracts/interfaces/ISlashVerifier.sol (1)

8-18: LGTM! Clean interface design.

The interface is well-documented and provides a clear contract for slash proof verification. The function signature appropriately uses external view for read-only verification, and the parameter names are descriptive.

packages/enclave-contracts/ignition/modules/mockStableToken.ts (1)

10-15: LGTM! Module structure follows Ignition conventions.

The module correctly retrieves the initialSupply parameter and deploys the MockUSDC contract with it.

packages/enclave-contracts/contracts/interfaces/IRegistryFilter.sol (2)

20-22: LGTM! Clean accessor function.

The getCommittee function provides a straightforward way to retrieve committee data by E3 ID.


9-13: Confirm publicKey stores the keccak256 hash; bytes32 is sufficient.

packages/enclave-contracts/test/CiphernodeRegistry/NaiveRegistryFilter.spec.ts (1)

106-122: LGTM! Test properly validates registry-mediated committee flow.

The refactored test now correctly exercises the registry-mediated committee request path, which better reflects the actual usage pattern. The test:

  1. Wires the filter to the registry
  2. Requests the committee through the registry (not directly on the filter)
  3. Publishes the committee with nodes and public key
  4. Validates the threshold was set correctly

This is a more realistic integration test.

packages/enclave-contracts/ignition/modules/bondingRegistry.ts (1)

21-31: Verify BondingRegistry constructor parameter order

I was unable to locate the BondingRegistry constructor in the codebase—please manually confirm that the instantiation arguments
[owner, ticketToken, licenseToken, registry, slashedFundsTreasury, ticketPrice, licenseRequiredBond, minTicketBalance, exitDelay]
match the BondingRegistry.sol constructor signature.

packages/enclave-contracts/ignition/modules/enclave.ts (1)

15-16: Constructor parameter order matches Enclave.sol signature.

No changes needed.

packages/enclave-contracts/ignition/modules/slashingManager.ts (1)

14-17: Constructor parameter order matches contract signature. The deployment args [admin, bondingRegistry] align with SlashingManager.sol’s constructor(address admin, address _bondingRegistry).

packages/enclave-contracts/ignition/modules/enclaveTicketToken.ts (1)

10-22: LGTM!

The ignition module correctly retrieves the three required constructor parameters and passes them to the EnclaveTicketToken contract. The pattern is consistent with other ignition modules in the codebase.

packages/enclave-contracts/test/Enclave.spec.ts (5)

66-78: LGTM! Helper improves test readability.

The makeRequest helper centralizes the USDC approval + request flow, reducing duplication across tests. The optional signer parameter enables testing with different accounts.


276-290: LGTM! Token minting provides sufficient test balances.

The setup mints USDC and EnclaveToken allocations for both owner and notTheOwner accounts, enabling comprehensive testing of fee payments and bonding flows.


42-45: False positive: Not an API key.

The static analysis tool flagged encryptionSchemeId as a potential API key. This is a test fixture constant representing a keccak256 hash used as an encryption scheme identifier, not a secret credential.


733-746: Confirm ERC20InsufficientAllowance is defined in imported ERC20
I couldn’t locate any error ERC20InsufficientAllowance in MockUSDC or test contracts—this must come from the OpenZeppelin ERC20 you import. Please verify your ERC20 dependency actually declares that custom error (otherwise update the test to use the correct revert string, e.g. "ERC20: insufficient allowance").


85-233: No circular‐dependency issues—constructors use non-zero placeholders
Each contract’s constructor requires a non-zero address but is passed the dummy addressOne placeholder (not zero) at deploy, so no logic ever operates on an uninitialized registry or slashing manager before the subsequent setters configure the real addresses.

packages/enclave-contracts/contracts/test/MockRegistryFilter.sol (3)

30-31: LGTM! Type migration to external interface.

The storage mapping correctly adopts IRegistryFilter.Committee from the external interface, ensuring consistency across the codebase.


116-122: LGTM! New getter enforces publication requirement.

The getCommittee function correctly returns the committee and enforces that it has been published (publicKey is non-zero). This aligns with the interface requirements.


93-98: publicKey stored as hash correctly; full key passed through for activation
IRegistryFilter.Committee.publicKey is defined as bytes32 and both NaiveRegistryFilter and MockRegistryFilter consistently use keccak256(publicKey) for storage. The full public key is still forwarded via publishCommittee (and emitted by CiphernodeRegistryOwnable) for Enclave.activate to verify and use.

packages/enclave-contracts/scripts/deployEnclave.ts (7)

57-63: LGTM!

EnclaveToken deployment is correctly configured with the owner address and proper logging.


65-73: LGTM!

EnclaveTicketToken deployment correctly uses addressOne as a placeholder for the registry address, which is properly configured later in the cross-contract dependencies section (line 144).


75-82: LGTM!

SlashingManager deployment correctly uses addressOne as a placeholder for the bonding registry address, which is properly configured later (line 150).


84-98: Verify the slashed funds treasury address.

The slashedFundsTreasury is currently set to the owner address. Ensure this is the intended destination for slashed funds in production deployments, as a dedicated treasury or multisig wallet may be more appropriate.


109-120: LGTM!

Enclave deployment is correctly configured with all required parameters, including the registry addresses and USDC token.


131-156: LGTM!

Cross-contract dependency configuration is comprehensive and correctly resolves all placeholder addresses used during deployment. The wiring sequence properly connects all contracts in the system.


187-200: LGTM!

Deployment summary provides comprehensive logging of all deployed contract addresses with clear formatting.

packages/enclave-contracts/test/SlashingManager.spec.ts (9)

1-23: LGTM on imports and test setup.

The imports and dynamic network connection pattern align with the Hardhat v3 and Ignition workflow. The destructuring of helpers from networkHelpers is clear and appropriate.


75-204: Fixture setup is well-structured.

The setup function properly deploys all contracts, assigns roles, and wires dependencies. The two-step registry setup pattern (deploy with placeholder, then call setRegistry) at lines 103, 135, and 171-177 is acceptable for test fixtures, though it requires care to ensure all connections are established before tests run.


246-420: Policy validation tests are comprehensive.

The tests thoroughly cover policy validation rules, including the mutual exclusivity of proof requirements and appeal windows (lines 385-401 and 403-419). This ensures that governance cannot configure conflicting policy parameters.


482-672: Slash proposal tests are thorough.

The tests cover both proof-based (immediate execution) and evidence-based (appeal window) proposals. Event emission verification includes proper timestamp calculations (lines 498-515, 539-557), and the totalProposals counter is verified to increment correctly.


674-807: Execution tests cover critical paths.

The tests properly verify immediate execution for proof-based slashes, delayed execution after appeal windows (lines 712-736), and automatic node banning (lines 738-760). Error conditions are appropriately tested.


809-1070: Appeal system tests are comprehensive.

The appeal lifecycle is thoroughly tested, including filing, resolution (approve/deny), and the interaction with execution. The tests correctly verify that pending appeals block execution (lines 985-1010), approved appeals permanently block execution (lines 1012-1039), and denied appeals allow execution to proceed (lines 1041-1069).


1072-1135: Ban management tests are appropriate.

The tests verify governance control over banning/unbanning and correctly test that new slash proposals are rejected for banned nodes (lines 1115-1134). This prevents redundant slashing of already-sanctioned operators.


1137-1193: View function tests validate data retrieval.

The tests appropriately verify that policies and proposals can be retrieved with accurate field values, and that invalid proposal IDs are rejected (lines 1185-1191).


463-471: Assertion syntax is correct The .to.be.revert(ethers) matcher aligns with Hardhat v3’s updated syntax for asserting reverts (hardhat.org)

Likely an incorrect or invalid review comment.

packages/enclave-contracts/scripts/deployAndSave/enclaveTicketToken.ts (3)

1-13: LGTM on license header and imports.

The file includes the required license header and imports all necessary dependencies for the deployment script.


15-23: Interface design supports reusable deployments.

The optional parameters (underlyingUSDC, registry, owner) enable the deployment function to reuse pre-deployed contracts, which is the intended pattern shown in the function implementation.


64-101: Deployment and storage logic is sound.

The deployment flow properly waits for contract deployment, captures the block number and address, and stores the deployment arguments for future reuse. The factory connection pattern at lines 95-98 is correct.

packages/enclave-contracts/scripts/index.ts (1)

10-17: LGTM on new deployment script exports.

The new exports at lines 10, 13-15, and 17 follow the established pattern and provide centralized access to the deployment utilities introduced in this PR.

packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json (1)

19-31: Artifact additions align with new economic features.

The new BondingRegistrySet and UsdcTokenSet events, along with the getE3Quote view function, correctly reflect the interface expansions for the USDC-based payment subsystem. The E3RequestParams struct properly encapsulates the parameters needed for quote calculation.

Also applies to: 307-319, 473-528

packages/enclave-contracts/scripts/deployAndSave/mockStableToken.ts (3)

37-50: Deployment reuse logic is correct.

The condition properly handles reusing an existing deployment when initialSupply is unchanged. The type conversion via toString() correctly compares the numeric parameter with the stored string value from previous deployments.


52-76: Deployment flow follows best practices.

The ignition-based deployment correctly waits for confirmation, captures the block number for history tracking, and persists deployment arguments for future reuse. Storing initialSupply as a string maintains consistency with the reuse logic.


78-83: Contract connection and return are correct.

The factory pattern correctly connects to the deployed contract address with the signer, enabling transaction signing. The return type matches the function signature.

packages/enclave-contracts/test/BondingRegistry.spec.ts (4)

43-200: Comprehensive test fixture with proper contract wiring.

The setup fixture correctly deploys and integrates all components of the bonding/slashing subsystem. Note that AddressOne is used as a placeholder for ticketToken.registry (line 86) and slashingManager.bondingRegistry (line 99), which are correctly set to the actual BondingRegistry address after deployment (lines 151-156). This pattern is appropriate for circular dependencies in deployment.


202-226: Constructor tests validate all initial parameters.

The test correctly verifies that all constructor parameters are properly set, including the default licenseActiveBps value of 8000 (80%).


228-371: Bonding and unbonding tests are comprehensive.

Tests correctly cover the full lifecycle of license bonding: successful bonding with event emission, zero amount validation, exit-in-progress restrictions, balance accumulation, unbonding flows, and exit queue management. All assertions verify both state changes and event emissions.


1018-1158: Edge case tests validate complex state transitions.

The test suite thoroughly exercises complex scenarios: operator deactivation through license reduction (testing the boundary condition with unbondAmount + 1n), concurrent multi-operator states, and complete lifecycle flows including exit, claim, and re-registration. These tests provide strong confidence in the system's handling of real-world usage patterns.

packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol (7)

8-8: LGTM - Import enables shared Committee type.

The import of IRegistryFilter is necessary to reference the IRegistryFilter.Committee type used in the getCommittee return value, establishing proper type coupling between the registry and filter interfaces.


26-29: LGTM - Event properly documents committee lifecycle.

The CommitteeActivationChanged event provides clear signaling for committee status transitions (active/completed), which is essential for off-chain monitoring and state tracking.


61-64: LGTM - isEnabled function signature is clean.

The isEnabled function provides a straightforward view function to check ciphernode status without side effects.


66-68: LGTM - addCiphernode function signature is appropriate.

The function signature is clean and follows Solidity conventions for registry operations.


109-113: LGTM - getFilter function is straightforward.

The function provides clean access to the filter address for a given E3 ID with appropriate revert behavior documented.


115-121: LGTM - getCommittee properly uses shared Committee type.

The function correctly returns IRegistryFilter.Committee type, establishing a consistent data structure across the registry and filter components. The documentation clearly specifies revert conditions.


70-76: Validate siblingNodes length in Merkle removal
We couldn’t locate the LeanIMTData._remove implementation in the repo—confirm that the Merkle-proof array is validated (correct length and order) in the tree‐removal logic or document and enforce its expected size.

packages/enclave-contracts/contracts/registry/NaiveRegistryFilter.sol (3)

23-24: LGTM - Storage properly uses shared Committee type.

The mapping correctly uses IRegistryFilter.Committee as the value type, aligning with the interface standardization across the codebase.


95-95: LGTM - Correct storage reference for Committee.

The storage reference properly uses IRegistryFilter.Committee type, ensuring type consistency with the mapping declaration.


122-128: LGTM - getCommittee correctly returns shared Committee type.

The function properly returns IRegistryFilter.Committee memory with appropriate validation (checking for non-zero public key), aligning with the interface contract.

packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol (6)

9-9: LGTM - Import required for Committee type.

The import is necessary to construct and return IRegistryFilter.Committee instances in the mock implementations.


24-30: LGTM - Mock stubs are appropriate.

The empty implementations for addCiphernode and removeCiphernode, along with isEnabled always returning true, are reasonable defaults for test mocks where the specific behavior isn't under test.


51-53: LGTM - getFilter returns appropriate default.

Returning address(0) is a reasonable default for a mock when the specific filter address isn't relevant to the test scenario.


55-64: LGTM - getCommittee returns valid empty Committee.

The mock returns a properly constructed IRegistryFilter.Committee with empty values (empty nodes array, zero threshold, zero public key), which is appropriate for tests that don't specifically validate committee data.


80-87: LGTM - Consistent mock implementation in second contract.

The MockCiphernodeRegistryEmptyKey contract correctly duplicates the stub implementations for addCiphernode, isEnabled, and removeCiphernode, maintaining consistency across mock variants.


95-108: LGTM - Consistent getFilter and getCommittee implementations.

Both mocks now implement getFilter and getCommittee consistently, returning appropriate defaults. The distinction between the two mocks (standard vs. empty key) is preserved in their respective committeePublicKey implementations.

packages/enclave-contracts/contracts/lib/ExitQueueLib.sol (5)

10-14: Timestamp range is adequate for use case.

Using uint64 for unlockTimestamp supports dates until year 2554, which is sufficient for this use case. The overflow check at line 68-71 ensures safety.


112-136: LGTM!

Helper functions correctly delegate to the main queueing function and provide clean read access to pending amounts.


157-193: Claim logic is well-structured.

The function correctly:

  • Processes tickets and licenses independently
  • Only updates pending totals when assets are actually claimed
  • Cleans up empty tranches once
  • Emits events only when state changes

195-237: Slash logic mirrors claim logic correctly.

The slashPendingAssets function follows the same pattern as claimAssets but adds the includeLockedAssets flag. The structure is consistent and correct.


282-339: Asset taking logic is correct and handles partial claims.

The _takeAssetsFromQueue function correctly:

  • Iterates through tranches from the head index
  • Respects unlock timestamps unless includeLockedAssets is set
  • Handles partial consumption of tranches (one asset type may be depleted while the other remains)
  • Only advances past tranches when both asset types are fully consumed

The interaction between line 315-318 (skipping empty asset types) and line 333-335 (advancing only when both are zero) is correct.

packages/enclave-contracts/contracts/Enclave.sol (4)

11-12: LGTM - Proper imports for token integration.

The addition of IBondingRegistry, IRegistryFilter, SafeERC20, and IERC20 imports correctly supports the new payment and reward distribution features.

Also applies to: 17-20, 29-29


38-39: Storage variables added appropriately.

The new storage variables (bondingRegistry, usdcToken, e3Payments) are properly placed and support the new economic model.

Also applies to: 63-63


106-143: Initialization properly extended for new dependencies.

The constructor and initialize function correctly accept and set the new bondingRegistry and usdcToken parameters.


221-232: Incorrect assumption: payment isn’t lost on failure
Reverting via require undoes all prior state changes in the transaction—including the ERC-20 transfer—so there is no need for an explicit refund path here.

Likely an incorrect or invalid review comment.

packages/enclave-contracts/contracts/token/EnclaveTicketToken.sol (8)

26-32: Inheritance order is correct.

The contract properly inherits ERC20, ERC20Permit, ERC20Votes, Ownable, and ERC20Wrapper. This order ensures correct method resolution order for OpenZeppelin's diamond pattern.


40-43: Access control properly implemented.

The onlyRegistry modifier and validation in constructor/setter ensure only authorized addresses can perform critical operations.

Also applies to: 55-56, 60-60


70-80: Auto-delegation on deposit is well-designed.

The depositFor function correctly auto-delegates voting power to the operator on first deposit. This ensures voting power is tracked without requiring separate delegation transactions.


89-98: depositFrom provides flexible deposit flow.

This function allows depositing from one address while minting to another, useful for registry-managed operations. Auto-delegation is correctly applied to the recipient.


138-147: Transfer restrictions correctly implemented.

The _update override properly prevents transfers between users, allowing only minting and burning. This makes the token non-transferable as required for staking tickets.


153-155: Delegation correctly locked to preserve voting power.

Overriding delegate and delegateBySig to revert ensures that voting power cannot be transferred separately from the ticket tokens. Combined with auto-delegation on deposit, this maintains the invariant that tickets = voting power.

Also applies to: 161-170


172-185: Multiple inheritance overrides properly specified.

The decimals and nonces functions correctly specify all parent contracts being overridden and delegate to super, allowing OpenZeppelin's method resolution order to work correctly.


131-133: Clarify payout function intent.

payout transfers underlying USDC without burning ETK (breaking the 1:1 backing). Since withdrawTo already handles burning + transfer, document or rename payout, restrict it to reward distributions funded from a separate pool, or remove it if unused.

packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol (13)

22-28: LGTM!

Event declaration is well-structured with indexed parameter for efficient off-chain filtering.


37-37: LGTM!

Public storage variable placement and visibility are appropriate.


57-62: LGTM!

Custom error declarations are specific and provide clear failure reasons for the new bonding integration paths.


75-86: LGTM!

Access control modifiers are correctly implemented with appropriate authorization checks and custom error reverts.


98-105: LGTM!

Zero-address validation guards prevent initialization with invalid addresses, protecting against deployment errors.


142-156: LGTM!

Access control expansion and short-circuit logic for duplicate adds are correct. The early return prevents unnecessary state mutations and events.


158-169: LGTM!

Access control changes and the enabled-state guard correctly prevent removal of non-existent nodes.


177-181: LGTM!

Zero-address validation is consistent with initialization guards and prevents misconfiguration.


183-187: LGTM!

Admin setter is properly guarded with owner-only access and zero-address validation. Event emission provides transparency.


221-223: LGTM!

Visibility change to public allows both internal and external access. Return type simplification to address is a minor ABI refinement.


225-229: LGTM!

Delegation to the filter contract is correct. The function will revert with a clear error if the filter is not set, which is acceptable behavior.


235-237: LGTM!

Standard getter implementation for external access to the bonding registry address.


202-207: Initialization flow validated — guard is safe.

Deployment scripts (deployEnclave.ts) always call setBondingRegistry before any calls to isCiphernodeEligible, and tests for isCiphernodeEligible only exercise the disabled‐node path (never hitting the require), so the new require cannot revert in practice.

packages/enclave-contracts/contracts/interfaces/IBondingRegistry.sol (12)

1-7: LGTM!

License and pragma declarations are standard and consistent with the codebase.


9-11: LGTM!

Import statements are appropriate for the interface's type dependencies.


18-37: LGTM!

Custom error definitions provide comprehensive coverage of failure modes with clear, descriptive names.


43-55: LGTM!

Event design with signed delta and indexed reason provides excellent audit trail capabilities. NatSpec clearly documents the semantics.


57-69: LGTM!

Event structure is consistent with TicketBalanceUpdated, maintaining a parallel design pattern.


71-110: LGTM!

Remaining events are well-documented with appropriate indexed parameters for efficient filtering and lifecycle tracking.


112-205: LGTM!

View function interface is comprehensive and well-organized, with clear NatSpec documentation for each query method.


207-250: LGTM!

Operator lifecycle functions are well-designed with clear preconditions documented. The deregisterOperator signature requiring sibling nodes enables immediate IMT removal.


252-264: LGTM!

The claimExits function design with separate max parameters provides flexibility for partial claims.


266-294: LGTM!

Slashing functions are well-separated by balance type, use absolute amounts for precision, and include reason parameters for audit trails.


296-310: LGTM!

Batch reward distribution design is gas-efficient and flexible with the generic IERC20 reward token parameter.


312-395: LGTM!

Admin interface is comprehensive with clear separation between configuration parameters and component addresses. All functions are appropriately documented as owner-only.

packages/enclave-contracts/contracts/slashing/SlashingManager.sol (10)

1-14: LGTM!

License, pragma, and imports are appropriate for the contract's functionality.


16-47: LGTM!

Role constants and storage layout are well-organized with appropriate visibility modifiers. Internal _proposals mapping ensures controlled access via getters.


73-85: LGTM!

Constructor implements proper input validation and secure-by-default role initialization. Only admin and governance roles are granted initially.


87-106: LGTM!

View functions include appropriate bounds checking and return memory copies for safety.


112-132: LGTM!

Policy validation correctly enforces mutually exclusive proof-based and evidence-based modes. The logic ensures proper configuration for each slashing approach.


134-163: LGTM!

Admin functions use appropriate role guards and include necessary zero-address validation.


282-300: LGTM!

Appeal filing logic correctly enforces access control, timing windows, and single-appeal constraints. Evidence is appropriately logged via events without incurring storage costs.


302-323: LGTM!

Appeal resolution logic correctly enforces governance-only access and maintains the proposal state machine.


325-341: LGTM!

Ban management functions are correctly restricted to governance with appropriate validation and event emission.


222-280: Verify partial execution behavior and add tests/documentation.

Logic in SlashingManager.sol:227 allows ticket and license slashes in separate transactions, but I found no tests or documentation covering this path. Confirm it’s intentional and add tests or docs to ensure both branches behave correctly.

packages/enclave-contracts/contracts/token/EnclaveToken.sol (5)

1-85: LGTM! Initialization and setup are correct.

The constructor properly initializes all roles, state variables, and events. The owner is whitelisted and transfers are restricted by default, which is a secure starting configuration for a governance token.


87-107: LGTM! Minting logic is secure and correct.

The function properly validates inputs, enforces the supply cap, and updates state. The order of operations is safe and event emission is appropriate.


145-198: LGTM! Transfer control functions are well-designed.

All functions are properly access-controlled, emit appropriate events, and handle edge cases (e.g., zero address checks in whitelistContracts).


203-214: Verify the transfer restriction logic meets requirements.

The current implementation allows transfers if either the sender or recipient is whitelisted. This means:

  • Whitelisted addresses can send to anyone (expected).
  • Non-whitelisted addresses can send to whitelisted addresses (potentially unintended).

If the intent is to prevent all non-whitelisted transfers during the restricted period, consider requiring the sender to be whitelisted:

if (from != address(0) && to != address(0) && transfersRestricted) {
    if (!transferWhitelisted[from])
        revert TransferNotAllowed();
}

Confirm whether the current OR-based logic is intentional (e.g., to allow deposits to whitelisted contracts).


216-233: LGTM! Interface overrides are correct.

Both functions properly resolve multi-inheritance ambiguity by delegating to the parent implementation. This is the standard pattern for handling diamond inheritance in Solidity.

packages/enclave-contracts/contracts/registry/BondingRegistry.sol (5)

1-111: LGTM! Contract structure and storage are well-organized.

The contract uses clear naming conventions, appropriate access control modifiers, and a comprehensive storage layout. The noExitInProgress modifier correctly prevents operations during active exit delays.


113-158: LGTM! Initialization pattern is correct.

The constructor and initialize function follow the standard upgradeable contract pattern. Ownership is correctly transferred to _owner after initialization, and all configuration is validated through setter functions.


160-216: LGTM! View functions are well-implemented.

All view functions correctly expose operator state and handle edge cases (e.g., zero ticketPrice in availableTickets). The use of getPastVotes for historical balance queries is appropriate for governance integration.


404-502: LGTM! Slashing logic is sound (pending syntax fixes).

The slashing functions correctly:

  • Prioritize slashing from active balances before pending exits
  • Use Math.min to prevent over-slashing
  • Update cumulative slashed totals for treasury withdrawal
  • Emit appropriate events and update operator status

Once the require/custom error syntax issues are fixed throughout the contract, this logic will work correctly.


641-658: LGTM! Internal helpers are well-designed.

_updateOperatorStatus correctly maintains the active flag based on all relevant conditions, including graceful handling of zero ticketPrice. The percentage-based _minLicenseBond provides flexibility for active status thresholds.

Comment thread packages/enclave-contracts/contracts/Enclave.sol
Comment thread packages/enclave-contracts/contracts/Enclave.sol Outdated
Comment thread packages/enclave-contracts/contracts/Enclave.sol
Comment thread packages/enclave-contracts/contracts/Enclave.sol Outdated
Comment thread packages/enclave-contracts/contracts/registry/BondingRegistry.sol
Comment thread packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts Outdated
Comment thread packages/enclave-contracts/scripts/deployAndSave/slashingManager.ts
Comment thread packages/enclave-contracts/scripts/deployEnclave.ts
Comment thread packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

♻️ Duplicate comments (4)
packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts (1)

34-37: Avoid non-standard hre.network.connect(); use hre directly and key deployments by chainId

This issue was flagged in previous review. hre.network.connect() isn't part of Hardhat's public API. Access ignition/ethers from hre directly and derive a stable chain key from chainId.

Based on learnings.

Apply this diff:

-  const { ignition, ethers } = await hre.network.connect();
-  const [signer] = await ethers.getSigners();
-  const chain = (await signer.provider?.getNetwork())?.name ?? "localhost";
+  const { ignition, ethers } = hre;
+  const [signer] = await ethers.getSigners();
+  const { chainId } = await ethers.provider.getNetwork();
+  const chain = String(chainId);
packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol (1)

103-112: Empty committee return may cause division by zero in Enclave.

Same issue as in MockCiphernodeRegistry: the empty nodes array will cause division by zero in Enclave._distributeRewards (line 371) if this mock is used in integration tests that trigger reward distribution.

Apply the same fix as suggested for MockCiphernodeRegistry.getCommittee above, or ensure tests never reach the reward distribution code path when using this mock.

packages/enclave-contracts/contracts/Enclave.sol (1)

364-384: Reentrancy protection improved, but division by zero risk remains.

Good news: The reentrancy issue flagged in past reviews has been addressed. The code now zeros out e3Payments[e3Id] (line 379) before the external call to bondingRegistry.distributeRewards (line 381), following the checks-effects-interactions pattern correctly.

Critical issue remaining: Line 371 divides e3Payments[e3Id] by committee.nodes.length without checking if the array is empty. If getCommittee returns an empty committee (as the mock implementations do), this will cause a division-by-zero revert.

Add a guard before the division:

 function _distributeRewards(uint256 e3Id) internal {
     IRegistryFilter.Committee memory committee = ciphernodeRegistry
         .getCommittee(e3Id);
+    require(committee.nodes.length > 0, "Empty committee");
     uint256[] memory amounts = new uint256[](committee.nodes.length);

     // We might need to pay different amounts to different nodes.

Note: The CiphernodeRegistryOwnable.getCommittee function (line 229) does validate non-empty committees, but relying on that implicit check is fragile. An explicit check here improves clarity and defense-in-depth.

packages/enclave-contracts/scripts/deployEnclave.ts (1)

40-55: Production deployment is blocked—accept USDC address via environment variable.

The production branch (lines 52-54) unconditionally throws an error, making it impossible to deploy to production networks without modifying the code.

Accept the USDC token address via an environment variable:

  const shouldDeployMocks = process.env.DEPLOY_MOCKS === "true" || withMocks;
  let usdcTokenAddress: string;

  if (shouldDeployMocks) {
    console.log("Deploying mock USDC token...");
    const { mockStableToken } = await deployAndSaveMockStableToken({
      initialSupply: 1000000,
      hre,
    });
    usdcTokenAddress = await mockStableToken.getAddress();
    console.log("MockUSDC deployed to:", usdcTokenAddress);
  } else {
-   throw new Error(
-     "USDC token address must be provided for production deployment",
-   );
+   const envUsdcAddress = process.env.USDC_TOKEN_ADDRESS;
+   if (!envUsdcAddress || !ethers.isAddress(envUsdcAddress)) {
+     throw new Error(
+       "USDC_TOKEN_ADDRESS environment variable must be set to a valid address for production deployment",
+     );
+   }
+   usdcTokenAddress = envUsdcAddress;
+   console.log("Using USDC token at:", usdcTokenAddress);
  }
🧹 Nitpick comments (14)
packages/enclave-contracts/tasks/ciphernode.ts (4)

85-86: Validate numeric string inputs.

The BigInt() constructor will throw if licenseBondAmount or ticketAmount contain invalid numeric strings. Consider adding try-catch around the conversion or validating the input format before conversion to provide clearer error messages.

+        let licenseBondAmountBigInt: bigint;
+        let ticketAmountBigInt: bigint;
+        
+        try {
+          licenseBondAmountBigInt = BigInt(licenseBondAmount);
+          ticketAmountBigInt = BigInt(ticketAmount);
+        } catch (error) {
+          throw new Error(
+            `Invalid numeric input. licenseBondAmount: ${licenseBondAmount}, ticketAmount: ${ticketAmount}`,
+          );
+        }
-        const licenseBondAmountBigInt = BigInt(licenseBondAmount);
-        const ticketAmountBigInt = BigInt(ticketAmount);

198-198: Validate siblings input format.

If the siblings parameter contains invalid comma-separated values (non-numeric strings), BigInt(s) will throw an unclear error. Consider validating the format and providing a helpful error message.

-      const siblingsArray = siblings.split(",").map((s: string) => BigInt(s));
+      const siblingsArray = siblings.split(",").map((s: string) => {
+        const trimmed = s.trim();
+        try {
+          return BigInt(trimmed);
+        } catch (error) {
+          throw new Error(`Invalid sibling value: "${trimmed}". Expected numeric string.`);
+        }
+      });

476-480: Consider whether second balance funding is necessary.

The impersonated account was funded with 1 ETH at lines 426-430. Unless gas consumption is expected to be extremely high, the second hardhat_setBalance call (lines 476-480) may be redundant since the account should still have sufficient ETH from the first funding.


417-510: Consider refactoring repeated impersonation pattern.

The task impersonates the ciphernode account twice (lines 421-460 and lines 471-510) with similar setup/teardown code. Consider extracting this pattern into a helper function to reduce duplication and improve maintainability.

Example:

async function impersonateAndExecute(
  connection: any,
  address: string,
  callback: (signer: any) => Promise<void>
) {
  await connection.provider.request({
    method: "hardhat_impersonateAccount",
    params: [address],
  });
  
  await connection.provider.request({
    method: "hardhat_setBalance",
    params: [address, "0x1000000000000000000000"],
  });
  
  try {
    const signer = await connection.ethers.getSigner(address);
    await callback(signer);
  } finally {
    await connection.provider.request({
      method: "hardhat_stopImpersonatingAccount",
      params: [address],
    });
  }
}
packages/enclave-contracts/test/SlashingManager.spec.ts (6)

80-209: Consider adding USDC and ticket token setup for the operator.

The setup fixture mints ENCL tokens to the operator (lines 186-190) but doesn't provide USDC or ticket tokens. Since the SlashingManager applies penalties to ticket and license balances via the BondingRegistry, tests that exercise the full slashing flow might need the operator to have:

  1. USDC balance for purchasing tickets
  2. Ticket balance for ticket penalty application
  3. Bonded ENCL tokens in the BondingRegistry

If later tests don't mock or stub these balances, execution penalties might fail silently or produce unexpected results.

Consider adding operator setup like:

 await enclaveToken.mintAllocation(
   operatorAddress,
   ethers.parseEther("2000"),
   "Test allocation",
 );
+
+// Provide USDC to operator for ticket purchases
+await usdcToken.transfer(operatorAddress, ethers.parseUnits("1000", 6));
+
+// Operator approves and bonds tokens (if needed for slashing tests)
+await usdcToken.connect(operator).approve(await ticketToken.getAddress(), ethers.parseUnits("100", 6));
+await enclaveToken.connect(operator).approve(await bondingRegistry.getAddress(), ethers.parseEther("1000"));

237-248: Improve error assertion specificity.

The test uses .to.be.rejected (line 247), which passes for any revert reason. This reduces test precision and makes it harder to catch regressions if the contract changes its error handling.

If the SlashingManager contract reverts with a custom error for zero addresses, use:

-      ).to.be.rejected;
+      ).to.be.revertedWithCustomError(SlashingManagerFactory, "ZeroAddress");

Additionally, consider adding a test for zero bondingRegistry address, since line 229-235 verify the registry is set correctly but don't test the validation.


265-267: Brittle event assertion using Object.values.

Using Object.values(policy) (line 267) to assert event arguments relies on the JavaScript object property iteration order matching the Solidity struct field order. This is fragile and will break silently if:

  1. The struct field order changes in the contract
  2. The TypeScript interface/type definition changes
  3. Object properties are inadvertently reordered

Use explicit individual arguments for clarity and maintainability:

-      .withArgs(REASON_MISBEHAVIOR, Object.values(policy));
+      .withArgs(
+        REASON_MISBEHAVIOR,
+        policy.ticketPenalty,
+        policy.licensePenalty,
+        policy.requiresProof,
+        policy.proofVerifier,
+        policy.banNode,
+        policy.appealWindow,
+        policy.enabled
+      );

This pattern should be applied to similar event assertions throughout the file (e.g., line 292).


506-520: Timestamp assertions are fragile.

Lines 518 and similar assertions throughout the file (e.g., line 560) use currentTime + 1 to predict the exact timestamp of the next block. This assumes deterministic single-block increments, which may not hold in all test environments or if Hardhat's behavior changes.

Consider using range checks or nearness assertions instead:

-        .withArgs(
-          0,
-          operatorAddress,
-          REASON_MISBEHAVIOR,
-          ethers.parseUnits("50", 6),
-          ethers.parseEther("100"),
-          currentTime + 1,
-          await slasher.getAddress(),
-        );
+        .to.emit(slashingManager, "SlashProposed");
+
+    const proposal = await slashingManager.getSlashProposal(0);
+    expect(proposal.executableAt).to.be.closeTo(currentTime, 2);

Alternatively, capture the emitted event and assert on the actual values rather than predicted ones. This pattern applies to multiple tests in the file.


679-812: Consider adding integration tests for penalty application.

The executeSlash tests verify events and state flags but don't validate that penalties are actually applied to the BondingRegistry. Since SlashingManager interacts with BondingRegistry to apply ticket and license penalties, integration tests should verify:

  1. Ticket balance decreases by the correct penalty amount
  2. License balance decreases by the correct penalty amount
  3. Slashed funds are transferred to the treasury
  4. Proper handling when operator has insufficient balance (partial vs. full execution)

Add test cases that query BondingRegistry, EnclaveToken, and EnclaveTicketToken balances before and after executeSlash to ensure end-to-end correctness:

it("should apply penalties to operator balances", async function () {
  // Setup: operator bonds tokens and purchases tickets
  // Propose and execute slash
  // Assert: balances decreased by expected amounts
  // Assert: treasury received slashed funds
});

This would catch integration issues that unit-level state checks might miss.


1077-1140: Add edge cases for ban management.

The ban management tests cover the main flows but could benefit from additional edge cases to ensure robustness:

  1. Attempting to ban a node that is already banned (idempotency/error handling)
  2. Attempting to unban a node that is not banned (should this error or no-op?)
  3. Banning a zero address (should be prevented)
it("should revert when banning zero address", async function () {
  await expect(
    slashingManager.connect(owner).banNode(ethers.ZeroAddress, reason)
  ).to.be.revertedWithCustomError(slashingManager, "ZeroAddress");
});

it("should handle banning already banned node", async function () {
  // Ban once, then try again - verify expected behavior
});

These tests would ensure the contract handles edge cases gracefully and document expected behavior for future maintainers.

packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol (1)

27-29: isEnabled always returns true—consider making it configurable for negative test cases.

The hardcoded true return prevents testing scenarios where a node is not enabled. Consider adding a mapping to track enabled state or a flag to toggle the return value for more flexible testing.

packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol (2)

80-86: Naming inconsistency: onlyOwnerOrBondingVault vs bondingRegistry.

The modifier is named onlyOwnerOrBondingVault but checks bondingRegistry and uses error NotOwnerOrBondingRegistry. Consider renaming to onlyOwnerOrBondingRegistry for consistency with the storage variable and error names.


142-156: Idempotent addCiphernode with silent early return.

The function now silently returns early if the node is already enabled (lines 143-145). This makes the operation idempotent, which is useful for bonding operations, but the silent behavior might be unexpected for direct owner calls.

Consider emitting a distinct event or logging when skipping an already-enabled node, if tracking re-add attempts is valuable for monitoring.

packages/enclave-contracts/scripts/deployEnclave.ts (1)

131-156: LGTM: Comprehensive cross-contract wiring.

The configuration section properly updates all placeholder addresses with the actual deployed contract addresses. The order appears correct:

  1. Registry contracts are configured first
  2. Token contracts are linked to registries
  3. Manager contracts are wired bidirectionally

Consider adding error handling (try-catch) around these configuration calls to provide clearer error messages if any setter reverts due to validation failures.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 513c843 and 799dbd2.

📒 Files selected for processing (10)
  • package.json (2 hunks)
  • packages/enclave-contracts/contracts/Enclave.sol (12 hunks)
  • packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol (10 hunks)
  • packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol (4 hunks)
  • packages/enclave-contracts/hardhat.config.ts (2 hunks)
  • packages/enclave-contracts/package.json (1 hunks)
  • packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts (1 hunks)
  • packages/enclave-contracts/scripts/deployEnclave.ts (3 hunks)
  • packages/enclave-contracts/tasks/ciphernode.ts (1 hunks)
  • packages/enclave-contracts/test/SlashingManager.spec.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-11T13:09:03.800Z
Learnt from: ctrlc03
PR: gnosisguild/enclave#677
File: packages/enclave-contracts/scripts/deployAndSave/naiveRegistryFilter.ts:30-31
Timestamp: 2025-09-11T13:09:03.800Z
Learning: In Hardhat v3 deployment scripts, use `(await signer.provider?.getNetwork())?.name ?? "localhost"` instead of `hre.globalOptions.network` to reliably get the network name, as `hre.globalOptions.network` can be undefined in some contexts.

Applied to files:

  • packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts
📚 Learning: 2025-09-11T13:09:03.800Z
Learnt from: ctrlc03
PR: gnosisguild/enclave#677
File: packages/enclave-contracts/scripts/deployAndSave/naiveRegistryFilter.ts:30-31
Timestamp: 2025-09-11T13:09:03.800Z
Learning: In Hardhat v3, hre.network.name is not available anymore. Use hre.globalOptions.network to get the network name instead.

Applied to files:

  • packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts
🧬 Code graph analysis (4)
packages/enclave-contracts/scripts/deployEnclave.ts (7)
packages/enclave-contracts/scripts/deployAndSave/mockStableToken.ts (1)
  • deployAndSaveMockStableToken (25-84)
packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts (1)
  • deployAndSaveEnclaveToken (28-109)
packages/enclave-contracts/scripts/deployAndSave/enclaveTicketToken.ts (1)
  • deployAndSaveEnclaveTicketToken (30-101)
packages/enclave-contracts/scripts/deployAndSave/slashingManager.ts (1)
  • deployAndSaveSlashingManager (29-95)
packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts (1)
  • deployAndSaveBondingRegistry (36-140)
packages/enclave-contracts/scripts/deployAndSave/enclave.ts (1)
  • deployAndSaveEnclave (30-108)
packages/enclave-contracts/scripts/deployAndSave/naiveRegistryFilter.ts (1)
  • deployAndSaveNaiveRegistryFilter (21-87)
packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts (1)
packages/enclave-contracts/scripts/utils.ts (2)
  • readDeploymentArgs (74-88)
  • storeDeploymentArgs (34-66)
packages/enclave-contracts/hardhat.config.ts (1)
packages/enclave-contracts/tasks/ciphernode.ts (2)
  • ciphernodeAdminAdd (310-535)
  • ciphernodeMintTokens (220-308)
packages/enclave-contracts/tasks/ciphernode.ts (3)
packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts (1)
  • deployAndSaveBondingRegistry (36-140)
packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts (1)
  • deployAndSaveEnclaveToken (28-109)
packages/enclave-contracts/scripts/deployAndSave/mockStableToken.ts (1)
  • deployAndSaveMockStableToken (25-84)
🪛 GitHub Actions: Build and Deploy Ciphernode
packages/enclave-contracts/tasks/ciphernode.ts

[error] 1-1: ERR_MODULE_NOT_FOUND Cannot find module '/build/packages/enclave-contracts/types' imported from /build/packages/enclave-contracts/tasks/ciphernode.ts. Command failed: 'cd packages/enclave-contracts && pnpm install && pnpm compile:contracts'.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build_enclave_cli
  • GitHub Check: build_e3_support_dev
  • GitHub Check: integration_prebuild
  • GitHub Check: test_net
  • GitHub Check: rust_unit
🔇 Additional comments (27)
packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts (1)

40-45: LGTM: Clear error handling for missing deployment

The logic correctly validates that a prior deployment address exists before attempting to connect, with a clear error message.

packages/enclave-contracts/tasks/ciphernode.ts (1)

220-308: LGTM - Well-structured test utility.

The ciphernodeMintTokens task provides a clean testing utility for token provisioning. Good use of TypeChain factories for proper typing (lines 266-273), clear step logging, and appropriate error handling.

packages/enclave-contracts/hardhat.config.ts (1)

16-17: LGTM - Proper task registration.

The new tasks ciphernodeAdminAdd and ciphernodeMintTokens are correctly imported and registered in the Hardhat configuration.

Also applies to: 102-103

package.json (1)

23-23: Verify the script change aligns with intended workflow.

The root ciphernode:add script now points to ciphernode:admin-add instead of the user-driven ciphernode:add task. This means developers using pnpm ciphernode:add from the root will get the admin flow (which requires admin privileges and uses impersonation) rather than the self-service flow (which requires users to have their own ENCL/USDC).

Ensure this change aligns with the intended developer experience. If both workflows should be accessible, consider:

  • Keeping this as admin-add and documenting when to use each
  • Or providing both scripts at the root level
packages/enclave-contracts/package.json (1)

153-154: LGTM - Script entries for new tasks.

The new scripts correctly map to the Hardhat tasks introduced in ciphernode.ts, following the existing pattern for task invocation.

packages/enclave-contracts/test/SlashingManager.spec.ts (3)

814-1075: Excellent coverage of the appeal lifecycle.

The appeal system tests comprehensively cover the entire lifecycle including filing, governance resolution (approve/deny), and interaction with execution. The tests properly verify:

  • Authorization checks (only operator can file)
  • Time-window constraints
  • State transitions (pending → resolved → approved/denied)
  • Execution blocking logic based on appeal state

Minor suggestion: Consider adding an edge case test for attempting to resolve an appeal that was never filed (if not already covered by the contract's validation).


1142-1198: LGTM - View functions are well tested.

The view function tests appropriately verify that getSlashPolicy and getSlashProposal return complete and accurate data structures, including:

  • All struct fields are returned with correct values
  • Computed fields like proofHash are correctly hashed
  • Invalid inputs (e.g., non-existent proposal ID) trigger appropriate errors

26-27: No action needed: network.connect() usage is consistent.
The top-level await network.connect() pattern is deliberately used across all test suites via Hardhat Ignition’s network helper and requires no changes.

packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol (7)

28-28: LGTM: Bonding registry storage and event added.

The new storage variable and event follow the existing pattern used for the enclave registry.

Also applies to: 37-37


57-62: LGTM: Comprehensive error set for bonding integration.

The new custom errors provide clear revert messages for bonding-related failures. Note that Unauthorized is quite generic—consider using more specific errors where possible to aid debugging.


98-105: LGTM: Zero-address validation added to initialization.

The new require checks prevent initializing the contract with invalid zero addresses for owner and enclave, improving contract safety.


158-169: LGTM: Enhanced access control and validation for node removal.

The modifier change allows the bonding registry to remove nodes, and the require(isEnabled(node)) check prevents removing non-existent nodes, improving safety.


177-187: LGTM: Setter validation is consistent.

Both setEnclave and setBondingRegistry now validate against zero addresses, following a consistent pattern.


225-230: LGTM: getCommittee validates non-empty committee.

The function correctly delegates to the registry filter and validates that the committee has been published by checking for non-empty nodes array (line 229). This prevents returning invalid committees.


236-238: LGTM: Simple bonding registry getter.

packages/enclave-contracts/contracts/Enclave.sol (8)

11-12: LGTM: Payment and bonding infrastructure added.

The new imports and storage variables properly set up the USDC payment system and bonding registry integration. Using SafeERC20 is a good practice.

Also applies to: 17-20, 29-29, 38-39, 62-63


94-97: LGTM: Payment-related errors defined.


108-145: LGTM: Initialization updated for new dependencies.

Constructor and initialize function properly incorporate the bonding registry and USDC token parameters.


156-157: LGTM: USDC payment collection implemented.

The payment model correctly shifts from ETH (msg.value) to USDC, using SafeERC20.safeTransferFrom for safe token transfers and recording the fee in the e3Payments mapping.

Also applies to: 223-225


353-353: Reward distribution triggered after plaintext output.

The call to _distributeRewards is appropriately placed after successful plaintext output publication. However, see the comments on _distributeRewards itself for critical issues that need addressing.


413-424: LGTM: Validation added to setBondingRegistry.

The setter now includes proper validation (lines 417-418) to prevent setting a zero address or redundantly setting the same address, addressing the concerns raised in past reviews.


426-436: LGTM: Validation added to setUsdcToken.

The setter now includes proper validation (line 430) to prevent setting a zero address or redundantly setting the same address, addressing the concerns raised in past reviews.


521-525: Placeholder pricing—implement dynamic fee calculation for production.

The function returns a hardcoded 1 USDC fee and ignores the request parameters. This is fine for testing but will need a proper pricing model for production (e.g., based on duration, threshold, computation complexity).

Consider implementing dynamic pricing based on:

  • requestParams.duration
  • requestParams.threshold (committee size affects cost)
  • E3 program complexity

Track this as a TODO if not planned for this PR.

packages/enclave-contracts/scripts/deployEnclave.ts (4)

57-82: LGTM: Token and slashing manager deployment.

The deployment sequence properly logs addresses and uses placeholder addresses (addressOne) where cross-contract dependencies will be configured later in the script.


84-129: LGTM: Core contracts deployed in correct order.

The deployment sequence handles circular dependencies by using placeholder addresses initially and configuring the actual addresses in the subsequent wiring section.


158-185: LGTM: Mock-specific configuration properly gated.

The mock deployment logic is correctly gated by shouldDeployMocks and sets up the necessary test infrastructure.


187-200: LGTM: Comprehensive deployment summary.

The deployment summary provides a clear overview of all deployed contract addresses, which is helpful for testing and verification.

Comment thread packages/enclave-contracts/contracts/registry/CiphernodeRegistryOwnable.sol Outdated
Comment thread packages/enclave-contracts/contracts/test/MockCiphernodeRegistry.sol Outdated
Comment thread packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts Outdated
Comment thread packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts Outdated
Comment thread packages/enclave-contracts/tasks/ciphernode.ts Outdated
Comment thread packages/enclave-contracts/tasks/ciphernode.ts
Comment thread packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts
@vercel vercel Bot temporarily deployed to Preview – crisp September 30, 2025 12:55 Inactive
@vercel vercel Bot temporarily deployed to Preview – enclave-docs September 30, 2025 12:55 Inactive

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
packages/enclave-contracts/tasks/ciphernode.ts (1)

106-106: Fix typo in console log.

The message reads "Licensed bonded" but should be "License bonded".

Apply this diff:

-          `Licensed bonded: ${ethers.formatEther(licenseBondAmountBigInt)} ENCL`,
+          `License bonded: ${ethers.formatEther(licenseBondAmountBigInt)} ENCL`,
🧹 Nitpick comments (1)
packages/enclave-contracts/tasks/ciphernode.ts (1)

456-458: Consider clarifying variable naming.

The variable ciphernodeSigner2 suggests this is a second signer, but it's actually a fresh signer instance for the same address after re-impersonating. Consider naming it to reflect the re-impersonation context (e.g., reimpersonatedCiphernodeSigner or similar) for better clarity.

Example:

-        const ciphernodeSigner2 = await ethers.getSigner(ciphernodeAddress);
-        const bondingRegistryAsCiphernode2 =
-          bondingRegistry.connect(ciphernodeSigner2);
+        const reimpersonatedCiphernodeSigner = await ethers.getSigner(ciphernodeAddress);
+        const bondingRegistryReimpersonated =
+          bondingRegistry.connect(reimpersonatedCiphernodeSigner);

And update the subsequent usage:

-        const mockUSDCAsCiphernode = mockUSDC.connect(ciphernodeSigner2);
+        const mockUSDCAsCiphernode = mockUSDC.connect(reimpersonatedCiphernodeSigner);
-          await bondingRegistryAsCiphernode2.addTicketBalance(ticketAmountWei);
+          await bondingRegistryReimpersonated.addTicketBalance(ticketAmountWei);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 799dbd2 and 31cc351.

📒 Files selected for processing (1)
  • packages/enclave-contracts/tasks/ciphernode.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/enclave-contracts/tasks/ciphernode.ts (4)
packages/enclave-contracts/scripts/deployAndSave/bondingRegistry.ts (1)
  • deployAndSaveBondingRegistry (36-140)
packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts (1)
  • deployAndSaveEnclaveToken (28-109)
packages/enclave-contracts/scripts/deployAndSave/enclaveTicketToken.ts (1)
  • deployAndSaveEnclaveTicketToken (30-101)
packages/enclave-contracts/scripts/deployAndSave/mockStableToken.ts (1)
  • deployAndSaveMockStableToken (25-84)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: build_sdk
  • GitHub Check: integration_prebuild
  • GitHub Check: build_enclave_cli
  • GitHub Check: test_net
  • GitHub Check: test_contracts
  • GitHub Check: rust_unit
  • GitHub Check: Build & Push Image
🔇 Additional comments (6)
packages/enclave-contracts/tasks/ciphernode.ts (6)

1-10: LGTM!

The imports are appropriate for the task file functionality.


11-155: Well-structured registration flow with comprehensive error handling.

The ciphernodeAdd task correctly implements the multi-step registration process with appropriate balance checks, approvals, and detailed logging at each step.


157-210: LGTM!

The deregistration flow is properly implemented with clear user messaging about the exit queue mechanism.


212-293: LGTM!

The token minting task is correctly implemented for testing purposes with appropriate validation and balance reporting.


295-505: Well-implemented admin registration with proper impersonation handling.

The task correctly orchestrates the complex flow of admin-initiated registration using Hardhat's account impersonation for testing. The two separate impersonation sessions are necessary because impersonation is stopped between operations.


507-539: LGTM!

The siblings generation task correctly builds a Lean IMT and generates the merkle proof for the specified ciphernode address.

@vercel vercel Bot temporarily deployed to Preview – crisp September 30, 2025 13:00 Inactive
@vercel vercel Bot temporarily deployed to Preview – enclave-docs September 30, 2025 13:00 Inactive

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts (1)

364-373: Fix inconsistent method name usage.

Line 367 correctly uses the new isEnabled method, but line 371 still references the old isCiphernodeEligible method. This inconsistency will cause test failures if the old method has been removed.

Apply this fix:

-      expect(await registry.isCiphernodeEligible(AddressThree)).to.be.false;
+      expect(await registry.isEnabled(AddressThree)).to.be.false;
♻️ Duplicate comments (2)
packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts (1)

468-476: Fix invalid revert assertion syntax.

Line 475 uses .to.be.revert(ethers), which is not valid Chai-Ethers syntax. According to Hardhat v3 conventions, the correct syntax is .to.be.reverted (without parameters for general reverts) or .to.be.revertedWithCustomError() for specific errors.

Based on learnings about Hardhat v3 chai matchers.

Apply this fix:

-      ).to.be.revert(ethers);
+      ).to.be.reverted;

Or more specifically, to match the access control pattern used elsewhere in the test:

-      ).to.be.revert(ethers);
+      ).to.be.revertedWithCustomError(slashingManager, "AccessControlUnauthorizedAccount");
packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts (1)

57-60: Use hre directly and key by chainId instead of network name.

The non-standard hre.network.connect() remains. Access ignition and ethers directly from hre and use numeric chainId as the storage key for deterministic deployments across providers.

Based on learnings.

Apply this diff:

-  const { ignition, ethers } = await hre.network.connect();
+  const { ignition, ethers } = hre;
   const [signer] = await ethers.getSigners();
-  const chain = (await signer.provider?.getNetwork())?.name ?? "localhost";
+  const { chainId } = await ethers.provider.getNetwork();
+  const chain = String(chainId);
🧹 Nitpick comments (2)
packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts (1)

237-248: Verify the zero-address admin rejection.

The test expects deployment to be rejected when admin is ZeroAddress, but uses a generic .to.be.rejected assertion. This doesn't validate the specific error that should be thrown by the constructor.

Consider strengthening the assertion to check for the specific error:

-      ).to.be.rejected;
+      ).to.be.revertedWithCustomError(slashingManager, "ZeroAddress");

This will ensure the constructor fails for the correct reason and improve test precision.

packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts (1)

375-384: Consider removing duplicate test block.

Lines 364-373 test isCiphernodeEligible() (which should be migrated to isEnabled), and lines 375-384 test isEnabled() with identical logic. These test blocks appear to be duplicates with one using the old method name.

Consider removing the isCiphernodeEligible() describe block (lines 364-373) entirely, as it's superseded by the isEnabled() tests at lines 375-384. This will eliminate redundancy and ensure tests only reference the current API.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 31cc351 and 6d39612.

📒 Files selected for processing (5)
  • packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts (1 hunks)
  • packages/enclave-contracts/test/Registry/BondingRegistry.spec.ts (1 hunks)
  • packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts (3 hunks)
  • packages/enclave-contracts/test/Registry/NaiveRegistryFilter.spec.ts (1 hunks)
  • packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-09-11T12:56:39.601Z
Learnt from: ctrlc03
PR: gnosisguild/enclave#677
File: packages/enclave-contracts/test/Enclave.spec.ts:804-807
Timestamp: 2025-09-11T12:56:39.601Z
Learning: In Hardhat v3, the chai matchers syntax changed: `.to.not.be.revert(ethers)` is the correct new syntax, replacing the old `.to.not.be.reverted` (without parameters). The ethers object must be passed as a parameter to the revert/reverted matchers in Hardhat v3.

Applied to files:

  • packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts
📚 Learning: 2025-09-11T12:56:39.601Z
Learnt from: ctrlc03
PR: gnosisguild/enclave#677
File: packages/enclave-contracts/test/Enclave.spec.ts:804-807
Timestamp: 2025-09-11T12:56:39.601Z
Learning: In Hardhat v3, the chai matchers syntax changed to support multiple network connections: `.to.not.be.revert(ethers)` is the correct new syntax, replacing the old `.to.not.be.reverted` (without parameters). The ethers object must be passed as a parameter to the revert/reverted matchers in Hardhat v3. Similarly, `.revertedWithoutReason(ethers)` replaces `.revertedWithoutReason`.

Applied to files:

  • packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts
📚 Learning: 2025-09-11T13:09:03.800Z
Learnt from: ctrlc03
PR: gnosisguild/enclave#677
File: packages/enclave-contracts/scripts/deployAndSave/naiveRegistryFilter.ts:30-31
Timestamp: 2025-09-11T13:09:03.800Z
Learning: In Hardhat v3 deployment scripts, use `(await signer.provider?.getNetwork())?.name ?? "localhost"` instead of `hre.globalOptions.network` to reliably get the network name, as `hre.globalOptions.network` can be undefined in some contexts.

Applied to files:

  • packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts
📚 Learning: 2025-09-11T13:09:03.800Z
Learnt from: ctrlc03
PR: gnosisguild/enclave#677
File: packages/enclave-contracts/scripts/deployAndSave/naiveRegistryFilter.ts:30-31
Timestamp: 2025-09-11T13:09:03.800Z
Learning: In Hardhat v3, hre.network.name is not available anymore. Use hre.globalOptions.network to get the network name instead.

Applied to files:

  • packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts
🧬 Code graph analysis (1)
packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts (1)
packages/enclave-contracts/scripts/utils.ts (2)
  • readDeploymentArgs (74-88)
  • storeDeploymentArgs (34-66)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: integration_prebuild
  • GitHub Check: test_contracts
  • GitHub Check: test_net
  • GitHub Check: rust_unit
  • GitHub Check: build_sdk
  • GitHub Check: build_e3_support_dev
  • GitHub Check: build_enclave_cli
  • GitHub Check: Build & Push Image
🔇 Additional comments (69)
packages/enclave-contracts/test/Slashing/SlashingManager.spec.ts (42)

41-78: LGTM: Well-structured policy setup helper.

The setupPolicies helper function provides clean test data for three different policy types (proof-based, evidence-based, and ban policy), which is reused across multiple test cases. The policy configurations are realistic and cover key variations.


80-209: LGTM: Comprehensive test fixture setup.

The setup fixture properly initializes all required contracts (USDC, EnclaveToken, TicketToken, BondingRegistry, SlashingManager, MockVerifier) with appropriate wiring and initial state. The use of Hardhat Ignition modules for deployment and proper contract interconnection demonstrates good integration testing practices.


211-227: LGTM: Constructor tests verify role assignment.

Tests correctly verify that both DEFAULT_ADMIN_ROLE and GOVERNANCE_ROLE are assigned to the owner during initialization.


252-275: LGTM: Policy setting with proper event verification.

The test correctly validates policy creation with event emission and subsequent retrieval. Using Object.values(policy) for event args is a clean pattern.


318-334: LGTM: Zero-reason validation.

Test correctly verifies that policies cannot be created with a zero reason identifier.


336-352: LGTM: Disabled policy rejection.

Test correctly verifies that disabled policies cannot be set, enforcing that only enabled policies can be created.


354-370: LGTM: No-penalty policy rejection.

Test correctly validates that at least one penalty type (ticket or license) must be non-zero.


372-388: LGTM: Proof-without-verifier validation.

Test correctly enforces that if requiresProof is true, a verifier address must be provided.


390-406: LGTM: Proof with appeal window validation.

Test correctly validates that proof-based policies (which are immediately executable) cannot have an appeal window.


408-424: LGTM: Evidence without appeal window validation.

Test correctly enforces that evidence-based policies (without immediate proof verification) must have an appeal window.


428-446: LGTM: Role management tests.

Tests correctly verify adding and removing slasher roles with proper role checks.


448-466: LGTM: Verifier role management.

Tests correctly verify adding and removing verifier roles.


478-484: LGTM: Zero-address slasher validation.

Test correctly prevents adding zero address as a slasher.


488-527: LGTM: Proof-based slash proposal.

Test comprehensively validates the proposal flow with proof verification, including event emission with correct parameters and state checks.


529-569: LGTM: Evidence-based slash proposal.

Test correctly validates proposals without proof verification, ensuring the appeal window is properly set and proofVerified is false.


571-582: LGTM: Authorization check for proposals.

Test correctly verifies that only slashers can propose slashes.


584-594: LGTM: Zero-address operator validation.

Test correctly prevents proposing slashes against zero address.


596-607: LGTM: Disabled reason validation.

Test correctly prevents proposing slashes for reasons without configured policies.


609-631: LGTM: Proof requirement validation.

Test correctly enforces that empty proof cannot be provided when policy requires proof.


633-676: LGTM: Proposal counter test.

Test correctly verifies that totalProposals increments with each proposal.


680-715: LGTM: Immediate execution with proof.

Test correctly validates that proof-verified proposals can be executed immediately with proper event emission and state updates.


717-741: LGTM: Appeal window enforcement.

Test correctly validates that execution is blocked during the appeal window and allowed after it expires.


743-765: LGTM: Node banning on execution.

Test correctly verifies that nodes are banned when the policy requires it, with proper event emission.


767-773: LGTM: Non-existent proposal validation.

Test correctly rejects execution of non-existent proposals.


775-790: LGTM: Double execution prevention.

Test correctly prevents executing the same proposal twice.


792-811: LGTM: Execution authorization.

Test correctly verifies that only slashers can execute proposals.


815-842: LGTM: Appeal filing.

Test correctly validates that operators can file appeals with proper event emission and state updates.


844-866: LGTM: Appeal authorization.

Test correctly verifies that only the affected operator can file an appeal.


868-892: LGTM: Appeal window expiry.

Test correctly prevents filing appeals after the appeal window has expired.


894-918: LGTM: Duplicate appeal prevention.

Test correctly prevents filing multiple appeals for the same proposal.


920-958: LGTM: Appeal approval resolution.

Test correctly validates governance's ability to approve appeals with proper event emission and state tracking.


960-988: LGTM: Appeal denial resolution.

Test correctly validates governance's ability to deny appeals.


990-1015: LGTM: Pending appeal blocks execution.

Test correctly verifies that unresolved appeals block execution even after the appeal window expires.


1017-1044: LGTM: Approved appeal blocks execution.

Test correctly verifies that approved appeals permanently block execution.


1046-1074: LGTM: Denied appeal allows execution.

Test correctly verifies that execution can proceed after an appeal is denied and the window expires.


1078-1091: LGTM: Manual node banning.

Test correctly validates governance's ability to manually ban nodes with proper event emission.


1093-1107: LGTM: Node unbanning.

Test correctly validates governance's ability to unban nodes.


1109-1118: LGTM: Ban authorization.

Test correctly verifies that only governance can ban nodes.


1120-1139: LGTM: Banned node protection.

Test correctly prevents proposing slashes against already-banned nodes.


1143-1167: LGTM: Policy retrieval.

Test correctly validates that stored policies can be retrieved with all fields intact.


1169-1188: LGTM: Proposal retrieval.

Test correctly validates that stored proposals can be retrieved with all fields intact, including the proof hash.


1190-1196: LGTM: Invalid proposal query.

Test correctly rejects queries for non-existent proposals.

packages/enclave-contracts/test/Registry/CiphernodeRegistryOwnable.spec.ts (3)

240-242: LGTM: Error type updated correctly.

The test now correctly expects NotOwnerOrBondingRegistry error when an unauthorized caller attempts to add a ciphernode, aligning with the updated authorization model.


244-247: LGTM: Method renamed to isEnabled.

The test correctly uses the renamed isEnabled method instead of the deprecated isCiphernodeEligible.


275-277: LGTM: Consistent error handling for removeCiphernode.

The test correctly expects NotOwnerOrBondingRegistry error for unauthorized removal attempts.

packages/enclave-contracts/test/Registry/NaiveRegistryFilter.spec.ts (7)

106-122: LGTM: Test updated for new three-argument API.

The test correctly reflects the updated API where:

  1. Registry is set via setRegistry
  2. Committee is requested via registry.requestCommittee(e3Id, filterAddress, threshold) with three arguments
  3. Committee is published via filter.publishCommittee(e3Id, nodes, publicKey)
  4. Committee data is retrieved and validated

This matches the interface changes described in the AI summary.


134-146: LGTM: Owner authorization check.

Test correctly verifies that only the owner can publish committees.


147-168: LGTM: Duplicate publication prevention.

Test correctly prevents publishing the same committee twice, following the new API flow.


169-185: LGTM: Node storage validation.

Test correctly validates that committee node addresses are stored and retrievable.


186-202: LGTM: Public key storage validation.

Test correctly validates that the committee public key is hashed and stored properly.


203-237: LGTM: E3-specific committee data.

Tests correctly verify that committees are stored and retrievable per E3 ID with correct nodes and public keys.


255-277: LGTM: Complete committee retrieval.

Test correctly validates that all committee fields (threshold, nodes, publicKey) are properly stored and retrieved for a given E3 ID.

packages/enclave-contracts/test/Registry/BondingRegistry.spec.ts (13)

1-31: LGTM! Well-structured test imports and setup.

The imports, hash helper using poseidon2, and reason constants are correctly configured for the BondingRegistry test suite.


202-226: LGTM! Constructor tests verify all initial parameters.

The test correctly validates that all configuration values are set during initialization.


228-303: LGTM! Comprehensive bondLicense tests.

The tests cover successful bonding, zero amount validation, exit-in-progress blocking, and accumulation behavior. Event assertions and balance checks are properly implemented.


305-371: LGTM! Complete unbondLicense test coverage.

The tests properly validate unbonding flows including zero amount checks, insufficient balance handling, and exit queue tracking.


373-441: LGTM! Thorough registerOperator tests.

The tests validate the registration flow including license requirements, duplicate prevention, and exit clearing during re-registration. The distinction between isRegistered and isActive states is properly tested.


443-507: LGTM! Complete deregisterOperator test coverage.

The tests properly validate deregistration flows including state transitions, exit queuing, and timestamp calculations. The verification that both ticket and license balances are queued for exit is important.


509-603: LGTM! Complete addTicketBalance test coverage.

The tests validate ticket balance additions including activation threshold behavior, registration requirements, and zero amount checks. The activation event tracking is properly tested.


605-728: LGTM! Thorough removeTicketBalance tests.

The tests cover ticket removal including exit queuing, deactivation when falling below minimum balance, and insufficient balance handling. The state transition testing is well-implemented.


730-846: LGTM! Comprehensive claimExits tests.

The tests validate exit claiming including time-based restrictions, full and partial claims, and balance updates. The partial claim test properly verifies remaining pending exits are tracked correctly.


848-876: LGTM! Proper isLicensed threshold tests.

The tests correctly validate the license bond threshold at 80% (licenseActiveBps), testing both the minimum required amount and amounts just below the threshold.


878-913: LGTM! Correct availableTickets calculation tests.

The tests validate ticket calculation logic including division by ticket price and zero price handling.


915-1016: LGTM! Complete admin function tests.

The tests thoroughly validate all admin functions including parameter validation (zero checks, bounds checks), event emissions, and ownership restrictions. The basis points validation correctly enforces the 0-10000 range.


1018-1158: LGTM! Excellent edge case and lifecycle tests.

The tests validate complex scenarios including:

  • Operator state changes due to license reduction
  • Multiple operators with different states operating independently
  • Complete operator lifecycle from bonding through exit and re-registration

These integration-level tests ensure the contract handles real-world operator flows correctly.

packages/enclave-contracts/scripts/deployAndSave/enclaveToken.ts (4)

23-44: Good refactor: duplication eliminated.

The helper function successfully addresses the duplicate transfer-restriction logic flagged in previous reviews. The implementation correctly checks the current state before toggling and handles errors gracefully.


61-77: LGTM: Reuse logic is sound.

The path for reusing existing deployments correctly validates the presence of an address, connects to the contract, and uses the extracted helper for transfer restrictions.


79-103: LGTM: Deployment and persistence logic is correct.

The new deployment path properly uses Ignition modules, captures deployment metadata (address, block number, constructor args), and persists the data for future reuse.


105-113: LGTM: Final setup is correct.

The contract connection and transfer restriction handling (via the helper) are correctly implemented before returning the deployed contract.

Comment thread packages/enclave-contracts/test/Registry/BondingRegistry.spec.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
examples/CRISP/server/.env.example (1)

13-16: Address updates look good; E3_PROGRAM_ADDRESS matches client config.

The updated contract addresses align with the new deployments. Confirmed that E3_PROGRAM_ADDRESS (0x1613...) is consistent with the client configuration.

Optionally, consider standardizing the quote style for consistency:

-ENCLAVE_ADDRESS="0x8A791620dd6260079BF849Dc5567aDC3F2FdC318"
-CIPHERNODE_REGISTRY_ADDRESS="0xa513E6E4b8f2a923D98304ec87F64353C4D5C853"
-NAIVE_REGISTRY_FILTER_ADDRESS="0x610178dA211FEF7D417bC0e6FeD39F05609AD788"
-E3_PROGRAM_ADDRESS="0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8" # CRISPProgram Contract Address
+ENCLAVE_ADDRESS=0x8A791620dd6260079BF849Dc5567aDC3F2FdC318
+CIPHERNODE_REGISTRY_ADDRESS=0xa513E6E4b8f2a923D98304ec87F64353C4D5C853
+NAIVE_REGISTRY_FILTER_ADDRESS=0x610178dA211FEF7D417bC0e6FeD39F05609AD788
+E3_PROGRAM_ADDRESS=0x1613beB3B2C4f22Ee086B2b38C1476A3cE7f78E8 # CRISPProgram Contract Address
examples/CRISP/enclave.config.yaml (1)

5-7: Consider adding e3_program address for consistency.

The templates/default/enclave.config.yaml includes an e3_program contract address (line 5), but this CRISP example configuration does not. If the CRISP example is meant to be a full-featured configuration template, consider whether e3_program should also be included here.

examples/CRISP/deploy/config.toml (1)

17-17: enclaveAddress update is consistent and correct.

The new address 0x8A791620dd6260079BF849Dc5567aDC3F2FdC318 is used uniformly across deployment configs, templates, and integration tests. No mismatches detected. Consider refactoring to programmatically read the deployed address from your artifacts in the future to avoid manual hardcoding.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3880dda and c75bdc0.

📒 Files selected for processing (8)
  • examples/CRISP/Readme.md (4 hunks)
  • examples/CRISP/client/.env.example (1 hunks)
  • examples/CRISP/deploy/config.toml (1 hunks)
  • examples/CRISP/enclave.config.yaml (1 hunks)
  • examples/CRISP/server/.env.example (1 hunks)
  • templates/default/enclave.config.yaml (1 hunks)
  • tests/integration/enclave.config.yaml (1 hunks)
  • tests/integration/fns.sh (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • tests/integration/enclave.config.yaml
🧰 Additional context used
🪛 dotenv-linter (3.3.0)
examples/CRISP/server/.env.example

[warning] 13-13: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 14-14: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 14-14: [UnorderedKey] The CIPHERNODE_REGISTRY_ADDRESS key should go before the ENCLAVE_ADDRESS key

(UnorderedKey)


[warning] 15-15: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 16-16: [UnorderedKey] The E3_PROGRAM_ADDRESS key should go before the ENCLAVE_ADDRESS key

(UnorderedKey)

examples/CRISP/client/.env.example

[warning] 4-4: [UnorderedKey] The VITE_E3_PROGRAM_ADDRESS key should go before the VITE_ENCLAVE_API key

(UnorderedKey)


[warning] 4-4: [ValueWithoutQuotes] This value needs to be surrounded in quotes

(ValueWithoutQuotes)


[warning] 5-5: [UnorderedKey] The VITE_SEMAPHORE_ADDRESS key should go before the VITE_TWITTER_SERVERLESS_API key

(UnorderedKey)

🪛 Shellcheck (0.11.0)
tests/integration/fns.sh

[warning] 24-24: ENCLAVE_CONTRACT appears unused. Verify use (or export if used externally).

(SC2034)


[warning] 25-25: REGISTRY_CONTRACT appears unused. Verify use (or export if used externally).

(SC2034)


[warning] 26-26: REGISTRY_FILTER_CONTRACT appears unused. Verify use (or export if used externally).

(SC2034)


[warning] 27-27: INPUT_VALIDATOR_CONTRACT appears unused. Verify use (or export if used externally).

(SC2034)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Build & Push Image
  • GitHub Check: build_sdk
  • GitHub Check: rust_unit
  • GitHub Check: test_net
  • GitHub Check: integration_prebuild
  • GitHub Check: build_enclave_cli
  • GitHub Check: test_contracts
  • GitHub Check: Build & Push Image
🔇 Additional comments (6)
examples/CRISP/client/.env.example (1)

4-5: LGTM! Address updates are consistent with new deployments.

The updated default addresses for E3_PROGRAM and SEMAPHORE contracts align with the new economic contracts introduced in this PR.

examples/CRISP/server/.env.example (1)

20-27: LGTM! New E3 configuration variables have sensible defaults.

The new E3 configuration extends functionality for thresholds, duration, and compute provider settings. All values are appropriate for local testing:

  • Threshold range (1-2) suits minimal node setups
  • Batch size of 4 satisfies the power-of-2 requirement
  • RISC0 provider with serial processing is a safe default
examples/CRISP/enclave.config.yaml (1)

5-7: Contract addresses match template configuration.

The addresses for enclave, ciphernode_registry, and filter_registry correctly match those in templates/default/enclave.config.yaml, ensuring consistency across configurations.

templates/default/enclave.config.yaml (1)

5-8: No address mismatches detected.

The enclave, ciphernode_registry, and filter_registry values in templates/default/enclave.config.yaml align exactly with those in tests/integration/enclave.config.yaml and examples/CRISP/enclave.config.yaml.

examples/CRISP/Readme.md (2)

25-25: LGTM! Formatting improvements enhance readability.

The added blank lines improve the visual structure of the documentation without altering any instructions or behavior.

Also applies to: 105-105


168-170: LGTM! Documentation formatting improved.

The section header and note formatting changes improve readability without altering the instructions.

Comment thread examples/CRISP/Readme.md
Comment thread tests/integration/fns.sh Outdated

@ctrlc03 ctrlc03 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work :) left some comment, will give it another pass tomorrow as there's some parts of the contracts I wanted to spend some more time on

Comment thread packages/enclave-contracts/contracts/Enclave.sol Outdated
Comment thread packages/enclave-contracts/contracts/Enclave.sol
Comment thread packages/enclave-contracts/contracts/Enclave.sol Outdated
Comment thread packages/enclave-contracts/contracts/Enclave.sol
Comment thread packages/enclave-contracts/contracts/registry/NaiveRegistryFilter.sol Outdated
Comment thread packages/enclave-contracts/contracts/token/EnclaveTicketToken.sol Outdated
Comment thread packages/enclave-contracts/contracts/token/EnclaveToken.sol Outdated
Comment thread packages/enclave-contracts/contracts/token/EnclaveToken.sol
Comment thread packages/enclave-contracts/tasks/ciphernode.ts Outdated
Comment thread packages/enclave-contracts/contracts/Enclave.sol Outdated
Comment thread packages/enclave-contracts/contracts/Enclave.sol Outdated
Comment thread packages/enclave-contracts/contracts/Enclave.sol
Comment thread packages/enclave-contracts/contracts/interfaces/ICiphernodeRegistry.sol Outdated
@vercel vercel Bot temporarily deployed to Preview – crisp October 1, 2025 11:34 Inactive

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
examples/CRISP/client/.env.example (1)

4-4: Address update aligns with PR objectives; minor linting warnings to address.

The E3 program address update reflects the new economic contract deployment from the PR. However, dotenv-linter flags two minor issues:

  1. Key ordering: VITE_E3_PROGRAM_ADDRESS should precede VITE_ENCLAVE_API alphabetically.
  2. Value quoting: The address value should be quoted for consistency.

If your project enforces these linting standards, apply this diff:

-VITE_E3_PROGRAM_ADDRESS=0xc5a5C42992dECbae36851359345FE25997F5C42d # Default E3 program address from hardhat
+VITE_E3_PROGRAM_ADDRESS="0xc5a5C42992dECbae36851359345FE25997F5C42d" # Default E3 program address from hardhat

And reorder the keys alphabetically if required by your project standards.

examples/CRISP/server/.env.example (1)

15-19: Remove quote characters and sort keys alphabetically for consistency.

The linter flags unnecessary quote characters on lines 16–17 and suggests alphabetizing keys. While quotes are functionally acceptable in env files, removing them and reordering alphabetically aligns with dotenv style conventions.

Apply this refactor to clean up the address configuration:

- # Based on Default Anvil Deployments (Only for testing)
- ENCLAVE_ADDRESS="0x8A791620dd6260079BF849Dc5567aDC3F2FdC318"
- CIPHERNODE_REGISTRY_ADDRESS="0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6"
- E3_PROGRAM_ADDRESS="0xc5a5C42992dECbae36851359345FE25997F5C42d" # CRISPProgram Contract Address
- FEE_TOKEN_ADDRESS="0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0"
+ # Based on Default Anvil Deployments (Only for testing)
+ CIPHERNODE_REGISTRY_ADDRESS=0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6
+ E3_PROGRAM_ADDRESS=0xc5a5C42992dECbae36851359345FE25997F5C42d # CRISPProgram Contract Address
+ ENCLAVE_ADDRESS=0x8A791620dd6260079BF849Dc5567aDC3F2FdC318
+ FEE_TOKEN_ADDRESS=0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0
examples/CRISP/client/libs/wasm/pkg/crisp_worker.js (1)

34-41: Consider renaming vote to encryptedData for clarity.

The field encryptedVote.vote contains result.encryptedData, which may be clearer if named encryptedVote.encryptedData or simply encryptedVote.data to better reflect its contents.

         self.postMessage({
           type: 'encrypt_vote',
           success: true,
           encryptedVote: {
-            vote: result.encryptedData,
+            encryptedData: result.encryptedData,
             proofData: result.proof,
           },
         })
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d1385b2 and 54e295b.

📒 Files selected for processing (4)
  • examples/CRISP/client/.env.example (1 hunks)
  • examples/CRISP/client/libs/wasm/pkg/crisp_worker.js (1 hunks)
  • examples/CRISP/server/.env.example (1 hunks)
  • examples/CRISP/server/src/server/indexer.rs (6 hunks)
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2024-10-08T19:45:18.209Z
Learnt from: ryardley
PR: gnosisguild/enclave#107
File: packages/ciphernode/sortition/src/distance.rs:1-1
Timestamp: 2024-10-08T19:45:18.209Z
Learning: In `packages/ciphernode/core/src/events.rs`, the import statements use the correct and updated `alloy::primitives` module.

Applied to files:

  • examples/CRISP/server/src/server/indexer.rs
📚 Learning: 2024-11-05T03:56:22.254Z
Learnt from: ryardley
PR: gnosisguild/enclave#170
File: packages/ciphernode/evm/tests/evm_reader.rs:63-63
Timestamp: 2024-11-05T03:56:22.254Z
Learning: In the Rust test function `test_logs` in `packages/ciphernode/evm/tests/evm_reader.rs`, a sleep duration of 1 millisecond is sufficient for reliable event processing, even in CI environments.

Applied to files:

  • examples/CRISP/server/src/server/indexer.rs
📚 Learning: 2024-10-22T03:30:03.606Z
Learnt from: ryardley
PR: gnosisguild/enclave#145
File: packages/ciphernode/enclave_node/src/shutdown.rs:21-26
Timestamp: 2024-10-22T03:30:03.606Z
Learning: In `packages/ciphernode/enclave_node/src/shutdown.rs`, the sleep duration after aborting the task is necessary to wait for other processes to complete during shutdown.

Applied to files:

  • examples/CRISP/server/src/server/indexer.rs
📚 Learning: 2025-09-19T11:16:53.825Z
Learnt from: cedoor
PR: gnosisguild/enclave#752
File: packages/enclave-contracts/contracts/Enclave.sol:15-17
Timestamp: 2025-09-19T11:16:53.825Z
Learning: The Enclave contract in the gnosisguild/enclave repository has not been deployed yet as of September 2025, so storage layout considerations for upgradeable contracts don't apply to current changes.

Applied to files:

  • examples/CRISP/server/.env.example
📚 Learning: 2025-10-10T12:56:40.538Z
Learnt from: 0xjei
PR: gnosisguild/enclave#830
File: templates/default/README.md:123-128
Timestamp: 2025-10-10T12:56:40.538Z
Learning: In the Enclave repository, the hard-coded Hardhat development private key `0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80` is acceptable in template README files and documentation for local testing/interaction purposes.

Applied to files:

  • examples/CRISP/server/.env.example
  • examples/CRISP/client/.env.example
🧬 Code graph analysis (2)
examples/CRISP/server/src/server/indexer.rs (1)
crates/evm-helpers/src/listener.rs (2)
  • contract_address (99-99)
  • create_contract_listener (97-104)
examples/CRISP/client/libs/wasm/pkg/crisp_worker.js (1)
packages/enclave-sdk/src/enclave-sdk.ts (1)
  • EnclaveSDK (48-614)
🪛 dotenv-linter (4.0.0)
examples/CRISP/server/.env.example

[warning] 16-16: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 17-17: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 17-17: [UnorderedKey] The CIPHERNODE_REGISTRY_ADDRESS key should go before the ENCLAVE_ADDRESS key

(UnorderedKey)


[warning] 18-18: [UnorderedKey] The E3_PROGRAM_ADDRESS key should go before the ENCLAVE_ADDRESS key

(UnorderedKey)

examples/CRISP/client/.env.example

[warning] 4-4: [UnorderedKey] The VITE_E3_PROGRAM_ADDRESS key should go before the VITE_ENCLAVE_API key

(UnorderedKey)


[warning] 4-4: [ValueWithoutQuotes] This value needs to be surrounded in quotes

(ValueWithoutQuotes)

🔇 Additional comments (10)
examples/CRISP/server/.env.example (1)

29-30: LGTM — sensible E3 compute provider defaults.

The new E3_COMPUTE_PROVIDER_PARALLEL and E3_COMPUTE_PROVIDER_BATCH_SIZE settings map to the PR's E3 compute provider configuration. The batch size of 4 is a valid power of 2, and the parallel flag defaults to false for safe local testing. The inline comment about batch size being a power of 2 is helpful for developers.

examples/CRISP/server/src/server/indexer.rs (5)

15-15: LGTM! Import changes support RPC-based timing.

The addition of Provider and ProviderBuilder enables RPC time retrieval, while replacing Instant with tokio::time::sleep aligns with the async event-driven architecture.

Also applies to: 35-36


331-339: Consider block timestamp granularity for timing-sensitive operations.

The function correctly retrieves on-chain time, which is appropriate for blockchain coordination. However, note that:

  • Block timestamps have limited granularity (updated only when new blocks are produced), potentially lagging real time by several seconds.
  • A new provider connection is created on each call. For high-frequency usage, consider caching the provider instance.

For the current use case (activation and expiration timing), this approach is acceptable and correctly aligns with on-chain state.


157-168: LGTM! Expiration timing correctly uses RPC-based time.

The wait duration calculation properly handles both future expirations and already-expired E3s. Using RPC time ensures alignment with on-chain state, which is appropriate for this blockchain-coordinated workflow.


299-319: Excellent! Activation timing now correctly respects the start window.

This implementation properly addresses the previous review concern (✅ commit 9366fe6) by calculating the wait duration based on e3.startWindow[0] and RPC time, replacing the problematic fixed 1-second sleep. The activation will now occur at the correct time, preventing premature activation attempts.

The idempotency check (lines 294-297) and detailed logging ensure robust behavior.


344-344: LGTM! Parameter rename improves clarity.

The rename from registry_filter_address to registry_address better reflects the parameter's purpose and usage.

Also applies to: 367-367

examples/CRISP/client/libs/wasm/pkg/crisp_worker.js (4)

7-8: LGTM: Import statements are correct.

The imports correctly reference the updated SDK package and circuit configuration.


32-32: LGTM: Encryption call correctly uses updated SDK interface.

The call to encryptNumberAndGenProof correctly passes the required parameters and the result structure aligns with the SDK's VerifiableEncryptionResult type.


42-44: LGTM: Error handling is appropriate.

The error handling correctly reports failures back to the main thread with clear error messages.


47-49: LGTM: Default case appropriately handles unknown message types.

The console error for unknown message types provides helpful debugging information.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
examples/CRISP/client/libs/wasm/pkg/crisp_worker.js (1)

20-23: Contract address duplication issue persists.

Both enclave and ciphernodeRegistry share the same address (0xc6e7DF5E7b4f2A278906862b61205850344D4e7d). Even in example code, distinct contracts should have unique addresses to avoid misleading developers.

Apply this diff to use distinct placeholder addresses:

         contracts: {
-          enclave: '0xc6e7DF5E7b4f2A278906862b61205850344D4e7d',
-          ciphernodeRegistry: '0xc6e7DF5E7b4f2A278906862b61205850344D4e7d',
+          enclave: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
+          ciphernodeRegistry: '0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512',
         },
🧹 Nitpick comments (1)
examples/CRISP/client/libs/wasm/pkg/crisp_worker.js (1)

27-27: Consider adding a comment clarifying this is for local development.

The hardcoded Anvil private key is appropriate for example code. However, adding a brief comment clarifying this is only for local testing would make the intent more explicit.

Apply this diff:

-          privateKey: '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
+          privateKey: '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80', // Anvil default key - local development only
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 54e295b and 4fce862.

📒 Files selected for processing (1)
  • examples/CRISP/client/libs/wasm/pkg/crisp_worker.js (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-11-05T06:48:58.177Z
Learnt from: ryardley
PR: gnosisguild/enclave#173
File: packages/ciphernode/config/src/app_config.rs:13-21
Timestamp: 2024-11-05T06:48:58.177Z
Learning: In the `packages/ciphernode/config/src/app_config.rs` file, for the `Contract` enum, the team prefers to use `String` type for `address` fields, relying on parsing to handle validation, instead of using the `Address` type.

Applied to files:

  • examples/CRISP/client/libs/wasm/pkg/crisp_worker.js
📚 Learning: 2025-09-19T11:16:53.825Z
Learnt from: cedoor
PR: gnosisguild/enclave#752
File: packages/enclave-contracts/contracts/Enclave.sol:15-17
Timestamp: 2025-09-19T11:16:53.825Z
Learning: The Enclave contract in the gnosisguild/enclave repository has not been deployed yet as of September 2025, so storage layout considerations for upgradeable contracts don't apply to current changes.

Applied to files:

  • examples/CRISP/client/libs/wasm/pkg/crisp_worker.js
🧬 Code graph analysis (1)
examples/CRISP/client/libs/wasm/pkg/crisp_worker.js (2)
packages/enclave-sdk/src/index.ts (2)
  • EnclaveSDK (8-8)
  • FheProtocol (47-47)
packages/enclave-sdk/src/enclave-sdk.ts (1)
  • EnclaveSDK (48-614)
🪛 Gitleaks (8.28.0)
examples/CRISP/client/libs/wasm/pkg/crisp_worker.js

[high] 27-27: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: build_enclave_cli
  • GitHub Check: build_sdk
  • GitHub Check: integration_prebuild
  • GitHub Check: test_net
  • GitHub Check: test_contracts
  • GitHub Check: rust_unit
  • GitHub Check: rust_integration
  • GitHub Check: build_e3_support_risc0
  • GitHub Check: Build & Push Image
  • GitHub Check: Build & Push Image

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
examples/CRISP/server/src/cli/commands.rs (2)

118-164: Fee quote uses stale start_window — refresh invalidates the quote.

The fee quote at lines 138-147 uses the start_window calculated at lines 119-122 (based on current_timestamp from line 118). However, after token approval, the code refreshes current_timestamp (line 160) and recalculates start_window (lines 161-164). The subsequent request_e3 call (lines 179-189) uses this new start_window, which differs from the one used to obtain the quote.

If the contract's fee calculation depends on start_window parameters, the approved amount may be insufficient or the quote may be rejected, causing the transaction to revert.

Options to fix:

  1. Re-fetch the quote after refreshing the timestamp and before calling request_e3:
     current_timestamp = get_current_timestamp().await?;
     start_window = [
         U256::from(current_timestamp),
         U256::from(current_timestamp + CONFIG.e3_window_size as u64),
     ];
+
+    // Re-fetch quote with updated start_window
+    let updated_fee = contract
+        .get_e3_quote(
+            threshold,
+            start_window,
+            duration,
+            e3_program,
+            e3_params.clone(),
+            compute_provider_params_bytes.clone(),
+        )
+        .await?;
+    
+    if updated_fee > fee_amount {
+        info!("Fee increased, approving additional tokens...");
+        approve::approve_token(
+            &CONFIG.http_rpc_url,
+            &CONFIG.private_key,
+            &CONFIG.fee_token_address,
+            &CONFIG.enclave_address,
+            updated_fee,
+        )
+        .await?;
+    }
  1. Do not refresh the timestamp/start_window after obtaining the quote — use the original values for request_e3 to maintain consistency.

61-70: Remove .unwrap() to prevent potential panic.

Line 66 uses .unwrap() on the provider result, which can cause a panic if the blockchain query fails. This breaks the error handling contract of the function.

Apply this diff to properly propagate the error:

 pub async fn get_current_timestamp() -> Result<u64, Box<dyn std::error::Error + Send + Sync>> {
     let provider = ProviderBuilder::new().connect(&CONFIG.http_rpc_url).await?;
     let block = provider
         .get_block_by_number(alloy::eips::BlockNumberOrTag::Latest)
         .await
-        .unwrap()
-        .ok_or_else(|| anyhow::anyhow!("Latest block not found"))?;
+        .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?
+        .ok_or_else(|| anyhow::anyhow!("Latest block not found"))?;
 
     Ok(block.header.timestamp)
 }
🧹 Nitpick comments (1)
examples/CRISP/server/src/cli/commands.rs (1)

132-178: Consider using debug! or trace! for verbose diagnostic logs.

Lines 132-136 and 168-177 contain multiple diagnostic logs prefixed with "Debug -". These are currently logged at info! level, which may clutter production logs.

Consider changing these to debug! or trace! level:

-    info!("Debug Before Fee Quote - start_window: {:?}", start_window);
+    debug!("Before fee quote - start_window: {:?}", start_window);
-    info!(
-        "Debug Before Fee Quote - current timestamp: {:?}",
-        current_timestamp
-    );
+    debug!("Before fee quote - current timestamp: {:?}", current_timestamp);

And similarly for lines 168-177:

-    info!("Debug - threshold: {:?}", threshold);
-    info!("Debug - start_window: {:?}", start_window);
-    info!("Debug - current timestamp: {:?}", current_timestamp);
-    info!("Debug - duration: {}", duration);
-    info!("Debug - e3_program: {}", e3_program);
+    debug!("Request parameters - threshold: {:?}, start_window: {:?}, timestamp: {:?}", threshold, start_window, current_timestamp);
+    debug!("Request parameters - duration: {}, e3_program: {}", duration, e3_program);
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ffdcf80 and b269b14.

📒 Files selected for processing (1)
  • examples/CRISP/server/src/cli/commands.rs (5 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-10-28T10:45:29.100Z
Learnt from: ryardley
PR: gnosisguild/enclave#156
File: packages/ciphernode/cipher/src/cipher.rs:164-164
Timestamp: 2024-10-28T10:45:29.100Z
Learning: In test code, it's acceptable to use `.unwrap()` instead of propagating errors with the `?` operator in Rust.

Applied to files:

  • examples/CRISP/server/src/cli/commands.rs
📚 Learning: 2024-11-05T06:48:58.177Z
Learnt from: ryardley
PR: gnosisguild/enclave#173
File: packages/ciphernode/config/src/app_config.rs:13-21
Timestamp: 2024-11-05T06:48:58.177Z
Learning: In the `packages/ciphernode/config/src/app_config.rs` file, for the `Contract` enum, the team prefers to use `String` type for `address` fields, relying on parsing to handle validation, instead of using the `Address` type.

Applied to files:

  • examples/CRISP/server/src/cli/commands.rs
🧬 Code graph analysis (1)
examples/CRISP/server/src/cli/commands.rs (2)
crates/evm-helpers/src/contracts.rs (1)
  • new (195-201)
examples/CRISP/server/src/cli/approve.rs (1)
  • approve_token (20-65)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: build_sdk
  • GitHub Check: test_net
  • GitHub Check: test_contracts
  • GitHub Check: build_enclave_cli
  • GitHub Check: integration_prebuild
  • GitHub Check: build_e3_support_risc0
  • GitHub Check: Build & Push Image
  • GitHub Check: rust_integration
  • GitHub Check: rust_unit
  • GitHub Check: Build & Push Image

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
examples/CRISP/test/crisp.spec.ts (1)

51-52: Replace fixed timeout with explicit wait for Committee publication.

Hard-coded timeouts make E2E tests flaky. The 4-second wait may be too short under load or unnecessarily long in fast environments.

Consider waiting for a deterministic signal instead:

  • Use page.waitForSelector() for a DOM element that appears when Committee is published
  • Poll an API endpoint with page.waitForFunction() to check publication status
  • Listen for a specific network request completion
examples/CRISP/client/libs/wasm/pkg/crisp_worker.js (1)

21-23: All three contracts still share the same address.

This issue was previously flagged: all three contract addresses (enclave, ciphernodeRegistry, feeToken) are set to the same value 0xc6e7DF5E7b4f2A278906862b61205850344D4e7d. Even in example code, these should be distinct contract instances to avoid misleading developers.

🧹 Nitpick comments (2)
examples/CRISP/client/libs/wasm/pkg/crisp_worker.js (1)

28-28: Consider adding a production warning comment.

While this is clearly marked as the default Anvil test key, adding an explicit warning comment (e.g., "// WARNING: Never use this key in production") would help prevent accidental misuse if developers copy this example code.

examples/CRISP/server/src/server/indexer.rs (1)

333-341: Consider reusing the provider to avoid connection overhead.

Creating a new provider on every call to get_current_timestamp_rpc() incurs connection setup overhead (potentially including DNS resolution, TCP handshake, and TLS negotiation). Since this function is called in time-sensitive activation and expiration paths, the overhead could accumulate.

Consider one of these approaches:

  1. Pass an existing provider as a parameter (preferred if a provider is already available in the calling context)
  2. Use a cached/shared provider (e.g., Arc<Provider> stored in a static or passed through the handler context)
  3. Reuse the WebSocket provider already created in start_indexer instead of creating a separate HTTP provider

Example of approach 1:

-pub async fn get_current_timestamp_rpc() -> eyre::Result<u64> {
-    let provider = ProviderBuilder::new().connect(&CONFIG.http_rpc_url).await?;
+pub async fn get_current_timestamp_rpc<P: Provider>(provider: &P) -> eyre::Result<u64> {
     let block = provider
         .get_block_by_number(alloy::eips::BlockNumberOrTag::Latest)
         .await?
         .ok_or_else(|| eyre::eyre!("Latest block not found"))?;
     
     Ok(block.header.timestamp)
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b269b14 and a11a8b3.

📒 Files selected for processing (3)
  • examples/CRISP/client/libs/wasm/pkg/crisp_worker.js (1 hunks)
  • examples/CRISP/server/src/server/indexer.rs (6 hunks)
  • examples/CRISP/test/crisp.spec.ts (4 hunks)
🧰 Additional context used
🧠 Learnings (8)
📚 Learning: 2024-10-08T19:45:18.209Z
Learnt from: ryardley
PR: gnosisguild/enclave#107
File: packages/ciphernode/sortition/src/distance.rs:1-1
Timestamp: 2024-10-08T19:45:18.209Z
Learning: In `packages/ciphernode/core/src/events.rs`, the import statements use the correct and updated `alloy::primitives` module.

Applied to files:

  • examples/CRISP/server/src/server/indexer.rs
📚 Learning: 2024-11-05T03:56:22.254Z
Learnt from: ryardley
PR: gnosisguild/enclave#170
File: packages/ciphernode/evm/tests/evm_reader.rs:63-63
Timestamp: 2024-11-05T03:56:22.254Z
Learning: In the Rust test function `test_logs` in `packages/ciphernode/evm/tests/evm_reader.rs`, a sleep duration of 1 millisecond is sufficient for reliable event processing, even in CI environments.

Applied to files:

  • examples/CRISP/server/src/server/indexer.rs
📚 Learning: 2024-10-22T03:30:03.606Z
Learnt from: ryardley
PR: gnosisguild/enclave#145
File: packages/ciphernode/enclave_node/src/shutdown.rs:21-26
Timestamp: 2024-10-22T03:30:03.606Z
Learning: In `packages/ciphernode/enclave_node/src/shutdown.rs`, the sleep duration after aborting the task is necessary to wait for other processes to complete during shutdown.

Applied to files:

  • examples/CRISP/server/src/server/indexer.rs
📚 Learning: 2024-09-26T04:59:28.043Z
Learnt from: ryardley
PR: gnosisguild/enclave#107
File: tests/basic_integration/test.sh:142-142
Timestamp: 2024-09-26T04:59:28.043Z
Learning: In the integration test script `tests/basic_integration/test.sh`, using a fixed sleep to wait for the input deadline is acceptable.

Applied to files:

  • examples/CRISP/test/crisp.spec.ts
📚 Learning: 2024-09-26T04:26:00.779Z
Learnt from: ryardley
PR: gnosisguild/enclave#107
File: tests/basic_integration/test.sh:142-142
Timestamp: 2024-09-26T04:26:00.779Z
Learning: In the `tests/basic_integration/test.sh` script, using fixed sleep durations is acceptable, and adding more complex waiting mechanisms may introduce unnecessary complexity for the test.

Applied to files:

  • examples/CRISP/test/crisp.spec.ts
📚 Learning: 2024-09-26T04:15:32.544Z
Learnt from: ryardley
PR: gnosisguild/enclave#107
File: tests/basic_integration/test.sh:81-83
Timestamp: 2024-09-26T04:15:32.544Z
Learning: In `tests/basic_integration/test.sh`, it's acceptable to wait indefinitely for the EVM node to start without a timeout, as it's unlikely to fail here.

Applied to files:

  • examples/CRISP/test/crisp.spec.ts
📚 Learning: 2024-11-05T06:48:58.177Z
Learnt from: ryardley
PR: gnosisguild/enclave#173
File: packages/ciphernode/config/src/app_config.rs:13-21
Timestamp: 2024-11-05T06:48:58.177Z
Learning: In the `packages/ciphernode/config/src/app_config.rs` file, for the `Contract` enum, the team prefers to use `String` type for `address` fields, relying on parsing to handle validation, instead of using the `Address` type.

Applied to files:

  • examples/CRISP/client/libs/wasm/pkg/crisp_worker.js
📚 Learning: 2025-09-19T11:16:53.825Z
Learnt from: cedoor
PR: gnosisguild/enclave#752
File: packages/enclave-contracts/contracts/Enclave.sol:15-17
Timestamp: 2025-09-19T11:16:53.825Z
Learning: The Enclave contract in the gnosisguild/enclave repository has not been deployed yet as of September 2025, so storage layout considerations for upgradeable contracts don't apply to current changes.

Applied to files:

  • examples/CRISP/client/libs/wasm/pkg/crisp_worker.js
🧬 Code graph analysis (2)
examples/CRISP/server/src/server/indexer.rs (1)
crates/evm-helpers/src/listener.rs (2)
  • contract_address (99-99)
  • create_contract_listener (97-104)
examples/CRISP/client/libs/wasm/pkg/crisp_worker.js (1)
packages/enclave-sdk/src/enclave-sdk.ts (1)
  • EnclaveSDK (48-614)
🪛 Gitleaks (8.28.0)
examples/CRISP/client/libs/wasm/pkg/crisp_worker.js

[high] 23-23: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


[high] 28-28: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: integration_prebuild
  • GitHub Check: test_net
  • GitHub Check: build_e3_support_risc0
  • GitHub Check: build_sdk
  • GitHub Check: test_contracts
  • GitHub Check: build_enclave_cli
  • GitHub Check: rust_integration
  • GitHub Check: rust_unit
  • GitHub Check: Build & Push Image
  • GitHub Check: Build & Push Image
🔇 Additional comments (9)
examples/CRISP/test/crisp.spec.ts (1)

13-26: LGTM!

The try/catch block with error logging improves observability for debugging test failures. Rethrowing the error ensures the test still fails appropriately.

examples/CRISP/client/libs/wasm/pkg/crisp_worker.js (4)

7-8: LGTM! Clean import refactoring.

The import statements are properly formatted with consistent quote styles and trailing semicolons removed.


11-13: LGTM! Improved message handling structure.

The refactored event handling using destructuring and a switch statement is cleaner and more maintainable than the previous approach.


32-44: LGTM! Result handling correctly updated for new SDK API.

The code properly uses the updated SDK return shape (result.encryptedData and result.proof) and includes appropriate error handling.


47-49: LGTM! Good defensive programming.

The default case appropriately handles unknown message types with logging.

examples/CRISP/server/src/server/indexer.rs (4)

15-15: LGTM! Import changes support RPC-based timing.

The shift from system time (SystemTime, UNIX_EPOCH, Instant, sleep_until) to RPC-based timing (Provider, ProviderBuilder, sleep) is consistent with the refactored timing logic that now queries blockchain timestamps.

Also applies to: 35-36


157-168: LGTM! Expiration timing logic is correct.

The RPC-based timing approach properly calculates the wait duration until expiration, handles already-expired E3s with Duration::ZERO, and includes helpful logging. Using blockchain time rather than system time is more accurate for blockchain-based deadlines.


299-321: LGTM! Activation timing now correctly respects the start window.

The implementation properly calculates the wait duration based on e3.startWindow[0] and current RPC time, addressing the concern from the previous review about premature activation. The verbose logging is helpful for debugging timing-related issues.


346-346: LGTM! Parameter rename improves clarity.

Renaming registry_filter_address to registry_address is more accurate since the parameter represents a contract address rather than a filter. The usage is updated consistently.

Also applies to: 369-369

@ryardley ryardley left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did a sweep over the ciphernode changes and have added some thoughts. Still need some more time with this to go over the logic and make sure I understand what is happening and maybe clear some of these comments up but I thought I would post these for now and come back in an hour or two.

Comment thread crates/aggregator/Cargo.toml
Comment thread crates/aggregator/src/committee_finalizer.rs Outdated
Comment thread crates/keyshare/src/threshold_keyshare.rs Outdated
Comment thread crates/request/src/context.rs Outdated
Comment thread crates/request/src/router.rs Outdated
Comment thread crates/sortition/src/backends.rs
Comment thread crates/request/src/context.rs Outdated
Comment thread crates/tests/tests/integration.rs Outdated
Comment thread crates/tests/tests/integration.rs Outdated
@ryardley

Copy link
Copy Markdown
Contributor

I made an issue to capture the ciphernode architecture stuff: #939

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (5)
crates/ciphernode-builder/src/ciphernode_builder.rs (1)

225-227: Comment still incorrectly says "writable".

The comment claims this method sets up a "writable BondingRegistry," but the implementation at lines 343-354 only attaches a reader (uses read_provider and bonding_registry_reader repository).

Apply this diff to correct the comment:

-    /// Setup a writable BondingRegistry for every evm chain provided
+    /// Setup a BondingRegistry reader for every EVM chain provided
     pub fn with_contract_bonding_registry(mut self) -> Self {
crates/tests/tests/integration.rs (1)

220-232: Derive the expected committee from the nodes under test.

Hardcoding the five addresses couples the test to the current RNG output and sortition implementation; any minor change in node generation breaks the assertion. Reuse the nodes you just built (or the same selection helper used in production) to compute the committee instead of literal strings—this mirrors the earlier feedback on the same block.

-    let committee: Vec<String> = vec![
-        "0x8f32E487328F04927f20c4B14399e4F3123763df".to_string(),
-        "0x95b8a2b9b93aE9e0F13e215A49b8C53172c4f4ba".to_string(),
-        "0x8966a013047aef67Cac52Bc96eB77bC11B5D2572".to_string(),
-        "0x2B1eD59AC30f668B5b9EcF3D8718A44C15E0E479".to_string(),
-        "0x83A06c5Ac9E4207526C3eFA79812808428Dd5FaB".to_string(),
-    ];
+    let committee: Vec<String> = nodes
+        .iter()
+        .map(|node| node.address())
+        .take(threshold_n as usize)
+        .collect();
crates/aggregator/src/committee_finalizer.rs (1)

107-125: Cancel existing scheduled finalizations before inserting the new handle.

A repeated CommitteeRequested for the same e3_id replaces the map entry but leaves the old SpawnHandle active, so both timers fire and we emit CommitteeFinalizeRequested twice. Clean out and cancel the previous handle before scheduling the new one.

-                        let handle = ctx.run_later(
+                        if let Some(prev) = act
+                            .pending_committees
+                            .remove(&e3_id_for_async.to_string())
+                        {
+                            ctx.cancel_future(prev);
+                        }
+
+                        let handle = ctx.run_later(
                             Duration::from_secs(seconds_until_deadline),
                             move |act, _ctx| {
                                 info!(e3_id = %e3_id_clone, "Dispatching CommitteeFinalizeRequested event");
@@
-                        act.pending_committees
-                            .insert(e3_id_for_async.to_string(), handle);
+                        act.pending_committees
+                            .insert(e3_id_for_async.to_string(), handle);
crates/evm/src/ciphernode_registry_sol.rs (2)

439-459: CRITICAL: Manual nonce management still present despite past review.

A previous review flagged manual nonce management as causing concurrency issues and was marked "Addressed in commit c5658c6", but the code still explicitly fetches and sets nonces (lines 447-456). Multiple concurrent calls will fetch the same pending nonce, causing "nonce too low" or "already used" errors.

Apply this diff to let the provider auto-fill nonces:

 pub async fn submit_ticket_to_registry<P: Provider + WalletProvider + Clone>(
     provider: EthProvider<P>,
     contract_address: Address,
     e3_id: E3id,
     ticket_number: u64,
 ) -> Result<TransactionReceipt> {
     let e3_id: U256 = e3_id.try_into()?;
     let ticket_number = U256::from(ticket_number);
-    let from_address = provider.provider().default_signer_address();
-    let current_nonce = provider
-        .provider()
-        .get_transaction_count(from_address)
-        .pending()
-        .await?;
     let contract = ICiphernodeRegistry::new(contract_address, provider.provider());
-    let builder = contract
+    let receipt = contract
         .submitTicket(e3_id, ticket_number)
-        .nonce(current_nonce);
-    let receipt = builder.send().await?.get_receipt().await?;
+        .send()
+        .await?
+        .get_receipt()
+        .await?;
     Ok(receipt)
 }

479-504: CRITICAL: Manual nonce management still present; MAJOR: Silent address filtering drops invalid nodes.

Two issues:

  1. Manual nonce management (lines 492-497, 501): Same concurrency hazard as other functions—marked addressed but unchanged.
  2. Silent address filtering (lines 488-491): Invalid node addresses are silently dropped via filter_map(|node| node.parse().ok()). This can cause incomplete committee publication without any error indication, which is a correctness issue.

Apply these diffs:

Fix manual nonce management:

 pub async fn publish_committee_to_registry<P: Provider + WalletProvider + Clone>(
     provider: EthProvider<P>,
     contract_address: Address,
     e3_id: E3id,
     nodes: OrderedSet<String>,
     public_key: Vec<u8>,
 ) -> Result<TransactionReceipt> {
     let e3_id: U256 = e3_id.try_into()?;
     let public_key = Bytes::from(public_key);
     let nodes_vec: Vec<Address> = nodes
         .into_iter()
-        .filter_map(|node| node.parse().ok())
+        .map(|node| {
+            node.parse::<Address>()
+                .map_err(|_| anyhow::anyhow!("Invalid node address: {}", node))
+        })
-        .collect();
-    let from_address = provider.provider().default_signer_address();
-    let current_nonce = provider
-        .provider()
-        .get_transaction_count(from_address)
-        .pending()
-        .await?;
+        .collect::<Result<Vec<_>>>()?;
     let contract = ICiphernodeRegistry::new(contract_address, provider.provider());
-    let builder = contract
+    let receipt = contract
         .publishCommittee(e3_id, nodes_vec, public_key)
-        .nonce(current_nonce);
-    let receipt = builder.send().await?.get_receipt().await?;
+        .send()
+        .await?
+        .get_receipt()
+        .await?;
     Ok(receipt)
 }
🧹 Nitpick comments (1)
crates/evm/src/ciphernode_registry_sol.rs (1)

265-298: Document the is_aggregator parameter.

The is_aggregator flag controls whether the writer subscribes to PublicKeyAggregated and CommitteeFinalizeRequested events, but there's no documentation explaining when this should be true vs false. Adding a doc comment would help callers understand the intended usage pattern.

Consider adding:

+    /// Attach a writer to the event bus.
+    ///
+    /// # Arguments
+    /// * `is_aggregator` - Set to `true` for nodes that aggregate and publish public keys,
+    ///   `false` for regular nodes that only submit tickets
     pub async fn attach(
         bus: &Addr<EventBus<EnclaveEvent>>,
         provider: EthProvider<P>,
         contract_address: &str,
         is_aggregator: bool,
     ) -> Result<Addr<CiphernodeRegistrySolWriter<P>>> {
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between da1badc and d3672d9.

📒 Files selected for processing (8)
  • crates/aggregator/src/committee_finalizer.rs (1 hunks)
  • crates/ciphernode-builder/src/ciphernode_builder.rs (9 hunks)
  • crates/events/src/enclave_event/committee_finalize_requested.rs (1 hunks)
  • crates/events/src/enclave_event/mod.rs (6 hunks)
  • crates/evm/src/ciphernode_registry_sol.rs (5 hunks)
  • crates/evm/src/lib.rs (1 hunks)
  • crates/tests/tests/integration.rs (6 hunks)
  • crates/tests/tests/integration_legacy.rs (15 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/evm/src/lib.rs
🧰 Additional context used
🧠 Learnings (29)
📚 Learning: 2025-04-30T06:25:14.721Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 372
File: packages/ciphernode/events/src/eventbus.rs:15-15
Timestamp: 2025-04-30T06:25:14.721Z
Learning: EnclaveEvent implements Display in packages/ciphernode/events/src/enclave_event/mod.rs, which satisfies the Event trait requirement. Static analysis tools may incorrectly flag implementations as missing when they do exist.

Applied to files:

  • crates/events/src/enclave_event/mod.rs
  • crates/events/src/enclave_event/committee_finalize_requested.rs
  • crates/tests/tests/integration.rs
  • crates/tests/tests/integration_legacy.rs
  • crates/evm/src/ciphernode_registry_sol.rs
📚 Learning: 2024-09-26T03:11:29.311Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 107
File: packages/ciphernode/sortition/src/distance.rs:1-1
Timestamp: 2024-09-26T03:11:29.311Z
Learning: In `packages/ciphernode/core/src/events.rs`, the import statements use the correct and updated `alloy::primitives` module.

Applied to files:

  • crates/events/src/enclave_event/mod.rs
  • crates/tests/tests/integration.rs
  • crates/ciphernode-builder/src/ciphernode_builder.rs
  • crates/tests/tests/integration_legacy.rs
  • crates/evm/src/ciphernode_registry_sol.rs
📚 Learning: 2024-10-10T23:24:43.341Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 143
File: packages/ciphernode/sortition/src/sortition.rs:4-9
Timestamp: 2024-10-10T23:24:43.341Z
Learning: In the `Sortition` module (`packages/ciphernode/sortition/src/sortition.rs`), errors are sent to the event bus using `self.bus.err`, which handles logging and printing. Therefore, explicit use of the `tracing` crate for logging errors may not be necessary in this context.

Applied to files:

  • crates/events/src/enclave_event/mod.rs
  • crates/tests/tests/integration.rs
  • crates/ciphernode-builder/src/ciphernode_builder.rs
  • crates/tests/tests/integration_legacy.rs
📚 Learning: 2024-11-05T06:51:46.701Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 173
File: packages/ciphernode/evm/src/event_reader.rs:270-286
Timestamp: 2024-11-05T06:51:46.701Z
Learning: In `packages/ciphernode/evm/src/event_reader.rs`, the `state.ids` HashSet is intended to grow indefinitely to store all processed event IDs for deduplication purposes until an alternative like a bloom filter is implemented.

Applied to files:

  • crates/events/src/enclave_event/mod.rs
  • crates/tests/tests/integration_legacy.rs
  • crates/evm/src/ciphernode_registry_sol.rs
📚 Learning: 2024-10-16T09:51:10.038Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 145
File: packages/ciphernode/router/src/committee_meta.rs:43-43
Timestamp: 2024-10-16T09:51:10.038Z
Learning: In `packages/ciphernode/router/src/committee_meta.rs`, avoid suggesting making the `on_event` method in `CommitteMetaFeature` asynchronous or adding error handling for the `write` operation to the data store.

Applied to files:

  • crates/events/src/enclave_event/mod.rs
  • crates/tests/tests/integration.rs
  • crates/ciphernode-builder/src/ciphernode_builder.rs
  • crates/aggregator/src/committee_finalizer.rs
  • crates/tests/tests/integration_legacy.rs
  • crates/evm/src/ciphernode_registry_sol.rs
📚 Learning: 2024-10-28T12:00:09.010Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 156
File: packages/ciphernode/enclave/src/commands/wallet/set.rs:17-23
Timestamp: 2024-10-28T12:00:09.010Z
Learning: In the `enclave` package of the `ciphernode` project, prefer using `println!` over logging macros like `error!` from the `tracing` crate for error output in CLI commands.

Applied to files:

  • crates/events/src/enclave_event/mod.rs
📚 Learning: 2024-10-22T02:10:34.864Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 145
File: packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs:82-83
Timestamp: 2024-10-22T02:10:34.864Z
Learning: In the file `packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs`, when reviewing test functions like `generate_pk_share`, minor performance optimizations (e.g., minimizing mutex locks) are not a priority.

Applied to files:

  • crates/tests/tests/integration.rs
  • crates/tests/tests/integration_legacy.rs
📚 Learning: 2025-10-27T15:37:59.138Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 904
File: crates/net/src/bin/p2p_test.rs:22-45
Timestamp: 2025-10-27T15:37:59.138Z
Learning: In test files (especially integration test binaries like p2p_test.rs), suggesting correlation ID filtering for DHT events may be unnecessarily complex since test environments are typically more controlled and false positives are less of a concern.

Applied to files:

  • crates/tests/tests/integration.rs
  • crates/tests/tests/integration_legacy.rs
📚 Learning: 2024-10-03T23:02:41.732Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 133
File: packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs:137-139
Timestamp: 2024-10-03T23:02:41.732Z
Learning: In `packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs`, using the same RNG instance `rng_test` for generating multiple key shares without advancing its state is acceptable.

Applied to files:

  • crates/tests/tests/integration.rs
  • crates/tests/tests/integration_legacy.rs
📚 Learning: 2024-10-23T01:59:42.967Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 156
File: packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs:274-274
Timestamp: 2024-10-23T01:59:42.967Z
Learning: In the `packages/ciphernode/tests/tests/test_aggregation_and_decryption.rs` file and other test files within this project, hardcoding `CIPHERNODE_SECRET` is acceptable for testing purposes.

Applied to files:

  • crates/tests/tests/integration.rs
  • crates/tests/tests/integration_legacy.rs
📚 Learning: 2024-10-08T19:45:18.209Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 107
File: tests/basic_integration/test.sh:142-142
Timestamp: 2024-10-08T19:45:18.209Z
Learning: In the `tests/basic_integration/test.sh` script, using fixed sleep durations is acceptable, and adding more complex waiting mechanisms may introduce unnecessary complexity for the test.

Applied to files:

  • crates/tests/tests/integration.rs
📚 Learning: 2024-11-05T03:56:22.254Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 170
File: packages/ciphernode/evm/tests/evm_reader.rs:63-63
Timestamp: 2024-11-05T03:56:22.254Z
Learning: In the Rust test function `test_logs` in `packages/ciphernode/evm/tests/evm_reader.rs`, a sleep duration of 1 millisecond is sufficient for reliable event processing, even in CI environments.

Applied to files:

  • crates/tests/tests/integration.rs
📚 Learning: 2024-09-26T04:59:28.043Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 107
File: tests/basic_integration/test.sh:142-142
Timestamp: 2024-09-26T04:59:28.043Z
Learning: In the integration test script `tests/basic_integration/test.sh`, using a fixed sleep to wait for the input deadline is acceptable.

Applied to files:

  • crates/tests/tests/integration.rs
📚 Learning: 2024-10-16T09:52:53.807Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 145
File: packages/ciphernode/data/src/data_store.rs:74-75
Timestamp: 2024-10-16T09:52:53.807Z
Learning: In this project, the actor model handles potential errors internally, so methods like `checkpoint` in the `Checkpoint` trait do not need to explicitly handle or propagate errors.

Applied to files:

  • crates/tests/tests/integration.rs
  • crates/tests/tests/integration_legacy.rs
📚 Learning: 2024-10-28T12:04:26.578Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 156
File: packages/ciphernode/enclave_node/src/ciphernode.rs:26-28
Timestamp: 2024-10-28T12:04:26.578Z
Learning: In the `setup_ciphernode` function in `packages/ciphernode/enclave_node/src/ciphernode.rs`, panicking on errors during setup is acceptable.

Applied to files:

  • crates/tests/tests/integration.rs
  • crates/ciphernode-builder/src/ciphernode_builder.rs
  • crates/tests/tests/integration_legacy.rs
  • crates/evm/src/ciphernode_registry_sol.rs
📚 Learning: 2024-10-23T02:03:02.008Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 156
File: packages/ciphernode/keyshare/src/encryption.rs:45-45
Timestamp: 2024-10-23T02:03:02.008Z
Learning: In the `packages/ciphernode/keyshare/src/encryption.rs` file, the environment variable `CIPHERNODE_SECRET` is used for the encryption password. A secure secret management solution is not currently available, but may be considered in future iterations.

Applied to files:

  • crates/tests/tests/integration.rs
  • crates/ciphernode-builder/src/ciphernode_builder.rs
📚 Learning: 2024-09-26T16:47:14.932Z
Learnt from: auryn-macmillan
Repo: gnosisguild/enclave PR: 107
File: packages/ciphernode/core/src/logger.rs:45-46
Timestamp: 2024-09-26T16:47:14.932Z
Learning: The `seed` in the `E3Requested` event is a seed for RNG and not sensitive. Logging it is acceptable.

Applied to files:

  • crates/tests/tests/integration.rs
📚 Learning: 2024-11-05T06:56:49.157Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 173
File: packages/ciphernode/enclave_node/src/aggregator.rs:0-0
Timestamp: 2024-11-05T06:56:49.157Z
Learning: `RegistryFilterSol` does not have a reader and does not require a repository reader or deploy block when calling `RegistryFilterSol::attach` in `packages/ciphernode/enclave_node/src/aggregator.rs`.

Applied to files:

  • crates/ciphernode-builder/src/ciphernode_builder.rs
  • crates/evm/src/ciphernode_registry_sol.rs
📚 Learning: 2024-10-22T03:39:29.448Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 145
File: packages/ciphernode/evm/src/enclave_sol_reader.rs:87-89
Timestamp: 2024-10-22T03:39:29.448Z
Learning: In the `ciphernode` project, specifically in `packages/ciphernode/evm/src/enclave_sol_reader.rs`, the method `EnclaveSolReader::attach` should be retained even if it directly calls `EvmEventReader::attach` without additional processing. Avoid suggesting its removal in future reviews.

Applied to files:

  • crates/ciphernode-builder/src/ciphernode_builder.rs
  • crates/evm/src/ciphernode_registry_sol.rs
📚 Learning: 2024-10-22T03:30:45.478Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 145
File: packages/ciphernode/data/src/repository.rs:58-60
Timestamp: 2024-10-22T03:30:45.478Z
Learning: In `packages/ciphernode/data/src/repository.rs`, the `write` method in `Repository<T>` must be synchronous and should not return errors.

Applied to files:

  • crates/ciphernode-builder/src/ciphernode_builder.rs
📚 Learning: 2024-10-29T00:05:52.278Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 156
File: packages/ciphernode/config/src/app_config.rs:133-144
Timestamp: 2024-10-29T00:05:52.278Z
Learning: In the `load_config` function in `packages/ciphernode/config/src/app_config.rs`, Figment ensures correct types for the final configuration from defaults, reducing the need for additional validation after loading.

Applied to files:

  • crates/ciphernode-builder/src/ciphernode_builder.rs
📚 Learning: 2024-11-05T06:48:58.177Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 173
File: packages/ciphernode/config/src/app_config.rs:13-21
Timestamp: 2024-11-05T06:48:58.177Z
Learning: In the `packages/ciphernode/config/src/app_config.rs` file, for the `Contract` enum, the team prefers to use `String` type for `address` fields, relying on parsing to handle validation, instead of using the `Address` type.

Applied to files:

  • crates/ciphernode-builder/src/ciphernode_builder.rs
  • crates/evm/src/ciphernode_registry_sol.rs
📚 Learning: 2024-10-28T10:45:29.100Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 156
File: packages/ciphernode/cipher/src/cipher.rs:164-164
Timestamp: 2024-10-28T10:45:29.100Z
Learning: In test code, it's acceptable to use `.unwrap()` instead of propagating errors with the `?` operator in Rust.

Applied to files:

  • crates/aggregator/src/committee_finalizer.rs
📚 Learning: 2024-10-08T01:50:45.185Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 139
File: packages/ciphernode/core/src/events.rs:323-323
Timestamp: 2024-10-08T01:50:45.185Z
Learning: When suggesting that instances of `E3Requested` should include `src_chain_id`, double-check to ensure that the field is actually missing before making the suggestion.

Applied to files:

  • crates/aggregator/src/committee_finalizer.rs
📚 Learning: 2024-11-05T06:49:46.285Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 173
File: packages/ciphernode/enclave_node/src/datastore.rs:14-16
Timestamp: 2024-11-05T06:49:46.285Z
Learning: In `packages/ciphernode/enclave_node/src/datastore.rs`, for internal functions like `get_in_mem_store`, adding extensive documentation and error handling may not be necessary if they are not client-facing.

Applied to files:

  • crates/tests/tests/integration_legacy.rs
📚 Learning: 2024-10-22T03:30:21.818Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 145
File: packages/ciphernode/enclave_node/src/shutdown.rs:12-12
Timestamp: 2024-10-22T03:30:21.818Z
Learning: In shutdown code (e.g., in `packages/ciphernode/enclave_node/src/shutdown.rs`), if signal creation fails, it's acceptable to panic rather than handle the error gracefully.

Applied to files:

  • crates/evm/src/ciphernode_registry_sol.rs
📚 Learning: 2024-10-22T03:30:03.606Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 145
File: packages/ciphernode/enclave_node/src/shutdown.rs:21-26
Timestamp: 2024-10-22T03:30:03.606Z
Learning: In `packages/ciphernode/enclave_node/src/shutdown.rs`, the sleep duration after aborting the task is necessary to wait for other processes to complete during shutdown.

Applied to files:

  • crates/evm/src/ciphernode_registry_sol.rs
📚 Learning: 2024-10-12T10:24:07.572Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 139
File: packages/ciphernode/evm/src/ciphernode_registry_sol.rs:133-143
Timestamp: 2024-10-12T10:24:07.572Z
Learning: In `packages/ciphernode/evm/src/ciphernode_registry_sol.rs`, the function `helpers::stream_from_evm` in Rust returns `()`, not a `Result`, so error handling with `if let Err(e) = ...` is not applicable.

Applied to files:

  • crates/evm/src/ciphernode_registry_sol.rs
📚 Learning: 2024-11-25T09:56:11.195Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 184
File: packages/ciphernode/net/src/network_relay.rs:0-0
Timestamp: 2024-11-25T09:56:11.195Z
Learning: In `NetworkPeer::new` in `packages/ciphernode/net/src/network_peer.rs`, peers are converted to multiaddrs, and validation of the address format occurs during this conversion. Duplicate addresses are managed as connections are deduplicated.

Applied to files:

  • crates/evm/src/ciphernode_registry_sol.rs
🧬 Code graph analysis (6)
crates/events/src/enclave_event/committee_finalize_requested.rs (1)
crates/events/src/enclave_event/mod.rs (1)
  • fmt (382-384)
crates/tests/tests/integration.rs (3)
crates/tests/tests/integration_legacy.rs (1)
  • setup_score_sortition_environment (118-153)
crates/events/src/enclave_event/mod.rs (3)
  • from (240-269)
  • try_from (365-367)
  • try_from (372-378)
crates/ciphernode-builder/src/ciphernode_builder.rs (2)
  • new (89-109)
  • new (485-491)
crates/ciphernode-builder/src/ciphernode_builder.rs (7)
crates/sortition/src/backends.rs (1)
  • score (252-254)
crates/sortition/src/repo.rs (2)
  • sortition (15-15)
  • sortition (19-21)
crates/evm/src/ciphernode_registry_sol.rs (4)
  • attach (217-242)
  • attach (265-298)
  • attach (510-531)
  • attach_writer (533-546)
crates/sortition/src/ciphernode_selector.rs (1)
  • attach (46-62)
crates/sortition/src/sortition.rs (1)
  • attach (168-210)
crates/evm/src/bonding_registry_sol.rs (2)
  • attach (144-169)
  • attach (176-197)
crates/ciphernode-builder/src/ciphernode.rs (1)
  • store (53-55)
crates/aggregator/src/committee_finalizer.rs (3)
crates/events/src/eventbus.rs (1)
  • error (92-96)
crates/evm/src/helpers.rs (3)
  • new (69-75)
  • new (111-113)
  • get_current_timestamp (200-220)
crates/events/src/enclave_event/mod.rs (1)
  • from (240-269)
crates/tests/tests/integration_legacy.rs (3)
crates/tests/tests/integration.rs (1)
  • setup_score_sortition_environment (35-70)
crates/events/src/enclave_event/mod.rs (3)
  • from (240-269)
  • try_from (365-367)
  • try_from (372-378)
crates/test-helpers/src/lib.rs (2)
  • new (167-173)
  • get_common_setup (71-99)
crates/evm/src/ciphernode_registry_sol.rs (4)
crates/events/src/enclave_event/mod.rs (1)
  • from (240-269)
crates/evm/src/bonding_registry_sol.rs (8)
  • from (30-38)
  • from (42-45)
  • from (51-68)
  • from (77-83)
  • from (87-90)
  • from (94-97)
  • attach (144-169)
  • attach (176-197)
crates/evm/src/helpers.rs (4)
  • new (69-75)
  • new (111-113)
  • chain_id (81-83)
  • provider (77-79)
crates/evm/src/enclave_sol_writer.rs (2)
  • new (39-49)
  • attach (51-65)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: test_net
  • GitHub Check: build_enclave_cli
  • GitHub Check: integration_prebuild
  • GitHub Check: build_e3_support_dev
  • GitHub Check: build_sdk
  • GitHub Check: rust_integration
  • GitHub Check: build_e3_support_risc0
  • GitHub Check: test_contracts
  • GitHub Check: rust_unit
  • GitHub Check: Build & Push Image
  • GitHub Check: Build & Push Image
🔇 Additional comments (6)
crates/ciphernode-builder/src/ciphernode_builder.rs (3)

207-211: LGTM: Score-based sortition builder method.

The implementation is clean and follows the builder pattern correctly. Good practice to provide an explicit configuration method even though score is the default.


289-301: LGTM: Repository-based sortition setup.

The sortition initialization correctly uses the new repository-based approach with node_state, finalized_committees, and the configured backend. The CiphernodeSelector attachment also correctly includes the store reference.


367-391: LGTM: Robust writer attachment with proper error handling.

The error handling now captures and logs error details instead of silently swallowing them, addressing the previous review concern. The conditional attachment of CommitteeFinalizer for score sortition with public key aggregation is appropriately logged and aligns with the score-based committee finalization flow described in the PR objectives.

crates/evm/src/ciphernode_registry_sol.rs (3)

89-154: LGTM! Event wrappers follow established patterns.

The new event wrapper structs (CommitteeRequestedWithChainId, CommitteeFinalizedWithChainId, TicketSubmittedWithChainId) and their From implementations correctly translate Solidity events to Rust event types with chain-id context, consistent with the existing CiphernodeAdded and CiphernodeRemoved wrappers.


176-202: LGTM! Event extractor properly handles new event types.

The additions to the extractor function correctly decode CommitteeRequested, CommitteeFinalized, and TicketSubmitted events, with appropriate error logging and chain-id wrapping consistent with the existing pattern.


335-371: The review comment is incorrect.

The TicketId enum has only one variant: Score(u64). Since this is the only variant, the match statement is exhaustive by design—there are no other variants to handle. The Rust compiler guarantees this is safe, and no wildcard arm is necessary.

Likely an incorrect or invalid review comment.

Comment thread crates/aggregator/src/committee_finalizer.rs
Comment thread crates/evm/src/ciphernode_registry_sol.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
examples/CRISP/client/libs/wasm/pkg/crisp_worker.js (1)

21-22: Both contracts share the same address.

Lines 21-22 configure enclave and ciphernodeRegistry with identical addresses. Even in example code, these should be distinct contract instances. This configuration is logically incorrect and may mislead developers.

Consider using distinct placeholder addresses:

         contracts: {
-          enclave: '0xc6e7DF5E7b4f2A278906862b61205850344D4e7d',
-          ciphernodeRegistry: '0xc6e7DF5E7b4f2A278906862b61205850344D4e7d',
+          enclave: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
+          ciphernodeRegistry: '0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512',
         },
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea97768 and 0ace70b.

📒 Files selected for processing (1)
  • examples/CRISP/client/libs/wasm/pkg/crisp_worker.js (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-11-05T06:48:58.177Z
Learnt from: ryardley
Repo: gnosisguild/enclave PR: 173
File: packages/ciphernode/config/src/app_config.rs:13-21
Timestamp: 2024-11-05T06:48:58.177Z
Learning: In the `packages/ciphernode/config/src/app_config.rs` file, for the `Contract` enum, the team prefers to use `String` type for `address` fields, relying on parsing to handle validation, instead of using the `Address` type.

Applied to files:

  • examples/CRISP/client/libs/wasm/pkg/crisp_worker.js
📚 Learning: 2025-09-19T11:16:53.825Z
Learnt from: cedoor
Repo: gnosisguild/enclave PR: 752
File: packages/enclave-contracts/contracts/Enclave.sol:15-17
Timestamp: 2025-09-19T11:16:53.825Z
Learning: The Enclave contract in the gnosisguild/enclave repository has not been deployed yet as of September 2025, so storage layout considerations for upgradeable contracts don't apply to current changes.

Applied to files:

  • examples/CRISP/client/libs/wasm/pkg/crisp_worker.js
🧬 Code graph analysis (1)
examples/CRISP/client/libs/wasm/pkg/crisp_worker.js (1)
packages/enclave-sdk/src/index.ts (2)
  • EnclaveSDK (8-8)
  • FheProtocol (47-47)
🪛 Gitleaks (8.28.0)
examples/CRISP/client/libs/wasm/pkg/crisp_worker.js

[high] 27-27: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: build_enclave_cli
  • GitHub Check: test_net
  • GitHub Check: build_e3_support_risc0
  • GitHub Check: Build & Push Image
🔇 Additional comments (4)
examples/CRISP/client/libs/wasm/pkg/crisp_worker.js (4)

7-8: LGTM! Clean import simplification.

The import statements are properly formatted and the simplified circuit import path improves readability.


10-12: LGTM! Clear message handling pattern.

The destructuring and switch-based routing is clean and maintainable.


31-31: LGTM! Encryption method call is correct.

The call to encryptNumberAndGenProof with the updated parameters aligns with the new SDK API.


33-43: LGTM! Result handling is well-structured.

The post-message payload correctly handles both success and error cases with clear field naming.

Comment thread examples/CRISP/client/libs/wasm/pkg/crisp_worker.js

@ryardley ryardley left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets Get This Merged 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ciphernode Bonding Contracts

5 participants