@openzeppelin/compact-contracts 0.4.0-alpha.2 → 0.4.0-alpha.3

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 (42) hide show
  1. package/access/AccessControl.compact +1 -1
  2. package/access/Ownable.compact +1 -1
  3. package/access/ShieldedAccessControl.compact +1 -1
  4. package/access/ZOwnablePK.compact +1 -1
  5. package/crypto/EcdhMask.compact +1 -1
  6. package/crypto/Ecdsa.compact +1 -1
  7. package/crypto/Eip712.compact +1 -1
  8. package/crypto/ElGamal.compact +44 -3
  9. package/multisig/EcdsaSignerManager.compact +1 -1
  10. package/multisig/ForwarderPrivate.compact +1 -1
  11. package/multisig/ForwarderShielded.compact +1 -1
  12. package/multisig/ForwarderUnshielded.compact +1 -1
  13. package/multisig/ProposalManager.compact +1 -1
  14. package/multisig/ShieldedTreasury.compact +1 -1
  15. package/multisig/ShieldedTreasuryStateless.compact +1 -1
  16. package/multisig/Signer.compact +1 -1
  17. package/multisig/UnshieldedTreasury.compact +1 -1
  18. package/multisig/examples/ForwarderPrivateExample.compact +1 -1
  19. package/multisig/examples/ForwarderShieldedExample.compact +1 -1
  20. package/multisig/examples/ForwarderUnshieldedExample.compact +1 -1
  21. package/multisig/examples/NativeShieldedTokenIssuerExample.compact +1 -1
  22. package/multisig/examples/ShieldedMultiSigV2Example.compact +1 -1
  23. package/multisig/presets/NativeShieldedTokenIssuer.compact +18 -3
  24. package/multisig/presets/ShieldedMultiSigV2.compact +1 -1
  25. package/package.json +1 -1
  26. package/security/Allowlist.compact +1 -1
  27. package/security/Blocklist.compact +1 -1
  28. package/security/Initializable.compact +1 -1
  29. package/security/Pausable.compact +1 -1
  30. package/token/ConfidentialFungibleToken.compact +268 -69
  31. package/token/FungibleToken.compact +1 -1
  32. package/token/MultiToken.compact +1 -1
  33. package/token/NativeShieldedToken.compact +59 -10
  34. package/token/NativeShieldedTokenCore.compact +41 -9
  35. package/token/NativeShieldedTokenFamily.compact +60 -10
  36. package/token/NonFungibleToken.compact +56 -12
  37. package/token/extensions/ConfidentialFungibleTokenPublicSupply.compact +1 -1
  38. package/token/extensions/NativeShieldedTokenDerivedNonce.compact +1 -1
  39. package/token/extensions/NativeShieldedTokenFamilyPublicSupply.compact +2 -2
  40. package/token/extensions/NativeShieldedTokenPublicSupply.compact +2 -2
  41. package/utils/EvmAbi.compact +1 -1
  42. package/utils/Utils.compact +11 -1
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.4.0-alpha.2 (token/NativeShieldedToken.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.3 (token/NativeShieldedToken.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -20,6 +20,10 @@ pragma language_version >= 0.26.0;
20
20
  * share one set of core state (a single init flag and one name/symbol/decimals).
21
21
  * Pick one flavor per contract.
22
22
  *
23
+ * @notice Recipients must be coin public keys: contract-addressed recipients
24
+ * are not supported at this layer. For a coin the contract holds itself, use
25
+ * `_mintToSelf`, which mints to `kernel.self()` and claims it in the same call.
26
+ *
23
27
  * @notice This module exposes composable building blocks for minting and
24
28
  * burning native shielded coins. It does NOT include access control: consuming
25
29
  * contracts compose that via the module/contract pattern (Ownable,
@@ -190,25 +194,65 @@ module NativeShieldedToken {
190
194
  * recipient; callers SHOULD deliver it out of band. Wallets cannot detect
191
195
  * contract-minted coins by scanning the chain.
192
196
  *
193
- * @constraints k=14, rows=10820
197
+ * @constraints k=14, rows=10450
194
198
  *
195
199
  * Requirements:
196
200
  *
197
201
  * - Contract is initialized.
198
202
  * - `recipient` is not zero.
199
203
  *
200
- * @param {Either<ZswapCoinPublicKey, ContractAddress>} recipient - The coin recipient.
204
+ * @param {ZswapCoinPublicKey} recipient - The coin recipient.
201
205
  * @param {Uint<64>} amount - Quantity to mint. Capped at `Uint<64>` by the protocol.
202
206
  * @param {Bytes<32>} nonce - Caller-supplied nonce. Must be unique for this
203
207
  * contract's color.
204
208
  * @return {ShieldedCoinInfo} - The newly created coin's info (nonce, color, value).
205
209
  */
206
210
  export circuit _mint(
207
- recipient: Either<ZswapCoinPublicKey, ContractAddress>,
211
+ recipient: ZswapCoinPublicKey,
212
+ amount: Uint<64>,
213
+ nonce: Bytes<32>
214
+ ): ShieldedCoinInfo {
215
+ return Core__mint(
216
+ _domain,
217
+ left<ZswapCoinPublicKey, ContractAddress>(recipient),
218
+ amount,
219
+ nonce
220
+ );
221
+ }
222
+
223
+ /**
224
+ * @description Mints `amount` to this contract itself, claiming the coin in
225
+ * the same call. Forwards to `NativeShieldedTokenCore._mint` with
226
+ * `right(kernel.self())`.
227
+ *
228
+ * @dev Nonce uniqueness is the caller's responsibility, as in `_mint`. Spend
229
+ * the coin later with `_burnFromSelf`, once it carries an `mt_index`.
230
+ *
231
+ * @notice The consumer SHOULD persist the returned coin info in its own
232
+ * ledger state: it is the contract's holding and is not otherwise
233
+ * recoverable.
234
+ *
235
+ * @constraints k=14, rows=16033
236
+ *
237
+ * Requirements:
238
+ *
239
+ * - Contract is initialized.
240
+ *
241
+ * @param {Uint<64>} amount - Quantity to mint. Capped at `Uint<64>` by the protocol.
242
+ * @param {Bytes<32>} nonce - Caller-supplied nonce. Must be unique for this
243
+ * contract's color.
244
+ * @return {ShieldedCoinInfo} - The newly created coin's info (nonce, color, value).
245
+ */
246
+ export circuit _mintToSelf(
208
247
  amount: Uint<64>,
209
248
  nonce: Bytes<32>
210
249
  ): ShieldedCoinInfo {
211
- return Core__mint(_domain, recipient, amount, nonce);
250
+ return Core__mint(
251
+ _domain,
252
+ right<ZswapCoinPublicKey, ContractAddress>(kernel.self()),
253
+ amount,
254
+ nonce
255
+ );
212
256
  }
213
257
 
214
258
  /**
@@ -229,7 +273,7 @@ module NativeShieldedToken {
229
273
  * `refundTo`; callers SHOULD deliver it out of band. Wallets cannot detect
230
274
  * contract-sent coins by scanning the chain.
231
275
  *
232
- * @constraints k=16, rows=47477
276
+ * @constraints k=16, rows=41327
233
277
  *
234
278
  * Requirements:
235
279
  *
@@ -241,17 +285,22 @@ module NativeShieldedToken {
241
285
  *
242
286
  * @param {ShieldedCoinInfo} coin - The coin to burn from.
243
287
  * @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`).
288
+ * @param {ZswapCoinPublicKey} refundTo - Where to route the unspent
289
+ * portion (`coin.value - amount`).
246
290
  * @return {Maybe<ShieldedCoinInfo>} - The refund coin created for `refundTo`,
247
291
  * or `none` if the coin was burned in full.
248
292
  */
249
293
  export circuit _burn(
250
294
  coin: ShieldedCoinInfo,
251
295
  amount: Uint<128>,
252
- refundTo: Either<ZswapCoinPublicKey, ContractAddress>
296
+ refundTo: ZswapCoinPublicKey
253
297
  ): Maybe<ShieldedCoinInfo> {
254
- return Core__burn(_domain, coin, amount, refundTo);
298
+ return Core__burn(
299
+ _domain,
300
+ coin,
301
+ amount,
302
+ left<ZswapCoinPublicKey, ContractAddress>(refundTo)
303
+ );
255
304
  }
256
305
 
257
306
  /**
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.4.0-alpha.2 (token/NativeShieldedTokenCore.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.3 (token/NativeShieldedTokenCore.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -44,6 +44,18 @@ pragma language_version >= 0.26.0;
44
44
  * compose `NativeShieldedTokenDerivedNonce` and pass its `_deriveNonce()`
45
45
  * output, at the cost of making those mints recipient-public.
46
46
  *
47
+ * @notice A recipient may be any coin public key, or this contract's own
48
+ * address. Any other contract address is rejected.
49
+ *
50
+ * @dev Recipient arm
51
+ * The ledger accepts a contract-addressed commitment only if the recipient
52
+ * contract's own call claims it as a shielded receive in the same transaction,
53
+ * and no primitive claims a receive on another contract's behalf. So the
54
+ * address arm is accepted only for `kernel.self()`, where `_mint` / `_burn`
55
+ * claim the coin themselves in the same call. The flavors do not expose the
56
+ * arm at all: they take a `ZswapCoinPublicKey` and offer `_mintToSelf` for
57
+ * contract-held coins.
58
+ *
47
59
  * @dev Burn variants
48
60
  * `_burn` destroys value from a coin provided within the current transaction
49
61
  * (e.g. the caller's wallet pays the contract): it receives the coin and spends
@@ -222,21 +234,26 @@ module NativeShieldedTokenCore {
222
234
  *
223
235
  * @dev The caller is fully responsible for nonce uniqueness. Reusing a
224
236
  * nonce for the same (domain, value, recipient) produces a duplicate
225
- * commitment, which the protocol rejects.
237
+ * commitment, which the protocol rejects. A `right(kernel.self())` recipient
238
+ * is claimed via `receiveShielded` in this same call, so the consumer SHOULD
239
+ * persist the returned coin info: it is the contract's holding and is not
240
+ * otherwise recoverable.
226
241
  *
227
242
  * @notice The returned coin info is the only copy available to the
228
243
  * recipient; callers SHOULD deliver it out of band. Wallets cannot detect
229
244
  * contract-minted coins by scanning the chain.
230
245
  *
231
- * @constraints k=14, rows=11096
246
+ * @constraints k=15, rows=16980
232
247
  *
233
248
  * Requirements:
234
249
  *
235
250
  * - Contract is initialized.
236
251
  * - `recipient` is not zero.
252
+ * - `recipient`, if a contract address, is this contract's own address.
237
253
  *
238
254
  * @param {Bytes<32>} domain - 32-byte token discriminator.
239
- * @param {Either<ZswapCoinPublicKey, ContractAddress>} recipient - The coin recipient.
255
+ * @param {Either<ZswapCoinPublicKey, ContractAddress>} recipient - The coin
256
+ * recipient: any coin public key, or this contract's own address.
240
257
  * @param {Uint<64>} amount - Quantity to mint. Capped at `Uint<64>` by the protocol.
241
258
  * @param {Bytes<32>} nonce - Caller-supplied nonce. Must be unique per (domain, contract).
242
259
  * @return {ShieldedCoinInfo} - The newly created coin's info (nonce, color, value).
@@ -248,9 +265,16 @@ module NativeShieldedTokenCore {
248
265
  nonce: Bytes<32>
249
266
  ): ShieldedCoinInfo {
250
267
  assertInitialized();
268
+ const self = kernel.self();
251
269
  assert(!Utils_isKeyOrAddressZero(recipient), "NativeShieldedToken: invalid recipient");
270
+ assert(recipient.is_left || recipient.right == self, "NativeShieldedToken: recipient contract must be self");
252
271
 
253
- return mintShieldedToken(disclose(domain), disclose(amount), disclose(nonce), disclose(recipient));
272
+ const coin = mintShieldedToken(disclose(domain), disclose(amount), disclose(nonce), disclose(recipient));
273
+ if (disclose(!recipient.is_left)) {
274
+ receiveShielded(coin);
275
+ }
276
+
277
+ return coin;
254
278
  }
255
279
 
256
280
  /**
@@ -265,13 +289,14 @@ module NativeShieldedTokenCore {
265
289
  * @dev The coin is received by the contract and spent in the same
266
290
  * transaction, so the spend goes through `sendImmediateShielded` (the
267
291
  * transient path). For coins the contract already holds, use
268
- * `_burnFromSelf` instead.
292
+ * `_burnFromSelf` instead. A `right(kernel.self())` refund is claimed via
293
+ * `receiveShielded` in this same call.
269
294
  *
270
295
  * @notice The returned refund coin info is the only copy available to
271
296
  * `refundTo`; callers SHOULD deliver it out of band. Wallets cannot detect
272
297
  * contract-sent coins by scanning the chain.
273
298
  *
274
- * @constraints k=16, rows=47753
299
+ * @constraints k=16, rows=53639
275
300
  *
276
301
  * Requirements:
277
302
  *
@@ -280,12 +305,14 @@ module NativeShieldedTokenCore {
280
305
  * - `amount` is less than or equal to `coin.value`.
281
306
  * - `refundTo` is not zero (the zero key is the burn address; a zero
282
307
  * `refundTo` would silently burn the change as well).
308
+ * - `refundTo`, if a contract address, is this contract's own address.
283
309
  *
284
310
  * @param {Bytes<32>} domain - 32-byte token discriminator.
285
311
  * @param {ShieldedCoinInfo} coin - The coin to burn from.
286
312
  * @param {Uint<128>} amount - Value to destroy. Must be <= `coin.value`.
287
313
  * @param {Either<ZswapCoinPublicKey, ContractAddress>} refundTo - Where to
288
- * route the unspent portion (`coin.value - amount`).
314
+ * route the unspent portion (`coin.value - amount`): any coin public key, or
315
+ * this contract's own address.
289
316
  * @return {Maybe<ShieldedCoinInfo>} - The refund coin created for `refundTo`,
290
317
  * or `none` if the coin was burned in full.
291
318
  */
@@ -296,9 +323,11 @@ module NativeShieldedTokenCore {
296
323
  refundTo: Either<ZswapCoinPublicKey, ContractAddress>
297
324
  ): Maybe<ShieldedCoinInfo> {
298
325
  assertInitialized();
299
- assert(coin.color == tokenType(disclose(domain), kernel.self()), "NativeShieldedToken: wrong token");
326
+ const self = kernel.self();
327
+ assert(coin.color == tokenType(disclose(domain), self), "NativeShieldedToken: wrong token");
300
328
  assert(coin.value >= amount, "NativeShieldedToken: insufficient coin value");
301
329
  assert(!Utils_isKeyOrAddressZero(refundTo), "NativeShieldedToken: invalid refund target");
330
+ assert(refundTo.is_left || refundTo.right == self, "NativeShieldedToken: refund contract must be self");
302
331
 
303
332
  receiveShielded(disclose(coin));
304
333
  const sendRes = sendImmediateShielded(disclose(coin), shieldedBurnAddress(), disclose(amount));
@@ -309,6 +338,9 @@ module NativeShieldedTokenCore {
309
338
  disclose(refundTo),
310
339
  disclose(sendRes.change.value.value)
311
340
  );
341
+ if (disclose(!refundTo.is_left)) {
342
+ receiveShielded(refundRes.sent);
343
+ }
312
344
  return some<ShieldedCoinInfo>(refundRes.sent);
313
345
  }
314
346
  return none<ShieldedCoinInfo>();
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.4.0-alpha.2 (token/NativeShieldedTokenFamily.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.3 (token/NativeShieldedTokenFamily.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -26,6 +26,10 @@ pragma language_version >= 0.26.0;
26
26
  * set of core state (a single init flag and one name/symbol/decimals).
27
27
  * Pick one flavor per contract.
28
28
  *
29
+ * @notice Recipients must be coin public keys: contract-addressed recipients
30
+ * are not supported at this layer. For a coin the contract holds itself, use
31
+ * `_mintToSelf`, which mints to `kernel.self()` and claims it in the same call.
32
+ *
29
33
  * @notice This module exposes composable building blocks for minting and
30
34
  * burning native shielded coins. It does NOT include access control: consuming
31
35
  * contracts compose that via the module/contract pattern (Ownable,
@@ -186,7 +190,7 @@ module NativeShieldedTokenFamily {
186
190
  * recipient; callers SHOULD deliver it out of band. Wallets cannot detect
187
191
  * contract-minted coins by scanning the chain.
188
192
  *
189
- * @constraints k=14, rows=11096
193
+ * @constraints k=14, rows=10727
190
194
  *
191
195
  * Requirements:
192
196
  *
@@ -195,18 +199,59 @@ module NativeShieldedTokenFamily {
195
199
  *
196
200
  * @param {Bytes<32>} domain - 32-byte token discriminator. Same domain + same
197
201
  * contract address produces the same coin color (i.e. same token type).
198
- * @param {Either<ZswapCoinPublicKey, ContractAddress>} recipient - The coin recipient.
202
+ * @param {ZswapCoinPublicKey} recipient - The coin recipient.
199
203
  * @param {Uint<64>} amount - Quantity to mint. Capped at `Uint<64>` by the protocol.
200
204
  * @param {Bytes<32>} nonce - Caller-supplied nonce. Must be unique per (domain, contract).
201
205
  * @return {ShieldedCoinInfo} - The newly created coin's info (nonce, color, value).
202
206
  */
203
207
  export circuit _mint(
204
208
  domain: Bytes<32>,
205
- recipient: Either<ZswapCoinPublicKey, ContractAddress>,
209
+ recipient: ZswapCoinPublicKey,
210
+ amount: Uint<64>,
211
+ nonce: Bytes<32>
212
+ ): ShieldedCoinInfo {
213
+ return Core__mint(
214
+ domain,
215
+ left<ZswapCoinPublicKey, ContractAddress>(recipient),
216
+ amount,
217
+ nonce
218
+ );
219
+ }
220
+
221
+ /**
222
+ * @description Mints `amount` of the token identified by `domain` to this
223
+ * contract itself, claiming the coin in the same call. Forwards to
224
+ * `NativeShieldedTokenCore._mint` with `right(kernel.self())`.
225
+ *
226
+ * @dev Nonce uniqueness is the caller's responsibility, as in `_mint`. Spend
227
+ * the coin later with `_burnFromSelf`, once it carries an `mt_index`.
228
+ *
229
+ * @notice The consumer SHOULD persist the returned coin info in its own
230
+ * ledger state: it is the contract's holding and is not otherwise
231
+ * recoverable.
232
+ *
233
+ * @constraints k=14, rows=16309
234
+ *
235
+ * Requirements:
236
+ *
237
+ * - Contract is initialized.
238
+ *
239
+ * @param {Bytes<32>} domain - 32-byte token discriminator.
240
+ * @param {Uint<64>} amount - Quantity to mint. Capped at `Uint<64>` by the protocol.
241
+ * @param {Bytes<32>} nonce - Caller-supplied nonce. Must be unique per (domain, contract).
242
+ * @return {ShieldedCoinInfo} - The newly created coin's info (nonce, color, value).
243
+ */
244
+ export circuit _mintToSelf(
245
+ domain: Bytes<32>,
206
246
  amount: Uint<64>,
207
247
  nonce: Bytes<32>
208
248
  ): ShieldedCoinInfo {
209
- return Core__mint(domain, recipient, amount, nonce);
249
+ return Core__mint(
250
+ domain,
251
+ right<ZswapCoinPublicKey, ContractAddress>(kernel.self()),
252
+ amount,
253
+ nonce
254
+ );
210
255
  }
211
256
 
212
257
  /**
@@ -227,7 +272,7 @@ module NativeShieldedTokenFamily {
227
272
  * `refundTo`; callers SHOULD deliver it out of band. Wallets cannot detect
228
273
  * contract-sent coins by scanning the chain.
229
274
  *
230
- * @constraints k=16, rows=47753
275
+ * @constraints k=16, rows=41603
231
276
  *
232
277
  * Requirements:
233
278
  *
@@ -240,8 +285,8 @@ module NativeShieldedTokenFamily {
240
285
  * @param {Bytes<32>} domain - 32-byte token discriminator.
241
286
  * @param {ShieldedCoinInfo} coin - The coin to burn from.
242
287
  * @param {Uint<128>} amount - Value to destroy. Must be <= `coin.value`.
243
- * @param {Either<ZswapCoinPublicKey, ContractAddress>} refundTo - Where to
244
- * route the unspent portion (`coin.value - amount`).
288
+ * @param {ZswapCoinPublicKey} refundTo - Where to route the unspent
289
+ * portion (`coin.value - amount`).
245
290
  * @return {Maybe<ShieldedCoinInfo>} - The refund coin created for `refundTo`,
246
291
  * or `none` if the coin was burned in full.
247
292
  */
@@ -249,9 +294,14 @@ module NativeShieldedTokenFamily {
249
294
  domain: Bytes<32>,
250
295
  coin: ShieldedCoinInfo,
251
296
  amount: Uint<128>,
252
- refundTo: Either<ZswapCoinPublicKey, ContractAddress>
297
+ refundTo: ZswapCoinPublicKey
253
298
  ): Maybe<ShieldedCoinInfo> {
254
- return Core__burn(domain, coin, amount, refundTo);
299
+ return Core__burn(
300
+ domain,
301
+ coin,
302
+ amount,
303
+ left<ZswapCoinPublicKey, ContractAddress>(refundTo)
304
+ );
255
305
  }
256
306
 
257
307
  /**
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.4.0-alpha.2 (token/NonFungibleToken.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.3 (token/NonFungibleToken.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -58,7 +58,7 @@ pragma language_version >= 0.26.0;
58
58
  * or in ledger writes. Canonicalization zeroes out the inactive branch of the Either,
59
59
  * ensuring that two values with the same active branch always resolve to the same map key
60
60
  * regardless of what data the inactive branch carries. Write paths are canonicalized in
61
- * `_update` (for `_owners` and `_balances`), `_approve` (for `_tokenApprovals`), and
61
+ * `_update` (for `_owners` and `_balances`), `_unsafeApprove` (for `_tokenApprovals`), and
62
62
  * `_setApprovalForAll` (for `_operatorApprovals`). Read paths are canonicalized in
63
63
  * `balanceOf`, `isApprovedForAll`, and `_isAuthorized`.
64
64
  *
@@ -343,7 +343,7 @@ module NonFungibleToken {
343
343
  * In the case of an external (non-contract) caller, the caller's identity is derived from the `wit_NonFungibleTokenSK`
344
344
  * witness as `persistentHash(secretKey)`.
345
345
  *
346
- * @constraints k=13, rows=3491
346
+ * @constraints k=13, rows=3406
347
347
  *
348
348
  * Requirements:
349
349
  *
@@ -444,7 +444,7 @@ module NonFungibleToken {
444
444
  * In the case of an external (non-contract) caller, the caller's identity is derived from the `wit_NonFungibleTokenSK`
445
445
  * witness as `persistentHash(secretKey)`.
446
446
  *
447
- * @constraints k=13, rows=4701
447
+ * @constraints k=13, rows=4702
448
448
  *
449
449
  * Requirements:
450
450
  *
@@ -482,7 +482,7 @@ module NonFungibleToken {
482
482
  * are not currently supported. Tokens sent to a contract address may become irretrievable.
483
483
  * Once contract-to-contract calls are supported, this circuit may be deprecated.
484
484
  *
485
- * @constraints k=13, rows=4698
485
+ * @constraints k=13, rows=4699
486
486
  *
487
487
  * Requirements:
488
488
  *
@@ -504,11 +504,14 @@ module NonFungibleToken {
504
504
  ): [] {
505
505
  assertInitialized();
506
506
  assert(!Utils_isTargetZero(to), "NonFungibleToken: invalid receiver");
507
- // Setting an "auth" argument enables the `_isAuthorized` check which verifies that the token exists
508
- // (fromAddress != 0). Therefore, it is not needed to verify that the return value is not 0 here.
509
507
  const auth = left<Bytes<32>, ContractAddress>(_computeAccountId());
510
508
  const previousOwner = _update(to, tokenId, auth);
511
509
 
510
+ // The `_isAuthorized` check inside `_update` already implies the token exists, but that
511
+ // holds only while approvals are never recorded for unminted tokens. Assert it directly
512
+ // so a misused `_unsafeApprove` cannot turn this circuit into an unauthorized mint.
513
+ assert(!Utils_isTargetZero(previousOwner), "NonFungibleToken: nonexistent token");
514
+
512
515
  const canonFrom = Utils_canonicalize<Bytes<32>, ContractAddress>(fromAddress);
513
516
  assert(previousOwner == canonFrom, "NonFungibleToken: incorrect owner");
514
517
  }
@@ -648,8 +651,8 @@ module NonFungibleToken {
648
651
 
649
652
  // Execute the update
650
653
  if (!Utils_isTargetZero(disclose(fromAddress))) {
651
- // Clear approval. No need to re-authorize
652
- _approve(Utils_zeroAccount(), tokenId, Utils_zeroAccount());
654
+ // Clear approval. No need to re-authorize or re-read the owner
655
+ _unsafeApprove(Utils_zeroAccount(), tokenId, Utils_zeroAccount(), false);
653
656
  const canonFrom = Utils_canonicalize<Bytes<32>, ContractAddress>(fromAddress);
654
657
  const newBalance = _balances.lookup(disclose(canonFrom)) - 1 as Uint<128>;
655
658
  _balances.insert(disclose(canonFrom), disclose(newBalance));
@@ -820,11 +823,12 @@ module NonFungibleToken {
820
823
  /**
821
824
  * @description Approve `to` to operate on `tokenId`.
822
825
  *
823
- * @constraints k=11, rows=1840
826
+ * @constraints k=11, rows=1755
824
827
  *
825
828
  * Requirements:
826
829
  *
827
830
  * - The contract is initialized.
831
+ * - `tokenId` must exist.
828
832
  * - If `auth` is non 0, then this function will check that `auth` is either the owner of the token,
829
833
  * or approved to operate on the token (by the owner).
830
834
  *
@@ -838,6 +842,43 @@ module NonFungibleToken {
838
842
  tokenId: Uint<128>,
839
843
  auth: Either<Bytes<32>, ContractAddress>
840
844
  ): [] {
845
+ _unsafeApprove(to, tokenId, auth, true);
846
+ }
847
+
848
+ /**
849
+ * @description Approve `to` to operate on `tokenId`, requiring the token to exist
850
+ * only when `isExistenceRequired` is true.
851
+ *
852
+ * @constraints k=11, rows=1868
853
+ *
854
+ * @warning Passing false skips the check that `tokenId` was minted. Both
855
+ * `_unsafeTransferFrom` and `_update` read that invariant back, so a planted approval
856
+ * can mint a token nobody authorized or outlive a later mint. Pass false only where
857
+ * the owner has already been read, as `_update` does.
858
+ *
859
+ * @dev Counterpart to Solidity's `_approve(to, tokenId, auth, bool emitEvent)`, where
860
+ * the flag also suppresses the `Approval` event. Compact has no events, so it carries
861
+ * the existence check alone.
862
+ *
863
+ * Requirements:
864
+ *
865
+ * - The contract is initialized.
866
+ * - `tokenId` must exist, unless `isExistenceRequired` is false and `auth` is zero.
867
+ * - If `auth` is non 0, then this function will check that `auth` is either the owner of the token,
868
+ * or approved to operate on the token (by the owner).
869
+ *
870
+ * @param {Either<Bytes<32>, ContractAddress>} to - The target account to approve
871
+ * @param {Uint<128>} tokenId - The token to approve
872
+ * @param {Either<Bytes<32>, ContractAddress>} auth - An account authorized to operate on all tokens held by the owner of the token
873
+ * @param {Boolean} isExistenceRequired - Whether `tokenId` must exist
874
+ * @return {[]} - Empty tuple.
875
+ */
876
+ export circuit _unsafeApprove(
877
+ to: Either<Bytes<32>, ContractAddress>,
878
+ tokenId: Uint<128>,
879
+ auth: Either<Bytes<32>, ContractAddress>,
880
+ isExistenceRequired: Boolean
881
+ ): [] {
841
882
  assertInitialized();
842
883
  // Canonicalize + normalize `to`
843
884
  const canonTo = Utils_canonicalize<Bytes<32>, ContractAddress>(to);
@@ -846,11 +887,14 @@ module NonFungibleToken {
846
887
  // Canonicalize auth
847
888
  const canonAuth = Utils_canonicalize<Bytes<32>, ContractAddress>(auth);
848
889
 
849
- if (!Utils_isTargetZero(disclose(canonAuth))) {
890
+ // Avoid reading the owner unless necessary
891
+ if (disclose(isExistenceRequired) || !Utils_isTargetZero(disclose(canonAuth))) {
850
892
  const owner = _requireOwned(tokenId);
851
893
 
852
894
  // We do not use _isAuthorized because single-token approvals should not be able to call approve
853
- assert((owner == disclose(canonAuth) || isApprovedForAll(owner, canonAuth)),
895
+ assert((Utils_isTargetZero(disclose(canonAuth)) ||
896
+ owner == disclose(canonAuth) ||
897
+ isApprovedForAll(owner, canonAuth)),
854
898
  "NonFungibleToken: invalid approver"
855
899
  );
856
900
  }
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.4.0-alpha.2 (token/extensions/ConfidentialFungibleTokenPublicSupply.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.3 (token/extensions/ConfidentialFungibleTokenPublicSupply.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.4.0-alpha.2 (token/extensions/NativeShieldedTokenDerivedNonce.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.3 (token/extensions/NativeShieldedTokenDerivedNonce.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -20,7 +20,7 @@ pragma language_version >= 0.26.0;
20
20
  *
21
21
  * export circuit mint(
22
22
  * domain: Bytes<32>,
23
- * recipient: Either<ZswapCoinPublicKey, ContractAddress>,
23
+ * recipient: ZswapCoinPublicKey,
24
24
  * amount: Uint<64>,
25
25
  * nonce: Bytes<32>
26
26
  * ): ShieldedCoinInfo {
@@ -33,7 +33,7 @@ pragma language_version >= 0.26.0;
33
33
  * domain: Bytes<32>,
34
34
  * coin: ShieldedCoinInfo,
35
35
  * amount: Uint<128>,
36
- * refundTo: Either<ZswapCoinPublicKey, ContractAddress>
36
+ * refundTo: ZswapCoinPublicKey
37
37
  * ): Maybe<ShieldedCoinInfo> {
38
38
  * const refund = Family__burn(domain, coin, amount, refundTo);
39
39
  * Supply__addBurned(domain, amount);
@@ -18,7 +18,7 @@ pragma language_version >= 0.26.0;
18
18
  * pieces, calling the accounting block alongside the matching token op:
19
19
  *
20
20
  * export circuit mint(
21
- * recipient: Either<ZswapCoinPublicKey, ContractAddress>,
21
+ * recipient: ZswapCoinPublicKey,
22
22
  * amount: Uint<64>,
23
23
  * nonce: Bytes<32>
24
24
  * ): ShieldedCoinInfo {
@@ -30,7 +30,7 @@ pragma language_version >= 0.26.0;
30
30
  * export circuit burn(
31
31
  * coin: ShieldedCoinInfo,
32
32
  * amount: Uint<128>,
33
- * refundTo: Either<ZswapCoinPublicKey, ContractAddress>
33
+ * refundTo: ZswapCoinPublicKey
34
34
  * ): Maybe<ShieldedCoinInfo> {
35
35
  * const refund = Token__burn(coin, amount, refundTo);
36
36
  * Supply__addBurned(amount);
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.4.0-alpha.2 (utils/EvmAbi.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.3 (utils/EvmAbi.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.4.0-alpha.2 (utils/Utils.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.3 (utils/Utils.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -150,6 +150,16 @@ module Utils {
150
150
  * ## ID Derivation
151
151
  * `accountId = persistentHash(secretKey)`
152
152
  *
153
+ * @dev The absence of a domain-separation tag is DELIBERATE. This identifier
154
+ * is global by design: the same key material yields the same identity in
155
+ * every module deriving one, so a user who wishes to carry one identity
156
+ * across modules can. Modules wanting a per-deployment, unlinkable identity
157
+ * should not use this circuit.
158
+ *
159
+ * @dev NOT FOR PRIVATE DERIVATION. This returns an identifier, not a secret,
160
+ * and the construction is untagged. A circuit deriving a PRIVATE value from a
161
+ * secret MUST use its own domain-separation tag.
162
+ *
153
163
  * @param {Bytes<32>} secretKey - A 32-byte cryptographically secure random value.
154
164
  *
155
165
  * @returns {Bytes<32>} accountId - The computed account identifier.