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