@openzeppelin/compact-contracts 0.2.0 → 0.3.0-alpha.1
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 +10 -10
- package/access/AccessControl.compact +3 -35
- package/access/Ownable.compact +5 -51
- package/access/ShieldedAccessControl.compact +2 -2
- package/access/ZOwnablePK.compact +2 -2
- package/crypto/EcdhMask.compact +177 -0
- package/crypto/ElGamal.compact +473 -0
- package/multisig/ForwarderPrivate.compact +233 -0
- package/multisig/ForwarderShielded.compact +149 -0
- package/multisig/ForwarderUnshielded.compact +152 -0
- package/multisig/ProposalManager.compact +328 -0
- package/multisig/ShieldedTreasury.compact +204 -0
- package/multisig/ShieldedTreasuryStateless.compact +80 -0
- package/multisig/Signer.compact +312 -0
- package/multisig/SignerManager.compact +205 -0
- package/multisig/UnshieldedTreasury.compact +114 -0
- package/multisig/presets/ShieldedMultiSig.compact +227 -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 +32 -16
- package/security/Allowlist.compact +101 -0
- package/security/Blocklist.compact +99 -0
- package/security/Initializable.compact +2 -2
- package/security/Pausable.compact +2 -2
- package/token/ConfidentialFungibleToken.compact +1180 -0
- package/token/FungibleToken.compact +24 -82
- package/token/MultiToken.compact +21 -80
- package/token/NativeShieldedToken.compact +285 -0
- package/token/NativeShieldedTokenCore.compact +343 -0
- package/token/NativeShieldedTokenFamily.compact +287 -0
- package/token/NonFungibleToken.compact +25 -83
- package/token/extensions/ConfidentialFungibleTokenPublicSupply.compact +90 -0
- package/token/extensions/NativeShieldedTokenDerivedNonce.compact +70 -0
- package/token/extensions/NativeShieldedTokenFamilySupply.compact +145 -0
- package/token/extensions/NativeShieldedTokenSupply.compact +142 -0
- package/token/extensions/NativeShieldedTokenSupplyCore.compact +163 -0
- package/utils/Utils.compact +86 -2
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.2.0 (token/extensions/NativeShieldedTokenSupplyCore.compact)
|
|
3
|
+
|
|
4
|
+
pragma language_version >= 0.23.0;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @module NativeShieldedTokenSupplyCore
|
|
8
|
+
* @description Shared, domain-keyed core of the optional supply-accounting
|
|
9
|
+
* extension. It owns the per-domain minted/burned maps and exposes the
|
|
10
|
+
* `_addMinted` / `_addBurned` building blocks plus the `totalMinted` /
|
|
11
|
+
* `totalBurned` / `totalSupply` getters, each keyed by domain.
|
|
12
|
+
*
|
|
13
|
+
* This module is NOT meant to be composed directly. It is the de-duplicated
|
|
14
|
+
* base behind the two user-facing flavors:
|
|
15
|
+
*
|
|
16
|
+
* - `NativeShieldedTokenSupply` — pairs with `NativeShieldedToken`; forwards a
|
|
17
|
+
* single fixed key (the contract has one token type).
|
|
18
|
+
* - `NativeShieldedTokenFamilySupply` — pairs with `NativeShieldedTokenFamily`;
|
|
19
|
+
* forwards the caller's per-call `domain`.
|
|
20
|
+
*
|
|
21
|
+
* @dev Assertion messages use the generic `NativeShieldedTokenSupply:` prefix
|
|
22
|
+
* so they read sensibly from either flavor. There is no initialization: an
|
|
23
|
+
* absent domain key reads as 0, and the getters do not gate on the paired token
|
|
24
|
+
* module's init flag (a counter reading 0 before init is correct), so init
|
|
25
|
+
* enforcement stays with the token module's mint/burn circuits.
|
|
26
|
+
*
|
|
27
|
+
* @notice Accounting guarantees (when wired correctly by the consumer):
|
|
28
|
+
* `totalMinted(domain)` is EXACT, `totalBurned(domain)` a LOWER BOUND (burns
|
|
29
|
+
* that bypass the contract are invisible), and `totalSupply(domain)` =
|
|
30
|
+
* minted - burned an UPPER BOUND on circulating supply. These hold only if the
|
|
31
|
+
* consumer pairs every `_mint` with `_addMinted` and every burn with
|
|
32
|
+
* `_addBurned` under the same domain; the extension cannot enforce the pairing
|
|
33
|
+
* itself.
|
|
34
|
+
*
|
|
35
|
+
* @notice `_addBurned` enforces `burned <= minted` per domain, so a
|
|
36
|
+
* wrong-domain, duplicate, or unpaired BURN reverts rather than pushing
|
|
37
|
+
* `totalSupply(domain)` negative (this also keeps the `_addBurned` add and the
|
|
38
|
+
* `totalSupply` subtraction within bounds). It does NOT catch mint-side drift:
|
|
39
|
+
* a duplicated `_addMinted`, or one keyed to the wrong domain, silently inflates
|
|
40
|
+
* `totalMinted` (and the `burned <= minted` headroom). Correct mint pairing
|
|
41
|
+
* remains the consumer's responsibility.
|
|
42
|
+
*
|
|
43
|
+
* @warning The mint/burn <-> accounting pairing is a security-critical invariant
|
|
44
|
+
* of the CONSUMING contract; this extension cannot enforce or detect a breach:
|
|
45
|
+
* - A mint whose `_addMinted` is missing or wrong-domain leaves `totalMinted`
|
|
46
|
+
* too low, so `_addBurned` REJECTS burns that are actually legitimate —
|
|
47
|
+
* holders cannot burn coins that genuinely exist.
|
|
48
|
+
* - A duplicate or wrong-domain `_addMinted` inflates `totalMinted`, letting
|
|
49
|
+
* `_addBurned` accept burns beyond what was minted.
|
|
50
|
+
* - A missing `_addBurned` leaves `totalBurned` too low, so `totalSupply`
|
|
51
|
+
* overstates circulating supply.
|
|
52
|
+
* Call the matching accounting block on every mint and burn path, same domain.
|
|
53
|
+
*/
|
|
54
|
+
module NativeShieldedTokenSupplyCore {
|
|
55
|
+
import CompactStandardLibrary;
|
|
56
|
+
import "../../utils/Utils" prefix Utils_;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @description Exact amount minted per domain. See "Accounting guarantees".
|
|
60
|
+
* @type {Map<Bytes<32>, Uint<128>>} _totalMinted
|
|
61
|
+
*/
|
|
62
|
+
export ledger _totalMinted: Map<Bytes<32>, Uint<128>>;
|
|
63
|
+
/**
|
|
64
|
+
* @description Contract-mediated amount burned per domain (lower bound).
|
|
65
|
+
* @type {Map<Bytes<32>, Uint<128>>} _totalBurned
|
|
66
|
+
*/
|
|
67
|
+
export ledger _totalBurned: Map<Bytes<32>, Uint<128>>;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @description Adds `amount` to the exact minted total for `domain`. Call
|
|
71
|
+
* once per successful token `_mint`.
|
|
72
|
+
* @dev Checks for overflow in order to output a readable error message.
|
|
73
|
+
*
|
|
74
|
+
* @circuitInfo k=10, rows=653
|
|
75
|
+
*
|
|
76
|
+
* @param {Bytes<32>} domain - 32-byte token discriminator.
|
|
77
|
+
* @param {Uint<64>} amount - The minted amount.
|
|
78
|
+
* @return {[]} - Empty tuple.
|
|
79
|
+
*/
|
|
80
|
+
export circuit _addMinted(domain: Bytes<32>, amount: Uint<64>): [] {
|
|
81
|
+
const current = totalMinted(domain);
|
|
82
|
+
assert(Utils_UINT128_MAX() - current >= amount, "NativeShieldedTokenSupply: arithmetic overflow");
|
|
83
|
+
_totalMinted.insert(disclose(domain), disclose((current + amount) as Uint<128>));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @description Adds `amount` to the contract-mediated burned total for
|
|
88
|
+
* `domain`. Call once per successful token `_burn` / `_burnFromSelf`.
|
|
89
|
+
* @dev Enforces the per-domain `burned <= minted` invariant: a wrong-domain,
|
|
90
|
+
* duplicate, or unpaired burn reverts rather than corrupting `totalSupply`.
|
|
91
|
+
* The guard also keeps the add and the `totalSupply` subtraction within
|
|
92
|
+
* bounds, so no separate overflow/underflow check is needed.
|
|
93
|
+
*
|
|
94
|
+
* @circuitInfo k=10, rows=755
|
|
95
|
+
*
|
|
96
|
+
* @param {Bytes<32>} domain - 32-byte token discriminator.
|
|
97
|
+
* @param {Uint<128>} amount - The burned amount.
|
|
98
|
+
* @return {[]} - Empty tuple.
|
|
99
|
+
*/
|
|
100
|
+
export circuit _addBurned(domain: Bytes<32>, amount: Uint<128>): [] {
|
|
101
|
+
const minted = totalMinted(domain);
|
|
102
|
+
const current = totalBurned(domain);
|
|
103
|
+
// First guards the subtraction below from underflowing on an already-corrupt
|
|
104
|
+
// state (stored burned past minted); the second is the reachable per-call
|
|
105
|
+
// check that this burn does not push the domain past its minted total.
|
|
106
|
+
assert(minted >= current, "NativeShieldedTokenSupply: burned total exceeds minted");
|
|
107
|
+
assert(minted - current >= amount, "NativeShieldedTokenSupply: burn exceeds available supply");
|
|
108
|
+
_totalBurned.insert(disclose(domain), disclose((current + amount) as Uint<128>));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @description Returns the exact amount ever minted for `domain`.
|
|
113
|
+
*
|
|
114
|
+
* @circuitInfo k=9, rows=344
|
|
115
|
+
*
|
|
116
|
+
* @param {Bytes<32>} domain - 32-byte token discriminator.
|
|
117
|
+
* @return {Uint<128>} - The total amount minted.
|
|
118
|
+
*/
|
|
119
|
+
export circuit totalMinted(domain: Bytes<32>): Uint<128> {
|
|
120
|
+
if (!_totalMinted.member(disclose(domain))) {
|
|
121
|
+
return 0;
|
|
122
|
+
}
|
|
123
|
+
return _totalMinted.lookup(disclose(domain));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @description Returns the contract-mediated amount burned for `domain`.
|
|
128
|
+
* @notice This is a lower bound: coins sent directly to the burn address
|
|
129
|
+
* without going through the contract are not counted.
|
|
130
|
+
*
|
|
131
|
+
* @circuitInfo k=9, rows=341
|
|
132
|
+
*
|
|
133
|
+
* @param {Bytes<32>} domain - 32-byte token discriminator.
|
|
134
|
+
* @return {Uint<128>} - The total amount burned through the contract.
|
|
135
|
+
*/
|
|
136
|
+
export circuit totalBurned(domain: Bytes<32>): Uint<128> {
|
|
137
|
+
if (!_totalBurned.member(disclose(domain))) {
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
return _totalBurned.lookup(disclose(domain));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* @description Returns `totalMinted(domain) - totalBurned(domain)`, or 0 if
|
|
145
|
+
* `totalBurned` somehow exceeds `totalMinted`.
|
|
146
|
+
* @notice This is an UPPER BOUND on circulating supply, not an exact value:
|
|
147
|
+
* burns that bypass the contract are invisible. See the module notes.
|
|
148
|
+
* @dev The `burned <= minted` invariant enforced by `_addBurned` already makes
|
|
149
|
+
* `burned > minted` unreachable through the accounting API, so the clamp never
|
|
150
|
+
* fires in normal operation. It is kept so the getter is a total function that
|
|
151
|
+
* reads cleanly (returning 0) rather than reverting on an out-of-bound state.
|
|
152
|
+
*
|
|
153
|
+
* @circuitInfo k=9, rows=439
|
|
154
|
+
*
|
|
155
|
+
* @param {Bytes<32>} domain - 32-byte token discriminator.
|
|
156
|
+
* @return {Uint<128>} - The upper bound on tokens in existence.
|
|
157
|
+
*/
|
|
158
|
+
export circuit totalSupply(domain: Bytes<32>): Uint<128> {
|
|
159
|
+
const minted = totalMinted(domain);
|
|
160
|
+
const burned = totalBurned(domain);
|
|
161
|
+
return (minted >= burned ? (minted - burned) : 0) as Uint<128>;
|
|
162
|
+
}
|
|
163
|
+
}
|
package/utils/Utils.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.1 (utils/Utils.compact)
|
|
3
3
|
|
|
4
|
-
pragma language_version >= 0.
|
|
4
|
+
pragma language_version >= 0.23.0;
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @module Utils.
|
|
@@ -95,4 +95,88 @@ module Utils {
|
|
|
95
95
|
? Either<T1, T2>{ is_left: true, left: value.left, right: default<T2> }
|
|
96
96
|
: Either<T1, T2>{ is_left: false, left: default<T1>, right: value.right };
|
|
97
97
|
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @description Returns the canonical zero `Either<Bytes<32>, ContractAddress>` value: the
|
|
101
|
+
* left variant (`Bytes<32>`) set to zero. Used as the sentinel for mint/burn operations
|
|
102
|
+
* (a zero `from` signals a mint, a zero `to` signals a burn) and for cleared approvals.
|
|
103
|
+
*
|
|
104
|
+
* The left variant is chosen so that `isTargetZero` checks against `default<Bytes<32>>`,
|
|
105
|
+
* and to avoid triggering contract-address guards (e.g. circuits that check `!to.is_left`
|
|
106
|
+
* before delegating to an `_unsafe` variant). A right variant with `default<ContractAddress>`
|
|
107
|
+
* would also pass `isTargetZero`, but the left variant keeps guards and error messages consistent.
|
|
108
|
+
*
|
|
109
|
+
* @return {Either<Bytes<32>, ContractAddress>} - The canonical zero value.
|
|
110
|
+
*/
|
|
111
|
+
export pure circuit zeroAccount(): Either<Bytes<32>, ContractAddress> {
|
|
112
|
+
return Either<Bytes<32>, ContractAddress> {
|
|
113
|
+
is_left: true, left: default<Bytes<32>>, right: default<ContractAddress>
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @description Returns `true` if `target`'s active branch (as indicated by `is_left`)
|
|
119
|
+
* holds the zero value.
|
|
120
|
+
*
|
|
121
|
+
* @param {Either<Bytes<32>, ContractAddress>} target - The value to check.
|
|
122
|
+
* @return {Boolean} - `true` if the active branch is zero, `false` otherwise.
|
|
123
|
+
*/
|
|
124
|
+
export pure circuit isTargetZero(target: Either<Bytes<32>, ContractAddress>): Boolean {
|
|
125
|
+
if (target.is_left) {
|
|
126
|
+
return target.left == default<Bytes<32>>;
|
|
127
|
+
} else {
|
|
128
|
+
return target.right == default<ContractAddress>;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* @description Computes an account identifier without on-chain state, allowing a user to derive
|
|
134
|
+
* their identity commitment before submitting an operation.
|
|
135
|
+
*
|
|
136
|
+
* @warning OpSec: The `secretKey` parameter is a sensitive secret. Mishandling it can
|
|
137
|
+
* permanently compromise the security of this system:
|
|
138
|
+
*
|
|
139
|
+
* - **Never log or persist** the `secretKey` in plaintext — avoid browser devtools,
|
|
140
|
+
* application logs, analytics pipelines, or any observable side-channel.
|
|
141
|
+
* - **Store offline or in secure enclaves** — hardware security modules (HSMs),
|
|
142
|
+
* air-gapped devices, or encrypted vaults are strongly preferred over hot storage.
|
|
143
|
+
* - **Use cryptographically secure randomness** — generate keys with `crypto.getRandomValues()`
|
|
144
|
+
* or equivalent; weak or predictable keys can be brute-forced to reveal your identity.
|
|
145
|
+
* - **Treat key loss as identity loss** — a lost key cannot be recovered.
|
|
146
|
+
* - **Avoid calling this circuit in untrusted environments** — executing this in an
|
|
147
|
+
* unverified browser extension, compromised runtime, or shared machine may expose
|
|
148
|
+
* the key to a malicious observer.
|
|
149
|
+
*
|
|
150
|
+
* ## ID Derivation
|
|
151
|
+
* `accountId = persistentHash(secretKey)`
|
|
152
|
+
*
|
|
153
|
+
* @param {Bytes<32>} secretKey - A 32-byte cryptographically secure random value.
|
|
154
|
+
*
|
|
155
|
+
* @returns {Bytes<32>} accountId - The computed account identifier.
|
|
156
|
+
*/
|
|
157
|
+
export pure circuit computeAccountId(secretKey: Bytes<32>): Bytes<32> {
|
|
158
|
+
return persistentHash<Vector<1, Bytes<32>>>([secretKey]);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @description Returns the current contract's address as an
|
|
163
|
+
* `Either<ZswapCoinPublicKey, ContractAddress>` for use as a
|
|
164
|
+
* recipient in shielded send operations (deposits and receiving change).
|
|
165
|
+
*
|
|
166
|
+
* @returns {Either<ZswapCoinPublicKey, ContractAddress>} The contract's address as a recipient.
|
|
167
|
+
*/
|
|
168
|
+
export circuit selfAsRecipient(): Either<ZswapCoinPublicKey, ContractAddress> {
|
|
169
|
+
return right<ZswapCoinPublicKey, ContractAddress>(kernel.self());
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* @description The maximum value representable by a `Uint<128>`.
|
|
174
|
+
*
|
|
175
|
+
* @notice TODO: Remove once a math module providing this constant is available.
|
|
176
|
+
*
|
|
177
|
+
* @returns {Uint<128>} `2^128 - 1`.
|
|
178
|
+
*/
|
|
179
|
+
export pure circuit UINT128_MAX(): Uint<128> {
|
|
180
|
+
return 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
|
|
181
|
+
}
|
|
98
182
|
}
|