@openzeppelin/compact-contracts 0.3.0-rc.1 → 0.3.0
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.
- package/README.md +11 -3
- package/access/AccessControl.compact +1 -1
- package/access/Ownable.compact +1 -1
- package/access/ShieldedAccessControl.compact +39 -36
- package/access/ZOwnablePK.compact +12 -3
- package/crypto/EcdhMask.compact +1 -1
- package/crypto/ElGamal.compact +1 -1
- package/multisig/ForwarderPrivate.compact +1 -1
- package/multisig/ForwarderShielded.compact +1 -1
- package/multisig/ForwarderUnshielded.compact +1 -1
- package/multisig/ProposalManager.compact +1 -1
- package/multisig/ShieldedTreasury.compact +1 -1
- package/multisig/ShieldedTreasuryStateless.compact +1 -1
- package/multisig/Signer.compact +1 -1
- package/multisig/UnshieldedTreasury.compact +1 -1
- package/multisig/presets/ShieldedMultiSig.compact +1 -1
- package/multisig/presets/ShieldedMultiSigV2.compact +1 -1
- package/multisig/presets/forwarder/ForwarderPrivate.compact +1 -1
- package/multisig/presets/forwarder/ForwarderShielded.compact +1 -1
- package/multisig/presets/forwarder/ForwarderUnshielded.compact +1 -1
- package/package.json +1 -1
- package/security/Allowlist.compact +1 -1
- package/security/Blocklist.compact +1 -1
- package/security/Initializable.compact +1 -1
- package/security/Pausable.compact +1 -1
- package/token/ConfidentialFungibleToken.compact +3 -3
- package/token/FungibleToken.compact +1 -1
- package/token/MultiToken.compact +1 -1
- package/token/NativeShieldedToken.compact +1 -1
- package/token/NativeShieldedTokenCore.compact +1 -1
- package/token/NativeShieldedTokenFamily.compact +1 -1
- package/token/NonFungibleToken.compact +1 -1
- package/token/examples/ZOwnableNativeShieldedTokenExample.compact +109 -0
- package/token/extensions/ConfidentialFungibleTokenPublicSupply.compact +1 -1
- package/token/extensions/NativeShieldedTokenDerivedNonce.compact +1 -1
- package/token/presets/ZOwnableNativeShieldedToken.compact +314 -0
- package/utils/Utils.compact +1 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ This project is built on the Midnight Network.
|
|
|
10
10
|
|
|
11
11
|
> ## ⚠️ WARNING! ⚠️
|
|
12
12
|
>
|
|
13
|
-
>
|
|
13
|
+
> Pre-release versions (`-alpha`, `-rc`) contain experimental, unaudited code.
|
|
14
14
|
> Expect rapid iteration.
|
|
15
15
|
> **Use at your own risk.**
|
|
16
16
|
|
|
@@ -268,9 +268,17 @@ rm -rf .turbo/
|
|
|
268
268
|
|
|
269
269
|
## Security
|
|
270
270
|
|
|
271
|
-
|
|
271
|
+
For production, use a stable release (no `-alpha` or `-rc` suffix) on the Midnight ledger it targets. Pre-releases are not audited and should not be used in production.
|
|
272
272
|
|
|
273
|
-
|
|
273
|
+
| Release | Midnight ledger | Compact compiler | Audits |
|
|
274
|
+
| ------- | --------------- | ---------------- | ------ |
|
|
275
|
+
| 0.3.0 | Ledger v8 | 0.31.0 | [Aug 2026](./audits/2026-08-v0.3.0-alpha.1.pdf), [Oct 2026](./audits/2026-10-v0.3.0-rc.2-v0.4.0-alpha.6.pdf) |
|
|
276
|
+
| 0.2.0 | Ledger v7 | 0.29.0 | None of its own. Its changes were reviewed in the Aug 2026 audit. |
|
|
277
|
+
| 0.1.0 | Ledger v7 | 0.29.0 | [May 2026](./audits/2026-05-v0.1.0.pdf) |
|
|
278
|
+
|
|
279
|
+
Scopes and audited commits are listed in [audits](./audits/README.md).
|
|
280
|
+
|
|
281
|
+
Please report any security issues you find to <security@openzeppelin.com>. See [SECURITY.md](SECURITY.md).
|
|
274
282
|
|
|
275
283
|
### Provenance
|
|
276
284
|
|
package/access/Ownable.compact
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.3.0
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.3.0 (access/ShieldedAccessControl.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.23.0;
|
|
5
5
|
|
|
@@ -108,8 +108,8 @@ pragma language_version >= 0.23.0;
|
|
|
108
108
|
* depends on the number of plausible `(role, accountId)` pairings. Since role identifiers
|
|
109
109
|
* are public, the anonymity set is effectively the number of plausible account IDs per role.
|
|
110
110
|
* - Outside observers will know when roles are granted and revoked.
|
|
111
|
-
* - Outside observers can infer the total number of role
|
|
112
|
-
*
|
|
111
|
+
* - Outside observers can infer the total number of distinct `(role, accountId)` pairings granted
|
|
112
|
+
* across all roles, not per-role counts.
|
|
113
113
|
* - Outside observers can link calls made by the same role instance across time.
|
|
114
114
|
* - Users can be retroactively deanonymized if their secret key is exposed.
|
|
115
115
|
* - Outside observers will NOT be able to identify the holder of any role
|
|
@@ -133,29 +133,20 @@ pragma language_version >= 0.23.0;
|
|
|
133
133
|
* - It's strongly recommended to use cryptographically secure random values for the `_instanceSalt`.
|
|
134
134
|
* Failure to do so may weaken privacy guarantees.
|
|
135
135
|
* - The `_instanceSalt` is immutable and used to differentiate deployments.
|
|
136
|
-
* - The `_operatorRoles` Merkle tree has a fixed capacity of 2^20 leaf slots.
|
|
137
|
-
*
|
|
138
|
-
* capacity through repeated grants of the same active (role, accountId) pairing.
|
|
139
|
-
* Duplicate grants are benign from a security perspective. They produce identical
|
|
140
|
-
* commitments and a single nullifier invalidates all copies; however, they waste tree capacity.
|
|
141
|
-
* Implementing contracts are responsible for mitigating tree exhaustion risk.
|
|
136
|
+
* - The `_operatorRoles` Merkle tree has a fixed capacity of 2^20 leaf slots. Each distinct
|
|
137
|
+
* `(role, accountId)` pairing consumes one slot; re-granting an active pairing is a no-op.
|
|
142
138
|
* - A single `secretKey` compromise exposes all roles held by that user within the contract
|
|
143
139
|
* instance, since all roles share the same `accountId`. Users requiring compartmentalization
|
|
144
140
|
* between roles should use separate keys per role and accept the additional key management overhead.
|
|
145
141
|
* In the event of key compromise, the user should coordinate with admins to revoke all roles
|
|
146
142
|
* under the compromised `accountId` and re-grant under a new identity.
|
|
147
|
-
* -
|
|
148
|
-
*
|
|
149
|
-
* nullifier for that pairing, blocking any future grant. Admin trust is a fundamental
|
|
150
|
-
* assumption of this system. Off-chain validation should confirm that a pairing was actually
|
|
151
|
-
* granted before submitting a revocation transaction.
|
|
152
|
-
* - `renounceRole` allows burning nullifiers for roles the caller does not currently hold,
|
|
153
|
-
* provided they possess the correct `secretKey`. This requires knowledge of the secret key,
|
|
154
|
-
* at which point the `accountId` should be considered fully compromised regardless.
|
|
143
|
+
* - A `(role, accountId)` pairing can only be revoked after it was granted, so the nullifier
|
|
144
|
+
* set holds at most one entry per granted pairing.
|
|
155
145
|
* - The Merkle tree capacity of 2^20 leaf slots is a permanent, irrecoverable resource.
|
|
156
146
|
* There is no mechanism to reclaim slots after revocation, no upgrade path, and no emergency
|
|
157
147
|
* recovery. Once exhausted, the contract is permanently unable to grant new roles. Deployers
|
|
158
|
-
* must monitor slot consumption off-chain and plan capacity accordingly.
|
|
148
|
+
* must monitor slot consumption off-chain and plan capacity accordingly. The exported tree
|
|
149
|
+
* reports occupancy via `firstFree()` (TypeScript) and `isFull()` (circuit).
|
|
159
150
|
*
|
|
160
151
|
* @notice Using the SHA256 hashing function comes at a significant performance cost. In the future, we
|
|
161
152
|
* plan on migrating to a ZK-friendly hashing function when an implementation is available.
|
|
@@ -209,6 +200,14 @@ module ShieldedAccessControl {
|
|
|
209
200
|
*/
|
|
210
201
|
export ledger _roleCommitmentNullifiers: Set<RoleNullifier>;
|
|
211
202
|
|
|
203
|
+
/**
|
|
204
|
+
* @description Nullifiers of every pairing ever granted. Makes a repeated grant a no-op so
|
|
205
|
+
* one pairing can never consume more than one `_operatorRoles` slot, and gates revocation
|
|
206
|
+
* so a nullifier can only exist for a granted pairing.
|
|
207
|
+
* @type {Set<RoleNullifier>} _grantedRoleNullifiers
|
|
208
|
+
*/
|
|
209
|
+
export ledger _grantedRoleNullifiers: Set<RoleNullifier>;
|
|
210
|
+
|
|
212
211
|
/**
|
|
213
212
|
* @sealed @ledger _instanceSalt
|
|
214
213
|
* @description A per-instance value provided at initialization used to namespace
|
|
@@ -387,9 +386,8 @@ module ShieldedAccessControl {
|
|
|
387
386
|
|
|
388
387
|
/**
|
|
389
388
|
* @description Grants `role` to `accountId` by inserting a role commitment unique to the
|
|
390
|
-
* `(role, accountId)` pairing into the `_operatorRoles` Merkle tree.
|
|
391
|
-
*
|
|
392
|
-
* limited Merkle tree storage slots. Once revoked, a role cannot be re-granted to the same `accountId`. A new
|
|
389
|
+
* `(role, accountId)` pairing into the `_operatorRoles` Merkle tree. Granting an already active
|
|
390
|
+
* pairing is a no-op. Once revoked, a role cannot be re-granted to the same `accountId`. A new
|
|
393
391
|
* `accountId` must be generated to be re-authorized for a revoked `role`.
|
|
394
392
|
*
|
|
395
393
|
* Requirements:
|
|
@@ -419,9 +417,8 @@ module ShieldedAccessControl {
|
|
|
419
417
|
|
|
420
418
|
/**
|
|
421
419
|
* @description Grants `role` to `accountId` by inserting a role commitment unique to the
|
|
422
|
-
* `(role, accountId)` pairing into the `_operatorRoles` Merkle tree.
|
|
423
|
-
*
|
|
424
|
-
* limited Merkle tree storage slots. Once revoked, a role cannot be re-granted to the same `accountId`. A new
|
|
420
|
+
* `(role, accountId)` pairing into the `_operatorRoles` Merkle tree. Granting an already active
|
|
421
|
+
* pairing is a no-op. Once revoked, a role cannot be re-granted to the same `accountId`. A new
|
|
425
422
|
* `accountId` must be generated to be re-authorized for a revoked `role`.
|
|
426
423
|
*
|
|
427
424
|
* Internal circuit without access restriction.
|
|
@@ -429,10 +426,8 @@ module ShieldedAccessControl {
|
|
|
429
426
|
* @warning Exposing this circuit directly in an implementing contract would allow anyone to grant
|
|
430
427
|
* roles without authorization. It must be wrapped with appropriate access control.
|
|
431
428
|
*
|
|
432
|
-
* @warning The `_operatorRoles` Merkle tree has a fixed capacity of 2^20 leaf slots
|
|
433
|
-
* Deployers should monitor slot consumption off-chain.
|
|
434
|
-
* but are otherwise benign. A single nullifier invalidates all duplicate commitments.
|
|
435
|
-
* Implementing contracts are responsible for mitigating tree exhaustion risk.
|
|
429
|
+
* @warning The `_operatorRoles` Merkle tree has a fixed capacity of 2^20 leaf slots, one per
|
|
430
|
+
* distinct pairing. Deployers should monitor slot consumption off-chain.
|
|
436
431
|
*
|
|
437
432
|
* Requirements:
|
|
438
433
|
*
|
|
@@ -462,6 +457,7 @@ module ShieldedAccessControl {
|
|
|
462
457
|
* @notice Roles are often managed via {grantRole} and {revokeRole}: this circuit's
|
|
463
458
|
* purpose is to provide a mechanism for accounts to lose their privileges
|
|
464
459
|
* if they are compromised (such as when a trusted device is misplaced).
|
|
460
|
+
* Consumers with no need for self-revocation should not export this circuit.
|
|
465
461
|
*
|
|
466
462
|
* @warning Outside observers may be able to use timing and pattern analysis to weaken pseudonymity
|
|
467
463
|
* guarantees if renounceRole is used in tandem with other on-chain actions.
|
|
@@ -469,6 +465,7 @@ module ShieldedAccessControl {
|
|
|
469
465
|
* Requirements:
|
|
470
466
|
*
|
|
471
467
|
* - The caller must provide a valid `accountId` for the role.
|
|
468
|
+
* - The `(role, accountId)` pairing must have been granted.
|
|
472
469
|
* - The `(role, accountId)` pairing must not be already revoked.
|
|
473
470
|
* - Contract is initialized.
|
|
474
471
|
*
|
|
@@ -494,13 +491,13 @@ module ShieldedAccessControl {
|
|
|
494
491
|
/**
|
|
495
492
|
* @description Permanently revokes `role` from `accountId` by inserting a role nullifier into the
|
|
496
493
|
* `_roleCommitmentNullifiers` set. Once revoked, a new `accountId` must be generated to be re-authorized for
|
|
497
|
-
* `role`.
|
|
498
|
-
* so a `(role, accountId)` pairing that does not exist can still be revoked.
|
|
494
|
+
* `role`.
|
|
499
495
|
*
|
|
500
496
|
* Requirements:
|
|
501
497
|
*
|
|
502
498
|
* - caller must prove they're an admin for `role`.
|
|
503
499
|
* - caller must not provide valid Merkle tree path for a different `(role, accountId)` pairing.
|
|
500
|
+
* - The `(role, accountId)` pairing must have been granted.
|
|
504
501
|
* - The `(role, accountId)` pairing must not be already revoked.
|
|
505
502
|
* - Contract is initialized.
|
|
506
503
|
*
|
|
@@ -525,8 +522,7 @@ module ShieldedAccessControl {
|
|
|
525
522
|
/**
|
|
526
523
|
* @description Permanently revokes `role` from `accountId` by inserting a role nullifier into the
|
|
527
524
|
* `_roleCommitmentNullifiers` set. Once revoked, a new `accountId` must be generated to be re-authorized for
|
|
528
|
-
* `role`.
|
|
529
|
-
* so a `(role, accountId)` pairing that does not exist can still be revoked.
|
|
525
|
+
* `role`.
|
|
530
526
|
*
|
|
531
527
|
* Internal circuit without access restriction.
|
|
532
528
|
*
|
|
@@ -535,6 +531,7 @@ module ShieldedAccessControl {
|
|
|
535
531
|
*
|
|
536
532
|
* Requirements:
|
|
537
533
|
*
|
|
534
|
+
* - The `(role, accountId)` pairing must have been granted.
|
|
538
535
|
* - The `(role, accountId)` pairing must not be already revoked.
|
|
539
536
|
* - Contract is initialized.
|
|
540
537
|
*
|
|
@@ -555,13 +552,14 @@ module ShieldedAccessControl {
|
|
|
555
552
|
/**
|
|
556
553
|
* @description Core business logic for the grant/revoke role circuits. Asserts that the
|
|
557
554
|
* `(role, accountId)` pairing has not already been revoked. On success, dispatches on
|
|
558
|
-
* `updateType`: a `Grant` inserts the role commitment into `_operatorRoles
|
|
559
|
-
* a `Revoke`
|
|
555
|
+
* `updateType`: a `Grant` inserts the role commitment into `_operatorRoles` unless the
|
|
556
|
+
* nullifier is already in `_grantedRoleNullifiers`, and a `Revoke` asserts the nullifier
|
|
557
|
+
* is in `_grantedRoleNullifiers` before inserting it into `_roleCommitmentNullifiers`.
|
|
560
558
|
*
|
|
561
559
|
* Disclosures:
|
|
562
560
|
*
|
|
563
561
|
* - A nullifier for the respective role commitment.
|
|
564
|
-
* - A role commitment (on Grant only).
|
|
562
|
+
* - A role commitment (on first Grant only).
|
|
565
563
|
*
|
|
566
564
|
* @notice The nullifier is disclosed via `_roleCommitmentNullifiers.member` on every call,
|
|
567
565
|
* regardless of the update type. Since `Set.member` publicly reveals its argument, the
|
|
@@ -584,9 +582,14 @@ module ShieldedAccessControl {
|
|
|
584
582
|
const isRevoked = _roleCommitmentNullifiers.member(disclose(roleNullifier));
|
|
585
583
|
assert(!isRevoked, "ShieldedAccessControl: role is already revoked");
|
|
586
584
|
|
|
585
|
+
const isGranted = _grantedRoleNullifiers.member(disclose(roleNullifier));
|
|
587
586
|
if (updateType == UpdateType.Grant) {
|
|
588
|
-
|
|
587
|
+
if (!isGranted) {
|
|
588
|
+
_grantedRoleNullifiers.insert(disclose(roleNullifier));
|
|
589
|
+
_operatorRoles.insert(disclose(roleCommitment));
|
|
590
|
+
}
|
|
589
591
|
} else {
|
|
592
|
+
assert(isGranted, "ShieldedAccessControl: role was never granted");
|
|
590
593
|
_roleCommitmentNullifiers.insert(disclose(roleNullifier));
|
|
591
594
|
}
|
|
592
595
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.3.0
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.3.0 (access/ZOwnablePK.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.23.0;
|
|
5
5
|
|
|
@@ -35,10 +35,17 @@ pragma language_version >= 0.23.0;
|
|
|
35
35
|
* providing unlinkability across ownership changes.
|
|
36
36
|
*
|
|
37
37
|
* @dev Security Considerations:
|
|
38
|
-
* -
|
|
39
|
-
*
|
|
38
|
+
* - `ownPublicKey()` is a witness: it returns the caller's Zswap coin public
|
|
39
|
+
* key, and nothing proves the caller holds the matching private key. See
|
|
40
|
+
* https://docs.midnight.network/compact/smart-contract-security#ownpublickey-is-a-witness-function.
|
|
41
|
+
* - The `secretNonce` is therefore the owner credential: the nonce and the
|
|
42
|
+
* owner's public key are enough to pass `assertOnlyOwner`. Generate it as
|
|
43
|
+
* 32 bytes from a CSPRNG and keep it private. Losing it prevents the owner
|
|
44
|
+
* from proving ownership or transferring it.
|
|
40
45
|
* - Ownership validation is entirely circuit-based using witness-provided values.
|
|
41
46
|
* - The `_instanceSalt` is immutable and used to differentiate deployments.
|
|
47
|
+
* Use a fresh random value per deployment; a reused salt links the same
|
|
48
|
+
* owner across deployments.
|
|
42
49
|
*
|
|
43
50
|
* @notice Best used for single-admin contracts with privacy requirements.
|
|
44
51
|
* It is not designed for multi-owner or role-based access control.
|
|
@@ -105,6 +112,7 @@ module ZOwnablePK {
|
|
|
105
112
|
*
|
|
106
113
|
* - Contract is not initialized.
|
|
107
114
|
* - `ownerId` is not zero.
|
|
115
|
+
* - `instanceSalt` is not zero.
|
|
108
116
|
*
|
|
109
117
|
* @param {Bytes<32>} ownerId - The owner's unique identifier SHA256(pk, nonce).
|
|
110
118
|
* @param {Bytes<32>} instanceSalt - Contract salt to prevent duplicate commitments if
|
|
@@ -116,6 +124,7 @@ module ZOwnablePK {
|
|
|
116
124
|
_isInitialized = true;
|
|
117
125
|
|
|
118
126
|
assert(ownerId != default<Bytes<32>>, "ZOwnablePK: invalid id");
|
|
127
|
+
assert(instanceSalt != default<Bytes<32>>, "ZOwnablePK: invalid salt");
|
|
119
128
|
_instanceSalt = disclose(instanceSalt);
|
|
120
129
|
_transferOwnership(ownerId);
|
|
121
130
|
}
|
package/crypto/EcdhMask.compact
CHANGED
package/crypto/ElGamal.compact
CHANGED
package/multisig/Signer.compact
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.3.0
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.3.0 (multisig/presets/forwarder/ForwarderPrivate.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.23.0;
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.3.0
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.3.0 (multisig/presets/forwarder/ForwarderShielded.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.23.0;
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.3.0
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.3.0 (multisig/presets/forwarder/ForwarderUnshielded.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.23.0;
|
|
5
5
|
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.3.0
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.3.0 (token/ConfidentialFungibleToken.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.23.0;
|
|
5
5
|
|
|
@@ -1037,7 +1037,7 @@ module ConfidentialFungibleToken {
|
|
|
1037
1037
|
* caller via `approve`) and credits it to `to`, reducing both escrow copies by
|
|
1038
1038
|
* `value`. The amount is hidden.
|
|
1039
1039
|
*
|
|
1040
|
-
*
|
|
1040
|
+
* @circuitInfo k=16, rows=72346
|
|
1041
1041
|
*
|
|
1042
1042
|
* @notice Returns the caller's (spender's) `accountId` for caller-side gating.
|
|
1043
1043
|
*
|
|
@@ -1286,7 +1286,7 @@ module ConfidentialFungibleToken {
|
|
|
1286
1286
|
* recipient credit. The building block for a composing contract's
|
|
1287
1287
|
* `burnFrom`; it performs no supply accounting (see `_burn`).
|
|
1288
1288
|
*
|
|
1289
|
-
*
|
|
1289
|
+
* @circuitInfo k=16, rows=45376
|
|
1290
1290
|
*
|
|
1291
1291
|
* Requirements:
|
|
1292
1292
|
*
|
package/token/MultiToken.compact
CHANGED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.3.0 (token/examples/ZOwnableNativeShieldedTokenExample.compact)
|
|
3
|
+
|
|
4
|
+
pragma language_version >= 0.23.0;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @title ZOwnableNativeShieldedTokenExample
|
|
8
|
+
* @description Deployable reference contract for the
|
|
9
|
+
* `ZOwnableNativeShieldedToken` preset: a native shielded token that only a
|
|
10
|
+
* single shielded owner mints and burns. Holders transfer coins through
|
|
11
|
+
* Zswap directly.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import CompactStandardLibrary;
|
|
15
|
+
|
|
16
|
+
import "../presets/ZOwnableNativeShieldedToken" prefix ZOwnableNativeShieldedToken_;
|
|
17
|
+
|
|
18
|
+
export { ZswapCoinPublicKey, ContractAddress, Either, Maybe, ShieldedCoinInfo };
|
|
19
|
+
|
|
20
|
+
// Surface the preset's state unprefixed so `ledger()` readers use the keys the
|
|
21
|
+
// modules declare.
|
|
22
|
+
import {
|
|
23
|
+
_ownerCommitment,
|
|
24
|
+
_counter,
|
|
25
|
+
_instanceSalt,
|
|
26
|
+
_ownableIsInitialized,
|
|
27
|
+
_domain,
|
|
28
|
+
_name,
|
|
29
|
+
_symbol,
|
|
30
|
+
_decimals,
|
|
31
|
+
_isInitialized,
|
|
32
|
+
_totalMinted,
|
|
33
|
+
_totalBurned,
|
|
34
|
+
} from "../presets/ZOwnableNativeShieldedToken";
|
|
35
|
+
export {
|
|
36
|
+
_ownerCommitment,
|
|
37
|
+
_counter,
|
|
38
|
+
_instanceSalt,
|
|
39
|
+
_ownableIsInitialized,
|
|
40
|
+
_domain,
|
|
41
|
+
_name,
|
|
42
|
+
_symbol,
|
|
43
|
+
_decimals,
|
|
44
|
+
_isInitialized,
|
|
45
|
+
_totalMinted,
|
|
46
|
+
_totalBurned,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @description Deploys the token with its owner, domain and metadata.
|
|
51
|
+
*
|
|
52
|
+
* @warning `ownerId` must be `_computeOwnerId(pk, secretNonce)` for the coin
|
|
53
|
+
* public key the owner's wallet will supply as `ownPublicKey()`. The circuit
|
|
54
|
+
* cannot check this at deploy; any other key leaves the contract with no
|
|
55
|
+
* usable owner and no recovery.
|
|
56
|
+
*
|
|
57
|
+
* @param {Bytes<32>} ownerId - `_computeOwnerId(pk, secretNonce)` of the owner.
|
|
58
|
+
* @param {Bytes<32>} instanceSalt - Random per-deployment salt for the owner commitment.
|
|
59
|
+
* @param {Bytes<32>} tokenDomain - Domain of this contract's token color.
|
|
60
|
+
*/
|
|
61
|
+
constructor(
|
|
62
|
+
ownerId: Bytes<32>,
|
|
63
|
+
instanceSalt: Bytes<32>,
|
|
64
|
+
tokenDomain: Bytes<32>,
|
|
65
|
+
name_: Opaque<"string">,
|
|
66
|
+
symbol_: Opaque<"string">,
|
|
67
|
+
decimals_: Uint<8>,
|
|
68
|
+
) {
|
|
69
|
+
ZOwnableNativeShieldedToken_initialize(
|
|
70
|
+
ownerId,
|
|
71
|
+
instanceSalt,
|
|
72
|
+
tokenDomain,
|
|
73
|
+
name_,
|
|
74
|
+
symbol_,
|
|
75
|
+
decimals_,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export circuit mint(recipient: ZswapCoinPublicKey, amount: Uint<64>, nonce: Bytes<32>): ShieldedCoinInfo {
|
|
80
|
+
return ZOwnableNativeShieldedToken_mint(recipient, amount, nonce);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export circuit burn(coin: ShieldedCoinInfo, amount: Uint<64>, refundTo: ZswapCoinPublicKey): Maybe<ShieldedCoinInfo> {
|
|
84
|
+
return ZOwnableNativeShieldedToken_burn(coin, amount, refundTo);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export circuit transferOwnership(newOwnerId: Bytes<32>): [] {
|
|
88
|
+
ZOwnableNativeShieldedToken_transferOwnership(newOwnerId);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export circuit renounceOwnership(): [] {
|
|
92
|
+
ZOwnableNativeShieldedToken_renounceOwnership();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export circuit owner(): Bytes<32> {
|
|
96
|
+
return ZOwnableNativeShieldedToken_owner();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export pure circuit _computeOwnerId(pk: Either<ZswapCoinPublicKey, ContractAddress>, nonce: Bytes<32>): Bytes<32> {
|
|
100
|
+
return ZOwnableNativeShieldedToken__computeOwnerId(pk, nonce);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export circuit totalMinted(): Uint<128> {
|
|
104
|
+
return ZOwnableNativeShieldedToken_totalMinted();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export circuit tokenColor(): Bytes<32> {
|
|
108
|
+
return ZOwnableNativeShieldedToken_tokenColor();
|
|
109
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.3.0
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.3.0 (token/extensions/ConfidentialFungibleTokenPublicSupply.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.23.0;
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.3.0
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.3.0 (token/extensions/NativeShieldedTokenDerivedNonce.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.23.0;
|
|
5
5
|
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.3.0 (token/presets/ZOwnableNativeShieldedToken.compact)
|
|
3
|
+
|
|
4
|
+
pragma language_version >= 0.23.0;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @module ZOwnableNativeShieldedToken
|
|
8
|
+
* @description
|
|
9
|
+
* A native shielded token with a single shielded owner. Only the owner mints
|
|
10
|
+
* (to a coin public key, with a caller-supplied nonce) and burns (a coin the
|
|
11
|
+
* owner's wallet pays into the tx). Minted amounts are counted on-chain.
|
|
12
|
+
*
|
|
13
|
+
* Composes `ZOwnablePK` as `ZOwnablePK_` (owner commitment),
|
|
14
|
+
* `NativeShieldedToken` as `NST_` (one token color) and
|
|
15
|
+
* `NativeShieldedTokenPublicSupply` as `NSTPublicSupply_` (minted total).
|
|
16
|
+
*
|
|
17
|
+
* @dev Derivations:
|
|
18
|
+
* - `H<T>(...)`: `persistentHash<T>`.
|
|
19
|
+
* - Owner id: `id = H<Vector<2, Bytes<32>>>([pk.bytes, secretNonce])`.
|
|
20
|
+
* - Owner commitment: `H<Vector<4, Bytes<32>>>([id, instanceSalt,
|
|
21
|
+
* counter as Field as Bytes<32>, pad(32, "ZOwnablePK:shield:")])`.
|
|
22
|
+
* - Token color: `tokenType(_domain, kernel.self())`.
|
|
23
|
+
*
|
|
24
|
+
* @notice Implementing contracts call `initialize` from their constructor and
|
|
25
|
+
* MUST NOT import the composed modules directly.
|
|
26
|
+
*
|
|
27
|
+
* @notice Privacy:
|
|
28
|
+
* - Public, per tx: every mint amount (the Zswap mint effect and
|
|
29
|
+
* `_totalMinted`), the coin commitments and nullifiers, and which circuit
|
|
30
|
+
* ran.
|
|
31
|
+
* - Public, as state: `_ownerCommitment`, `_counter` (the number of ownership
|
|
32
|
+
* changes), `_instanceSalt`, `_totalMinted`, `_domain`, `_name`, `_symbol`,
|
|
33
|
+
* `_decimals`.
|
|
34
|
+
* - Private: mint `recipient` and `nonce`. Burn `amount`, `coin` and
|
|
35
|
+
* `refundTo`, as long as the observer does not know the coin's nonce. A change
|
|
36
|
+
* output reveals that a burn was partial.
|
|
37
|
+
* - Owner: stored as a commitment to `id`; the key and nonce never leave the
|
|
38
|
+
* proof.
|
|
39
|
+
*
|
|
40
|
+
* @notice Security:
|
|
41
|
+
* - Owner check: proves knowledge of a `(pk, secretNonce)` whose commitment
|
|
42
|
+
* matches. `ownPublicKey()` is prover-supplied, so the check does not bind
|
|
43
|
+
* the submitting wallet; `secretNonce` is the only secret.
|
|
44
|
+
* - Every mutating circuit checks ownership before any token check.
|
|
45
|
+
* - `transferOwnership` is one step; a wrong `newOwnerId` loses the contract.
|
|
46
|
+
* - `renounceOwnership` stops mint and burn forever.
|
|
47
|
+
* - Upgrades: the CMA can replace any circuit's verifier key, so it sits
|
|
48
|
+
* above the owner and should not be a single key.
|
|
49
|
+
* - A failed assert rejects the whole tx; no state moves.
|
|
50
|
+
*
|
|
51
|
+
* @notice Concurrency:
|
|
52
|
+
* - `mint` reads and writes `_totalMinted`; the read pins it, so at most one
|
|
53
|
+
* mint lands per block.
|
|
54
|
+
* - `burn` only reads owner state: burns commute with each other and with one
|
|
55
|
+
* mint.
|
|
56
|
+
* - `transferOwnership` / `renounceOwnership` write the owner commitment and
|
|
57
|
+
* conflict with every owner-gated call and `owner()` in the same block.
|
|
58
|
+
* - Metadata views and `totalMinted` read no owner state; `totalMinted`
|
|
59
|
+
* submitted as a tx conflicts with a same-block mint.
|
|
60
|
+
* - A tx that loses a same-block race fails and must be re-proven against
|
|
61
|
+
* the new state.
|
|
62
|
+
*
|
|
63
|
+
* @notice Scope:
|
|
64
|
+
* Mint and burn only. Holders transfer through Zswap directly. No roles,
|
|
65
|
+
* freeze, seize, contract-held coins, burned total or total supply.
|
|
66
|
+
*
|
|
67
|
+
* @notice Client-side duties:
|
|
68
|
+
* - Build every owner tx with the coin public key in the current `ownerId`
|
|
69
|
+
* and the matching `wit_secretNonce`. A wrong nonce fails the ownership
|
|
70
|
+
* check and reveals nothing.
|
|
71
|
+
* - `ownPublicKey()` is supplied by the client like a witness; the circuit
|
|
72
|
+
* cannot check that it belongs to the submitting wallet. The client is
|
|
73
|
+
* responsible for supplying the coin public key that is in the current
|
|
74
|
+
* `ownerId`; any other key fails the ownership check.
|
|
75
|
+
* - Compute `ownerId` off-chain with `_computeOwnerId`.
|
|
76
|
+
* - `nonce` must be fresh random bytes per mint.
|
|
77
|
+
* - Persist the coin infos `mint` and `burn` return. midnight-js encrypts an
|
|
78
|
+
* output to its recipient only when it can resolve the recipient's
|
|
79
|
+
* encryption key (its own, or one given in
|
|
80
|
+
* `additionalCoinEncPublicKeyMappings`).
|
|
81
|
+
* - Run the proof server where the owner controls it; it sees
|
|
82
|
+
* `wit_secretNonce`.
|
|
83
|
+
*
|
|
84
|
+
* @dev Composition:
|
|
85
|
+
* - Burn accounting: calling `NSTPublicSupply__addBurned` in `burn` would
|
|
86
|
+
* publish every burn amount.
|
|
87
|
+
*/
|
|
88
|
+
module ZOwnableNativeShieldedToken {
|
|
89
|
+
import CompactStandardLibrary;
|
|
90
|
+
|
|
91
|
+
import "../../access/ZOwnablePK" prefix ZOwnablePK_;
|
|
92
|
+
import "../NativeShieldedToken" prefix NST_;
|
|
93
|
+
import "../extensions/NativeShieldedTokenPublicSupply" prefix NSTPublicSupply_;
|
|
94
|
+
|
|
95
|
+
// Named import/re-export of the composed modules' state only, so an
|
|
96
|
+
// implementing contract can surface every ledger field under its own key.
|
|
97
|
+
// The circuits stay behind the prefixes above. The ownable flag is aliased
|
|
98
|
+
// because the token module already owns `_isInitialized`.
|
|
99
|
+
// `_totalBurned` is never written, so it stays empty.
|
|
100
|
+
import { _ownerCommitment, _counter, _instanceSalt, _isInitialized as _ownableIsInitialized } from "../../access/ZOwnablePK";
|
|
101
|
+
import { _domain, _name, _symbol, _decimals, _isInitialized } from "../NativeShieldedToken";
|
|
102
|
+
import { _totalMinted, _totalBurned } from "../extensions/NativeShieldedTokenPublicSupply";
|
|
103
|
+
|
|
104
|
+
export { _ownerCommitment, _counter, _instanceSalt, _ownableIsInitialized };
|
|
105
|
+
export { _domain, _name, _symbol, _decimals, _isInitialized };
|
|
106
|
+
export { _totalMinted, _totalBurned };
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @description Sets the owner and the token's domain and metadata.
|
|
110
|
+
*
|
|
111
|
+
* @notice Call from the implementing contract's constructor only. It writes
|
|
112
|
+
* sealed fields, so implementing contracts MUST NOT expose it as a
|
|
113
|
+
* post-deploy circuit.
|
|
114
|
+
*
|
|
115
|
+
* @warning `ownerId` must be `_computeOwnerId(pk, secretNonce)` for the coin
|
|
116
|
+
* public key the owner's wallet will supply as `ownPublicKey()`. The circuit
|
|
117
|
+
* cannot check this at deploy; any other key leaves the contract with no
|
|
118
|
+
* usable owner and no recovery.
|
|
119
|
+
*
|
|
120
|
+
* Requirements:
|
|
121
|
+
*
|
|
122
|
+
* - Contract is not initialized.
|
|
123
|
+
* - `ownerId` is not zero.
|
|
124
|
+
*
|
|
125
|
+
* @param {Bytes<32>} ownerId - `_computeOwnerId(pk, secretNonce)` of the owner.
|
|
126
|
+
* @param {Bytes<32>} instanceSalt - Random per-deployment salt for the owner commitment.
|
|
127
|
+
* @param {Bytes<32>} tokenDomain - Domain of this contract's token color.
|
|
128
|
+
* @param {Opaque<"string">} name_ - The token name.
|
|
129
|
+
* @param {Opaque<"string">} symbol_ - The token symbol.
|
|
130
|
+
* @param {Uint<8>} decimals_ - The token decimals.
|
|
131
|
+
*/
|
|
132
|
+
export circuit initialize(
|
|
133
|
+
ownerId: Bytes<32>,
|
|
134
|
+
instanceSalt: Bytes<32>,
|
|
135
|
+
tokenDomain: Bytes<32>,
|
|
136
|
+
name_: Opaque<"string">,
|
|
137
|
+
symbol_: Opaque<"string">,
|
|
138
|
+
decimals_: Uint<8>,
|
|
139
|
+
): [] {
|
|
140
|
+
ZOwnablePK_initialize(ownerId, instanceSalt);
|
|
141
|
+
NST_initialize(tokenDomain, name_, symbol_, decimals_);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @description Mints `amount` to `recipient` and adds it to the minted total.
|
|
146
|
+
* Owner only.
|
|
147
|
+
*
|
|
148
|
+
* @circuitInfo k=15, rows=21354
|
|
149
|
+
*
|
|
150
|
+
* Requirements:
|
|
151
|
+
*
|
|
152
|
+
* - Caller is the owner.
|
|
153
|
+
* - `recipient` is not zero.
|
|
154
|
+
*
|
|
155
|
+
* @param {ZswapCoinPublicKey} recipient - Coin public key that receives the coin.
|
|
156
|
+
* @param {Uint<64>} amount - Amount to mint.
|
|
157
|
+
* @param {Bytes<32>} nonce - Fresh random bytes per mint.
|
|
158
|
+
* @returns {ShieldedCoinInfo} The minted coin; persist it.
|
|
159
|
+
*/
|
|
160
|
+
export circuit mint(recipient: ZswapCoinPublicKey, amount: Uint<64>, nonce: Bytes<32>): ShieldedCoinInfo {
|
|
161
|
+
ZOwnablePK_assertOnlyOwner();
|
|
162
|
+
const coin = NST__mint(recipient, amount, nonce);
|
|
163
|
+
NSTPublicSupply__addMinted(amount);
|
|
164
|
+
return coin;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @description Burns `amount` of `coin`, paid into the tx by the caller's
|
|
169
|
+
* wallet, and refunds the rest to `refundTo`. Owner only.
|
|
170
|
+
*
|
|
171
|
+
* @circuitInfo k=16, rows=52024
|
|
172
|
+
*
|
|
173
|
+
* Requirements:
|
|
174
|
+
*
|
|
175
|
+
* - Caller is the owner.
|
|
176
|
+
* - `coin.color` is this token's color.
|
|
177
|
+
* - `amount` <= `coin.value`.
|
|
178
|
+
* - `refundTo` is not zero.
|
|
179
|
+
*
|
|
180
|
+
* @param {ShieldedCoinInfo} coin - Coin of this token, paid into the tx by the caller's wallet.
|
|
181
|
+
* @param {Uint<64>} amount - Amount to burn from `coin`.
|
|
182
|
+
* @param {ZswapCoinPublicKey} refundTo - Coin public key that receives the change.
|
|
183
|
+
* @returns {Maybe<ShieldedCoinInfo>} The refund coin, or `none` on a full burn.
|
|
184
|
+
*/
|
|
185
|
+
export circuit burn(coin: ShieldedCoinInfo, amount: Uint<64>, refundTo: ZswapCoinPublicKey): Maybe<ShieldedCoinInfo> {
|
|
186
|
+
ZOwnablePK_assertOnlyOwner();
|
|
187
|
+
return NST__burn(coin, amount, refundTo);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* @description Transfers ownership to `newOwnerId`. Owner only.
|
|
192
|
+
*
|
|
193
|
+
* @warning `newOwnerId` must be `_computeOwnerId(pk, secretNonce)` for the
|
|
194
|
+
* coin public key the new owner's wallet will supply as `ownPublicKey()`.
|
|
195
|
+
* The circuit cannot check this; any other key leaves the contract with no
|
|
196
|
+
* usable owner and no recovery.
|
|
197
|
+
*
|
|
198
|
+
* @circuitInfo k=15, rows=17096
|
|
199
|
+
*
|
|
200
|
+
* Requirements:
|
|
201
|
+
*
|
|
202
|
+
* - Caller is the owner.
|
|
203
|
+
* - `newOwnerId` is not zero.
|
|
204
|
+
*
|
|
205
|
+
* @param {Bytes<32>} newOwnerId - `_computeOwnerId(pk, secretNonce)` of the new owner.
|
|
206
|
+
*/
|
|
207
|
+
export circuit transferOwnership(newOwnerId: Bytes<32>): [] {
|
|
208
|
+
ZOwnablePK_transferOwnership(newOwnerId);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* @description Clears the owner. Owner only.
|
|
213
|
+
*
|
|
214
|
+
* @warning Irreversible: mint and burn stop forever.
|
|
215
|
+
*
|
|
216
|
+
* @circuitInfo k=14, rows=10622
|
|
217
|
+
*
|
|
218
|
+
* Requirements:
|
|
219
|
+
*
|
|
220
|
+
* - Caller is the owner.
|
|
221
|
+
*/
|
|
222
|
+
export circuit renounceOwnership(): [] {
|
|
223
|
+
ZOwnablePK_renounceOwnership();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* @description Returns the current owner commitment.
|
|
228
|
+
*
|
|
229
|
+
* @circuitInfo k=6, rows=50
|
|
230
|
+
*
|
|
231
|
+
* Requirements:
|
|
232
|
+
*
|
|
233
|
+
* - Contract is initialized.
|
|
234
|
+
*/
|
|
235
|
+
export circuit owner(): Bytes<32> {
|
|
236
|
+
return ZOwnablePK_owner();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* @description Returns the owner id `H(pk, nonce)`.
|
|
241
|
+
*
|
|
242
|
+
* Requirements:
|
|
243
|
+
*
|
|
244
|
+
* - `pk` is a coin public key.
|
|
245
|
+
*
|
|
246
|
+
* @param {Either<ZswapCoinPublicKey, ContractAddress>} pk - The owner's coin public key.
|
|
247
|
+
* @param {Bytes<32>} nonce - The owner's secret nonce.
|
|
248
|
+
*/
|
|
249
|
+
export pure circuit _computeOwnerId(pk: Either<ZswapCoinPublicKey, ContractAddress>, nonce: Bytes<32>): Bytes<32> {
|
|
250
|
+
return ZOwnablePK__computeOwnerId(pk, nonce);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* @description Returns the total amount ever minted.
|
|
255
|
+
*
|
|
256
|
+
* @circuitInfo k=7, rows=65
|
|
257
|
+
*/
|
|
258
|
+
export circuit totalMinted(): Uint<128> {
|
|
259
|
+
return NSTPublicSupply_totalMinted();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* @description Returns the token name.
|
|
264
|
+
*
|
|
265
|
+
* @circuitInfo k=6, rows=28
|
|
266
|
+
*
|
|
267
|
+
* Requirements:
|
|
268
|
+
*
|
|
269
|
+
* - Contract is initialized.
|
|
270
|
+
*/
|
|
271
|
+
export circuit name(): Opaque<"string"> {
|
|
272
|
+
return NST_name();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* @description Returns the token symbol.
|
|
277
|
+
*
|
|
278
|
+
* @circuitInfo k=6, rows=28
|
|
279
|
+
*
|
|
280
|
+
* Requirements:
|
|
281
|
+
*
|
|
282
|
+
* - Contract is initialized.
|
|
283
|
+
*/
|
|
284
|
+
export circuit symbol(): Opaque<"string"> {
|
|
285
|
+
return NST_symbol();
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* @description Returns the token decimals.
|
|
290
|
+
*
|
|
291
|
+
* @circuitInfo k=6, rows=28
|
|
292
|
+
*
|
|
293
|
+
* Requirements:
|
|
294
|
+
*
|
|
295
|
+
* - Contract is initialized.
|
|
296
|
+
*/
|
|
297
|
+
export circuit decimals(): Uint<8> {
|
|
298
|
+
return NST_decimals();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* @description Returns this contract's token color,
|
|
303
|
+
* `tokenType(tokenDomain, kernel.self())`.
|
|
304
|
+
*
|
|
305
|
+
* @circuitInfo k=13, rows=3971
|
|
306
|
+
*
|
|
307
|
+
* Requirements:
|
|
308
|
+
*
|
|
309
|
+
* - Contract is initialized.
|
|
310
|
+
*/
|
|
311
|
+
export circuit tokenColor(): Bytes<32> {
|
|
312
|
+
return NST_tokenColor();
|
|
313
|
+
}
|
|
314
|
+
}
|
package/utils/Utils.compact
CHANGED