Skip to content

feat(token): native shielded token supply extension#638

Open
0xisk wants to merge 4 commits into
mainfrom
feat/native-shielded-token-supply-accounting
Open

feat(token): native shielded token supply extension#638
0xisk wants to merge 4 commits into
mainfrom
feat/native-shielded-token-supply-accounting

Conversation

@0xisk

@0xisk 0xisk commented Jun 29, 2026

Copy link
Copy Markdown
Member

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation Update (if none of the other choices apply)

Stacked on #621 (core native shielded token). Base this PR's review on the diff against feat/native-shielded-token-supply, not main.

Optional on-chain supply accounting for the native shielded token:

  • NativeShieldedTokenSupply (scalar) and NativeShieldedTokenFamily Supply (per-domain) are thin wrappers over a shared NativeShieldedTokenSupplyCore.
  • _addMinted / _addBurned building blocks the consumer pairs with the matching mint/burn op; totalMinted / totalBurned / totalSupply getters.
  • _addBurned enforces the per-domain burned <= minted invariant: a readable error that also makes the add-overflow and the totalSupply underflow unreachable.
  • UINT128_MAX comes from Utils.
  • Per-module mocks, simulators, and unit suites (extensions import no token module, so they are driven standalone).

Privacy note: composing supply accounting makes contract-mediated burn amounts public (the _addBurned write is the disclosure). The bare core burns stay amount-private.

PR Checklist

  • I have read the Contributing Guide
  • I have added tests that prove my fix is effective or that my feature works.
  • I have added documentation for new methods or changes to existing method behavior.
  • CI Workflows Are Passing

Summary by CodeRabbit

  • New Features

    • Added supply-tracking support for native shielded tokens, including total minted, total burned, and total supply views.
    • Added per-domain supply tracking for token families, keeping balances isolated by domain.
    • Added test simulators and mock contracts to exercise the new supply features.
  • Bug Fixes

    • Enforced that burned amounts cannot exceed minted amounts.
    • Added coverage for zero-value and multi-domain supply behavior.

@0xisk 0xisk requested review from a team as code owners June 29, 2026 13:27
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 22b03770-bc00-46e4-946d-ab484c582bf4

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Adds a new Compact supply-accounting module family: NativeShieldedTokenSupplyCore provides domain-keyed minted/burned ledgers with invariant checks, wrapped by single-domain NativeShieldedTokenSupply and multi-domain NativeShieldedTokenFamilySupply extensions. Includes test mocks, TypeScript simulators, unit tests, and property-based tests.

Changes

Native Shielded Token Supply Accounting

Layer / File(s) Summary
Core domain-keyed ledger
contracts/src/token/extensions/NativeShieldedTokenSupplyCore.compact
Adds _totalMinted/_totalBurned ledgers keyed by domain, _addMinted/_addBurned circuits with overflow and burned <= minted assertions, and totalMinted/totalBurned/totalSupply getters defaulting to 0 for unknown domains.
Single-domain wrapper
contracts/src/token/extensions/NativeShieldedTokenSupply.compact
Adds NativeShieldedTokenSupply module using a fixed 32-byte key to forward _addMinted/_addBurned and totalMinted/totalBurned/totalSupply to the core.
Per-domain family wrapper
contracts/src/token/extensions/NativeShieldedTokenFamilySupply.compact
Adds NativeShieldedTokenFamilySupply module exposing _addMinted/_addBurned and getters parameterized by domain, forwarding to the core.
Single-domain mock, simulator, and tests
contracts/src/token/test/mocks/MockNativeShieldedTokenSupply.compact, contracts/src/token/test/simulators/NativeShieldedTokenSupplySimulator.ts, contracts/src/token/test/extensions/NativeShieldedTokenSupply.test.ts, contracts/src/token/test/extensions/NativeShieldedTokenSupply.property.test.ts
Adds a test-only mock exposing the extension's circuits, a TypeScript simulator wrapper, unit tests for initial state/mint/burn/totalSupply, and a fast-check property test validating minted/burned/supply invariants.
Family mock, simulator, and tests
contracts/src/token/test/mocks/MockNativeShieldedTokenFamilySupply.compact, contracts/src/token/test/simulators/NativeShieldedTokenFamilySupplySimulator.ts, contracts/src/token/test/extensions/NativeShieldedTokenFamilySupply.test.ts
Adds a mock, simulator, and unit tests covering unknown domains, per-domain accounting, multi-domain isolation, and burn-exceeds-minted reverts.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers: andrew-fleming

Poem

A rabbit counts each coin that's minted, 🐇
and tracks each burn, however splinted,
per domain kept, or one key fixed,
supply invariants neatly mixed,
hop hop — the ledger's now well-hinted!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: a native shielded token supply extension.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/native-shielded-token-supply-accounting

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

Base automatically changed from feat/native-shielded-token-supply to main June 30, 2026 12:51
@0xisk 0xisk linked an issue Jun 30, 2026 that may be closed by this pull request
Optional on-chain supply accounting for the native shielded token,
stacked on the core PR. Adds NativeShieldedTokenSupply (scalar) and
NativeShieldedTokenFamilySupply (per-domain) over a shared
NativeShieldedTokenSupplyCore, plus per-module mocks, simulators, and
unit suites.

* _addMinted / _addBurned building blocks, paired by the consumer with
  the matching mint and burn ops; totalMinted / totalBurned / totalSupply
  getters
* _addBurned enforces burned <= minted: a readable error that also makes
  the add overflow and the totalSupply subtraction underflow unreachable
* UINT128_MAX comes from Utils

@andrew-fleming andrew-fleming 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.

Looking good, @0xisk! You mind fixing conflicts? Also, I didn't review the integration files. I assume these are meant to be removed for now since we can't run the integration tests yet

Comment thread contracts/src/token/extensions/NativeShieldedTokenSupply.compact
@0xisk 0xisk force-pushed the feat/native-shielded-token-supply-accounting branch from 22b84ac to 989538d Compare July 2, 2026 08:29

@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.

🧹 Nitpick comments (2)
contracts/src/token/test/extensions/NativeShieldedTokenFamilySupply.test.ts (1)

63-78: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider adding a mint-overflow test.

This suite covers the burned <= minted invariant well, but doesn't exercise _addMinted's overflow guard (the "NativeShieldedTokenSupply: arithmetic overflow" path in the core). Given supply accounting is a security-sensitive invariant, a small case is worth adding here (or confirming it's covered by the property-based tests for the single-domain flavor).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/src/token/test/extensions/NativeShieldedTokenFamilySupply.test.ts`
around lines 63 - 78, Add a mint-overflow test to the
NativeShieldedTokenFamilySupply test suite to cover the `_addMinted` overflow
guard. In `NativeShieldedTokenFamilySupply.test.ts`, extend the
`describe('per-domain burned <= minted invariant'...)` area or nearby with a
case that calls `_addMinted` on a domain using a value that would exceed the
core arithmetic limit and asserts it rejects with `NativeShieldedTokenSupply:
arithmetic overflow`. If this path is already covered elsewhere, point to that
coverage explicitly and keep this suite focused on the family-specific
invariant.
contracts/src/token/extensions/NativeShieldedTokenSupply.compact (1)

62-64: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider marking key() as pure.

key() only returns a constant and touches neither ledger nor witness state, so it qualifies for the pure modifier (consistent with the DEFAULT_ADMIN_ROLE-style pattern used elsewhere in the codebase).

♻️ Proposed fix
-  circuit key(): Bytes<32> {
+  pure circuit key(): Bytes<32> {
     return default<Bytes<32>>;
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@contracts/src/token/extensions/NativeShieldedTokenSupply.compact` around
lines 62 - 64, The key() circuit in NativeShieldedTokenSupply only returns a
constant value and does not read ledger or witness state, so update its
declaration to use the pure modifier. Make this change on the key() method
itself, keeping the existing return behavior intact and matching the pure
pattern used for similar constant-returning methods elsewhere in the codebase.

Source: Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@contracts/src/token/extensions/NativeShieldedTokenSupply.compact`:
- Around line 62-64: The key() circuit in NativeShieldedTokenSupply only returns
a constant value and does not read ledger or witness state, so update its
declaration to use the pure modifier. Make this change on the key() method
itself, keeping the existing return behavior intact and matching the pure
pattern used for similar constant-returning methods elsewhere in the codebase.

In `@contracts/src/token/test/extensions/NativeShieldedTokenFamilySupply.test.ts`:
- Around line 63-78: Add a mint-overflow test to the
NativeShieldedTokenFamilySupply test suite to cover the `_addMinted` overflow
guard. In `NativeShieldedTokenFamilySupply.test.ts`, extend the
`describe('per-domain burned <= minted invariant'...)` area or nearby with a
case that calls `_addMinted` on a domain using a value that would exceed the
core arithmetic limit and asserts it rejects with `NativeShieldedTokenSupply:
arithmetic overflow`. If this path is already covered elsewhere, point to that
coverage explicitly and keep this suite focused on the family-specific
invariant.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 640100bc-d789-4657-9891-eb33c3fd80b1

📥 Commits

Reviewing files that changed from the base of the PR and between c096e31 and 989538d.

📒 Files selected for processing (10)
  • contracts/src/token/extensions/NativeShieldedTokenFamilySupply.compact
  • contracts/src/token/extensions/NativeShieldedTokenSupply.compact
  • contracts/src/token/extensions/NativeShieldedTokenSupplyCore.compact
  • contracts/src/token/test/extensions/NativeShieldedTokenFamilySupply.test.ts
  • contracts/src/token/test/extensions/NativeShieldedTokenSupply.property.test.ts
  • contracts/src/token/test/extensions/NativeShieldedTokenSupply.test.ts
  • contracts/src/token/test/mocks/MockNativeShieldedTokenFamilySupply.compact
  • contracts/src/token/test/mocks/MockNativeShieldedTokenSupply.compact
  • contracts/src/token/test/simulators/NativeShieldedTokenFamilySupplySimulator.ts
  • contracts/src/token/test/simulators/NativeShieldedTokenSupplySimulator.ts

0xisk added 3 commits July 2, 2026 10:45
Apply the named import/re-export pattern to the supply extensions so an
implementing contract's ledger keys read as `_totalMinted` / `_totalBurned`
rather than `NativeShieldedTokenSupplyCore__totalMinted`. This mirrors how
`NativeShieldedToken` / `NativeShieldedTokenFamily` re-export the core
metadata keys, keeping the on-chain state readable from either flavor.

* extensions: re-export `_totalMinted` / `_totalBurned` from
  `NativeShieldedTokenSupplyCore` in both `NativeShieldedTokenSupply` and
  `NativeShieldedTokenFamilySupply`; circuits keep the `Core_` prefix.
* mocks: surface the same keys unprefixed so tests can read them via
  `getPublicState()`.
* tests: add a simulator-wiring case to each unit suite asserting the
  getters return the ledger slots verbatim.

Addresses review feedback on the supply extension.
`totalSupply` computed `minted - burned`, which reverts on underflow if
`burned` ever exceeds `minted`. The `_addBurned` invariant makes that
state unreachable through the accounting API, but a getter should read
cleanly rather than revert on an out-of-bound state. Clamp to 0 so
`totalSupply` is a total function.

* SupplyCore: return `minted >= burned ? minted - burned : 0`; the
  guarded subtraction also nets one row cheaper (rows 440 -> 439, and
  the scalar wrapper 161 -> 160).
* mocks: add a test-only `unsafeSetBurned` that writes `_totalBurned`
  directly, bypassing the invariant, so tests can reach `burned > minted`.
* simulators/tests: expose `unsafeSetBurned` and assert `totalSupply`
  reads 0 on the corrupted state (scalar and per-domain).

Addresses review feedback on the supply extension.
Consolidate the supply property test to cover both flavors in a single
file: the scalar `NativeShieldedTokenSupply` and the per-domain
`NativeShieldedTokenFamilySupply`. Both delegate to the shared
`NativeShieldedTokenSupplyCore`, so the core accounting is fuzzed
transitively through each; no standalone core mock is needed.

* keep the scalar sequence property (totalMinted exact, totalSupply =
  minted - burned, burned <= minted)
* add a per-domain family property that interleaves ops across three
  domains and checks the same invariants per domain

Stays a unit-level property test (in-memory simulator, no deploy); an
on-chain mint/burn-vs-counter scenario is left to the integration suite.

Addresses review feedback on the supply extension.
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.

Add Native Shielded Token supply extension

2 participants