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

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/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 +1 -1
  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 +25 -5
  22. package/multisig/examples/ShieldedMultiSigV2Example.compact +1 -1
  23. package/multisig/presets/NativeShieldedTokenIssuer.compact +253 -115
  24. package/multisig/presets/ShieldedMultiSigV2.compact +1 -1
  25. package/package.json +5 -3
  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 +1 -1
  31. package/token/FungibleToken.compact +1 -1
  32. package/token/MultiToken.compact +1 -1
  33. package/token/NativeShieldedToken.compact +1 -1
  34. package/token/NativeShieldedTokenCore.compact +1 -1
  35. package/token/NativeShieldedTokenFamily.compact +1 -1
  36. package/token/NonFungibleToken.compact +1 -1
  37. package/token/extensions/ConfidentialFungibleTokenPublicSupply.compact +1 -1
  38. package/token/extensions/NativeShieldedTokenDerivedNonce.compact +1 -1
  39. package/utils/EvmAbi.compact +1 -1
  40. package/utils/Utils.compact +1 -1
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.4.0-alpha.3 (access/AccessControl.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (access/AccessControl.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.3 (access/Ownable.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (access/Ownable.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.3 (access/ShieldedAccessControl.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (access/ShieldedAccessControl.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.3 (access/ZOwnablePK.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (access/ZOwnablePK.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.3 (crypto/EcdhMask.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (crypto/EcdhMask.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.3 (crypto/Ecdsa.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (crypto/Ecdsa.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.3 (crypto/Eip712.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (crypto/Eip712.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.3 (crypto/ElGamal.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (crypto/ElGamal.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.3 (multisig/EcdsaSignerManager.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (multisig/EcdsaSignerManager.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.3 (multisig/ForwarderPrivate.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (multisig/ForwarderPrivate.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.3 (multisig/ForwarderShielded.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (multisig/ForwarderShielded.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.3 (multisig/ForwarderUnshielded.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (multisig/ForwarderUnshielded.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.3 (multisig/ProposalManager.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (multisig/ProposalManager.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.3 (multisig/ShieldedTreasury.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (multisig/ShieldedTreasury.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.3 (multisig/ShieldedTreasuryStateless.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (multisig/ShieldedTreasuryStateless.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.3 (multisig/Signer.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (multisig/Signer.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.3 (multisig/UnshieldedTreasury.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (multisig/UnshieldedTreasury.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.3 (multisig/examples/ForwarderPrivateExample.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (multisig/examples/ForwarderPrivateExample.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.3 (multisig/examples/ForwarderShieldedExample.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (multisig/examples/ForwarderShieldedExample.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.3 (multisig/examples/ForwarderUnshieldedExample.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (multisig/examples/ForwarderUnshieldedExample.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.3 (multisig/examples/NativeShieldedTokenIssuerExample.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (multisig/examples/NativeShieldedTokenIssuerExample.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -7,10 +7,12 @@ pragma language_version >= 0.26.0;
7
7
  * @title NativeShieldedTokenIssuerExample
8
8
  * @description Deployable reference contract for the `NativeShieldedTokenIssuer`
9
9
  * preset: a native shielded token whose supply enters only through an
10
- * ECDSA-authorized mint and leaves only through an ECDSA-authorized burn.
10
+ * ECDSA-authorized mint, to a holder (`mint`) or to the contract itself
11
+ * (`mintToSelf`), and leaves only through an ECDSA-authorized burn, of a
12
+ * holder's coin (`burn`) or of one the contract holds (`burnFromSelf`).
11
13
  *
12
14
  * The token is non-transferable by construction. There is no transfer circuit,
13
- * so a holder's only exit is to present the coin back for burn.
15
+ * so a holder's only exit is to pay the coin into a `burn` tx.
14
16
  */
15
17
 
16
18
  import CompactStandardLibrary;
@@ -94,20 +96,38 @@ export pure circuit _calculateSignerId(
94
96
 
95
97
  export circuit mint(
96
98
  amount: Uint<64>,
97
- recipient: Either<ZswapCoinPublicKey, ContractAddress>,
99
+ recipient: ZswapCoinPublicKey,
98
100
  pubkeys: Vector<2, Secp256k1Point>,
99
101
  signatures: Vector<2, Secp256k1EcdsaSignature>
100
102
  ): ShieldedCoinInfo {
101
103
  return NativeShieldedTokenIssuer_mint(amount, recipient, pubkeys, signatures);
102
104
  }
103
105
 
106
+ export circuit mintToSelf(
107
+ amount: Uint<64>,
108
+ pubkeys: Vector<2, Secp256k1Point>,
109
+ signatures: Vector<2, Secp256k1EcdsaSignature>
110
+ ): ShieldedCoinInfo {
111
+ return NativeShieldedTokenIssuer_mintToSelf(amount, pubkeys, signatures);
112
+ }
113
+
104
114
  export circuit burn(
115
+ coin: ShieldedCoinInfo,
116
+ amount: Uint<64>,
117
+ refundTo: ZswapCoinPublicKey,
118
+ pubkeys: Vector<2, Secp256k1Point>,
119
+ signatures: Vector<2, Secp256k1EcdsaSignature>
120
+ ): Maybe<ShieldedCoinInfo> {
121
+ return NativeShieldedTokenIssuer_burn(coin, amount, refundTo, pubkeys, signatures);
122
+ }
123
+
124
+ export circuit burnFromSelf(
105
125
  coin: QualifiedShieldedCoinInfo,
106
126
  amount: Uint<64>,
107
127
  pubkeys: Vector<2, Secp256k1Point>,
108
128
  signatures: Vector<2, Secp256k1EcdsaSignature>
109
129
  ): Maybe<ShieldedCoinInfo> {
110
- return NativeShieldedTokenIssuer_burn(coin, amount, pubkeys, signatures);
130
+ return NativeShieldedTokenIssuer_burnFromSelf(coin, amount, pubkeys, signatures);
111
131
  }
112
132
 
113
133
  export circuit getNonce(): Uint<64> {
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.4.0-alpha.3 (multisig/examples/ShieldedMultiSigV2Example.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (multisig/examples/ShieldedMultiSigV2Example.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.3 (multisig/presets/NativeShieldedTokenIssuer.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (multisig/presets/NativeShieldedTokenIssuer.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
@@ -7,8 +7,10 @@ pragma language_version >= 0.26.0;
7
7
  * @module NativeShieldedTokenIssuer
8
8
  * @description
9
9
  * A 2-of-3 ECDSA multisig that mints and burns one native shielded token,
10
- * for tokenized deposits. Value enters only through `mint` and leaves the
11
- * contract only through `burn`; each is a single tx carrying two registered
10
+ * for tokenized deposits. Value enters only through `mint` (to a holder's
11
+ * coin public key) or `mintToSelf` (a coin this contract holds) and leaves
12
+ * only through `burn` (a holder's coin, paid into the tx) or `burnFromSelf`
13
+ * (a coin this contract holds); each is a single tx carrying two registered
12
14
  * signers' approvals, so no single party creates or destroys tokens.
13
15
  *
14
16
  * Composes `EcdsaSignerManager` (signer commitments, approval checks),
@@ -23,8 +25,9 @@ pragma language_version >= 0.26.0;
23
25
  * - `DS`: `_domainSeparator`, the EIP-712 domain separator fixed at
24
26
  * construction.
25
27
  * - `self`: `kernel.self().bytes`, this contract's address.
26
- * - `opNonce`: the `_counter` value read at the start of `mint` / `burn`. It
27
- * is the signature replay counter and is separate from the
28
+ * - `opNonce`: the `_counter` value read at the start of every operation
29
+ * (`mint`, `mintToSelf`, `burn`, `burnFromSelf`). It is the signature
30
+ * replay counter and is separate from the
28
31
  * `NativeShieldedTokenDerivedNonce` counter that feeds coin nonces.
29
32
  * - `cm`: a signer commitment, the registry key.
30
33
  * - `color`: `tokenType(_domain, self)`, this token's coin color.
@@ -37,15 +40,19 @@ pragma language_version >= 0.26.0;
37
40
  * and a 32-byte address does not fit `address`. `self` inside each struct
38
41
  * separates deployments instead.
39
42
  * - Mint digest: `keccak(0x1901 ‖ DS ‖ keccak([MINT_TYPEHASH, self,
40
- * recipientWord, boolWord(isContract), uint64Word(opNonce),
41
- * uint64Word(amount)]))`, with `canonRecipient =
42
- * Utils_canonicalize(recipient)` (unused `Either` arm zeroed),
43
- * `recipientWord` its active arm and `isContract = !canonRecipient.is_left`.
43
+ * recipient.bytes, uint64Word(opNonce), uint64Word(amount)]))`.
44
+ * - MintToSelf digest: `keccak(0x1901 ‖ DS ‖ keccak([MINT_TO_SELF_TYPEHASH,
45
+ * self, uint64Word(opNonce), uint64Word(amount)]))`.
44
46
  * - Burn digest: `keccak(0x1901 ‖ DS ‖ keccak([BURN_TYPEHASH, self,
45
- * uint64Word(opNonce), uint64Word(amount)]))`.
47
+ * refundTo.bytes, uint64Word(opNonce), uint64Word(amount)]))`.
48
+ * - BurnFromSelf digest: `keccak(0x1901 ‖ DS ‖ keccak([
49
+ * BURN_FROM_SELF_TYPEHASH, self, uint64Word(opNonce), uint64Word(amount)]))`.
46
50
  * - Type hashes: `MINT_TYPEHASH = keccak("Mint(bytes32 contractAddress,
47
- * bytes32 recipient,bool isContract,uint256 nonce,uint256 amount)")` and
48
- * `BURN_TYPEHASH = keccak("Burn(bytes32 contractAddress,uint256 nonce,
51
+ * bytes32 recipient,uint256 nonce,uint256 amount)")`,
52
+ * `MINT_TO_SELF_TYPEHASH = keccak("MintToSelf(bytes32 contractAddress,
53
+ * uint256 nonce,uint256 amount)")`, `BURN_TYPEHASH = keccak("Burn(bytes32 contractAddress,bytes32 refundTo,
54
+ * uint256 nonce,uint256 amount)")` and `BURN_FROM_SELF_TYPEHASH =
55
+ * keccak("BurnFromSelf(bytes32 contractAddress,uint256 nonce,
49
56
  * uint256 amount)")`, written as literal words.
50
57
  * - Signer commitment: `cm = H<SignerCommitmentInput>({ pkX, pkY, salt,
51
58
  * domain: pad(32, "multisig:signer:") })`, `pkX` / `pkY` the affine
@@ -57,7 +64,8 @@ pragma language_version >= 0.26.0;
57
64
  * Digest integers are big-endian ABI words (`utils/EvmAbi`), so an EVM
58
65
  * signer's typed-data encoder reproduces them.
59
66
  * `contracts/src/multisig/test/EcdsaTestUtils.ts` pins the digests
60
- * through ethers (`mintMsgHash`, `burnMsgHash`).
67
+ * through ethers (`mintMsgHash`, `mintToSelfMsgHash`, `burnMsgHash`,
68
+ * `burnFromSelfMsgHash`).
61
69
  *
62
70
  * @notice Implementing contracts register the signer set from their
63
71
  * constructor by calling this module's `initialize`, and MUST NOT import
@@ -68,8 +76,8 @@ pragma language_version >= 0.26.0;
68
76
  *
69
77
  * @dev Compatibility:
70
78
  * The wire format the custodian's signer implements is the EIP-712 domain
71
- * (`NativeShieldedTokenIssuer`, version `1`, salt), the `Mint` / `Burn`
72
- * type strings, the `multisig:signer:` commitment domain (generic prefix,
79
+ * (`NativeShieldedTokenIssuer`, version `1`, salt), the `Mint` /
80
+ * `MintToSelf` / `Burn` / `BurnFromSelf` type strings, the `multisig:signer:` commitment domain (generic prefix,
73
81
  * owned by `EcdsaSignerManager`), the `cm` preimage struct, and the
74
82
  * `Vector<2>` / `Vector<3>` shapes. Changing any of them breaks every
75
83
  * existing signer setup. The test suite pins them through
@@ -77,22 +85,30 @@ pragma language_version >= 0.26.0;
77
85
  *
78
86
  * @notice Privacy:
79
87
  * - Public, per tx:
80
- * - Entry point: mint vs burn.
88
+ * - Entry point: which of the four operations ran.
81
89
  * - Approvers: the two signer commitments (`disclose(commitment)` in
82
90
  * `Signer_assertSigner`), so WHICH two of the three signers approved.
83
91
  * - Mint: `amount`, as the `shieldedMints` effect under the token domain,
84
92
  * and the coin commitment as a spend claim. Recipient and nonce are not
85
93
  * published. The nonce is `evolveNonce` of the public
86
94
  * `_derivedNonceCounter`, so a candidate recipient can be tested
87
- * against the commitment. A mint to this contract adds the same
88
- * commitment as a receive claim and names the contract in the Zswap
89
- * output.
90
- * - Burn: the spent coin's nullifier, the burn-output commitment and, on a
91
- * partial burn, the change commitment as both spend and receive claim.
92
- * `amount`, the coin's value and `mt_index` are not published. Every
93
- * coin this contract holds was minted to it, so its nullifier is
94
- * computable and a burn links to its mint; the amount is protected by
95
- * the commitment hash alone.
95
+ * against the commitment.
96
+ * - MintToSelf: as `mint`, plus the same commitment as a receive claim and
97
+ * the contract named in the Zswap output.
98
+ * - Burn: the holder's coin commitment as a receive claim, the burn-output
99
+ * commitment and, on a partial burn, the refund commitment. `amount`,
100
+ * `coin.value` and `refundTo` stay hidden only while `coin` carries a
101
+ * nonce the observer does not know. A coin passed straight from `mint`
102
+ * has a derived nonce, so the receive claim links the burn to its mint
103
+ * and exposes all three by trial: `coin.value` from the mint, `amount`
104
+ * over `[0, value]` against the burn output, `refundTo` against the
105
+ * refund commitment.
106
+ * - BurnFromSelf: the spent coin's nullifier, the burn-output commitment
107
+ * and, on a partial burn, the change commitment as both spend and
108
+ * receive claim. `amount`, the coin's value and `mt_index` are not
109
+ * published, but every held coin has a derived nonce, so its nullifier
110
+ * links the burn to its mint and `amount` is recoverable by trial over
111
+ * `[0, value]`.
96
112
  * - Public, as state:
97
113
  * - Cells: `_counter`, `_domainSeparator`, `_derivedNonceCounter`,
98
114
  * `_signers`, `_signerCount`, `_threshold`, `_instanceSalt`, `_domain`,
@@ -102,37 +118,38 @@ pragma language_version >= 0.26.0;
102
118
  * - Private:
103
119
  * - Signer material: public keys and signatures are verified in-circuit,
104
120
  * never disclosed.
105
- * - Operation data: burn amounts. Mint recipients, up to the commitment
106
- * test above.
121
+ * - Operation data: `burn` amounts and `refundTo`, for a coin whose nonce
122
+ * is secret. Mint recipients, up to the commitment test above.
107
123
  * - Signer identity: keys sit behind `cm` with a public salt, so an observer
108
124
  * holding a candidate key can test membership; one without cannot
109
125
  * correlate a signer across deployments.
110
126
  * - Counterparty: a mint recipient learns nothing beyond the coin handed over
111
127
  * out of band; every observer learns the amount of every mint and can
112
128
  * test a candidate recipient against the commitment.
113
- * - Auditability: mint amounts are public, burn amounts are not. No
129
+ * - Auditability: mint amounts are public; `burnFromSelf` amounts are
130
+ * recoverable by trial, `burn` amounts only for a linkable coin. No
114
131
  * on-chain total is kept. `NativeShieldedTokenPublicSupply` adds one and
115
132
  * makes burn amounts public.
116
133
  *
117
134
  * @notice Security:
118
135
  * - Digest binding: each struct binds its type hash, `self`, `opNonce` and
119
- * `amount` (`mint` also the recipient word and its kind) under `DS`, so a
136
+ * `amount` (`mint` also `recipient`, `burn` also `refundTo`) under `DS`,
137
+ * so a
120
138
  * signature authorizes one operation on one instance at one nonce, and is
121
- * neither replayable as the other operation, nor under another domain,
139
+ * neither replayable as another operation, nor under another domain,
122
140
  * nor redirectable. Removing the `self` word removes cross-instance
123
- * replay protection, since `DS` cannot carry the address.
141
+ * replay protection, since `DS` cannot carry the address. `Burn` does not
142
+ * bind `coin`: the payer picks it, so a signature moved to another coin
143
+ * burns only the payer's own value and refunds the bound `refundTo`.
124
144
  * - Low-s: verification goes through `Ecdsa_secp256k1EcdsaVerifyLowS`, so a
125
145
  * high-s encoding is an invalid signature.
126
146
  * - Distinct signers: `assertApprovals` asserts both entries and rejects a
127
147
  * repeated commitment, so two distinct registered signers are always
128
148
  * required. The threshold `2` is the ceiling `EcdsaSignerManager` accepts.
129
- * - Canonical recipient: `Utils_canonicalize` zeroes the unused `Either` arm
130
- * before hashing and minting, so a recipient with both arms populated
131
- * cannot yield two digests for one destination.
132
149
  * - Immutable signer set: no signer reconfiguration circuit is exported, so
133
150
  * the three commitments and the threshold are fixed. Losing two of the
134
151
  * three keys freezes the contract.
135
- * - Griefing: the derived-nonce mint is the only mint path and it is
152
+ * - Griefing: the derived-nonce mints are the only mint paths and both are
136
153
  * threshold-gated, so `NativeShieldedTokenDerivedNonce`'s
137
154
  * collision-griefing precondition (a caller-nonce mint at another trust
138
155
  * level) is absent.
@@ -140,24 +157,24 @@ pragma language_version >= 0.26.0;
140
157
  * neither counter advances and the sender pays no fee.
141
158
  *
142
159
  * @notice Concurrency:
143
- * - Replay counter: `mint` and `burn` both read `_counter` into the digest.
144
- * The read is pinned, so any two of them in one block conflict: at most one
145
- * mint-or-burn lands per block, and the losing signer pair re-signs at the
146
- * next nonce.
147
- * - Derived-nonce counter: `mint` also pins it (`evolveNonce` consumes it);
148
- * the `_counter` conflict already covers that pair.
149
- * - `getNonce`: submitted as a tx it pins `_counter` and conflicts with a
150
- * same-block mint or burn.
160
+ * - Replay counter: `mint`, `mintToSelf`, `burn` and `burnFromSelf` read
161
+ * `_counter` into the digest. The read is pinned, so any two of them in one block
162
+ * conflict: at most one operation lands per block, and the losing signer
163
+ * pair re-signs at the next nonce.
164
+ * - Derived-nonce counter: both mints also pin it (`evolveNonce` consumes
165
+ * it); the `_counter` conflict already covers those pairs.
166
+ * - `getNonce`: submitted as a tx it pins `_counter` and conflicts with any
167
+ * same-block operation.
151
168
  * - Views: metadata, `tokenColor` and the signer views read only state
152
169
  * written by `initialize`, so they commute with everything.
153
170
  *
154
171
  * @notice Scope:
155
172
  * Mint and burn only. No send, transfer or execute circuit exists, so
156
- * contract-held coins leave only through `burn`. No deposit or receive
157
- * circuit exists either: `burn` spends a coin the contract already holds,
158
- * supplied by the operator as `QualifiedShieldedCoinInfo`. Coins minted to
159
- * users are ordinary Zswap coins the contract neither tracks nor restricts.
160
- * No supply accounting.
173
+ * contract-held coins (from `mintToSelf` or `burnFromSelf` change) leave only
174
+ * through `burnFromSelf`, supplied by the operator as
175
+ * `QualifiedShieldedCoinInfo`. Coins minted to users are ordinary
176
+ * Zswap coins the contract neither tracks nor restricts; a holder redeems by
177
+ * paying the coin into a `burn` tx. No supply accounting.
161
178
  *
162
179
  * @notice Client-side duties:
163
180
  * - Before deploy: pick a random `instanceSalt`, derive each `cm` with
@@ -165,28 +182,35 @@ pragma language_version >= 0.26.0;
165
182
  * - Per operation: read `getNonce()`, rebuild the digest as
166
183
  * `EcdsaTestUtils.ts` does, collect two low-s signatures off-chain, submit
167
184
  * keys and signatures in matching `Vector<2>` positions.
168
- * - Coin delivery: hand the `ShieldedCoinInfo` returned by `mint` to the
169
- * recipient out of band; contract-initiated mints create no ciphertext, so
170
- * a wallet cannot find the coin by scanning.
171
- * - Coin tracking: keep contract-held coins off-chain, recovering each
172
- * coin's `mt_index` from the indexer's `ZswapOutput` / ledger events to
173
- * build the `QualifiedShieldedCoinInfo` that `burn` takes.
174
- * - Change coin: persist the one `burn` returns. It replaces the spent coin
175
- * as the contract's holding and is not otherwise recoverable.
185
+ * - `mint`: hand the returned `ShieldedCoinInfo` to the recipient out of
186
+ * band; contract-initiated mints create no ciphertext, so a wallet cannot
187
+ * find the coin by scanning.
188
+ * - `mintToSelf`: persist the returned coin as a contract holding, as for
189
+ * `burnFromSelf` change.
190
+ * - `burn`: the holder's wallet funds `coin` into the tx; hand the returned
191
+ * refund coin to `refundTo` out of band. For an unlinkable burn the holder
192
+ * re-nonces the coin first (a wallet self-transfer yields a fresh secret
193
+ * nonce); the minted coin as-is is valid but linkable. The contract cannot
194
+ * enforce this.
195
+ * - `burnFromSelf` coin tracking: keep contract-held coins off-chain,
196
+ * recovering each coin's `mt_index` from the indexer's `ZswapOutput` /
197
+ * ledger events to build the `QualifiedShieldedCoinInfo` it takes.
198
+ * - `burnFromSelf` change coin: persist the one it returns. It replaces the
199
+ * spent coin as the contract's holding and is not otherwise recoverable.
176
200
  *
177
201
  * @notice Scalability:
178
- * - Throughput: one mint-or-burn per block (see Concurrency).
202
+ * - Throughput: one operation per block (see Concurrency).
179
203
  * - State: constant-size, two counters and a three-entry signer set. Nothing
180
204
  * grows per operation.
181
- * - Amounts: `mint` caps at `Uint<64>` by `mintShieldedToken`; `burn` takes
182
- * `Uint<64>` by its signature although `NST__burnFromSelf` accepts
205
+ * - Amounts: both mints cap at `Uint<64>` by `mintShieldedToken`; both burns
206
+ * take `Uint<64>` by their signatures and widen into the module's
183
207
  * `Uint<128>`.
184
208
  *
185
209
  * @dev Composition:
186
210
  * - Recipient privacy: pass a caller-supplied secret random nonce to
187
- * `NST__mint` instead of `NSTDerivedNonce__deriveNonce()`.
188
- * - Deposits: a deposit path needs a `receiveShielded` circuit; none is
189
- * included.
211
+ * `NST__mint` / `NST__mintToSelf` instead of `NSTDerivedNonce__deriveNonce()`.
212
+ * - Deposits: no deposit-and-hold path; `burn` receives and spends the
213
+ * holder's coin in one call.
190
214
  *
191
215
  * @notice Requires ZKIR v3: `keccak256` is unavailable on the v2 backend.
192
216
  */
@@ -195,7 +219,6 @@ module NativeShieldedTokenIssuer {
195
219
 
196
220
  import "../EcdsaSignerManager" prefix EcdsaSignerManager_;
197
221
  import "../../crypto/Eip712" prefix Eip712_;
198
- import "../../utils/Utils" prefix Utils_;
199
222
  import "../../utils/EvmAbi" prefix EvmAbi_;
200
223
  import "../../token/NativeShieldedToken" prefix NST_;
201
224
  import "../../token/extensions/NativeShieldedTokenDerivedNonce" prefix NSTDerivedNonce_;
@@ -212,10 +235,11 @@ module NativeShieldedTokenIssuer {
212
235
  export { _derivedNonceCounter };
213
236
 
214
237
  /**
215
- * @description Signature replay counter. `mint` and `burn` read it into
216
- * their digest and increment it; `getNonce` returns the value the next
217
- * digest binds to. The read is pinned, so at most one mint-or-burn lands
218
- * per block.
238
+ * @description Signature replay counter. Every operation (`mint`,
239
+ * `mintToSelf`, `burn`, `burnFromSelf`) reads it into its digest and
240
+ * increments it; `getNonce` returns the value
241
+ * the next digest binds to. The read is pinned, so at most one operation
242
+ * lands per block.
219
243
  */
220
244
  export ledger _counter: Counter;
221
245
 
@@ -277,19 +301,39 @@ module NativeShieldedTokenIssuer {
277
301
 
278
302
  /**
279
303
  * @description `keccak256` of the `Mint` type string:
280
- * `Mint(bytes32 contractAddress,bytes32 recipient,bool isContract,uint256 nonce,uint256 amount)`
304
+ * `Mint(bytes32 contractAddress,bytes32 recipient,uint256 nonce,uint256 amount)`
281
305
  *
282
- * @dev Written as a literal word; see `BURN_TYPEHASH`. The spec checks this
306
+ * @dev Written as a literal word; see `BURN_FROM_SELF_TYPEHASH`. The spec checks this
283
307
  * word against the type string above.
284
308
  *
285
309
  * @returns {Bytes<32>} The `Mint` type hash.
286
310
  */
287
311
  pure circuit MINT_TYPEHASH(): Bytes<32> {
288
312
  const typeHash: Vector<32, Uint<8>> = [
289
- 0x28, 0xd4, 0xc8, 0x40, 0xbc, 0x95, 0xfe, 0x08,
290
- 0x4e, 0xa5, 0xe3, 0x20, 0x94, 0x43, 0xba, 0x67,
291
- 0x21, 0xb7, 0x17, 0x09, 0x71, 0xee, 0x05, 0xef,
292
- 0xaf, 0x0a, 0x98, 0x51, 0xee, 0x5f, 0xd1, 0xf5
313
+ 0x65, 0x2d, 0x4d, 0xad, 0x8f, 0xb2, 0x6c, 0x5c,
314
+ 0x50, 0xc9, 0x87, 0xd4, 0x19, 0xdc, 0x93, 0x53,
315
+ 0x14, 0xa0, 0x1e, 0xd4, 0x5d, 0xb1, 0x59, 0xf2,
316
+ 0x30, 0xad, 0xc1, 0xa7, 0xe9, 0x02, 0x91, 0x1c
317
+ ];
318
+
319
+ return typeHash as Bytes<32>;
320
+ }
321
+
322
+ /**
323
+ * @description `keccak256` of the `MintToSelf` type string:
324
+ * `MintToSelf(bytes32 contractAddress,uint256 nonce,uint256 amount)`
325
+ *
326
+ * @dev Written as a literal word; see `BURN_FROM_SELF_TYPEHASH`. The spec
327
+ * checks this word against the type string above.
328
+ *
329
+ * @returns {Bytes<32>} The `MintToSelf` type hash.
330
+ */
331
+ pure circuit MINT_TO_SELF_TYPEHASH(): Bytes<32> {
332
+ const typeHash: Vector<32, Uint<8>> = [
333
+ 0xf8, 0xb3, 0x1e, 0x4e, 0x0f, 0x2d, 0x10, 0x33,
334
+ 0x82, 0xb4, 0xb3, 0x72, 0xac, 0x65, 0xa7, 0xad,
335
+ 0xcd, 0x03, 0x89, 0x39, 0x8d, 0x24, 0x6c, 0x5e,
336
+ 0xc2, 0xc9, 0x09, 0x38, 0x53, 0xdb, 0x49, 0x91
293
337
  ];
294
338
 
295
339
  return typeHash as Bytes<32>;
@@ -297,21 +341,41 @@ module NativeShieldedTokenIssuer {
297
341
 
298
342
  /**
299
343
  * @description `keccak256` of the `Burn` type string:
300
- * `Burn(bytes32 contractAddress,uint256 nonce,uint256 amount)`
344
+ * `Burn(bytes32 contractAddress,bytes32 refundTo,uint256 nonce,uint256 amount)`
345
+ *
346
+ * @dev Written as a literal word; see `BURN_FROM_SELF_TYPEHASH`. The spec
347
+ * checks this word against the type string above.
348
+ *
349
+ * @returns {Bytes<32>} The `Burn` type hash.
350
+ */
351
+ pure circuit BURN_TYPEHASH(): Bytes<32> {
352
+ const typeHash: Vector<32, Uint<8>> = [
353
+ 0x96, 0x8e, 0x03, 0xa5, 0xba, 0x91, 0xc8, 0xdd,
354
+ 0x27, 0x56, 0x79, 0x5a, 0xc6, 0x57, 0x52, 0x13,
355
+ 0x06, 0xe0, 0xd9, 0x7c, 0xe8, 0x1e, 0xbf, 0x9b,
356
+ 0x68, 0x24, 0x45, 0xa2, 0x86, 0xfc, 0xeb, 0xef
357
+ ];
358
+
359
+ return typeHash as Bytes<32>;
360
+ }
361
+
362
+ /**
363
+ * @description `keccak256` of the `BurnFromSelf` type string:
364
+ * `BurnFromSelf(bytes32 contractAddress,uint256 nonce,uint256 amount)`
301
365
  *
302
366
  * @dev Written as a literal word. `compactc` emits a full keccak
303
367
  * permutation even for a constant argument, so hashing the type string
304
368
  * in-circuit would add ~4.2k rows to every operation. The spec checks this
305
369
  * word against the type string above.
306
370
  *
307
- * @returns {Bytes<32>} The `Burn` type hash.
371
+ * @returns {Bytes<32>} The `BurnFromSelf` type hash.
308
372
  */
309
- pure circuit BURN_TYPEHASH(): Bytes<32> {
373
+ pure circuit BURN_FROM_SELF_TYPEHASH(): Bytes<32> {
310
374
  const typeHash: Vector<32, Uint<8>> = [
311
- 0xdf, 0x62, 0xd6, 0x7d, 0x39, 0x08, 0x06, 0xa6,
312
- 0xb6, 0x3f, 0xbd, 0x9f, 0xb5, 0xb8, 0x90, 0x4e,
313
- 0xf8, 0x6a, 0x4d, 0x67, 0xf3, 0x82, 0x4a, 0x3d,
314
- 0x85, 0x5f, 0x6e, 0xef, 0xb8, 0xe7, 0x98, 0x09
375
+ 0x08, 0xba, 0x21, 0x80, 0x7e, 0x7f, 0x75, 0x47,
376
+ 0xa2, 0x70, 0x1d, 0x8c, 0x53, 0x06, 0x39, 0x7d,
377
+ 0x97, 0xad, 0xfd, 0x3e, 0xa6, 0xf6, 0xa2, 0x8c,
378
+ 0x10, 0x0c, 0xbd, 0x64, 0x9c, 0x7f, 0x5d, 0x94
315
379
  ];
316
380
 
317
381
  return typeHash as Bytes<32>;
@@ -348,7 +412,7 @@ module NativeShieldedTokenIssuer {
348
412
  * @notice The returned coin info is the only copy available to the
349
413
  * recipient; deliver it out of band.
350
414
  *
351
- * @constraints k=17, rows=105173 (zkir-v3)
415
+ * @constraints k=17, rows=89815 (zkir-v3)
352
416
  *
353
417
  * Requirements:
354
418
  *
@@ -357,61 +421,135 @@ module NativeShieldedTokenIssuer {
357
421
  * - Signers must not be duplicates.
358
422
  * - Threshold must be met.
359
423
  * - `recipient` is not zero.
360
- * - A contract `recipient` is this contract's own address.
361
424
  *
362
425
  * @param {Uint<64>} amount - The token amount to mint.
363
- * @param {Either<ZswapCoinPublicKey, ContractAddress>} recipient - The address to receive the minted tokens.
426
+ * @param {ZswapCoinPublicKey} recipient - The coin public key to receive the minted tokens.
364
427
  * @param {Vector<2, Secp256k1Point>} pubkeys - Public keys of approving signers.
365
428
  * @param {Vector<2, Secp256k1EcdsaSignature>} signatures - ECDSA signatures over the mint hash.
366
429
  * @returns {ShieldedCoinInfo} The minted coin (nonce, color, value).
367
430
  */
368
431
  export circuit mint(
369
432
  amount: Uint<64>,
370
- recipient: Either<ZswapCoinPublicKey, ContractAddress>,
433
+ recipient: ZswapCoinPublicKey,
371
434
  pubkeys: Vector<2, Secp256k1Point>,
372
435
  signatures: Vector<2, Secp256k1EcdsaSignature>
373
436
  ): ShieldedCoinInfo {
374
437
  const opNonce = _counter;
375
438
  _counter.increment(1);
376
439
 
377
- // Canonicalize recipient to ensure garbage values don't sully the hash
378
- const canonRecipient = Utils_canonicalize<ZswapCoinPublicKey, ContractAddress>(recipient);
440
+ const msgHash = Eip712_hashTypedData(_domainSeparator, keccak256<Vector<5, Bytes<32>>>([
441
+ MINT_TYPEHASH(),
442
+ // Binds the digest to this deployment. The domain cannot carry the
443
+ // address, so removing this word removes cross-instance replay
444
+ // protection.
445
+ kernel.self().bytes,
446
+ recipient.bytes,
447
+ EvmAbi_uint64Word(opNonce),
448
+ EvmAbi_uint64Word(amount)
449
+ ]));
450
+
451
+ EcdsaSignerManager_assertApprovals(msgHash, pubkeys, signatures);
379
452
 
380
- // Only the active arm reaches the digest, so a crafted value carrying data
381
- // in its unused arm cannot alter what was signed.
382
- const recipientWord = canonRecipient.is_left
383
- ? canonRecipient.left.bytes
384
- : canonRecipient.right.bytes;
453
+ return NST__mint(recipient, amount, NSTDerivedNonce__deriveNonce());
454
+ }
385
455
 
386
- const msgHash = Eip712_hashTypedData(_domainSeparator, keccak256<Vector<6, Bytes<32>>>([
387
- MINT_TYPEHASH(),
456
+ /**
457
+ * @description Mints a new shielded coin of this contract's token to the
458
+ * contract itself, claimed in the same call, authorized by threshold
459
+ * signatures.
460
+ *
461
+ * @notice The returned coin info is the contract's only record of the
462
+ * holding; persist it for `burnFromSelf`.
463
+ *
464
+ * @constraints k=17, rows=91259 (zkir-v3)
465
+ *
466
+ * Requirements:
467
+ *
468
+ * - Both public keys must hash to registered signer commitments.
469
+ * - Both signatures must be valid over the mint-to-self message hash.
470
+ * - Signers must not be duplicates.
471
+ * - Threshold must be met.
472
+ *
473
+ * @param {Uint<64>} amount - The token amount to mint.
474
+ * @param {Vector<2, Secp256k1Point>} pubkeys - Public keys of approving signers.
475
+ * @param {Vector<2, Secp256k1EcdsaSignature>} signatures - ECDSA signatures over the mint-to-self hash.
476
+ * @returns {ShieldedCoinInfo} The minted coin (nonce, color, value).
477
+ */
478
+ export circuit mintToSelf(
479
+ amount: Uint<64>,
480
+ pubkeys: Vector<2, Secp256k1Point>,
481
+ signatures: Vector<2, Secp256k1EcdsaSignature>
482
+ ): ShieldedCoinInfo {
483
+ const opNonce = _counter;
484
+ _counter.increment(1);
485
+
486
+ const msgHash = Eip712_hashTypedData(_domainSeparator, keccak256<Vector<4, Bytes<32>>>([
487
+ MINT_TO_SELF_TYPEHASH(),
488
+ // Binds the digest to this deployment. The domain cannot carry the
489
+ // address, so removing this word removes cross-instance replay
490
+ // protection.
491
+ kernel.self().bytes,
492
+ EvmAbi_uint64Word(opNonce),
493
+ EvmAbi_uint64Word(amount)
494
+ ]));
495
+
496
+ EcdsaSignerManager_assertApprovals(msgHash, pubkeys, signatures);
497
+
498
+ return NST__mintToSelf(amount, NSTDerivedNonce__deriveNonce());
499
+ }
500
+
501
+ /**
502
+ * @description Burns `amount` from a holder's coin, authorized by threshold
503
+ * signatures. The holder's transaction pays `coin` in; the contract
504
+ * receives it and burns it in the same call. Any change goes to `refundTo`.
505
+ *
506
+ * @notice The returned refund coin info is the only copy available to
507
+ * `refundTo`; deliver it out of band.
508
+ *
509
+ * @constraints k=17, rows=118642 (zkir-v3)
510
+ *
511
+ * Requirements:
512
+ *
513
+ * - Both public keys must hash to registered signer commitments.
514
+ * - Both signatures must be valid over the burn message hash.
515
+ * - Signers must not be duplicates.
516
+ * - Threshold must be met.
517
+ * - `coin.color` is this contract's token color.
518
+ * - `amount` is less than or equal to `coin.value`.
519
+ * - `refundTo` is not zero.
520
+ *
521
+ * @param {ShieldedCoinInfo} coin - The holder's coin to burn from.
522
+ * @param {Uint<64>} amount - The token amount to burn.
523
+ * @param {ZswapCoinPublicKey} refundTo - Receives `coin.value - amount`.
524
+ * @param {Vector<2, Secp256k1Point>} pubkeys - Public keys of approving signers.
525
+ * @param {Vector<2, Secp256k1EcdsaSignature>} signatures - ECDSA signatures over the burn hash.
526
+ * @returns {Maybe<ShieldedCoinInfo>} The refund coin created for `refundTo`,
527
+ * or `none` if the coin was burned in full.
528
+ */
529
+ export circuit burn(
530
+ coin: ShieldedCoinInfo,
531
+ amount: Uint<64>,
532
+ refundTo: ZswapCoinPublicKey,
533
+ pubkeys: Vector<2, Secp256k1Point>,
534
+ signatures: Vector<2, Secp256k1EcdsaSignature>
535
+ ): Maybe<ShieldedCoinInfo> {
536
+ const opNonce = _counter;
537
+ _counter.increment(1);
538
+
539
+ const msgHash = Eip712_hashTypedData(_domainSeparator, keccak256<Vector<5, Bytes<32>>>([
540
+ BURN_TYPEHASH(),
388
541
  // Binds the digest to this deployment. The domain cannot carry the
389
542
  // address, so removing this word removes cross-instance replay
390
543
  // protection.
391
544
  kernel.self().bytes,
392
- recipientWord,
393
- EvmAbi_boolWord(!canonRecipient.is_left),
545
+ refundTo.bytes,
394
546
  EvmAbi_uint64Word(opNonce),
395
547
  EvmAbi_uint64Word(amount)
396
548
  ]));
397
549
 
398
550
  EcdsaSignerManager_assertApprovals(msgHash, pubkeys, signatures);
399
551
 
400
- // `NST__mint` takes a coin public key only; the contract arm is limited to
401
- // this contract, since a mint to any other contract leaves an unclaimed
402
- // output.
403
- // The recipient is public (it is already in the signed digest), so the
404
- // arm may gate which mint runs.
405
- const target = disclose(canonRecipient);
406
- const self = kernel.self();
407
- assert(target.is_left || target.right == self,
408
- "NativeShieldedToken: recipient contract must be self");
409
- const nonce = NSTDerivedNonce__deriveNonce();
410
- if (target.is_left) {
411
- return NST__mint(target.left, amount, nonce);
412
- } else {
413
- return NST__mintToSelf(amount, nonce);
414
- }
552
+ return NST__burn(coin, amount, refundTo);
415
553
  }
416
554
 
417
555
  /**
@@ -428,7 +566,7 @@ module NativeShieldedTokenIssuer {
428
566
  * Requirements:
429
567
  *
430
568
  * - Both public keys must hash to registered signer commitments.
431
- * - Both signatures must be valid over the burn message hash.
569
+ * - Both signatures must be valid over the burn-from-self message hash.
432
570
  * - Signers must not be duplicates.
433
571
  * - Threshold must be met.
434
572
  * - `coin.color` is this contract's token color.
@@ -437,11 +575,11 @@ module NativeShieldedTokenIssuer {
437
575
  * @param {QualifiedShieldedCoinInfo} coin - The contract-held coin to burn from.
438
576
  * @param {Uint<64>} amount - The token amount to burn.
439
577
  * @param {Vector<2, Secp256k1Point>} pubkeys - Public keys of approving signers.
440
- * @param {Vector<2, Secp256k1EcdsaSignature>} signatures - ECDSA signatures over the burn hash.
578
+ * @param {Vector<2, Secp256k1EcdsaSignature>} signatures - ECDSA signatures over the burn-from-self hash.
441
579
  * @returns {Maybe<ShieldedCoinInfo>} The change coin retained by the
442
580
  * contract, or `none` if the coin was burned in full.
443
581
  */
444
- export circuit burn(
582
+ export circuit burnFromSelf(
445
583
  coin: QualifiedShieldedCoinInfo,
446
584
  amount: Uint<64>,
447
585
  pubkeys: Vector<2, Secp256k1Point>,
@@ -451,7 +589,7 @@ module NativeShieldedTokenIssuer {
451
589
  _counter.increment(1);
452
590
 
453
591
  const msgHash = Eip712_hashTypedData(_domainSeparator, keccak256<Vector<4, Bytes<32>>>([
454
- BURN_TYPEHASH(),
592
+ BURN_FROM_SELF_TYPEHASH(),
455
593
  // Binds the digest to this deployment. The domain cannot carry the
456
594
  // address, so removing this word removes cross-instance replay
457
595
  // protection.
@@ -466,7 +604,7 @@ module NativeShieldedTokenIssuer {
466
604
  }
467
605
 
468
606
  /**
469
- * @description Returns the counter the next mint or burn digest will bind to.
607
+ * @description Returns the counter the next operation's digest will bind to.
470
608
  *
471
609
  * @constraints k=6, rows=34 (zkir-v3)
472
610
  *
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.4.0-alpha.3 (multisig/presets/ShieldedMultiSigV2.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (multisig/presets/ShieldedMultiSigV2.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openzeppelin/compact-contracts",
3
- "version": "0.4.0-alpha.3",
3
+ "version": "0.4.0-alpha.4",
4
4
  "description": "OpenZeppelin Compact contract library",
5
5
  "keywords": [
6
6
  "openzeppelin",
@@ -63,13 +63,15 @@
63
63
  "@midnightntwrk/ledger-v9": "1.0.0-rc.5",
64
64
  "@midnightntwrk/wallet-sdk-address-format": "4.0.0-beta.2",
65
65
  "@noble/curves": "^2.2.0",
66
- "@noble/hashes": "^2.2.0",
66
+ "@noble/hashes": "^2.4.0",
67
+ "@openzeppelin/compact-deployer": "^0.3.1",
67
68
  "@openzeppelin/compact-simulator": "^0.4.0",
68
69
  "@tsconfig/node24": "^24.0.5",
69
- "@types/node": "26.5.1",
70
+ "@types/node": "26.6.2",
70
71
  "@vitest/coverage-v8": "^5.0.0",
71
72
  "ethers": "^6.17.0",
72
73
  "fast-check": "^4.10.1",
74
+ "pino": "^9.7.0",
73
75
  "ts-node": "^10.9.2",
74
76
  "typescript": "^6.0.3",
75
77
  "vite": "^8.3.0",
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- // OpenZeppelin Compact Contracts v0.4.0-alpha.3 (security/Allowlist.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (security/Allowlist.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.3 (security/Blocklist.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (security/Blocklist.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.3 (security/Initializable.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (security/Initializable.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.3 (security/Pausable.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (security/Pausable.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.3 (token/ConfidentialFungibleToken.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (token/ConfidentialFungibleToken.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.3 (token/FungibleToken.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (token/FungibleToken.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.3 (token/MultiToken.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (token/MultiToken.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.3 (token/NativeShieldedToken.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (token/NativeShieldedToken.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.3 (token/NativeShieldedTokenCore.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (token/NativeShieldedTokenCore.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.3 (token/NativeShieldedTokenFamily.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (token/NativeShieldedTokenFamily.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.3 (token/NonFungibleToken.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (token/NonFungibleToken.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.3 (token/extensions/ConfidentialFungibleTokenPublicSupply.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (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.3 (token/extensions/NativeShieldedTokenDerivedNonce.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (token/extensions/NativeShieldedTokenDerivedNonce.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.3 (utils/EvmAbi.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (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.3 (utils/Utils.compact)
2
+ // OpenZeppelin Compact Contracts v0.4.0-alpha.4 (utils/Utils.compact)
3
3
 
4
4
  pragma language_version >= 0.26.0;
5
5