Skip to content

feat: implement parametric E3 pricing [skip-line-limit]#1422

Merged
ctrlc03 merged 19 commits into
mainfrom
feat/e3-pricing
Mar 27, 2026
Merged

feat: implement parametric E3 pricing [skip-line-limit]#1422
ctrlc03 merged 19 commits into
mainfrom
feat/e3-pricing

Conversation

@hmzakhalid

@hmzakhalid hmzakhalid commented Mar 13, 2026

Copy link
Copy Markdown
Collaborator

E3 fees should be a function of committee size ($c^1$), decryption threshold ($c^2$), time availability ($\hat{t}$), and publication costs. The critical insight is that coordination costs in a flat committee topology scale quadratically with $O\binom{n}{2}$ pairwise interactions, so the fee formula must capture this or large committees become economically unsustainable for ciphernodes.

How the pricing works

The fee formula in getE3Quote():

baseFee = keyGenPerNode × n                           // Linear: key generation
        + coordinationPerPair × n(n-1)/2              // Quadratic: DKG coordination
        + availabilityPerNodePerSec × n × duration    // Linear × time: availability
        + decryptionPerNode × m                       // Linear: decryption
        + coordinationPerPair × m(m-1)/2              // Quadratic: decryption coordination
        + publicationBase                             // Fixed: on-chain publication

fee = baseFee × (10000 + marginBps) / 10000           // Apply governance markup

Where:

  • n = total committee size (threshold[1])
  • m = quorum/decryption threshold (threshold[0])
  • duration = inputWindow[1] - inputWindow[0] + dkgWindow + computeWindow + decryptionWindow
  • marginBps = governance-tunable markup (default 10%)

On successful completion, _distributeRewards() now splits the fee:

  • protocolShareBps (e.g. 20%) → protocol treasury
  • Remainder -> equally among active (non-expelled) committee members via BondingRegistry

Summary by CodeRabbit

  • New Features

    • Configurable pricing (get/set) with pricing update event and on-chain initialization; dynamic E3 quote now reflects committee size, duration, and margins.
  • Bug Fixes

    • Reward distribution updated to split protocol treasury fees from committee payouts and handle dust/bonding correctly.
  • Tests

    • Added end-to-end pricing and governance tests validating quotes, fee splits, access controls, and input validations.
  • Chores

    • Updated deployment flow to set pricing, example configs, environment sample, and CI/artifact metadata.

@vercel

vercel Bot commented Mar 13, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
crisp Ready Ready Preview, Comment Mar 27, 2026 6:42am
enclave-docs Ready Ready Preview, Comment Mar 27, 2026 6:42am

Request Review

@coderabbitai

coderabbitai Bot commented Mar 13, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds an on-chain PricingConfig to Enclave, dynamic fee computation in getE3Quote, owner-settable pricing via setPricingConfig, protocol treasury share extraction in reward distribution, committee threshold validations, tests, and deployment/script/config updates.

Changes

Cohort / File(s) Summary
Core Pricing & Interface
packages/enclave-contracts/contracts/interfaces/IEnclave.sol, packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json
Added PricingConfig struct, PricingConfigUpdated event, setPricingConfig(PricingConfig) and getPricingConfig() signatures; artifact ABI updated.
Enclave Implementation
packages/enclave-contracts/contracts/Enclave.sol
Stored global _pricingConfig and per-request protocol snapshot; replaced fixed getE3Quote logic with view computation using pricing and committee parameters; owner-only setter with validations; reward distribution now splits protocol treasury share before node payouts; initialization updated.
Tests
packages/enclave-contracts/test/Pricing/Pricing.spec.ts
New comprehensive tests for getE3Quote formula, setPricingConfig governance/validation/event, protocol treasury splitting behavior, committee threshold minimum enforcement, and end-to-end request/fee charging flows.
Deployment & Scripts
packages/enclave-contracts/scripts/deployEnclave.ts, packages/enclave-contracts/scripts/deployAndSave/mockPkVerifier.ts, packages/enclave-contracts/scripts/deployMocks.ts
deployEnclave now calls setPricingConfig(...) with initial params and logs the tx; minor formatting simplifications in mock deploy scripts.
Artifacts / Metadata
packages/enclave-contracts/artifacts/.../IBondingRegistry.json, .../ICiphernodeRegistry.json, .../ISlashingManager.json, .../CiphernodeRegistryOwnable.json
Updated buildInfoId metadata entries only; no ABI or functional changes.
CRISP Example & Env
examples/CRISP/enclave.config.yaml, examples/CRISP/packages/crisp-contracts/deployed_contracts.json, examples/CRISP/server/.env.example
Normalized YAML quoting/formatting, updated deployed contract addresses and block numbers for the example, and updated E3_PROGRAM_ADDRESS in the example env.
CI Workflow
.github/workflows/ci.yml
Fixed malformed runs-on formatting and removed trailing whitespace in a job condition.

Sequence Diagram

sequenceDiagram
    participant Client as Client
    participant Enclave as Enclave (contract)
    participant Pricing as PricingConfig (storage)
    participant Treasury as ProtocolTreasury
    participant Nodes as CommitteeNodes

    Client->>Enclave: call getE3Quote(params)
    Enclave->>Pricing: read PricingConfig
    Pricing-->>Enclave: pricing fields
    Enclave->>Enclave: compute fee (components + duration + margin)
    Enclave-->>Client: fee (view)

    Client->>Enclave: request(...) with USDC approval & transfer
    Enclave->>Enclave: _distributeRewards(totalFee, committee)
    Enclave->>Treasury: transfer protocolShare (protocolShareBps)
    Enclave->>Nodes: distribute remainder among committee nodes
    Nodes-->>Enclave: ack
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • ctrlc03
  • cedoor

Poem

🐰
A config snapped into the chain, so neat,
Fees now dance with margins and a treasury seat.
Nodes share wisely, protocol gets its part,
Owner sets the rules, the quote's a work of art.
This rabbit hops — the contracts sing, on beat!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely describes the main feature addition: implementing a parametric pricing mechanism for E3 fees, which is reflected throughout the changeset (new PricingConfig struct, setPricingConfig/getPricingConfig functions, parametric getE3Quote logic, and reward distribution updates).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/e3-pricing

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.

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/enclave-contracts/contracts/interfaces/IEnclave.sol`:
- Around line 79-92: The PricingConfig struct includes publicationPerByte but
getE3Quote never uses it; update the fee logic in getE3Quote to include a
per-byte publication charge by adding (pricing.publicationPerByte * outputSize)
to the publication fee (ensure getE3Quote has or accepts an outputSize/numBytes
parameter and use pricing.publicationBase + pricing.publicationPerByte *
outputSize), or if per-byte pricing is not intended, remove publicationPerByte
from PricingConfig and all initializations and deployments that set it;
reference the PricingConfig struct, its publicationPerByte field, and the
getE3Quote function when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c0133057-ac67-4583-99ae-52a9d243fbe6

📥 Commits

Reviewing files that changed from the base of the PR and between 6bcf88c and 7cbbf0d.

📒 Files selected for processing (8)
  • packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json
  • packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json
  • packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json
  • packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json
  • packages/enclave-contracts/contracts/Enclave.sol
  • packages/enclave-contracts/contracts/interfaces/IEnclave.sol
  • packages/enclave-contracts/scripts/deployEnclave.ts
  • packages/enclave-contracts/test/Pricing/Pricing.spec.ts

Comment thread packages/enclave-contracts/contracts/interfaces/IEnclave.sol
@vercel vercel Bot temporarily deployed to Preview – crisp March 13, 2026 18:44 Inactive
@vercel vercel Bot temporarily deployed to Preview – enclave-docs March 13, 2026 18:44 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: 2

🧹 Nitpick comments (2)
packages/enclave-contracts/contracts/Enclave.sol (1)

569-583: Skip CN reward transfer path when CN allocation is zero.

Avoid external approve/distribute calls when cnAmount == 0; it reduces unnecessary risk and call overhead.

♻️ Suggested change
-        paymentToken.forceApprove(address(bondingRegistry), cnAmount);
-
-        bondingRegistry.distributeRewards(paymentToken, activeNodes, amounts);
-
-        paymentToken.forceApprove(address(bondingRegistry), 0);
+        if (cnAmount > 0) {
+            paymentToken.forceApprove(address(bondingRegistry), cnAmount);
+            bondingRegistry.distributeRewards(paymentToken, activeNodes, amounts);
+            paymentToken.forceApprove(address(bondingRegistry), 0);
+        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/enclave-contracts/contracts/Enclave.sol` around lines 569 - 583, The
CN reward path should be skipped when cnAmount == 0 to avoid unnecessary
approves/calls; in the function handling distribution, add a guard that if
cnAmount == 0 you do not build the amounts array, call
paymentToken.forceApprove(address(bondingRegistry), ...), or call
bondingRegistry.distributeRewards(paymentToken, activeNodes, amounts). Locate
the block that computes amount/amounts/distributed and the subsequent calls to
paymentToken.forceApprove and bondingRegistry.distributeRewards and wrap them in
a conditional (or early continue/return) so they run only when cnAmount > 0.
packages/enclave-contracts/test/Pricing/Pricing.spec.ts (1)

331-342: Unused helper function.

The makeRequest helper is defined but not used in any of the test cases. Tests at lines 597-598, 679-680, and 766-767 manually call approve and request instead.

Consider either using this helper in the tests for consistency or removing it to reduce dead code.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/enclave-contracts/test/Pricing/Pricing.spec.ts` around lines 331 -
342, The helper makeRequest is unused; either remove it or refactor tests to use
it for consistency: replace the manual sequences of calling enclave.getE3Quote/
usdcToken.approve(...) and enclave.request(...) in the tests that manually
perform approve+request with a single call to makeRequest(enclave, usdcToken,
requestParams, signer) (ensure signer is passed when tests connect contracts) so
fee retrieval and approval are centralized via makeRequest; alternatively,
delete the makeRequest function if you prefer to keep the explicit
approve/request flows (remove references to makeRequest and its imports like
Enclave.getE3Quote and MockUSDC if orphaned).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/enclave-contracts/contracts/Enclave.sol`:
- Around line 1043-1053: The setPricingConfig function currently allows a
PricingConfig with protocolShareBps > 0 and protocolTreasury == address(0),
which causes protocol rewards to be skipped; update the validation in
setPricingConfig to reject such configs by adding a require that when
config.protocolShareBps > 0 then config.protocolTreasury != address(0) before
assigning _pricingConfig and emitting PricingConfigUpdated; reference the
PricingConfig struct fields protocolShareBps and protocolTreasury and the
setPricingConfig function/_pricingConfig assignment to locate where to add this
check.
- Around line 1071-1075: The getE3Quote function currently doesn't validate that
the provided committee size is configured in committeeThresholds; add the same
guard used in request() by loading uint32[2] memory threshold =
committeeThresholds[committeeSize] (or using requestParams.committeeSize if that
variable is in scope) and require that threshold[1] != 0 (or both elements
non-zero) with a clear revert string like "invalid committee size" before using
threshold to compute n and m; this mirrors request() behavior and prevents
unconfigured sizes from being accepted.

---

Nitpick comments:
In `@packages/enclave-contracts/contracts/Enclave.sol`:
- Around line 569-583: The CN reward path should be skipped when cnAmount == 0
to avoid unnecessary approves/calls; in the function handling distribution, add
a guard that if cnAmount == 0 you do not build the amounts array, call
paymentToken.forceApprove(address(bondingRegistry), ...), or call
bondingRegistry.distributeRewards(paymentToken, activeNodes, amounts). Locate
the block that computes amount/amounts/distributed and the subsequent calls to
paymentToken.forceApprove and bondingRegistry.distributeRewards and wrap them in
a conditional (or early continue/return) so they run only when cnAmount > 0.

In `@packages/enclave-contracts/test/Pricing/Pricing.spec.ts`:
- Around line 331-342: The helper makeRequest is unused; either remove it or
refactor tests to use it for consistency: replace the manual sequences of
calling enclave.getE3Quote/ usdcToken.approve(...) and enclave.request(...) in
the tests that manually perform approve+request with a single call to
makeRequest(enclave, usdcToken, requestParams, signer) (ensure signer is passed
when tests connect contracts) so fee retrieval and approval are centralized via
makeRequest; alternatively, delete the makeRequest function if you prefer to
keep the explicit approve/request flows (remove references to makeRequest and
its imports like Enclave.getE3Quote and MockUSDC if orphaned).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dbaabff4-e0dd-4bf1-98d5-5cafe3138c02

📥 Commits

Reviewing files that changed from the base of the PR and between 7cbbf0d and a063b77.

📒 Files selected for processing (11)
  • packages/enclave-contracts/artifacts/contracts/interfaces/IBondingRegistry.sol/IBondingRegistry.json
  • packages/enclave-contracts/artifacts/contracts/interfaces/ICiphernodeRegistry.sol/ICiphernodeRegistry.json
  • packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json
  • packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json
  • packages/enclave-contracts/artifacts/contracts/token/EnclaveTicketToken.sol/EnclaveTicketToken.json
  • packages/enclave-contracts/artifacts/contracts/verifier/DkgPkVerifier.sol/DkgPkVerifier.json
  • packages/enclave-contracts/artifacts/contracts/verifier/DkgPkVerifier.sol/ZKTranscriptLib.json
  • packages/enclave-contracts/contracts/Enclave.sol
  • packages/enclave-contracts/contracts/interfaces/IEnclave.sol
  • packages/enclave-contracts/scripts/deployEnclave.ts
  • packages/enclave-contracts/test/Pricing/Pricing.spec.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/enclave-contracts/artifacts/contracts/verifier/DkgPkVerifier.sol/ZKTranscriptLib.json
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/enclave-contracts/artifacts/contracts/interfaces/ISlashingManager.sol/ISlashingManager.json
  • packages/enclave-contracts/contracts/interfaces/IEnclave.sol
  • packages/enclave-contracts/scripts/deployEnclave.ts

Comment thread packages/enclave-contracts/contracts/Enclave.sol
Comment thread packages/enclave-contracts/contracts/Enclave.sol

@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: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@packages/enclave-contracts/artifacts/contracts/verifier/ThresholdPkAggregationVerifier.sol/ZKTranscriptLib.json`:
- Around line 393-394: Update the artifact metadata so inputSourceName matches
the actual source: replace the incorrect "inputSourceName":
"project/contracts/verifier/DkgPkVerifier.sol" with
"project/contracts/verifier/ThresholdPkAggregationVerifier.sol" in the
ZKTranscriptLib.json artifact (the one containing the ZKTranscriptLib entry) so
it aligns with the existing sourceName and other artifacts; ensure the change is
applied to the artifact JSON that references ThresholdPkAggregationVerifier.sol
to keep build metadata consistent.

In `@packages/enclave-contracts/contracts/Enclave.sol`:
- Around line 1066-1077: After loading threshold and deriving n and m in
getE3Quote/request paths, add runtime checks that enforce the stored values meet
current pricing-config minimums: require(n >= pc.minCommitteeSize && m >=
pc.minThreshold) (with appropriate error/revert like
CommitteeSizeTooSmall/ThresholdTooSmall) so previously-written thresholds cannot
bypass governance-updated minimums; locate the check right after "uint256 n =
uint256(threshold[1]); uint256 m = uint256(threshold[0]); PricingConfig memory
pc = _pricingConfig;" in the getE3Quote and request-related code paths and use
the existing PricingConfig fields (minCommitteeSize/minThreshold) and existing
error types or add clear revert errors if needed.
- Around line 1079-1083: The subtraction of requestParams.inputWindow[0] from
requestParams.inputWindow[1] can underflow; before computing duration, add an
explicit guard like require(requestParams.inputWindow[1] >=
requestParams.inputWindow[0], "InvalidInputWindow") (or revert with your
domain-specific error) to validate ordering of requestParams.inputWindow, then
compute duration using the existing expression that adds
_timeoutConfig.dkgWindow, _timeoutConfig.computeWindow and
_timeoutConfig.decryptionWindow; ensure the check is placed immediately before
the duration calculation that references requestParams.inputWindow and
_timeoutConfig.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 85cb6d50-c087-46f2-bd3d-81a4a9acf2de

📥 Commits

Reviewing files that changed from the base of the PR and between a063b77 and 1a5d434.

📒 Files selected for processing (3)
  • packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json
  • packages/enclave-contracts/artifacts/contracts/verifier/ThresholdPkAggregationVerifier.sol/ZKTranscriptLib.json
  • packages/enclave-contracts/contracts/Enclave.sol
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/enclave-contracts/artifacts/contracts/interfaces/IEnclave.sol/IEnclave.json

Comment thread packages/enclave-contracts/contracts/Enclave.sol
Comment thread packages/enclave-contracts/contracts/Enclave.sol Outdated
@hmzakhalid hmzakhalid marked this pull request as draft March 16, 2026 18:00
@hmzakhalid hmzakhalid linked an issue Mar 21, 2026 that may be closed by this pull request

@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)
packages/enclave-contracts/contracts/Enclave.sol (1)

1145-1159: ⚠️ Potential issue | 🟠 Major

The quote formula still charges non-spec proof surcharges.

The PR objective defines getE3Quote() in terms of n, m, duration, coordination, availability, decryption, publication, and margin. Lines 1145-1159 add proof-count and verification costs with a hard-coded 2-moduli assumption, so on-chain fees will not match the published pricing formula.

🧮 Minimal fix to align the quote path with the stated formula
-        // ZK proof count per node: 6 fixed + 2 × (N-1) × L_dkg scaling
-        // TODO: get dkgModuliCount from E3 params instead of hardcoding
-        uint256 proofsPerNode = 6 + 2 * (n - 1) * 2;
-
-        // Key generation cost: fixed per-node + per-proof (quadratic in n)
+        // Key generation cost (linear in n)
         uint256 baseFee = pc.keyGenFixedPerNode * n;
-        baseFee += pc.keyGenPerEncryptionProof * n * proofsPerNode;
 
         // Key generation coordination cost (quadratic in n)
         if (n > 1) {
             baseFee += (pc.coordinationPerPair * (n * (n - 1))) / 2;
         }
 
-        // Proof verification cost: each node verifies all others' proofs (quadratic)
-        baseFee += pc.verificationPerProof * n * proofsPerNode;
-
         // Availability cost (linear in n × duration)
         baseFee += pc.availabilityPerNodePerSec * n * duration;
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/enclave-contracts/contracts/Enclave.sol`:
- Around line 573-586: The protocol treasury split is being skipped on the early
return when activeLength == 0; compute and apply the protocol share using
_e3ProtocolShareBps[e3Id] and _e3ProtocolTreasury[e3Id] (calculate
protocolAmount and call paymentToken.safeTransfer to _protocolTreasury if >0)
before the zero-active-member refund path/return so the treasury still receives
its configured share even when all committee members are expelled; ensure you
still subtract protocolAmount from the remaining refund logic (cnAmount =
totalAmount - protocolAmount).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 79faf048-d2c4-44fd-a3b3-f404257fa76c

📥 Commits

Reviewing files that changed from the base of the PR and between f5f3094 and 5f2f2a0.

📒 Files selected for processing (3)
  • .github/workflows/ci.yml
  • packages/enclave-contracts/contracts/Enclave.sol
  • packages/enclave-contracts/scripts/deployEnclave.ts

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 Outdated
@ctrlc03 ctrlc03 merged commit 7aa3315 into main Mar 27, 2026
33 checks passed
@ctrlc03 ctrlc03 deleted the feat/e3-pricing branch March 27, 2026 09:36
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.

E3 Pricing Design getE3Quote

2 participants