@openzeppelin/compact-contracts 0.3.0-alpha → 0.3.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +7 -7
  2. package/access/AccessControl.compact +1 -1
  3. package/access/Ownable.compact +1 -1
  4. package/access/ShieldedAccessControl.compact +1 -1
  5. package/access/ZOwnablePK.compact +1 -1
  6. package/crypto/EcdhMask.compact +177 -0
  7. package/crypto/ElGamal.compact +1 -1
  8. package/multisig/ForwarderPrivate.compact +16 -12
  9. package/multisig/ForwarderShielded.compact +2 -2
  10. package/multisig/ForwarderUnshielded.compact +2 -2
  11. package/multisig/ProposalManager.compact +35 -1
  12. package/multisig/ShieldedTreasury.compact +46 -13
  13. package/multisig/ShieldedTreasuryStateless.compact +56 -23
  14. package/multisig/Signer.compact +45 -41
  15. package/multisig/UnshieldedTreasury.compact +44 -17
  16. package/multisig/presets/ShieldedMultiSig.compact +10 -19
  17. package/multisig/presets/ShieldedMultiSigV2.compact +2 -2
  18. package/multisig/presets/ShieldedMultiSigV3.compact +2 -2
  19. package/multisig/presets/forwarder/ForwarderPrivate.compact +1 -1
  20. package/multisig/presets/forwarder/ForwarderShielded.compact +1 -1
  21. package/multisig/presets/forwarder/ForwarderUnshielded.compact +1 -1
  22. package/package.json +32 -20
  23. package/security/Allowlist.compact +1 -1
  24. package/security/Blocklist.compact +1 -1
  25. package/security/Initializable.compact +1 -1
  26. package/security/Pausable.compact +1 -1
  27. package/token/ConfidentialFungibleToken.compact +1208 -0
  28. package/token/FungibleToken.compact +1 -1
  29. package/token/MultiToken.compact +1 -1
  30. package/token/NativeShieldedToken.compact +2 -2
  31. package/token/NativeShieldedTokenCore.compact +1 -1
  32. package/token/NativeShieldedTokenFamily.compact +2 -2
  33. package/token/NonFungibleToken.compact +1 -1
  34. package/token/extensions/ConfidentialFungibleTokenPublicSupply.compact +90 -0
  35. package/token/extensions/NativeShieldedTokenDerivedNonce.compact +70 -0
  36. package/token/extensions/NativeShieldedTokenFamilyPublicSupply.compact +145 -0
  37. package/token/extensions/NativeShieldedTokenPublicSupply.compact +142 -0
  38. package/token/extensions/NativeShieldedTokenPublicSupplyCore.compact +163 -0
  39. package/utils/Utils.compact +1 -1
  40. package/multisig/SignerManager.compact +0 -205
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.3.0-alpha (token/FungibleToken.compact)
2
+ // OpenZeppelin Compact Contracts v0.3.0-alpha.2 (token/FungibleToken.compact)
3
3
 
4
4
  pragma language_version >= 0.23.0;
5
5
 
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.3.0-alpha (token/MultiToken.compact)
2
+ // OpenZeppelin Compact Contracts v0.3.0-alpha.2 (token/MultiToken.compact)
3
3
 
4
4
  pragma language_version >= 0.23.0;
5
5
 
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.3.0-alpha (token/NativeShieldedToken.compact)
2
+ // OpenZeppelin Compact Contracts v0.3.0-alpha.2 (token/NativeShieldedToken.compact)
3
3
 
4
4
  pragma language_version >= 0.23.0;
5
5
 
@@ -45,7 +45,7 @@ pragma language_version >= 0.23.0;
45
45
  * recipient-public.
46
46
  *
47
47
  * @notice Supply accounting is opt-in: this module tracks NO supply totals.
48
- * Compose the optional `NativeShieldedTokenSupply` extension only if you need an
48
+ * Compose the optional `NativeShieldedTokenPublicSupply` extension only if you need an
49
49
  * on-chain `totalSupply()`; see it for the privacy trade-off it introduces (it
50
50
  * makes contract-mediated burn amounts public).
51
51
  *
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.3.0-alpha (token/NativeShieldedTokenCore.compact)
2
+ // OpenZeppelin Compact Contracts v0.3.0-alpha.2 (token/NativeShieldedTokenCore.compact)
3
3
 
4
4
  pragma language_version >= 0.23.0;
5
5
 
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.3.0-alpha (token/NativeShieldedTokenFamily.compact)
2
+ // OpenZeppelin Compact Contracts v0.3.0-alpha.2 (token/NativeShieldedTokenFamily.compact)
3
3
 
4
4
  pragma language_version >= 0.23.0;
5
5
 
@@ -53,7 +53,7 @@ pragma language_version >= 0.23.0;
53
53
  * output, at the cost of making those mints recipient-public.
54
54
  *
55
55
  * @notice Supply accounting is opt-in: this module tracks NO supply totals.
56
- * Compose the optional `NativeShieldedTokenFamilySupply` extension only if you
56
+ * Compose the optional `NativeShieldedTokenFamilyPublicSupply` extension only if you
57
57
  * need an on-chain `totalSupply(domain)`; see it for the privacy trade-off it
58
58
  * introduces (it makes contract-mediated burn amounts public).
59
59
  *
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.3.0-alpha (token/NonFungibleToken.compact)
2
+ // OpenZeppelin Compact Contracts v0.3.0-alpha.2 (token/NonFungibleToken.compact)
3
3
 
4
4
  pragma language_version >= 0.23.0;
5
5
 
@@ -0,0 +1,90 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // OpenZeppelin Compact Contracts v0.3.0-alpha.2 (token/extensions/ConfidentialFungibleTokenPublicSupply.compact)
3
+
4
+ pragma language_version >= 0.23.0;
5
+
6
+ /**
7
+ * @module ConfidentialFungibleTokenPublicSupply
8
+ * @description Optional standalone extension that adds public supply
9
+ * accounting to a `ConfidentialFungibleToken`. It exposes the scalar
10
+ * `_addSupply` / `_subSupply` building blocks plus the `totalSupply` getter.
11
+ * It imports no token module.
12
+ *
13
+ * "Public" supply means the total, and therefore each mint/burn delta, is
14
+ * disclosed on chain. Balances stay confidential; only the aggregate is
15
+ * visible. This is the right default for issuers who must attest to a public
16
+ * circulating supply (stablecoins, tokenized funds). A confidential-supply
17
+ * extension (ElGamal supply cell + auditor viewing key) is a separate variant.
18
+ *
19
+ * @notice Pairs with `ConfidentialFungibleToken`. The consuming contract
20
+ * composes the pieces, calling the accounting block alongside the matching
21
+ * token op (the integration suite drives an assembled example,
22
+ * `test/integration/_mocks/ConfidentialFungibleTokenPublicSupply`):
23
+ *
24
+ * export circuit mint(account: Bytes<32>, value: Uint<128>): [] {
25
+ * Supply__addSupply(value);
26
+ * Token__mint(account, value);
27
+ * }
28
+ *
29
+ * The pairing is the consuming contract's responsibility: a token op called
30
+ * without its accounting block (or vice versa) breaks the
31
+ * `sum(balances) == totalSupply` invariant this extension exists to attest.
32
+ */
33
+ module ConfidentialFungibleTokenPublicSupply {
34
+ import CompactStandardLibrary;
35
+
36
+ // The public circulating supply. Mint and burn disclose their amounts
37
+ // through the delta on this value.
38
+ export ledger _totalSupply: Uint<128>;
39
+
40
+ /**
41
+ * @description Increases `totalSupply` by `value`. Call alongside the token
42
+ * module's `_mint`. The added amount is disclosed via the supply delta.
43
+ *
44
+ * @circuitInfo k=9, rows=401
45
+ *
46
+ * Requirements:
47
+ *
48
+ * - `totalSupply + value` does not overflow `Uint<128>`.
49
+ *
50
+ * @param {Uint<128>} value - The amount added to the supply.
51
+ * @return {[]} - Empty tuple.
52
+ */
53
+ export circuit _addSupply(value: Uint<128>): [] {
54
+ const MAX_UINT128 = 340282366920938463463374607431768211455;
55
+ assert(MAX_UINT128 - _totalSupply >= value,
56
+ "ConfidentialFungibleTokenPublicSupply: overflow");
57
+ _totalSupply = disclose(_totalSupply + value as Uint<128>);
58
+ }
59
+
60
+ /**
61
+ * @description Decreases `totalSupply` by `value`. Call alongside the token
62
+ * module's `_burn` or `_burnFrom`. The removed amount is disclosed via the
63
+ * supply delta.
64
+ *
65
+ * @circuitInfo k=9, rows=256
66
+ *
67
+ * Requirements:
68
+ *
69
+ * - `totalSupply` is at least `value`.
70
+ *
71
+ * @param {Uint<128>} value - The amount removed from the supply.
72
+ * @return {[]} - Empty tuple.
73
+ */
74
+ export circuit _subSupply(value: Uint<128>): [] {
75
+ assert(_totalSupply >= value,
76
+ "ConfidentialFungibleTokenPublicSupply: supply underflow");
77
+ _totalSupply = disclose(_totalSupply - value as Uint<128>);
78
+ }
79
+
80
+ /**
81
+ * @description Returns the public total token supply.
82
+ *
83
+ * @circuitInfo k=6, rows=26
84
+ *
85
+ * @return {Uint<128>} - The total supply.
86
+ */
87
+ export circuit totalSupply(): Uint<128> {
88
+ return _totalSupply;
89
+ }
90
+ }
@@ -0,0 +1,70 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // OpenZeppelin Compact Contracts v0.3.0-alpha.2 (token/extensions/NativeShieldedTokenDerivedNonce.compact)
3
+
4
+ pragma language_version >= 0.23.0;
5
+
6
+ /**
7
+ * @module NativeShieldedTokenDerivedNonce
8
+ * @description Optional standalone extension providing derived coin nonces for
9
+ * native shielded token mints, so callers need not manage nonces themselves. It
10
+ * owns a monotonic `_counter` and exposes `_deriveNonce`; it imports no token
11
+ * module and needs no initialization.
12
+ *
13
+ * @notice Pairs with `NativeShieldedToken` and `NativeShieldedTokenFamily`. The
14
+ * consuming contract composes the pieces:
15
+ *
16
+ * export circuit mint(recipient: ..., amount: Uint<64>): ShieldedCoinInfo {
17
+ * return Token__mint(recipient, amount, Derived__deriveNonce());
18
+ * }
19
+ *
20
+ * @dev Derivation
21
+ * Each call derives `evolveNonce(_counter, pad(32, "NativeShieldedToken:nonce"))`.
22
+ * `evolveNonce` hashes the counter into a full 32-byte nonce, so uniqueness comes
23
+ * from the monotonic counter, not the tag. The fixed tag is a domain separator:
24
+ * it keeps these derived nonces from clashing with any other `evolveNonce` call
25
+ * the implementing contract makes on the same counter for a different purpose.
26
+ * One counter serves the whole contract, so derived nonces never repeat across
27
+ * token types (domains) of one contract.
28
+ *
29
+ * @notice This variant is deterministic and recipient-PUBLIC by design: derived
30
+ * nonces are predictable from public state, so anyone can recompute the coin
31
+ * commitment for candidate recipient keys. For recipient privacy, call the base
32
+ * `_mint` with a secret, cryptographically random nonce instead. (An earlier
33
+ * design seeded a hash chain to make the sequence look random; that bought
34
+ * nothing here because the seed and chain are disclosed to public state, so it
35
+ * was removed.)
36
+ *
37
+ * @notice Collision-griefing. Derived nonces are predictable, so an actor with a
38
+ * caller-nonce mint can pre-mint a colliding commitment and make the matching
39
+ * derived-nonce mint fail. Mitigations:
40
+ * - Access-control both mint paths, and avoid exposing them at different trust
41
+ * levels.
42
+ * - Do not rely on retrying. The nonce is a pure function of the counter, and a
43
+ * reverted mint will not advance it, so an identical retry collides again.
44
+ * Recovery is a mint with different inputs (recipient or amount): it advances
45
+ * the counter, so the original tuple then mints against a fresh nonce.
46
+ */
47
+ module NativeShieldedTokenDerivedNonce {
48
+ import CompactStandardLibrary;
49
+
50
+ /**
51
+ * @description Monotonic index feeding the derived-nonce derivation.
52
+ */
53
+ export ledger _counter: Counter;
54
+
55
+ /**
56
+ * @description Advances the counter and returns the next derived coin nonce,
57
+ * for use as the `nonce` argument of a native token mint.
58
+ *
59
+ * @notice The derivation input is public ledger state, so mints using this
60
+ * nonce are recipient-public; see the module notes.
61
+ *
62
+ * @circuitInfo k=9, rows=451
63
+ *
64
+ * @return {Bytes<32>} - The next derived coin nonce.
65
+ */
66
+ export circuit _deriveNonce(): Bytes<32> {
67
+ _counter.increment(1);
68
+ return evolveNonce(_counter, pad(32, "NativeShieldedToken:nonce"));
69
+ }
70
+ }
@@ -0,0 +1,145 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // OpenZeppelin Compact Contracts v0.2.0 (token/extensions/NativeShieldedTokenFamilyPublicSupply.compact)
3
+
4
+ pragma language_version >= 0.23.0;
5
+
6
+ /**
7
+ * @module NativeShieldedTokenFamilyPublicSupply
8
+ * @description Optional standalone extension that adds per-domain on-chain
9
+ * supply accounting to a `NativeShieldedTokenFamily`. It exposes `_addMinted` /
10
+ * `_addBurned` building blocks plus the `totalMinted` / `totalBurned` /
11
+ * `totalSupply` getters, each keyed by domain. It imports no token module.
12
+ *
13
+ * It is a thin wrapper over `NativeShieldedTokenPublicSupplyCore`, forwarding the
14
+ * caller's per-call `domain`, so the accounting logic and guarantees live in
15
+ * the shared core.
16
+ *
17
+ * @notice Pairs with `NativeShieldedTokenFamily`. The consuming contract
18
+ * composes the pieces, calling the accounting block alongside the matching
19
+ * token op:
20
+ *
21
+ * export circuit mint(
22
+ * domain: Bytes<32>,
23
+ * recipient: Either<ZswapCoinPublicKey, ContractAddress>,
24
+ * amount: Uint<64>,
25
+ * nonce: Bytes<32>
26
+ * ): ShieldedCoinInfo {
27
+ * const coin = Family__mint(domain, recipient, amount, nonce);
28
+ * Supply__addMinted(domain, amount);
29
+ * return coin;
30
+ * }
31
+ *
32
+ * export circuit burn(
33
+ * domain: Bytes<32>,
34
+ * coin: ShieldedCoinInfo,
35
+ * amount: Uint<128>,
36
+ * refundTo: Either<ZswapCoinPublicKey, ContractAddress>
37
+ * ): Maybe<ShieldedCoinInfo> {
38
+ * const refund = Family__burn(domain, coin, amount, refundTo);
39
+ * Supply__addBurned(domain, amount);
40
+ * return refund;
41
+ * }
42
+ *
43
+ * @notice Privacy trade-off (read before composing): composing this extension
44
+ * makes contract-mediated BURN amounts PUBLIC. The bare family's `_burn` /
45
+ * `_burnFromSelf` are amount-private (their coin operations emit only
46
+ * commitments and nullifiers, never the value; the `disclose()` wrappers they
47
+ * require are compiler permission markers, not disclosure sinks). The
48
+ * `_addBurned` write here is what puts the burned amount into the public
49
+ * transcript. Compose this only when on-chain auditability is worth more than
50
+ * burn-amount privacy.
51
+ *
52
+ * @dev Mint amounts are already public via the protocol's `shieldedMints`
53
+ * effect, so `_addMinted` adds accounting, not disclosure. See
54
+ * `NativeShieldedTokenPublicSupplyCore` for the exact / lower-bound / upper-bound
55
+ * guarantees, the per-domain `burned <= minted` invariant, and the mint-side
56
+ * drift caveat.
57
+ *
58
+ * @warning Pair every mint with `_addMinted` and every burn with `_addBurned` on
59
+ * every path, under the matching domain; mis-wiring is a security-critical,
60
+ * undetectable error (e.g. an undercounted mint makes `_addBurned` reject
61
+ * legitimate burns). See `NativeShieldedTokenPublicSupplyCore`.
62
+ */
63
+ module NativeShieldedTokenFamilyPublicSupply {
64
+ import CompactStandardLibrary;
65
+ import "./NativeShieldedTokenPublicSupplyCore" prefix Core_;
66
+
67
+ // Circuits use the `Core_` prefix above. The named import/re-export below is
68
+ // only for the core supply ledger keys, so the implementing contract's ledger
69
+ // keys read as `_totalMinted` / `_totalBurned` rather than
70
+ // `NativeShieldedTokenPublicSupplyCore__totalMinted`. The circuit names are
71
+ // intentionally NOT imported unprefixed, to avoid potential clashes.
72
+ import { _totalMinted, _totalBurned } from "./NativeShieldedTokenPublicSupplyCore";
73
+ export { _totalMinted, _totalBurned };
74
+
75
+ /**
76
+ * @description Adds `amount` to the exact minted total for `domain`. Call
77
+ * once per successful `NativeShieldedTokenFamily._mint`.
78
+ *
79
+ * @circuitInfo k=10, rows=653
80
+ *
81
+ * @param {Bytes<32>} domain - 32-byte token discriminator.
82
+ * @param {Uint<64>} amount - The minted amount.
83
+ * @return {[]} - Empty tuple.
84
+ */
85
+ export circuit _addMinted(domain: Bytes<32>, amount: Uint<64>): [] {
86
+ Core__addMinted(domain, amount);
87
+ }
88
+
89
+ /**
90
+ * @description Adds `amount` to the contract-mediated burned total for
91
+ * `domain`. Call once per successful `NativeShieldedTokenFamily._burn` /
92
+ * `_burnFromSelf`.
93
+ * @dev Enforces per-domain `burned <= minted`; an unpaired, duplicate, or
94
+ * wrong-domain burn reverts.
95
+ *
96
+ * @circuitInfo k=10, rows=755
97
+ *
98
+ * @param {Bytes<32>} domain - 32-byte token discriminator.
99
+ * @param {Uint<128>} amount - The burned amount.
100
+ * @return {[]} - Empty tuple.
101
+ */
102
+ export circuit _addBurned(domain: Bytes<32>, amount: Uint<128>): [] {
103
+ Core__addBurned(domain, amount);
104
+ }
105
+
106
+ /**
107
+ * @description Returns the exact amount ever minted for `domain`.
108
+ *
109
+ * @circuitInfo k=9, rows=344
110
+ *
111
+ * @param {Bytes<32>} domain - 32-byte token discriminator.
112
+ * @return {Uint<128>} - The total amount minted.
113
+ */
114
+ export circuit totalMinted(domain: Bytes<32>): Uint<128> {
115
+ return Core_totalMinted(domain);
116
+ }
117
+
118
+ /**
119
+ * @description Returns the contract-mediated amount burned for `domain`.
120
+ * @notice This is a lower bound: coins sent directly to the burn address
121
+ * without going through the contract are not counted.
122
+ *
123
+ * @circuitInfo k=9, rows=341
124
+ *
125
+ * @param {Bytes<32>} domain - 32-byte token discriminator.
126
+ * @return {Uint<128>} - The total amount burned through the contract.
127
+ */
128
+ export circuit totalBurned(domain: Bytes<32>): Uint<128> {
129
+ return Core_totalBurned(domain);
130
+ }
131
+
132
+ /**
133
+ * @description Returns `totalMinted(domain) - totalBurned(domain)`.
134
+ * @notice This is an UPPER BOUND on circulating supply, not an exact value:
135
+ * burns that bypass the contract are invisible. See the module notes.
136
+ *
137
+ * @circuitInfo k=9, rows=439
138
+ *
139
+ * @param {Bytes<32>} domain - 32-byte token discriminator.
140
+ * @return {Uint<128>} - The upper bound on tokens in existence.
141
+ */
142
+ export circuit totalSupply(domain: Bytes<32>): Uint<128> {
143
+ return Core_totalSupply(domain);
144
+ }
145
+ }
@@ -0,0 +1,142 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // OpenZeppelin Compact Contracts v0.2.0 (token/extensions/NativeShieldedTokenPublicSupply.compact)
3
+
4
+ pragma language_version >= 0.23.0;
5
+
6
+ /**
7
+ * @module NativeShieldedTokenPublicSupply
8
+ * @description Optional standalone extension that adds on-chain supply
9
+ * accounting to a `NativeShieldedToken`. It exposes scalar `_addMinted` /
10
+ * `_addBurned` building blocks plus the `totalMinted` / `totalBurned` /
11
+ * `totalSupply` getters. It imports no token module.
12
+ *
13
+ * It is a thin wrapper over `NativeShieldedTokenPublicSupplyCore`, forwarding a
14
+ * single fixed key (the paired contract has exactly one token type), so the
15
+ * accounting logic and guarantees live in the shared core.
16
+ *
17
+ * @notice Pairs with `NativeShieldedToken`. The consuming contract composes the
18
+ * pieces, calling the accounting block alongside the matching token op:
19
+ *
20
+ * export circuit mint(
21
+ * recipient: Either<ZswapCoinPublicKey, ContractAddress>,
22
+ * amount: Uint<64>,
23
+ * nonce: Bytes<32>
24
+ * ): ShieldedCoinInfo {
25
+ * const coin = Token__mint(recipient, amount, nonce);
26
+ * Supply__addMinted(amount);
27
+ * return coin;
28
+ * }
29
+ *
30
+ * export circuit burn(
31
+ * coin: ShieldedCoinInfo,
32
+ * amount: Uint<128>,
33
+ * refundTo: Either<ZswapCoinPublicKey, ContractAddress>
34
+ * ): Maybe<ShieldedCoinInfo> {
35
+ * const refund = Token__burn(coin, amount, refundTo);
36
+ * Supply__addBurned(amount);
37
+ * return refund;
38
+ * }
39
+ *
40
+ * @notice Privacy trade-off (read before composing): composing this extension
41
+ * makes contract-mediated BURN amounts PUBLIC. The bare token's `_burn` /
42
+ * `_burnFromSelf` are amount-private (their coin operations emit only
43
+ * commitments and nullifiers, never the value; the `disclose()` wrappers they
44
+ * require are compiler permission markers, not disclosure sinks). The
45
+ * `_addBurned` write here is what puts the burned amount into the public
46
+ * transcript. Compose this only when on-chain auditability is worth more than
47
+ * burn-amount privacy.
48
+ *
49
+ * @dev Mint amounts are already public via the protocol's `shieldedMints`
50
+ * effect, so `_addMinted` adds accounting, not disclosure. See
51
+ * `NativeShieldedTokenPublicSupplyCore` for the exact / lower-bound / upper-bound
52
+ * guarantees, the `burned <= minted` invariant, and the mint-side drift caveat.
53
+ *
54
+ * @warning Pair every mint with `_addMinted` and every burn with `_addBurned` on
55
+ * every path; mis-wiring is a security-critical, undetectable error (e.g. an
56
+ * undercounted mint makes `_addBurned` reject legitimate burns). See
57
+ * `NativeShieldedTokenPublicSupplyCore`.
58
+ */
59
+ module NativeShieldedTokenPublicSupply {
60
+ import CompactStandardLibrary;
61
+ import "./NativeShieldedTokenPublicSupplyCore" prefix Core_;
62
+
63
+ // Circuits use the `Core_` prefix above. The named import/re-export below is
64
+ // only for the core supply ledger keys, so the implementing contract's ledger
65
+ // keys read as `_totalMinted` / `_totalBurned` rather than
66
+ // `NativeShieldedTokenPublicSupplyCore__totalMinted`. The circuit names are
67
+ // intentionally NOT imported unprefixed, to avoid potential clashes.
68
+ import { _totalMinted, _totalBurned } from "./NativeShieldedTokenPublicSupplyCore";
69
+ export { _totalMinted, _totalBurned };
70
+
71
+ /**
72
+ * @description The single fixed domain key. The paired `NativeShieldedToken`
73
+ * has exactly one token type, so all accounting lives under one key.
74
+ */
75
+ circuit key(): Bytes<32> {
76
+ return default<Bytes<32>>;
77
+ }
78
+
79
+ /**
80
+ * @description Adds `amount` to the exact minted total. Call once per
81
+ * successful `NativeShieldedToken._mint`.
82
+ *
83
+ * @circuitInfo k=9, rows=375
84
+ *
85
+ * @param {Uint<64>} amount - The minted amount.
86
+ * @return {[]} - Empty tuple.
87
+ */
88
+ export circuit _addMinted(amount: Uint<64>): [] {
89
+ Core__addMinted(key(), amount);
90
+ }
91
+
92
+ /**
93
+ * @description Adds `amount` to the contract-mediated burned total. Call once
94
+ * per successful `NativeShieldedToken._burn` / `_burnFromSelf`.
95
+ * @dev Enforces `burned <= minted`; an unpaired or duplicate burn reverts.
96
+ *
97
+ * @circuitInfo k=9, rows=477
98
+ *
99
+ * @param {Uint<128>} amount - The burned amount.
100
+ * @return {[]} - Empty tuple.
101
+ */
102
+ export circuit _addBurned(amount: Uint<128>): [] {
103
+ Core__addBurned(key(), amount);
104
+ }
105
+
106
+ /**
107
+ * @description Returns the exact amount ever minted.
108
+ *
109
+ * @circuitInfo k=7, rows=65
110
+ *
111
+ * @return {Uint<128>} - The total amount minted.
112
+ */
113
+ export circuit totalMinted(): Uint<128> {
114
+ return Core_totalMinted(key());
115
+ }
116
+
117
+ /**
118
+ * @description Returns the contract-mediated amount burned.
119
+ * @notice This is a lower bound: coins sent directly to the burn address
120
+ * without going through the contract are not counted.
121
+ *
122
+ * @circuitInfo k=7, rows=62
123
+ *
124
+ * @return {Uint<128>} - The total amount burned through the contract.
125
+ */
126
+ export circuit totalBurned(): Uint<128> {
127
+ return Core_totalBurned(key());
128
+ }
129
+
130
+ /**
131
+ * @description Returns `totalMinted() - totalBurned()`.
132
+ * @notice This is an UPPER BOUND on circulating supply, not an exact value:
133
+ * burns that bypass the contract are invisible. See the module notes.
134
+ *
135
+ * @circuitInfo k=9, rows=160
136
+ *
137
+ * @return {Uint<128>} - The upper bound on tokens in existence.
138
+ */
139
+ export circuit totalSupply(): Uint<128> {
140
+ return Core_totalSupply(key());
141
+ }
142
+ }
@@ -0,0 +1,163 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // OpenZeppelin Compact Contracts v0.2.0 (token/extensions/NativeShieldedTokenPublicSupplyCore.compact)
3
+
4
+ pragma language_version >= 0.23.0;
5
+
6
+ /**
7
+ * @module NativeShieldedTokenPublicSupplyCore
8
+ * @description Shared, domain-keyed core of the optional supply-accounting
9
+ * extension. It owns the per-domain minted/burned maps and exposes the
10
+ * `_addMinted` / `_addBurned` building blocks plus the `totalMinted` /
11
+ * `totalBurned` / `totalSupply` getters, each keyed by domain.
12
+ *
13
+ * This module is NOT meant to be composed directly. It is the de-duplicated
14
+ * base behind the two user-facing flavors:
15
+ *
16
+ * - `NativeShieldedTokenPublicSupply` — pairs with `NativeShieldedToken`; forwards a
17
+ * single fixed key (the contract has one token type).
18
+ * - `NativeShieldedTokenFamilyPublicSupply` — pairs with `NativeShieldedTokenFamily`;
19
+ * forwards the caller's per-call `domain`.
20
+ *
21
+ * @dev Assertion messages use the generic `NativeShieldedTokenPublicSupply:` prefix
22
+ * so they read sensibly from either flavor. There is no initialization: an
23
+ * absent domain key reads as 0, and the getters do not gate on the paired token
24
+ * module's init flag (a counter reading 0 before init is correct), so init
25
+ * enforcement stays with the token module's mint/burn circuits.
26
+ *
27
+ * @notice Accounting guarantees (when wired correctly by the consumer):
28
+ * `totalMinted(domain)` is EXACT, `totalBurned(domain)` a LOWER BOUND (burns
29
+ * that bypass the contract are invisible), and `totalSupply(domain)` =
30
+ * minted - burned an UPPER BOUND on circulating supply. These hold only if the
31
+ * consumer pairs every `_mint` with `_addMinted` and every burn with
32
+ * `_addBurned` under the same domain; the extension cannot enforce the pairing
33
+ * itself.
34
+ *
35
+ * @notice `_addBurned` enforces `burned <= minted` per domain, so a
36
+ * wrong-domain, duplicate, or unpaired BURN reverts rather than pushing
37
+ * `totalSupply(domain)` negative (this also keeps the `_addBurned` add and the
38
+ * `totalSupply` subtraction within bounds). It does NOT catch mint-side drift:
39
+ * a duplicated `_addMinted`, or one keyed to the wrong domain, silently inflates
40
+ * `totalMinted` (and the `burned <= minted` headroom). Correct mint pairing
41
+ * remains the consumer's responsibility.
42
+ *
43
+ * @warning The mint/burn <-> accounting pairing is a security-critical invariant
44
+ * of the CONSUMING contract; this extension cannot enforce or detect a breach:
45
+ * - A mint whose `_addMinted` is missing or wrong-domain leaves `totalMinted`
46
+ * too low, so `_addBurned` REJECTS burns that are actually legitimate —
47
+ * holders cannot burn coins that genuinely exist.
48
+ * - A duplicate or wrong-domain `_addMinted` inflates `totalMinted`, letting
49
+ * `_addBurned` accept burns beyond what was minted.
50
+ * - A missing `_addBurned` leaves `totalBurned` too low, so `totalSupply`
51
+ * overstates circulating supply.
52
+ * Call the matching accounting block on every mint and burn path, same domain.
53
+ */
54
+ module NativeShieldedTokenPublicSupplyCore {
55
+ import CompactStandardLibrary;
56
+ import "../../utils/Utils" prefix Utils_;
57
+
58
+ /**
59
+ * @description Exact amount minted per domain. See "Accounting guarantees".
60
+ * @type {Map<Bytes<32>, Uint<128>>} _totalMinted
61
+ */
62
+ export ledger _totalMinted: Map<Bytes<32>, Uint<128>>;
63
+ /**
64
+ * @description Contract-mediated amount burned per domain (lower bound).
65
+ * @type {Map<Bytes<32>, Uint<128>>} _totalBurned
66
+ */
67
+ export ledger _totalBurned: Map<Bytes<32>, Uint<128>>;
68
+
69
+ /**
70
+ * @description Adds `amount` to the exact minted total for `domain`. Call
71
+ * once per successful token `_mint`.
72
+ * @dev Checks for overflow in order to output a readable error message.
73
+ *
74
+ * @circuitInfo k=10, rows=653
75
+ *
76
+ * @param {Bytes<32>} domain - 32-byte token discriminator.
77
+ * @param {Uint<64>} amount - The minted amount.
78
+ * @return {[]} - Empty tuple.
79
+ */
80
+ export circuit _addMinted(domain: Bytes<32>, amount: Uint<64>): [] {
81
+ const current = totalMinted(domain);
82
+ assert(Utils_UINT128_MAX() - current >= amount, "NativeShieldedTokenPublicSupply: arithmetic overflow");
83
+ _totalMinted.insert(disclose(domain), disclose((current + amount) as Uint<128>));
84
+ }
85
+
86
+ /**
87
+ * @description Adds `amount` to the contract-mediated burned total for
88
+ * `domain`. Call once per successful token `_burn` / `_burnFromSelf`.
89
+ * @dev Enforces the per-domain `burned <= minted` invariant: a wrong-domain,
90
+ * duplicate, or unpaired burn reverts rather than corrupting `totalSupply`.
91
+ * The guard also keeps the add and the `totalSupply` subtraction within
92
+ * bounds, so no separate overflow/underflow check is needed.
93
+ *
94
+ * @circuitInfo k=10, rows=755
95
+ *
96
+ * @param {Bytes<32>} domain - 32-byte token discriminator.
97
+ * @param {Uint<128>} amount - The burned amount.
98
+ * @return {[]} - Empty tuple.
99
+ */
100
+ export circuit _addBurned(domain: Bytes<32>, amount: Uint<128>): [] {
101
+ const minted = totalMinted(domain);
102
+ const current = totalBurned(domain);
103
+ // First guards the subtraction below from underflowing on an already-corrupt
104
+ // state (stored burned past minted); the second is the reachable per-call
105
+ // check that this burn does not push the domain past its minted total.
106
+ assert(minted >= current, "NativeShieldedTokenPublicSupply: burned total exceeds minted");
107
+ assert(minted - current >= amount, "NativeShieldedTokenPublicSupply: burn exceeds available supply");
108
+ _totalBurned.insert(disclose(domain), disclose((current + amount) as Uint<128>));
109
+ }
110
+
111
+ /**
112
+ * @description Returns the exact amount ever minted for `domain`.
113
+ *
114
+ * @circuitInfo k=9, rows=344
115
+ *
116
+ * @param {Bytes<32>} domain - 32-byte token discriminator.
117
+ * @return {Uint<128>} - The total amount minted.
118
+ */
119
+ export circuit totalMinted(domain: Bytes<32>): Uint<128> {
120
+ if (!_totalMinted.member(disclose(domain))) {
121
+ return 0;
122
+ }
123
+ return _totalMinted.lookup(disclose(domain));
124
+ }
125
+
126
+ /**
127
+ * @description Returns the contract-mediated amount burned for `domain`.
128
+ * @notice This is a lower bound: coins sent directly to the burn address
129
+ * without going through the contract are not counted.
130
+ *
131
+ * @circuitInfo k=9, rows=341
132
+ *
133
+ * @param {Bytes<32>} domain - 32-byte token discriminator.
134
+ * @return {Uint<128>} - The total amount burned through the contract.
135
+ */
136
+ export circuit totalBurned(domain: Bytes<32>): Uint<128> {
137
+ if (!_totalBurned.member(disclose(domain))) {
138
+ return 0;
139
+ }
140
+ return _totalBurned.lookup(disclose(domain));
141
+ }
142
+
143
+ /**
144
+ * @description Returns `totalMinted(domain) - totalBurned(domain)`, or 0 if
145
+ * `totalBurned` somehow exceeds `totalMinted`.
146
+ * @notice This is an UPPER BOUND on circulating supply, not an exact value:
147
+ * burns that bypass the contract are invisible. See the module notes.
148
+ * @dev The `burned <= minted` invariant enforced by `_addBurned` already makes
149
+ * `burned > minted` unreachable through the accounting API, so the clamp never
150
+ * fires in normal operation. It is kept so the getter is a total function that
151
+ * reads cleanly (returning 0) rather than reverting on an out-of-bound state.
152
+ *
153
+ * @circuitInfo k=9, rows=439
154
+ *
155
+ * @param {Bytes<32>} domain - 32-byte token discriminator.
156
+ * @return {Uint<128>} - The upper bound on tokens in existence.
157
+ */
158
+ export circuit totalSupply(domain: Bytes<32>): Uint<128> {
159
+ const minted = totalMinted(domain);
160
+ const burned = totalBurned(domain);
161
+ return (minted >= burned ? (minted - burned) : 0) as Uint<128>;
162
+ }
163
+ }