@openzeppelin/compact-contracts 0.2.0 → 0.3.0-alpha

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 (33) hide show
  1. package/README.md +3 -3
  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/ElGamal.compact +473 -0
  7. package/multisig/ForwarderPrivate.compact +229 -0
  8. package/multisig/ForwarderShielded.compact +149 -0
  9. package/multisig/ForwarderUnshielded.compact +152 -0
  10. package/multisig/ProposalManager.compact +328 -0
  11. package/multisig/ShieldedTreasury.compact +201 -0
  12. package/multisig/ShieldedTreasuryStateless.compact +76 -0
  13. package/multisig/Signer.compact +312 -0
  14. package/multisig/SignerManager.compact +205 -0
  15. package/multisig/UnshieldedTreasury.compact +114 -0
  16. package/multisig/presets/ShieldedMultiSig.compact +236 -0
  17. package/multisig/presets/ShieldedMultiSigV2.compact +280 -0
  18. package/multisig/presets/ShieldedMultiSigV3.compact +368 -0
  19. package/multisig/presets/forwarder/ForwarderPrivate.compact +125 -0
  20. package/multisig/presets/forwarder/ForwarderShielded.compact +62 -0
  21. package/multisig/presets/forwarder/ForwarderUnshielded.compact +64 -0
  22. package/package.json +9 -5
  23. package/security/Allowlist.compact +101 -0
  24. package/security/Blocklist.compact +99 -0
  25. package/security/Initializable.compact +2 -2
  26. package/security/Pausable.compact +2 -2
  27. package/token/FungibleToken.compact +24 -82
  28. package/token/MultiToken.compact +21 -80
  29. package/token/NativeShieldedToken.compact +285 -0
  30. package/token/NativeShieldedTokenCore.compact +343 -0
  31. package/token/NativeShieldedTokenFamily.compact +287 -0
  32. package/token/NonFungibleToken.compact +25 -83
  33. package/utils/Utils.compact +86 -2
@@ -0,0 +1,285 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // OpenZeppelin Compact Contracts v0.3.0-alpha (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
+ }
@@ -0,0 +1,343 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // OpenZeppelin Compact Contracts v0.3.0-alpha (token/NativeShieldedTokenCore.compact)
3
+
4
+ pragma language_version >= 0.23.0;
5
+
6
+ /**
7
+ * @module NativeShieldedTokenCore
8
+ * @description Shared, domain-parameterized core of the native shielded
9
+ * (Zswap) token standard. It owns the initialization flag, the contract-wide
10
+ * metadata, and the mint/burn building blocks; every token operation takes the
11
+ * `domain` separator as a parameter.
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
+ * - `NativeShieldedToken` — single token type; stores one `sealed _domain` and
17
+ * forwards it to every core op.
18
+ * - `NativeShieldedTokenFamily` — many token types; forwards the caller's
19
+ * per-call `domain` to every core op.
20
+ *
21
+ * @dev The flavors are thin wrappers; all of the bug-prone coin logic lives
22
+ * here so a fix or change happens once. Assertion messages use the generic
23
+ * `NativeShieldedToken:` prefix so they read sensibly from either flavor. The
24
+ * coin's `color` is `tokenType(domain, kernel.self())`, computed at call time
25
+ * (never in the constructor), so only this contract can mint coins of its
26
+ * colors. Metadata follows the FungibleToken (ERC-20) convention: contract-wide
27
+ * `name`, `symbol`, and `decimals`, fixed at construction. Mint amounts are
28
+ * `Uint<64>` and burn amounts `Uint<128>`; that asymmetry is imposed by the
29
+ * protocol primitives (`mintShieldedToken` caps at 64, `sendShielded` accepts
30
+ * 128), not a choice the module makes.
31
+ *
32
+ * @notice Constructor usage: an implementing contract's constructor MUST call
33
+ * only `initialize` (via the wrapping flavor). Do NOT perform any other token
34
+ * operation (`_mint`, `_burn`, `_burnFromSelf`, `tokenColor`) during
35
+ * construction. `assertInitialized` gates on the init flag but does not stop a
36
+ * constructor from calling `initialize` and then a token op in the same scope;
37
+ * those operations belong in post-deployment circuits, not construction.
38
+ *
39
+ * @notice Minting and recipient privacy: `_mint` takes a caller-supplied nonce,
40
+ * mirroring `mintShieldedToken` one-to-one, and the caller is responsible for
41
+ * nonce uniqueness (reusing a nonce for the same (value, recipient) produces a
42
+ * duplicate commitment, which the ledger rejects). With a secret, random nonce
43
+ * the mint is recipient-private; for mints that need no caller-managed nonce,
44
+ * compose `NativeShieldedTokenDerivedNonce` and pass its `_deriveNonce()`
45
+ * output, at the cost of making those mints recipient-public.
46
+ *
47
+ * @dev Burn variants
48
+ * `_burn` destroys value from a coin provided within the current transaction
49
+ * (e.g. the caller's wallet pays the contract): it receives the coin and spends
50
+ * via `sendImmediateShielded`, the required path for coins created in the same
51
+ * transaction. `_burnFromSelf` destroys value from a coin the contract already
52
+ * holds (a Merkle-tree entry with a valid `mt_index`): it spends via
53
+ * `sendShielded`, and any change is auto-received by the contract and returned
54
+ * for the consumer to persist.
55
+ *
56
+ * @notice Contract-initiated sends do not create coin ciphertexts, so recipient
57
+ * wallets cannot detect coins minted or refunded to them by scanning the chain.
58
+ * The returned `ShieldedCoinInfo` values are the only copies of those coins'
59
+ * info: DApps SHOULD capture and deliver them out of band; dropping them strands
60
+ * value irrecoverably.
61
+ */
62
+ module NativeShieldedTokenCore {
63
+ import CompactStandardLibrary;
64
+ import "../utils/Utils" prefix Utils_;
65
+
66
+ /**
67
+ * @description Initialization flag, tracked per-module to avoid the compiler's
68
+ * shared transitive-dependency state bug (LFDT-Minokawa/compact#270). See the
69
+ * Initializable module for rationale.
70
+ */
71
+ export ledger _isInitialized: Boolean;
72
+
73
+ export sealed ledger _name: Opaque<"string">;
74
+ export sealed ledger _symbol: Opaque<"string">;
75
+ export sealed ledger _decimals: Uint<8>;
76
+
77
+ /**
78
+ * @description Initializes the contract-wide metadata.
79
+ * @dev This MUST be called in the implementing contract's constructor (via
80
+ * the wrapping flavor's `initialize`). Failure to do so can lead to an
81
+ * irreparable contract.
82
+ *
83
+ * @param {Opaque<"string">} name_ - The name of the token.
84
+ * @param {Opaque<"string">} symbol_ - The symbol of the token.
85
+ * @param {Uint<8>} decimals_ - The number of decimals used to get the user representation.
86
+ * @return {[]} - Empty tuple.
87
+ */
88
+ export circuit initialize(
89
+ name_: Opaque<"string">,
90
+ symbol_: Opaque<"string">,
91
+ decimals_: Uint<8>
92
+ ): [] {
93
+ assertNotInitialized();
94
+ _isInitialized = true;
95
+ _name = disclose(name_);
96
+ _symbol = disclose(symbol_);
97
+ _decimals = disclose(decimals_);
98
+ }
99
+
100
+ /**
101
+ * @description Asserts that the contract has been initialized, throwing an
102
+ * error if not.
103
+ *
104
+ * Requirements:
105
+ *
106
+ * - Contract must be initialized.
107
+ *
108
+ * @return {[]} - Empty tuple.
109
+ */
110
+ export circuit assertInitialized(): [] {
111
+ assert(_isInitialized, "NativeShieldedToken: contract not initialized");
112
+ }
113
+
114
+ /**
115
+ * @description Asserts that the contract has not been initialized, throwing
116
+ * an error if it has.
117
+ *
118
+ * Requirements:
119
+ *
120
+ * - Contract must not be initialized.
121
+ *
122
+ * @return {[]} - Empty tuple.
123
+ */
124
+ export circuit assertNotInitialized(): [] {
125
+ assert(!_isInitialized, "NativeShieldedToken: contract already initialized");
126
+ }
127
+
128
+ /**
129
+ * @description Returns whether the contract has been initialized.
130
+ * @dev Does not gate on init (it reports it), so it is safe to call before
131
+ * `initialize`.
132
+ *
133
+ * @circuitInfo k=6, rows=26
134
+ *
135
+ * @return {Boolean} - True once `initialize` has run.
136
+ */
137
+ export circuit isInitialized(): Boolean {
138
+ return _isInitialized;
139
+ }
140
+
141
+ /**
142
+ * @description Returns the token name.
143
+ *
144
+ * @circuitInfo k=6, rows=28
145
+ *
146
+ * Requirements:
147
+ *
148
+ * - Contract is initialized.
149
+ *
150
+ * @return {Opaque<"string">} - The token name.
151
+ */
152
+ export circuit name(): Opaque<"string"> {
153
+ assertInitialized();
154
+ return _name;
155
+ }
156
+
157
+ /**
158
+ * @description Returns the symbol of the token.
159
+ *
160
+ * @circuitInfo k=6, rows=28
161
+ *
162
+ * Requirements:
163
+ *
164
+ * - Contract is initialized.
165
+ *
166
+ * @return {Opaque<"string">} - The token symbol.
167
+ */
168
+ export circuit symbol(): Opaque<"string"> {
169
+ assertInitialized();
170
+ return _symbol;
171
+ }
172
+
173
+ /**
174
+ * @description Returns the number of decimals used to get its user representation.
175
+ * @dev This is a display convention only. The protocol stores and operates
176
+ * on integer values without reference to decimals.
177
+ *
178
+ * @circuitInfo k=6, rows=28
179
+ *
180
+ * Requirements:
181
+ *
182
+ * - Contract is initialized.
183
+ *
184
+ * @return {Uint<8>} - The decimals value.
185
+ */
186
+ export circuit decimals(): Uint<8> {
187
+ assertInitialized();
188
+ return _decimals;
189
+ }
190
+
191
+ /**
192
+ * @description Returns the coin color for `domain` under this contract's
193
+ * address: `tokenType(domain, kernel.self())`, computed at call time.
194
+ *
195
+ * @circuitInfo k=13, rows=4248
196
+ *
197
+ * Requirements:
198
+ *
199
+ * - Contract is initialized.
200
+ *
201
+ * @param {Bytes<32>} domain - 32-byte token discriminator.
202
+ * @return {Bytes<32>} - The coin color.
203
+ */
204
+ export circuit tokenColor(domain: Bytes<32>): Bytes<32> {
205
+ assertInitialized();
206
+ return tokenType(disclose(domain), kernel.self());
207
+ }
208
+
209
+ /**
210
+ * @description Mints `amount` of the token identified by `domain` to
211
+ * `recipient`, using a caller-supplied nonce.
212
+ *
213
+ * @dev The caller is fully responsible for nonce uniqueness. Reusing a
214
+ * nonce for the same (domain, value, recipient) produces a duplicate
215
+ * commitment, which the protocol rejects.
216
+ *
217
+ * @notice The returned coin info is the only copy available to the
218
+ * recipient; callers SHOULD deliver it out of band. Wallets cannot detect
219
+ * contract-minted coins by scanning the chain.
220
+ *
221
+ * @circuitInfo k=14, rows=11119
222
+ *
223
+ * Requirements:
224
+ *
225
+ * - Contract is initialized.
226
+ * - `recipient` is not zero.
227
+ *
228
+ * @param {Bytes<32>} domain - 32-byte token discriminator.
229
+ * @param {Either<ZswapCoinPublicKey, ContractAddress>} recipient - The coin recipient.
230
+ * @param {Uint<64>} amount - Quantity to mint. Capped at `Uint<64>` by the protocol.
231
+ * @param {Bytes<32>} nonce - Caller-supplied nonce. Must be unique per (domain, contract).
232
+ * @return {ShieldedCoinInfo} - The newly created coin's info (nonce, color, value).
233
+ */
234
+ export circuit _mint(
235
+ domain: Bytes<32>,
236
+ recipient: Either<ZswapCoinPublicKey, ContractAddress>,
237
+ amount: Uint<64>,
238
+ nonce: Bytes<32>
239
+ ): ShieldedCoinInfo {
240
+ assertInitialized();
241
+ assert(!Utils_isKeyOrAddressZero(recipient), "NativeShieldedToken: invalid recipient");
242
+
243
+ return mintShieldedToken(disclose(domain), disclose(amount), disclose(nonce), disclose(recipient));
244
+ }
245
+
246
+ /**
247
+ * @description Burns `amount` from `coin`, a coin provided within the
248
+ * current transaction (e.g. paid in by the caller's wallet), and routes the
249
+ * remaining change to `refundTo`.
250
+ *
251
+ * To destroy `coin` in full, pass `coin.value` as `amount`; the change
252
+ * branch will not fire and `none` is returned. The `refundTo` value is
253
+ * inert in that case but must still be supplied and non-zero.
254
+ *
255
+ * @dev The coin is received by the contract and spent in the same
256
+ * transaction, so the spend goes through `sendImmediateShielded` (the
257
+ * transient path). For coins the contract already holds, use
258
+ * `_burnFromSelf` instead.
259
+ *
260
+ * @notice The returned refund coin info is the only copy available to
261
+ * `refundTo`; callers SHOULD deliver it out of band. Wallets cannot detect
262
+ * contract-sent coins by scanning the chain.
263
+ *
264
+ * @circuitInfo k=16, rows=47932
265
+ *
266
+ * Requirements:
267
+ *
268
+ * - Contract is initialized.
269
+ * - `coin.color` is this contract's token type for `domain`.
270
+ * - `amount` is less than or equal to `coin.value`.
271
+ * - `refundTo` is not zero (the zero key is the burn address; a zero
272
+ * `refundTo` would silently burn the change as well).
273
+ *
274
+ * @param {Bytes<32>} domain - 32-byte token discriminator.
275
+ * @param {ShieldedCoinInfo} coin - The coin to burn from.
276
+ * @param {Uint<128>} amount - Value to destroy. Must be <= `coin.value`.
277
+ * @param {Either<ZswapCoinPublicKey, ContractAddress>} refundTo - Where to
278
+ * route the unspent portion (`coin.value - amount`).
279
+ * @return {Maybe<ShieldedCoinInfo>} - The refund coin created for `refundTo`,
280
+ * or `none` if the coin was burned in full.
281
+ */
282
+ export circuit _burn(
283
+ domain: Bytes<32>,
284
+ coin: ShieldedCoinInfo,
285
+ amount: Uint<128>,
286
+ refundTo: Either<ZswapCoinPublicKey, ContractAddress>
287
+ ): Maybe<ShieldedCoinInfo> {
288
+ assertInitialized();
289
+ assert(coin.color == tokenType(disclose(domain), kernel.self()), "NativeShieldedToken: wrong token");
290
+ assert(coin.value >= amount, "NativeShieldedToken: insufficient coin value");
291
+ assert(!Utils_isKeyOrAddressZero(refundTo), "NativeShieldedToken: invalid refund target");
292
+
293
+ receiveShielded(disclose(coin));
294
+ const sendRes = sendImmediateShielded(disclose(coin), shieldedBurnAddress(), disclose(amount));
295
+
296
+ if (disclose(sendRes.change.is_some)) {
297
+ const refundRes = sendImmediateShielded(
298
+ disclose(sendRes.change.value),
299
+ disclose(refundTo),
300
+ disclose(sendRes.change.value.value)
301
+ );
302
+ return some<ShieldedCoinInfo>(refundRes.sent);
303
+ }
304
+ return none<ShieldedCoinInfo>();
305
+ }
306
+
307
+ /**
308
+ * @description Burns `amount` from `coin`, a coin this contract already
309
+ * holds (a Merkle-tree entry with a valid `mt_index`). Any change is
310
+ * auto-received by the contract and returned.
311
+ *
312
+ * @dev The consumer SHOULD persist the returned change coin info in its own
313
+ * ledger state: the change replaces `coin` as the contract's holding, and
314
+ * its info is not otherwise recoverable.
315
+ *
316
+ * @circuitInfo k=15, rows=23583
317
+ *
318
+ * Requirements:
319
+ *
320
+ * - Contract is initialized.
321
+ * - `coin.color` is this contract's token type for `domain`.
322
+ * - `amount` is less than or equal to `coin.value`.
323
+ *
324
+ * @param {Bytes<32>} domain - 32-byte token discriminator.
325
+ * @param {QualifiedShieldedCoinInfo} coin - The contract-held coin to burn from.
326
+ * @param {Uint<128>} amount - Value to destroy. Must be <= `coin.value`.
327
+ * @return {Maybe<ShieldedCoinInfo>} - The change coin retained by the
328
+ * contract, or `none` if the coin was burned in full.
329
+ */
330
+ export circuit _burnFromSelf(
331
+ domain: Bytes<32>,
332
+ coin: QualifiedShieldedCoinInfo,
333
+ amount: Uint<128>
334
+ ): Maybe<ShieldedCoinInfo> {
335
+ assertInitialized();
336
+ assert(coin.color == tokenType(disclose(domain), kernel.self()), "NativeShieldedToken: wrong token");
337
+ assert(coin.value >= amount, "NativeShieldedToken: insufficient coin value");
338
+
339
+ const sendRes = sendShielded(disclose(coin), shieldedBurnAddress(), disclose(amount));
340
+
341
+ return disclose(sendRes.change);
342
+ }
343
+ }