@openzeppelin/compact-contracts 0.2.0 → 0.3.0-alpha.1

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 +10 -10
  2. package/access/AccessControl.compact +3 -35
  3. package/access/Ownable.compact +5 -51
  4. package/access/ShieldedAccessControl.compact +2 -2
  5. package/access/ZOwnablePK.compact +2 -2
  6. package/crypto/EcdhMask.compact +177 -0
  7. package/crypto/ElGamal.compact +473 -0
  8. package/multisig/ForwarderPrivate.compact +233 -0
  9. package/multisig/ForwarderShielded.compact +149 -0
  10. package/multisig/ForwarderUnshielded.compact +152 -0
  11. package/multisig/ProposalManager.compact +328 -0
  12. package/multisig/ShieldedTreasury.compact +204 -0
  13. package/multisig/ShieldedTreasuryStateless.compact +80 -0
  14. package/multisig/Signer.compact +312 -0
  15. package/multisig/SignerManager.compact +205 -0
  16. package/multisig/UnshieldedTreasury.compact +114 -0
  17. package/multisig/presets/ShieldedMultiSig.compact +227 -0
  18. package/multisig/presets/ShieldedMultiSigV2.compact +280 -0
  19. package/multisig/presets/ShieldedMultiSigV3.compact +368 -0
  20. package/multisig/presets/forwarder/ForwarderPrivate.compact +125 -0
  21. package/multisig/presets/forwarder/ForwarderShielded.compact +62 -0
  22. package/multisig/presets/forwarder/ForwarderUnshielded.compact +64 -0
  23. package/package.json +32 -16
  24. package/security/Allowlist.compact +101 -0
  25. package/security/Blocklist.compact +99 -0
  26. package/security/Initializable.compact +2 -2
  27. package/security/Pausable.compact +2 -2
  28. package/token/ConfidentialFungibleToken.compact +1180 -0
  29. package/token/FungibleToken.compact +24 -82
  30. package/token/MultiToken.compact +21 -80
  31. package/token/NativeShieldedToken.compact +285 -0
  32. package/token/NativeShieldedTokenCore.compact +343 -0
  33. package/token/NativeShieldedTokenFamily.compact +287 -0
  34. package/token/NonFungibleToken.compact +25 -83
  35. package/token/extensions/ConfidentialFungibleTokenPublicSupply.compact +90 -0
  36. package/token/extensions/NativeShieldedTokenDerivedNonce.compact +70 -0
  37. package/token/extensions/NativeShieldedTokenFamilySupply.compact +145 -0
  38. package/token/extensions/NativeShieldedTokenSupply.compact +142 -0
  39. package/token/extensions/NativeShieldedTokenSupplyCore.compact +163 -0
  40. package/utils/Utils.compact +86 -2
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.2.0 (token/FungibleToken.compact)
2
+ // OpenZeppelin Compact Contracts v0.3.0-alpha.1 (token/FungibleToken.compact)
3
3
 
4
- pragma language_version >= 0.21.0;
4
+ pragma language_version >= 0.23.0;
5
5
 
6
6
  /**
7
7
  * @module FungibleToken
@@ -111,19 +111,6 @@ module FungibleToken {
111
111
  */
112
112
  witness wit_FungibleTokenSK(): Bytes<32>;
113
113
 
114
- /**
115
- * @description Returns a canonical zero Either value for Bytes<32> and ContractAddress.
116
- * This circuit returns the left variant (Bytes<32>) to avoid misleading contract-to-contract
117
- * error messages.
118
- *
119
- * @return {Either<Bytes<32>, ContractAddress>} - The zero value.
120
- */
121
- export pure circuit ZERO(): Either<Bytes<32>, ContractAddress> {
122
- return Either<Bytes<32>, ContractAddress> {
123
- is_left: true, left: default<Bytes<32>>, right: default<ContractAddress>
124
- };
125
- }
126
-
127
114
  /**
128
115
  * @description Initializes the contract by setting the name, symbol, and decimals.
129
116
  * @dev This MUST be called in the implementing contract's constructor. Failure to do so
@@ -509,8 +496,8 @@ module FungibleToken {
509
496
  value: Uint<128>
510
497
  ): [] {
511
498
  assertInitialized();
512
- assert(!_isTargetZero(fromAddress), "FungibleToken: invalid sender");
513
- assert(!_isTargetZero(to), "FungibleToken: invalid receiver");
499
+ assert(!Utils_isTargetZero(fromAddress), "FungibleToken: invalid sender");
500
+ assert(!Utils_isTargetZero(to), "FungibleToken: invalid receiver");
514
501
 
515
502
  _update(fromAddress, to, value);
516
503
  }
@@ -539,7 +526,7 @@ module FungibleToken {
539
526
  const canonFrom = Utils_canonicalize<Bytes<32>, ContractAddress>(fromAddress);
540
527
  const canonTo = Utils_canonicalize<Bytes<32>, ContractAddress>(to);
541
528
 
542
- if (_isTargetZero(disclose(canonFrom))) {
529
+ if (Utils_isTargetZero(disclose(canonFrom))) {
543
530
  // Mint
544
531
  const MAX_UINT128 = 340282366920938463463374607431768211455;
545
532
  assert(MAX_UINT128 - _totalSupply >= value, "FungibleToken: arithmetic overflow");
@@ -551,7 +538,7 @@ module FungibleToken {
551
538
  _balances.insert(disclose(canonFrom), disclose(fromBal - value as Uint<128>));
552
539
  }
553
540
 
554
- if (_isTargetZero(disclose(canonTo))) {
541
+ if (Utils_isTargetZero(disclose(canonTo))) {
555
542
  // Burn
556
543
  _totalSupply = disclose(_totalSupply - value as Uint<128>);
557
544
  } else {
@@ -610,8 +597,8 @@ module FungibleToken {
610
597
  value: Uint<128>
611
598
  ): [] {
612
599
  assertInitialized();
613
- assert(!_isTargetZero(account), "FungibleToken: invalid receiver");
614
- _update(ZERO(), account, value);
600
+ assert(!Utils_isTargetZero(account), "FungibleToken: invalid receiver");
601
+ _update(Utils_zeroAccount(), account, value);
615
602
  }
616
603
 
617
604
  /**
@@ -632,8 +619,8 @@ module FungibleToken {
632
619
  */
633
620
  export circuit _burn(account: Either<Bytes<32>, ContractAddress>, value: Uint<128>): [] {
634
621
  assertInitialized();
635
- assert(!_isTargetZero(account), "FungibleToken: invalid sender");
636
- _update(account, ZERO(), value);
622
+ assert(!Utils_isTargetZero(account), "FungibleToken: invalid sender");
623
+ _update(account, Utils_zeroAccount(), value);
637
624
  }
638
625
 
639
626
  /**
@@ -663,8 +650,8 @@ module FungibleToken {
663
650
  const canonOwner = Utils_canonicalize<Bytes<32>, ContractAddress>(owner);
664
651
  const canonSpender = Utils_canonicalize<Bytes<32>, ContractAddress>(spender);
665
652
 
666
- assert(!_isTargetZero(canonOwner), "FungibleToken: invalid owner");
667
- assert(!_isTargetZero(canonSpender), "FungibleToken: invalid spender");
653
+ assert(!Utils_isTargetZero(canonOwner), "FungibleToken: invalid owner");
654
+ assert(!Utils_isTargetZero(canonSpender), "FungibleToken: invalid spender");
668
655
 
669
656
  if (!_allowances.member(disclose(canonOwner))) {
670
657
  // If owner doesn't exist, create and insert a new sub-map directly
@@ -701,16 +688,17 @@ module FungibleToken {
701
688
  const canonOwner = Utils_canonicalize<Bytes<32>, ContractAddress>(owner);
702
689
  const canonSpender = Utils_canonicalize<Bytes<32>, ContractAddress>(spender);
703
690
 
704
- assert((_allowances.member(disclose(canonOwner)) &&
705
- _allowances.lookup(canonOwner).member(disclose(canonSpender))),
706
- "FungibleToken: insufficient allowance"
707
- );
708
-
709
- const currentAllowance = _allowances.lookup(canonOwner).lookup(disclose(canonSpender));
710
- const MAX_UINT128 = 340282366920938463463374607431768211455;
711
- if (currentAllowance < MAX_UINT128) {
712
- assert(currentAllowance >= value, "FungibleToken: insufficient allowance");
713
- _approve(canonOwner, canonSpender, currentAllowance - value as Uint<128>);
691
+ // A missing allowance entry is treated as a zero allowance, mirroring
692
+ // `allowance`, and a zero-value spend is a no-op. Guarding on `value`
693
+ // avoids reverting (outside an assertion) on a missing entry and avoids
694
+ // writing a spurious zero entry for a zero-value spend.
695
+ if (disclose(value > 0)) {
696
+ const currentAllowance = allowance(canonOwner, canonSpender);
697
+ const MAX_UINT128 = 340282366920938463463374607431768211455;
698
+ if (currentAllowance < MAX_UINT128) {
699
+ assert(currentAllowance >= value, "FungibleToken: insufficient allowance");
700
+ _approve(canonOwner, canonSpender, currentAllowance - value as Uint<128>);
701
+ }
714
702
  }
715
703
  }
716
704
 
@@ -725,52 +713,6 @@ module FungibleToken {
725
713
  * @returns {Bytes<32>} accountId - The computed account identifier.
726
714
  */
727
715
  circuit _computeAccountId(): Bytes<32> {
728
- return computeAccountId(wit_FungibleTokenSK());
729
- }
730
-
731
- /**
732
- * @description Computes an account identifier without on-chain state, allowing a user to derive
733
- * their identity commitment before submitting a token operation.
734
- * This is the off-chain counterpart to {_computeAccountId} and produces an identical result
735
- * given the same inputs.
736
- *
737
- * @warning OpSec: The `secretKey` parameter is a sensitive secret. Mishandling it can
738
- * permanently compromise the security of this system:
739
- *
740
- * - **Never log or persist** the `secretKey` in plaintext — avoid browser devtools,
741
- * application logs, analytics pipelines, or any observable side-channel.
742
- * - **Store offline or in secure enclaves** — hardware security modules (HSMs),
743
- * air-gapped devices, or encrypted vaults are strongly preferred over hot storage.
744
- * - **Use cryptographically secure randomness** — generate keys with `crypto.getRandomValues()`
745
- * or equivalent; weak or predictable keys can be brute-forced to reveal your identity.
746
- * - **Treat key loss as identity loss** — a lost key cannot be recovered.
747
- * - **Avoid calling this circuit in untrusted environments** — executing this in an
748
- * unverified browser extension, compromised runtime, or shared machine may expose
749
- * the key to a malicious observer.
750
- *
751
- * ## ID Derivation
752
- * `accountId = persistentHash(secretKey)`
753
- *
754
- * @param {Bytes<32>} secretKey - A 32-byte cryptographically secure random value.
755
- *
756
- * @returns {Bytes<32>} accountId - The computed account identifier.
757
- */
758
- export pure circuit computeAccountId(secretKey: Bytes<32>): Bytes<32> {
759
- return persistentHash<Vector<1, Bytes<32>>>([secretKey]);
760
- }
761
-
762
- /**
763
- * @description Returns `true` if `target`'s active branch (as indicated by `is_left`)
764
- * holds the zero value.
765
- *
766
- * @param {Either<Bytes<32>, ContractAddress>} target - The value to check.
767
- * @returns {Boolean} - `true` if the active branch is zero, `false` otherwise.
768
- */
769
- circuit _isTargetZero(target: Either<Bytes<32>, ContractAddress>): Boolean {
770
- if (target.is_left) {
771
- return target.left == default<Bytes<32>>;
772
- } else {
773
- return target.right == default<ContractAddress>;
774
- }
716
+ return Utils_computeAccountId(wit_FungibleTokenSK());
775
717
  }
776
718
  }
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.2.0 (token/MultiToken.compact)
2
+ // OpenZeppelin Compact Contracts v0.3.0-alpha.1 (token/MultiToken.compact)
3
3
 
4
- pragma language_version >= 0.21.0;
4
+ pragma language_version >= 0.23.0;
5
5
 
6
6
  /**
7
7
  * @module MultiToken
@@ -173,25 +173,6 @@ module MultiToken {
173
173
  */
174
174
  witness wit_MultiTokenSK(): Bytes<32>;
175
175
 
176
- /**
177
- * @description Returns a canonical zero Either value (left variant with zero Bytes<32>).
178
- * Used as the zero value for mint/burn operations in `_update`, where a zero
179
- * `fromAddress` signals a mint and a zero `to` signals a burn.
180
- *
181
- * The left variant is chosen so that `_isTargetZero` checks against `default<Bytes<32>>`,
182
- * which is consistent with how `_update` dispatches on the zero check. A right variant
183
- * with `default<ContractAddress>` would also pass `_isTargetZero`, but using the left
184
- * variant avoids triggering contract address guards in circuits like `_mint` that check
185
- * `!to.is_left` before delegating to `_unsafeMint`.
186
- *
187
- * @return {Either<Bytes<32>, ContractAddress>} - The zero value.
188
- */
189
- export pure circuit ZERO(): Either<Bytes<32>, ContractAddress> {
190
- return Either<Bytes<32>, ContractAddress> {
191
- is_left: true, left: default<Bytes<32>>, right: default<ContractAddress>
192
- };
193
- }
194
-
195
176
  /**
196
177
  * @description Initializes the contract by setting the base URI for all tokens.
197
178
  *
@@ -441,14 +422,20 @@ module MultiToken {
441
422
  const canonFrom = Utils_canonicalize<Bytes<32>, ContractAddress>(fromAddress);
442
423
  const canonTo = Utils_canonicalize<Bytes<32>, ContractAddress>(to);
443
424
 
444
- if (!_isTargetZero(disclose(canonFrom))) {
425
+ if (!Utils_isTargetZero(disclose(canonFrom))) {
445
426
  const fromBalance = balanceOf(canonFrom, id);
446
427
  assert(fromBalance >= value, "MultiToken: insufficient balance");
447
- const newBalance = fromBalance - value;
448
- _balances.lookup(id).insert(disclose(canonFrom), disclose(newBalance));
428
+ // Skip the write when `value` is zero. A zero-value update leaves the
429
+ // balance unchanged, so there is nothing to write, and guarding here
430
+ // avoids `_balances.lookup(id)` reverting outside an assertion when `id`
431
+ // has never been initialized.
432
+ if (disclose(value > 0)) {
433
+ const newBalance = fromBalance - value;
434
+ _balances.lookup(id).insert(disclose(canonFrom), disclose(newBalance));
435
+ }
449
436
  }
450
437
 
451
- if (!_isTargetZero(disclose(canonTo))) {
438
+ if (!Utils_isTargetZero(disclose(canonTo)) && disclose(value > 0)) {
452
439
  if (!_balances.member(disclose(id))) {
453
440
  _balances.insert(
454
441
  disclose(id),
@@ -540,8 +527,8 @@ module MultiToken {
540
527
  ): [] {
541
528
  assertInitialized();
542
529
 
543
- assert(!_isTargetZero(fromAddress), "MultiToken: invalid sender");
544
- assert(!_isTargetZero(to), "MultiToken: invalid receiver");
530
+ assert(!Utils_isTargetZero(fromAddress), "MultiToken: invalid sender");
531
+ assert(!Utils_isTargetZero(to), "MultiToken: invalid receiver");
545
532
  _update(fromAddress, to, id, value);
546
533
  }
547
534
 
@@ -627,8 +614,8 @@ module MultiToken {
627
614
  value: Uint<128>
628
615
  ): [] {
629
616
  assertInitialized();
630
- assert(!_isTargetZero(to), "MultiToken: invalid receiver");
631
- _update(ZERO(), to, id, value);
617
+ assert(!Utils_isTargetZero(to), "MultiToken: invalid receiver");
618
+ _update(Utils_zeroAccount(), to, id, value);
632
619
  }
633
620
 
634
621
  /**
@@ -652,8 +639,8 @@ module MultiToken {
652
639
  value: Uint<128>
653
640
  ): [] {
654
641
  assertInitialized();
655
- assert(!_isTargetZero(fromAddress), "MultiToken: invalid sender");
656
- _update(fromAddress, ZERO(), id, value);
642
+ assert(!Utils_isTargetZero(fromAddress), "MultiToken: invalid sender");
643
+ _update(fromAddress, Utils_zeroAccount(), id, value);
657
644
  }
658
645
 
659
646
  /**
@@ -684,10 +671,10 @@ module MultiToken {
684
671
  ): [] {
685
672
  assertInitialized();
686
673
  const canonOwner = Utils_canonicalize<Bytes<32>, ContractAddress>(owner);
687
- assert(!_isTargetZero(canonOwner), "MultiToken: invalid owner");
674
+ assert(!Utils_isTargetZero(canonOwner), "MultiToken: invalid owner");
688
675
 
689
676
  const canonOp = Utils_canonicalize<Bytes<32>, ContractAddress>(operator);
690
- assert(!_isTargetZero(canonOp), "MultiToken: invalid operator");
677
+ assert(!Utils_isTargetZero(canonOp), "MultiToken: invalid operator");
691
678
 
692
679
  if (!_operatorApprovals.member(disclose(canonOwner))) {
693
680
  _operatorApprovals.insert(
@@ -710,52 +697,6 @@ module MultiToken {
710
697
  * @returns {Bytes<32>} accountId - The computed account identifier.
711
698
  */
712
699
  circuit _computeAccountId(): Bytes<32> {
713
- return computeAccountId(wit_MultiTokenSK());
714
- }
715
-
716
- /**
717
- * @description Computes an account identifier without on-chain state, allowing a user to derive
718
- * their identity commitment before submitting it in a token operation.
719
- * This is the off-chain counterpart to {_computeAccountId} and produces an identical result
720
- * given the same inputs.
721
- *
722
- * @warning OpSec: The `secretKey` parameter is a sensitive secret. Mishandling it can
723
- * permanently compromise the security of this system:
724
- *
725
- * - **Never log or persist** the `secretKey` in plaintext — avoid browser devtools,
726
- * application logs, analytics pipelines, or any observable side-channel.
727
- * - **Store offline or in secure enclaves** — hardware security modules (HSMs),
728
- * air-gapped devices, or encrypted vaults are strongly preferred over hot storage.
729
- * - **Use cryptographically secure randomness** — generate keys with `crypto.getRandomValues()`
730
- * or equivalent; weak or predictable keys can be brute-forced to reveal your identity.
731
- * - **Treat key loss as identity loss** — a lost key cannot be recovered.
732
- * - **Avoid calling this circuit in untrusted environments** — executing this in an
733
- * unverified browser extension, compromised runtime, or shared machine may expose
734
- * the key to a malicious observer.
735
- *
736
- * ## ID Derivation
737
- * `accountId = persistentHash(secretKey)`
738
- *
739
- * @param {Bytes<32>} secretKey - A 32-byte cryptographically secure random value.
740
- *
741
- * @returns {Bytes<32>} accountId - The computed account identifier.
742
- */
743
- export pure circuit computeAccountId(secretKey: Bytes<32>): Bytes<32> {
744
- return persistentHash<Vector<1, Bytes<32>>>([secretKey]);
745
- }
746
-
747
- /**
748
- * @description Returns `true` if `target`'s active branch (as indicated by `is_left`)
749
- * holds the zero value.
750
- *
751
- * @param {Either<Bytes<32>, ContractAddress>} target - The value to check.
752
- * @returns {Boolean} - `true` if the active branch is zero, `false` otherwise.
753
- */
754
- circuit _isTargetZero(target: Either<Bytes<32>, ContractAddress>): Boolean {
755
- if (target.is_left) {
756
- return target.left == default<Bytes<32>>;
757
- } else {
758
- return target.right == default<ContractAddress>;
759
- }
700
+ return Utils_computeAccountId(wit_MultiTokenSK());
760
701
  }
761
702
  }
@@ -0,0 +1,285 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // OpenZeppelin Compact Contracts v0.3.0-alpha.1 (token/NativeShieldedToken.compact)
3
+
4
+ pragma language_version >= 0.23.0;
5
+
6
+ /**
7
+ * @module NativeShieldedToken
8
+ * @description A module for issuing a SINGLE native shielded (Zswap) token —
9
+ * the ERC-20-shaped flavor of the native shielded token standard. The token's
10
+ * domain separator is fixed at construction; no circuit takes a domain
11
+ * parameter. To issue multiple token types from one contract, use
12
+ * `NativeShieldedTokenFamily` instead.
13
+ *
14
+ * This module is a thin wrapper over `NativeShieldedTokenCore`: it stores the
15
+ * one `sealed _domain` and forwards it to every core op, so the mint/burn logic
16
+ * and metadata live in the shared core, not here.
17
+ *
18
+ * @notice A contract MUST NOT import both `NativeShieldedToken` and
19
+ * `NativeShieldedTokenFamily`. Both wrap `NativeShieldedTokenCore`, so they
20
+ * share one set of core state (a single init flag and one name/symbol/decimals).
21
+ * Pick one flavor per contract.
22
+ *
23
+ * @notice This module exposes composable building blocks for minting and
24
+ * burning native shielded coins. It does NOT include access control: consuming
25
+ * contracts compose that via the module/contract pattern (Ownable,
26
+ * AccessControl, etc.) and SHOULD gate the mint and burn circuits, which are
27
+ * unrestricted at the module level.
28
+ *
29
+ * @dev The domain separator is stored as `sealed ledger _domain` at
30
+ * construction and never exposed as a circuit parameter, eliminating
31
+ * caller-supplied domain misuse. The coin's `color` is
32
+ * `tokenType(_domain, kernel.self())`, computed at call time; only this
33
+ * contract can mint coins of its color. Metadata follows the FungibleToken
34
+ * (ERC-20) convention (contract-wide `name`, `symbol`, `decimals`, fixed at
35
+ * construction). Mint amounts are `Uint<64>`, burn amounts `Uint<128>` — an
36
+ * asymmetry imposed by the protocol primitives, not a module choice. The
37
+ * mint/burn mechanics and the `_burn` vs `_burnFromSelf` spend paths live in
38
+ * `NativeShieldedTokenCore`.
39
+ *
40
+ * @notice With a secret, cryptographically random `_mint` nonce the mint is
41
+ * recipient-private; the caller is responsible for nonce uniqueness (a reused
42
+ * nonce produces a duplicate commitment, which the ledger rejects). For mints
43
+ * that need no caller-managed nonce, compose `NativeShieldedTokenDerivedNonce`
44
+ * and pass its `_deriveNonce()` output, at the cost of making those mints
45
+ * recipient-public.
46
+ *
47
+ * @notice Supply accounting is opt-in: this module tracks NO supply totals.
48
+ * Compose the optional `NativeShieldedTokenSupply` extension only if you need an
49
+ * on-chain `totalSupply()`; see it for the privacy trade-off it introduces (it
50
+ * makes contract-mediated burn amounts public).
51
+ *
52
+ * @dev Composition
53
+ * Dual-representation tokens (shielded + unshielded with conversion) MUST build
54
+ * on the token-family modules and the `NativeTokenConverter` extension, not on
55
+ * this module: it stores a single load-bearing sealed `_domain` (one token
56
+ * type), while the converter composes both bases' Family profiles.
57
+ * (Initialization is tracked per-module inline, so the former
58
+ * shared-`Initializable`-flag "one `initialize` per contract" limit no longer
59
+ * applies.)
60
+ *
61
+ * @notice Out of scope (phase two, pending contract-to-contract support):
62
+ * `balanceOf`, `allowance`, and transfer mediation are not representable for
63
+ * native shielded UTXOs — once a user holds a coin, the contract cannot observe
64
+ * or restrict its movement.
65
+ */
66
+ module NativeShieldedToken {
67
+ import CompactStandardLibrary;
68
+ import "./NativeShieldedTokenCore" prefix Core_;
69
+
70
+ // Circuits use the `Core_` prefix above. The named import/re-export below is
71
+ // only for the core state variables, so the implementing contract's ledger
72
+ // keys read as `_name` etc. rather than `NativeShieldedTokenCore__name`. The
73
+ // circuit names are intentionally NOT imported unprefixed, to avoid potential clashes.
74
+ import { _name, _symbol, _decimals, _isInitialized } from "./NativeShieldedTokenCore";
75
+ export { _name, _symbol, _decimals, _isInitialized };
76
+
77
+ /**
78
+ * @description Domain separator fixed at construction; with the contract
79
+ * address it determines this token's color.
80
+ */
81
+ export sealed ledger _domain: Bytes<32>;
82
+
83
+ /**
84
+ * @description Initializes the module's domain and metadata.
85
+ * @dev This MUST be called in the implementing contract's constructor, and it
86
+ * MUST be the only token operation performed there. Failure to call it can
87
+ * lead to an irreparable contract; perform mint, burn, and `tokenColor` only
88
+ * in post-deployment circuits, never in the constructor.
89
+ *
90
+ * @param {Bytes<32>} domainSep - Domain separator for this token's color.
91
+ * @param {Opaque<"string">} name_ - The name of the token.
92
+ * @param {Opaque<"string">} symbol_ - The symbol of the token.
93
+ * @param {Uint<8>} decimals_ - The number of decimals used to get the user representation.
94
+ * @return {[]} - Empty tuple.
95
+ */
96
+ export circuit initialize(
97
+ domainSep: Bytes<32>,
98
+ name_: Opaque<"string">,
99
+ symbol_: Opaque<"string">,
100
+ decimals_: Uint<8>
101
+ ): [] {
102
+ Core_initialize(name_, symbol_, decimals_);
103
+ _domain = disclose(domainSep);
104
+ }
105
+
106
+ /**
107
+ * @description Returns whether the contract has been initialized.
108
+ *
109
+ * @circuitInfo k=6, rows=26
110
+ *
111
+ * @return {Boolean} - True once `initialize` has run.
112
+ */
113
+ export circuit isInitialized(): Boolean {
114
+ return Core_isInitialized();
115
+ }
116
+
117
+ /**
118
+ * @description Returns the token name.
119
+ *
120
+ * @circuitInfo k=6, rows=28
121
+ *
122
+ * Requirements:
123
+ *
124
+ * - Contract is initialized.
125
+ *
126
+ * @return {Opaque<"string">} - The token name.
127
+ */
128
+ export circuit name(): Opaque<"string"> {
129
+ return Core_name();
130
+ }
131
+
132
+ /**
133
+ * @description Returns the symbol of the token.
134
+ *
135
+ * @circuitInfo k=6, rows=28
136
+ *
137
+ * Requirements:
138
+ *
139
+ * - Contract is initialized.
140
+ *
141
+ * @return {Opaque<"string">} - The token symbol.
142
+ */
143
+ export circuit symbol(): Opaque<"string"> {
144
+ return Core_symbol();
145
+ }
146
+
147
+ /**
148
+ * @description Returns the number of decimals used to get its user representation.
149
+ *
150
+ * @circuitInfo k=6, rows=28
151
+ *
152
+ * Requirements:
153
+ *
154
+ * - Contract is initialized.
155
+ *
156
+ * @return {Uint<8>} - The decimals value.
157
+ */
158
+ export circuit decimals(): Uint<8> {
159
+ return Core_decimals();
160
+ }
161
+
162
+ /**
163
+ * @description Returns this token's coin color:
164
+ * `tokenType(_domain, kernel.self())`, computed at call time.
165
+ *
166
+ * @circuitInfo k=13, rows=3971
167
+ *
168
+ * Requirements:
169
+ *
170
+ * - Contract is initialized.
171
+ *
172
+ * @return {Bytes<32>} - The coin color.
173
+ */
174
+ export circuit tokenColor(): Bytes<32> {
175
+ return Core_tokenColor(_domain);
176
+ }
177
+
178
+ /**
179
+ * @description Mints `amount` of the token to `recipient`, using a
180
+ * caller-supplied nonce.
181
+ *
182
+ * @dev The caller is fully responsible for nonce uniqueness. Reusing a
183
+ * nonce for the same (value, recipient) produces a duplicate commitment,
184
+ * which the protocol rejects. With a secret random nonce this is the
185
+ * recipient-private mint. For mints that require no caller-managed nonce,
186
+ * compose the `NativeShieldedTokenDerivedNonce` extension and pass its
187
+ * `_deriveNonce()` output.
188
+ *
189
+ * @notice The returned coin info is the only copy available to the
190
+ * recipient; callers SHOULD deliver it out of band. Wallets cannot detect
191
+ * contract-minted coins by scanning the chain.
192
+ *
193
+ * @circuitInfo k=14, rows=10843
194
+ *
195
+ * Requirements:
196
+ *
197
+ * - Contract is initialized.
198
+ * - `recipient` is not zero.
199
+ *
200
+ * @param {Either<ZswapCoinPublicKey, ContractAddress>} recipient - The coin recipient.
201
+ * @param {Uint<64>} amount - Quantity to mint. Capped at `Uint<64>` by the protocol.
202
+ * @param {Bytes<32>} nonce - Caller-supplied nonce. Must be unique for this
203
+ * contract's color.
204
+ * @return {ShieldedCoinInfo} - The newly created coin's info (nonce, color, value).
205
+ */
206
+ export circuit _mint(
207
+ recipient: Either<ZswapCoinPublicKey, ContractAddress>,
208
+ amount: Uint<64>,
209
+ nonce: Bytes<32>
210
+ ): ShieldedCoinInfo {
211
+ return Core__mint(_domain, recipient, amount, nonce);
212
+ }
213
+
214
+ /**
215
+ * @description Burns `amount` from `coin`, a coin provided within the
216
+ * current transaction (e.g. paid in by the caller's wallet), and routes the
217
+ * remaining change to `refundTo`.
218
+ *
219
+ * To destroy `coin` in full, pass `coin.value` as `amount`; the change
220
+ * branch will not fire and `none` is returned. The `refundTo` value is
221
+ * inert in that case but must still be supplied and non-zero.
222
+ *
223
+ * @dev The coin is received by the contract and spent in the same
224
+ * transaction, so the spend goes through `sendImmediateShielded` (the
225
+ * transient path). For coins the contract already holds, use
226
+ * `_burnFromSelf` instead.
227
+ *
228
+ * @notice The returned refund coin info is the only copy available to
229
+ * `refundTo`; callers SHOULD deliver it out of band. Wallets cannot detect
230
+ * contract-sent coins by scanning the chain.
231
+ *
232
+ * @circuitInfo k=16, rows=47656
233
+ *
234
+ * Requirements:
235
+ *
236
+ * - Contract is initialized.
237
+ * - `coin.color` is this contract's token color.
238
+ * - `amount` is less than or equal to `coin.value`.
239
+ * - `refundTo` is not zero (the zero key is the burn address; a zero
240
+ * `refundTo` would silently burn the change as well).
241
+ *
242
+ * @param {ShieldedCoinInfo} coin - The coin to burn from.
243
+ * @param {Uint<128>} amount - Value to destroy. Must be <= `coin.value`.
244
+ * @param {Either<ZswapCoinPublicKey, ContractAddress>} refundTo - Where to
245
+ * route the unspent portion (`coin.value - amount`).
246
+ * @return {Maybe<ShieldedCoinInfo>} - The refund coin created for `refundTo`,
247
+ * or `none` if the coin was burned in full.
248
+ */
249
+ export circuit _burn(
250
+ coin: ShieldedCoinInfo,
251
+ amount: Uint<128>,
252
+ refundTo: Either<ZswapCoinPublicKey, ContractAddress>
253
+ ): Maybe<ShieldedCoinInfo> {
254
+ return Core__burn(_domain, coin, amount, refundTo);
255
+ }
256
+
257
+ /**
258
+ * @description Burns `amount` from `coin`, a coin this contract already
259
+ * holds (a Merkle-tree entry with a valid `mt_index`). Any change is
260
+ * auto-received by the contract and returned.
261
+ *
262
+ * @dev The consumer SHOULD persist the returned change coin info in its own
263
+ * ledger state: the change replaces `coin` as the contract's holding, and
264
+ * its info is not otherwise recoverable.
265
+ *
266
+ * @circuitInfo k=15, rows=23307
267
+ *
268
+ * Requirements:
269
+ *
270
+ * - Contract is initialized.
271
+ * - `coin.color` is this contract's token color.
272
+ * - `amount` is less than or equal to `coin.value`.
273
+ *
274
+ * @param {QualifiedShieldedCoinInfo} coin - The contract-held coin to burn from.
275
+ * @param {Uint<128>} amount - Value to destroy. Must be <= `coin.value`.
276
+ * @return {Maybe<ShieldedCoinInfo>} - The change coin retained by the
277
+ * contract, or `none` if the coin was burned in full.
278
+ */
279
+ export circuit _burnFromSelf(
280
+ coin: QualifiedShieldedCoinInfo,
281
+ amount: Uint<128>
282
+ ): Maybe<ShieldedCoinInfo> {
283
+ return Core__burnFromSelf(_domain, coin, amount);
284
+ }
285
+ }