@openzeppelin/compact-contracts 0.3.0-alpha.1 → 0.3.0-alpha.2
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/access/AccessControl.compact +1 -1
- package/access/Ownable.compact +1 -1
- package/access/ShieldedAccessControl.compact +1 -1
- package/access/ZOwnablePK.compact +1 -1
- package/crypto/EcdhMask.compact +1 -1
- package/crypto/ElGamal.compact +1 -1
- package/multisig/ForwarderPrivate.compact +2 -2
- package/multisig/ForwarderShielded.compact +2 -2
- package/multisig/ForwarderUnshielded.compact +2 -2
- package/multisig/ProposalManager.compact +35 -1
- package/multisig/ShieldedTreasury.compact +34 -4
- package/multisig/ShieldedTreasuryStateless.compact +46 -17
- package/multisig/Signer.compact +45 -41
- package/multisig/UnshieldedTreasury.compact +44 -17
- package/multisig/presets/ShieldedMultiSig.compact +4 -4
- package/multisig/presets/ShieldedMultiSigV2.compact +2 -2
- package/multisig/presets/ShieldedMultiSigV3.compact +2 -2
- 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 +3 -3
- 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 +39 -11
- package/token/FungibleToken.compact +1 -1
- package/token/MultiToken.compact +1 -1
- package/token/NativeShieldedToken.compact +2 -2
- package/token/NativeShieldedTokenCore.compact +1 -1
- package/token/NativeShieldedTokenFamily.compact +2 -2
- package/token/NonFungibleToken.compact +1 -1
- package/token/extensions/ConfidentialFungibleTokenPublicSupply.compact +1 -1
- package/token/extensions/NativeShieldedTokenDerivedNonce.compact +1 -1
- package/token/extensions/{NativeShieldedTokenFamilySupply.compact → NativeShieldedTokenFamilyPublicSupply.compact} +9 -9
- package/token/extensions/{NativeShieldedTokenSupply.compact → NativeShieldedTokenPublicSupply.compact} +9 -9
- package/token/extensions/{NativeShieldedTokenSupplyCore.compact → NativeShieldedTokenPublicSupplyCore.compact} +9 -9
- package/utils/Utils.compact +1 -1
- package/multisig/SignerManager.compact +0 -205
package/access/Ownable.compact
CHANGED
package/crypto/EcdhMask.compact
CHANGED
package/crypto/ElGamal.compact
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.3.0-alpha.
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.3.0-alpha.2 (multisig/ForwarderPrivate.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.23.0;
|
|
5
5
|
|
|
@@ -110,7 +110,7 @@ module ForwarderPrivate {
|
|
|
110
110
|
* would defeat the private-parent guarantee. A coin public key is encrypted
|
|
111
111
|
* inside the Zswap output and never appears on the public transcript.
|
|
112
112
|
*
|
|
113
|
-
* @circuitInfo k=
|
|
113
|
+
* @circuitInfo k=15, rows=23937
|
|
114
114
|
*
|
|
115
115
|
* Requirements:
|
|
116
116
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.3.0-alpha.
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.3.0-alpha.2 (multisig/ForwarderShielded.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.23.0;
|
|
5
5
|
|
|
@@ -103,7 +103,7 @@ module ForwarderShielded {
|
|
|
103
103
|
* `sendImmediateShielded` to the stored parent recipient (always the
|
|
104
104
|
* `left` / coin-public-key arm today).
|
|
105
105
|
*
|
|
106
|
-
* @circuitInfo k=15, rows=
|
|
106
|
+
* @circuitInfo k=15, rows=24446
|
|
107
107
|
*
|
|
108
108
|
* Requirements:
|
|
109
109
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.3.0-alpha.
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.3.0-alpha.2 (multisig/ForwarderUnshielded.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.23.0;
|
|
5
5
|
|
|
@@ -105,7 +105,7 @@ module ForwarderUnshielded {
|
|
|
105
105
|
* forwards it to `_parent` via `sendUnshielded` (always the `right` /
|
|
106
106
|
* user-address arm today).
|
|
107
107
|
*
|
|
108
|
-
* @circuitInfo k=
|
|
108
|
+
* @circuitInfo k=10, rows=565
|
|
109
109
|
*
|
|
110
110
|
* Requirements:
|
|
111
111
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.3.0-alpha.
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.3.0-alpha.2 (multisig/ProposalManager.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.23.0;
|
|
5
5
|
|
|
@@ -12,12 +12,33 @@ pragma language_version >= 0.23.0;
|
|
|
12
12
|
* Recipient type with a RecipientKind tag. Typed helper circuits
|
|
13
13
|
* provide safe construction of recipients without exposing the
|
|
14
14
|
* internal Bytes<32> representation to consumers.
|
|
15
|
+
*
|
|
16
|
+
* @warning PROPOSALS ARE PUBLIC. `_proposals` is exported ledger state, so
|
|
17
|
+
* every proposal's recipient address, token color, amount, and status is
|
|
18
|
+
* readable off-chain, and `_nextProposalId` reveals how many have been created.
|
|
19
|
+
* This holds regardless of which treasury a consumer composes: pairing this
|
|
20
|
+
* module with `ShieldedTreasuryStateless` still publishes the full intent of
|
|
21
|
+
* every transfer, even though the transfer itself is shielded. The transfer is
|
|
22
|
+
* private; the plan is not.
|
|
23
|
+
*
|
|
24
|
+
* @notice `RecipientKind` is the only thing distinguishing what a recipient's
|
|
25
|
+
* 32 bytes mean, the same value is read as a Zswap coin public key, a contract
|
|
26
|
+
* address, or a user address depending on the tag. `Contract` is accepted by
|
|
27
|
+
* both `toShieldedRecipient` and `toUnshieldedRecipient`, so the tag alone does
|
|
28
|
+
* not determine the rail; a consumer composing both treasuries could route the
|
|
29
|
+
* same proposal either way.
|
|
15
30
|
*/
|
|
16
31
|
module ProposalManager {
|
|
17
32
|
import CompactStandardLibrary;
|
|
18
33
|
|
|
19
34
|
// ─── Types ──────────────────────────────────────────────────────
|
|
20
35
|
|
|
36
|
+
/**
|
|
37
|
+
* @description Proposal lifecycle states. `Inactive` is the zero value and is
|
|
38
|
+
* never stored: `_createProposal` writes `Active`, and every view asserts the
|
|
39
|
+
* proposal exists before reading, so a stored proposal can never read
|
|
40
|
+
* `Inactive`.
|
|
41
|
+
*/
|
|
21
42
|
export enum ProposalStatus {
|
|
22
43
|
Inactive,
|
|
23
44
|
Active,
|
|
@@ -176,6 +197,19 @@ module ProposalManager {
|
|
|
176
197
|
/**
|
|
177
198
|
* @description Creates a new proposal.
|
|
178
199
|
*
|
|
200
|
+
* @warning The recipient is NOT validated beyond being well-typed. Two
|
|
201
|
+
* consequences the consumer must handle:
|
|
202
|
+
*
|
|
203
|
+
* - A `kind` the execution rail cannot accept (an `UnshieldedUser` recipient
|
|
204
|
+
* on a shielded treasury, say) creates a proposal that passes here and then
|
|
205
|
+
* reverts in `toShieldedRecipient` at execution. It stays `Active` and
|
|
206
|
+
* unexecutable on that rail until `_cancelProposal` retires it.
|
|
207
|
+
* - A zero or otherwise unspendable `address` is accepted, and funds sent to
|
|
208
|
+
* it on execution are unrecoverable.
|
|
209
|
+
*
|
|
210
|
+
* @notice `_nextProposalId` is incremented and then read back for the map key,
|
|
211
|
+
* making this a pinned read-modify-write.
|
|
212
|
+
*
|
|
179
213
|
* @notice Access control is NOT enforced here.
|
|
180
214
|
* The consuming contract must gate this behind its own
|
|
181
215
|
* authorization policy.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.3.0-alpha.
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.3.0-alpha.2 (multisig/ShieldedTreasury.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.23.0;
|
|
5
5
|
|
|
@@ -18,6 +18,19 @@ pragma language_version >= 0.23.0;
|
|
|
18
18
|
* purposes. The canonical balance query is `getTokenBalance`, which
|
|
19
19
|
* reads the actual coin value from the UTXO map.
|
|
20
20
|
*
|
|
21
|
+
* @warning This treasury's HOLDINGS ARE PUBLIC. `_coins` is exported ledger
|
|
22
|
+
* state, so every coin it holds is readable off-chain in plaintext (nonce,
|
|
23
|
+
* color, value, and Merkle index) as are the cumulative `_received` and
|
|
24
|
+
* `_sent` totals. "Shielded" here refers to the token rail, not to the
|
|
25
|
+
* treasury's balance: individual transfers are shielded, but what this
|
|
26
|
+
* contract holds is not. Anyone can observe the treasury's balance per color
|
|
27
|
+
* and identify the exact UTXO backing it.
|
|
28
|
+
*
|
|
29
|
+
* Storing the coin is what buys the convenience: the contract can spend later
|
|
30
|
+
* without the caller supplying the UTXO. If public holdings are unacceptable,
|
|
31
|
+
* use `ShieldedTreasuryStateless`, which stores nothing and has the operator
|
|
32
|
+
* supply the coin at spend time.
|
|
33
|
+
*
|
|
21
34
|
* Underscore-prefixed circuits (_deposit, _send) have no access control
|
|
22
35
|
* enforcement. The consuming contract must gate these behind its own
|
|
23
36
|
* authorization policy.
|
|
@@ -42,9 +55,13 @@ module ShieldedTreasury {
|
|
|
42
55
|
* The coin is then merged with any existing coin of the same color,
|
|
43
56
|
* or inserted as a new entry if no coin of that color exists.
|
|
44
57
|
*
|
|
45
|
-
* Zero-value deposits are permitted
|
|
46
|
-
*
|
|
47
|
-
*
|
|
58
|
+
* Zero-value deposits are permitted and are not always a no-op. On a color the
|
|
59
|
+
* treasury does not yet hold, a zero-value coin is inserted and occupies that
|
|
60
|
+
* color's single slot until a later deposit merges into it; if a coin of that
|
|
61
|
+
* color already exists, the zero merges into it via `mergeCoinImmediate` and
|
|
62
|
+
* the stored value is unchanged. They may serve as signaling mechanisms when
|
|
63
|
+
* events are supported. They are not useful as privacy decoys, this
|
|
64
|
+
* treasury's holdings are public (see the module header).
|
|
48
65
|
*
|
|
49
66
|
* @notice Access control is NOT enforced here.
|
|
50
67
|
* The consuming contract must gate this behind its own
|
|
@@ -92,6 +109,12 @@ module ShieldedTreasury {
|
|
|
92
109
|
* belongs to the treasury and must not be re-spent by the caller (doing
|
|
93
110
|
* so would double-spend the recorded coin).
|
|
94
111
|
*
|
|
112
|
+
* @notice The returned `ShieldedSendResult` carries the sent and change coins
|
|
113
|
+
* in plaintext, so sinking it (returning it from an entry point, writing it
|
|
114
|
+
* to ledger, passing it cross-contract) discloses them. `_coins` publishes only
|
|
115
|
+
* the retained change coin, and nothing at all when the send leaves no change,
|
|
116
|
+
* so the sent coin's plaintext is disclosed beyond anything in public state.
|
|
117
|
+
*
|
|
95
118
|
* @notice Access control is NOT enforced here.
|
|
96
119
|
* The consuming contract must gate this behind its own
|
|
97
120
|
* authorization policy.
|
|
@@ -194,6 +217,13 @@ module ShieldedTreasury {
|
|
|
194
217
|
* and cumulative sent totals for a color. Should equal
|
|
195
218
|
* `getTokenBalance` if accounting is consistent.
|
|
196
219
|
*
|
|
220
|
+
* @notice The subtraction is unguarded. `_send` cannot drive the sent total
|
|
221
|
+
* past the received total. Change re-insertion never increments `_received`
|
|
222
|
+
* so the invariant holds for any consumer that goes through `_deposit` and
|
|
223
|
+
* `_send`. A consumer that writes the exported `_coins` directly can break
|
|
224
|
+
* it, and this circuit then fails as an unprovable range violation rather
|
|
225
|
+
* than a named assert.
|
|
226
|
+
*
|
|
197
227
|
* @param {Bytes<32>} color - The token color.
|
|
198
228
|
*
|
|
199
229
|
* @returns {Uint<128>} Received minus sent.
|
|
@@ -1,22 +1,38 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.3.0-alpha.
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.3.0-alpha.2 (multisig/ShieldedTreasuryStateless.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.23.0;
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* @module
|
|
8
|
-
* @description Manages shielded (private) token deposits
|
|
9
|
-
*
|
|
7
|
+
* @module ShieldedTreasuryStateless
|
|
8
|
+
* @description Manages shielded (private) token deposits and transfers for
|
|
9
|
+
* multisig governance contracts, without any ledger state.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
11
|
+
* This module holds NO state: no coin map, no balances, no received/sent
|
|
12
|
+
* totals. Deposits call `receiveShielded` only, and spends take the coin as a
|
|
13
|
+
* circuit parameter. The operator discovers coin indices from `ZswapOutput`
|
|
14
|
+
* events via the indexer, constructs the `QualifiedShieldedCoinInfo` off-chain,
|
|
15
|
+
* and supplies it at spend time. Coin data therefore never reaches the public
|
|
16
|
+
* ledger, and there is no on-chain balance to query — contrast
|
|
17
|
+
* `ShieldedTreasury`, which stores one merged UTXO per color and tracks
|
|
18
|
+
* cumulative totals.
|
|
16
19
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
+
* @notice Because nothing is tracked on-chain, this module cannot detect a
|
|
21
|
+
* double spend or a coin it does not own; the protocol rejects both at
|
|
22
|
+
* application time. Sufficiency is likewise enforced by `sendShielded` rather
|
|
23
|
+
* than by an assert here.
|
|
24
|
+
*
|
|
25
|
+
* @notice "No ledger state" is not the same as "unobservable". A coin
|
|
26
|
+
* commitment is a hash of the coin's data, so `sendShielded` can link a coin
|
|
27
|
+
* receive to its later spend; `_send` declares that disclosure on the
|
|
28
|
+
* consumer's behalf. Consumers that need to make the trade-off themselves
|
|
29
|
+
* should note it is only declared here for convenience — it could be moved to
|
|
30
|
+
* the caller. Separately, see `_send`'s warning about its return value, which
|
|
31
|
+
* carries coin plaintext.
|
|
32
|
+
*
|
|
33
|
+
* Underscore-prefixed circuits (_deposit, _send) have no access control
|
|
34
|
+
* enforcement. The consuming contract must gate these behind its own
|
|
35
|
+
* authorization policy.
|
|
20
36
|
*/
|
|
21
37
|
module ShieldedTreasuryStateless {
|
|
22
38
|
import CompactStandardLibrary;
|
|
@@ -40,13 +56,24 @@ module ShieldedTreasuryStateless {
|
|
|
40
56
|
* Executes the shielded send and returns the result untouched.
|
|
41
57
|
* `sendShielded` already routes any change back to this contract as a
|
|
42
58
|
* self-owned output, so `result.change` is a live, spendable coin. The
|
|
43
|
-
* implementing contract chooses what to do with it:
|
|
44
|
-
* it back as the `coin` for a later spend, or spend it onward to a
|
|
59
|
+
* implementing contract chooses what to do with it: retain it off-chain and
|
|
60
|
+
* pass it back as the `coin` for a later spend, or spend it onward to a
|
|
45
61
|
* different recipient in the same transaction via `sendImmediateShielded`.
|
|
46
62
|
* (Re-spending change is only meaningful when it goes somewhere other than
|
|
47
63
|
* self — `sendShielded` already routes it to self.) No balance or
|
|
48
64
|
* received/sent accounting is tracked on the ledger.
|
|
49
65
|
*
|
|
66
|
+
* @warning The returned `ShieldedSendResult` holds the sent and change coins
|
|
67
|
+
* in plaintext (`nonce`, `color`, `value`). Consuming it in-circuit is safe
|
|
68
|
+
* (passing `result.change` to `sendImmediateShielded`, for instance). Returning
|
|
69
|
+
* it from your entry point, writing it to ledger, or passing it to another
|
|
70
|
+
* contract publishes those coin details, including the amount left over from
|
|
71
|
+
* the coin that was spent. Note the compiler will NOT stop you: the
|
|
72
|
+
* `disclose` calls below clear the witness taint, so such a return compiles
|
|
73
|
+
* with no `disclose` of your own and no marker at the disclosure site. If you
|
|
74
|
+
* do not need in-circuit change handling, ignore the result. The change is
|
|
75
|
+
* recoverable off-chain from the transaction's private proving state.
|
|
76
|
+
*
|
|
50
77
|
* @notice Access control is NOT enforced here.
|
|
51
78
|
* The consuming contract must gate this behind its own
|
|
52
79
|
* authorization policy.
|
|
@@ -72,9 +99,11 @@ module ShieldedTreasuryStateless {
|
|
|
72
99
|
// `_send` returns it untouched: it must not re-spend the change here and
|
|
73
100
|
// then hand that same (now-nullified) coin back via `result.change`. That
|
|
74
101
|
// is the double spend a node rejects on the next spend. The implementing
|
|
75
|
-
// contract decides what to do with the returned change:
|
|
76
|
-
// spend it later, or route it onward itself. The rule is only that the
|
|
77
|
-
// change must never be re-spent AND still treated as held.
|
|
102
|
+
// contract decides what to do with the returned change: retain it off-chain
|
|
103
|
+
// and spend it later, or route it onward itself. The rule is only that the
|
|
104
|
+
// change must never be re-spent AND still treated as held. "Retain" means
|
|
105
|
+
// off-chain. This module keeps no state, and writing the coin to ledger
|
|
106
|
+
// would publish its nonce, color, and value.
|
|
78
107
|
return result;
|
|
79
108
|
}
|
|
80
109
|
}
|
package/multisig/Signer.compact
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.3.0-alpha.
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.3.0-alpha.2 (multisig/Signer.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.23.0;
|
|
5
5
|
|
|
@@ -42,6 +42,25 @@ pragma language_version >= 0.23.0;
|
|
|
42
42
|
* call `initialize` in the contract's constructor. Contracts MUST NOT
|
|
43
43
|
* call `initialize` outside of the constructor context because
|
|
44
44
|
* this could corrupt the signer set and threshold configuration.
|
|
45
|
+
*
|
|
46
|
+
* `_isInitialized` records only whether `initialize` has run; it gates
|
|
47
|
+
* re-entry into `initialize` and nothing else. No other circuit consults it,
|
|
48
|
+
* so a contract that configures signers through its own flow
|
|
49
|
+
* (`_addSigner` / `_setThreshold`) is fully functional without ever calling
|
|
50
|
+
* `initialize`. Provided it gates those circuits behind its own
|
|
51
|
+
* authorization policy. Leaving them ungated lets the first caller register
|
|
52
|
+
* their own signer set and take control of the contract.
|
|
53
|
+
*
|
|
54
|
+
* @notice Not asserting initialization on the guard and view circuits is a
|
|
55
|
+
* deliberate divergence from the other modules in this library, which do.
|
|
56
|
+
* Those modules gate a zero-valued identity (an owner, a role holder); this
|
|
57
|
+
* one gates an empty collection, which rejects by absence. Safety here comes
|
|
58
|
+
* from the state rather than from a flag: an empty registry makes `isSigner`
|
|
59
|
+
* false for every input, so `assertSigner` rejects every caller, and a zero
|
|
60
|
+
* `_threshold` makes `assertThresholdMet` reject before it compares any
|
|
61
|
+
* approval count regardless of how the threshold came to be zero. Contracts
|
|
62
|
+
* needing a "configured" signal beyond that should track one at the contract
|
|
63
|
+
* layer.
|
|
45
64
|
*/
|
|
46
65
|
module Signer<T> {
|
|
47
66
|
import CompactStandardLibrary;
|
|
@@ -66,8 +85,6 @@ module Signer<T> {
|
|
|
66
85
|
* and an initial set of signers.
|
|
67
86
|
* If used, it should only be called in the contract's constructor.
|
|
68
87
|
*
|
|
69
|
-
* @circuitInfo k=11, rows=1815
|
|
70
|
-
*
|
|
71
88
|
* Requirements:
|
|
72
89
|
*
|
|
73
90
|
* - If used, can only be called once (in the constructor).
|
|
@@ -98,37 +115,43 @@ module Signer<T> {
|
|
|
98
115
|
/**
|
|
99
116
|
* @description Asserts that the given caller is an active signer.
|
|
100
117
|
*
|
|
101
|
-
* @circuitInfo k=10, rows=585
|
|
102
|
-
*
|
|
103
118
|
* Requirements:
|
|
104
119
|
*
|
|
105
|
-
* - Contract must be initialized.
|
|
106
120
|
* - `caller` must be a member of the signers registry.
|
|
107
121
|
*
|
|
108
122
|
* @param {T} caller - The identity to validate.
|
|
109
123
|
* @returns {[]} Empty tuple.
|
|
110
124
|
*/
|
|
111
125
|
export circuit assertSigner(caller: T): [] {
|
|
112
|
-
assertInitialized();
|
|
113
126
|
assert(isSigner(caller), "Signer: not a signer");
|
|
114
127
|
}
|
|
115
128
|
|
|
116
129
|
/**
|
|
117
130
|
* @description Asserts that the given approval count meets the threshold.
|
|
118
131
|
*
|
|
132
|
+
* @notice `approvalCount` is supplied by the consuming contract and is
|
|
133
|
+
* compared against the threshold only. This circuit never consults the signer
|
|
134
|
+
* registry. Ensuring the count reflects distinct, currently-registered signers
|
|
135
|
+
* is the consuming contract's responsibility, and a contract whose approval
|
|
136
|
+
* accounting is not itself gated by `assertSigner` gets no protection here.
|
|
137
|
+
*
|
|
138
|
+
* The count is deliberately left unbounded by `_signerCount`: approvals
|
|
139
|
+
* collected before a signer was removed must still count toward a threshold
|
|
140
|
+
* the remaining signers satisfy, and bounding the count would reject those.
|
|
141
|
+
* See the stale-signer-set guidance in the module header.
|
|
142
|
+
*
|
|
119
143
|
* @circuitInfo k=9, rows=54
|
|
120
144
|
*
|
|
121
145
|
* Requirements:
|
|
122
146
|
*
|
|
123
|
-
* -
|
|
124
|
-
*
|
|
147
|
+
* - Ledger threshold must be set (not be zero). An unconfigured module has a
|
|
148
|
+
* zero threshold, so this rejects before any approval count is compared.
|
|
125
149
|
* - `approvalCount` must be >= threshold.
|
|
126
150
|
*
|
|
127
151
|
* @param {Uint<8>} approvalCount - The current number of approvals.
|
|
128
152
|
* @returns {[]} Empty tuple.
|
|
129
153
|
*/
|
|
130
154
|
export circuit assertThresholdMet(approvalCount: Uint<8>): [] {
|
|
131
|
-
assertInitialized();
|
|
132
155
|
assert(_threshold != 0, "Signer: threshold not set");
|
|
133
156
|
assert(approvalCount >= _threshold, "Signer: threshold not met");
|
|
134
157
|
}
|
|
@@ -136,42 +159,32 @@ module Signer<T> {
|
|
|
136
159
|
// ─── View ──────────────────────────────────────────────────────────
|
|
137
160
|
|
|
138
161
|
/**
|
|
139
|
-
* @description Returns the current signer count.
|
|
162
|
+
* @description Returns the current signer count. Returns zero when no
|
|
163
|
+
* signers have been registered.
|
|
140
164
|
*
|
|
141
165
|
* @circuitInfo k=6, rows=26
|
|
142
166
|
*
|
|
143
|
-
* Requirements:
|
|
144
|
-
*
|
|
145
|
-
* - Contract must be initialized.
|
|
146
|
-
*
|
|
147
167
|
* @returns {Uint<8>} The number of active signers.
|
|
148
168
|
*/
|
|
149
169
|
export circuit getSignerCount(): Uint<8> {
|
|
150
|
-
assertInitialized();
|
|
151
170
|
return _signerCount;
|
|
152
171
|
}
|
|
153
172
|
|
|
154
173
|
/**
|
|
155
|
-
* @description Returns the approval threshold.
|
|
174
|
+
* @description Returns the approval threshold. Returns zero when the
|
|
175
|
+
* threshold has not been set.
|
|
156
176
|
*
|
|
157
177
|
* @circuitInfo k=6, rows=26
|
|
158
178
|
*
|
|
159
|
-
* Requirements:
|
|
160
|
-
*
|
|
161
|
-
* - Contract must be initialized.
|
|
162
|
-
*
|
|
163
179
|
* @returns {Uint<8>} The threshold.
|
|
164
180
|
*/
|
|
165
181
|
export circuit getThreshold(): Uint<8> {
|
|
166
|
-
assertInitialized();
|
|
167
182
|
return _threshold;
|
|
168
183
|
}
|
|
169
184
|
|
|
170
185
|
/**
|
|
171
186
|
* @description Returns whether the given account is an active signer.
|
|
172
187
|
*
|
|
173
|
-
* @circuitInfo k=10, rows=605
|
|
174
|
-
*
|
|
175
188
|
* @param {T} account - The account to check.
|
|
176
189
|
* @returns {Boolean} True if the account is an active signer.
|
|
177
190
|
*/
|
|
@@ -188,8 +201,6 @@ module Signer<T> {
|
|
|
188
201
|
* The consuming contract must gate this behind its own
|
|
189
202
|
* authorization policy.
|
|
190
203
|
*
|
|
191
|
-
* @circuitInfo k=10, rows=598
|
|
192
|
-
*
|
|
193
204
|
* Requirements:
|
|
194
205
|
*
|
|
195
206
|
* - `signer` must not already be an active signer.
|
|
@@ -214,8 +225,6 @@ module Signer<T> {
|
|
|
214
225
|
* The consuming contract must gate this behind its own
|
|
215
226
|
* authorization policy.
|
|
216
227
|
*
|
|
217
|
-
* @circuitInfo k=10, rows=612
|
|
218
|
-
*
|
|
219
228
|
* Requirements:
|
|
220
229
|
*
|
|
221
230
|
* - `signer` must be an active signer.
|
|
@@ -263,6 +272,14 @@ module Signer<T> {
|
|
|
263
272
|
* @warning This is intended for use during contract construction
|
|
264
273
|
* or custom setup flows where signers may not yet be registered.
|
|
265
274
|
*
|
|
275
|
+
* Leaving `newThreshold` above the signer count is permitted and expected
|
|
276
|
+
* mid-setup, and is not self-enforcing: `assertThresholdMet` compares
|
|
277
|
+
* `approvalCount` against the threshold alone, so a caller supplying a matching
|
|
278
|
+
* count satisfies it even against an empty registry. Contracts must not gate
|
|
279
|
+
* operations on `assertThresholdMet` while setup is incomplete. Lower the
|
|
280
|
+
* threshold with `_setThreshold`; `_changeThreshold` cannot lower one while the
|
|
281
|
+
* signer count is zero, because that would require a zero threshold.
|
|
282
|
+
*
|
|
266
283
|
* @notice Access control is NOT enforced here.
|
|
267
284
|
* The consuming contract must gate this behind its own
|
|
268
285
|
* authorization policy. Use `_changeThreshold` for
|
|
@@ -284,19 +301,6 @@ module Signer<T> {
|
|
|
284
301
|
|
|
285
302
|
// ─── Init guards ─────────────────────────────────────────────────────────
|
|
286
303
|
|
|
287
|
-
/**
|
|
288
|
-
* @description Asserts that the contract has been initialized, throwing an error if not.
|
|
289
|
-
*
|
|
290
|
-
* Requirements:
|
|
291
|
-
*
|
|
292
|
-
* - Contract must be initialized.
|
|
293
|
-
*
|
|
294
|
-
* @return {[]} - Empty tuple.
|
|
295
|
-
*/
|
|
296
|
-
circuit assertInitialized(): [] {
|
|
297
|
-
assert(_isInitialized, "Signer: contract not initialized");
|
|
298
|
-
}
|
|
299
|
-
|
|
300
304
|
/**
|
|
301
305
|
* @description Asserts that the contract has not been initialized, throwing an error if it has.
|
|
302
306
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.3.0-alpha.
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.3.0-alpha.2 (multisig/UnshieldedTreasury.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.23.0;
|
|
5
5
|
|
|
@@ -8,10 +8,24 @@ pragma language_version >= 0.23.0;
|
|
|
8
8
|
* @description Manages unshielded (transparent) token deposits and
|
|
9
9
|
* transfers for multisig governance contracts.
|
|
10
10
|
*
|
|
11
|
-
* Balances are tracked per token color in
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* Balances are tracked per token color in `_balances`, this module's own
|
|
12
|
+
* accounting and the authority for its overflow and sufficiency guards. The
|
|
13
|
+
* protocol balance remains authoritative for what the contract actually holds,
|
|
14
|
+
* and the ledger independently enforces conservation — a contract cannot send
|
|
15
|
+
* funds it does not have.
|
|
16
|
+
*
|
|
17
|
+
* @notice The guards deliberately do NOT consult `unshieldedBalance` or its
|
|
18
|
+
* comparison circuits. That balance is fixed to the value provided at the start
|
|
19
|
+
* of execution, so it cannot observe a credit or debit made earlier in the same
|
|
20
|
+
* transaction: guarding on it rejects a legitimate receive-then-spend and misses
|
|
21
|
+
* an intra-transaction overspend. Guarding on `_balances` (the same value the
|
|
22
|
+
* arithmetic mutates) is correct in both cases, and keeps the named asserts
|
|
23
|
+
* reachable for the conditions they describe.
|
|
24
|
+
*
|
|
25
|
+
* @notice Consumers must route every unshielded credit and debit through
|
|
26
|
+
* `_deposit` / `_send`. A flow that calls `receiveUnshielded` or
|
|
27
|
+
* `sendUnshielded` directly leaves `_balances` out of step with the protocol
|
|
28
|
+
* balance, and `getTokenBalance` will misreport.
|
|
15
29
|
*
|
|
16
30
|
* Underscore-prefixed circuits (_deposit, _send) have no access control
|
|
17
31
|
* enforcement. The consuming contract must gate these behind its own
|
|
@@ -35,7 +49,9 @@ module UnshieldedTreasury {
|
|
|
35
49
|
*
|
|
36
50
|
* Zero-value deposits are permitted. While currently a no-op
|
|
37
51
|
* economically, they may serve as signaling mechanisms when events
|
|
38
|
-
* are supported.
|
|
52
|
+
* are supported. On a color the treasury has not held, a zero-value
|
|
53
|
+
* operation creates a zero-valued `_balances` entry, which
|
|
54
|
+
* `getTokenBalance` reports the same as absence.
|
|
39
55
|
*
|
|
40
56
|
* @notice Access control is NOT enforced here.
|
|
41
57
|
* The consuming contract must gate this behind its own
|
|
@@ -51,21 +67,35 @@ module UnshieldedTreasury {
|
|
|
51
67
|
* @returns {[]} Empty tuple.
|
|
52
68
|
*/
|
|
53
69
|
export circuit _deposit(color: Bytes<32>, amount: Uint<128>): [] {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
70
|
+
// `Utils_UINT128_MAX() - amount` stays in-circuit. Passing it to a protocol
|
|
71
|
+
// balance comparison would hand that built-in an operand near 2^128, which
|
|
72
|
+
// fails at runtime with "failed to decode for built-in type u64" despite the
|
|
73
|
+
// circuit's declared `Uint<128>` parameter.
|
|
74
|
+
const bal = getTokenBalance(color);
|
|
75
|
+
assert(bal <= Utils_UINT128_MAX() - amount, "UnshieldedTreasury: overflow");
|
|
58
76
|
|
|
59
77
|
receiveUnshielded(disclose(color), disclose(amount));
|
|
60
78
|
|
|
61
|
-
const bal = getTokenBalance(color);
|
|
62
79
|
_balances.insert(disclose(color), disclose(bal + amount as Uint<128>));
|
|
63
80
|
}
|
|
64
81
|
|
|
65
82
|
// ─── Send ───────────────────────────────────────────────────────
|
|
66
83
|
|
|
67
84
|
/**
|
|
68
|
-
* @description Sends unshielded tokens from the treasury.
|
|
85
|
+
* @description Sends unshielded tokens from the treasury. Zero-amount sends
|
|
86
|
+
* are permitted and are value-preserving no-ops.
|
|
87
|
+
*
|
|
88
|
+
* @warning Recipients are not validated. An unspendable recipient (such as a
|
|
89
|
+
* zero address) is accepted and permanently removes the funds from the
|
|
90
|
+
* contract. This is not a sanctioned burn — no unshielded equivalent of
|
|
91
|
+
* `shieldedBurnAddress` is exposed to contracts — so treat recipient
|
|
92
|
+
* validation as the consuming contract's responsibility.
|
|
93
|
+
*
|
|
94
|
+
* @warning Sending to this contract's own address zeroes the tracked balance
|
|
95
|
+
* while the funds stay held and spendable at the protocol level, after which
|
|
96
|
+
* `_send` will refuse to touch them. Recovery is only possible outside this
|
|
97
|
+
* module: a consumer may reconcile the exported `_balances`, or spend the
|
|
98
|
+
* funds through its own `sendUnshielded` call.
|
|
69
99
|
*
|
|
70
100
|
* @notice Access control is NOT enforced here.
|
|
71
101
|
* The consuming contract must gate this behind its own
|
|
@@ -86,12 +116,9 @@ module UnshieldedTreasury {
|
|
|
86
116
|
color: Bytes<32>,
|
|
87
117
|
amount: Uint<128>
|
|
88
118
|
): [] {
|
|
89
|
-
assert(
|
|
90
|
-
unshieldedBalanceGte(disclose(color), disclose(amount)),
|
|
91
|
-
"UnshieldedTreasury: insufficient balance"
|
|
92
|
-
);
|
|
93
|
-
|
|
94
119
|
const bal = getTokenBalance(color);
|
|
120
|
+
assert(bal >= amount, "UnshieldedTreasury: insufficient balance");
|
|
121
|
+
|
|
95
122
|
_balances.insert(disclose(color), disclose(bal - amount as Uint<128>));
|
|
96
123
|
sendUnshielded(disclose(color), disclose(amount), disclose(recipient));
|
|
97
124
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.3.0-alpha.
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.3.0-alpha.2 (multisig/presets/ShieldedMultiSig.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.23.0;
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @module ShieldedMultiSig
|
|
8
|
-
* @description A shielded multisig preset composing `
|
|
8
|
+
* @description A shielded multisig preset composing `Signer`,
|
|
9
9
|
* `ProposalManager`, and `ShieldedTreasury`. Signers approve proposals that
|
|
10
10
|
* transfer shielded tokens out of the treasury once the configured threshold
|
|
11
11
|
* is met.
|
|
@@ -29,7 +29,7 @@ import CompactStandardLibrary;
|
|
|
29
29
|
|
|
30
30
|
import "../ProposalManager" prefix Proposal_;
|
|
31
31
|
import "../ShieldedTreasury" prefix Treasury_;
|
|
32
|
-
import "../
|
|
32
|
+
import "../Signer"<Either<ZswapCoinPublicKey, ContractAddress>> prefix Signer_;
|
|
33
33
|
|
|
34
34
|
// ─── State ───────────────────────────────────────────────────────────────
|
|
35
35
|
|
|
@@ -212,7 +212,7 @@ export circuit getSentTotal(color: Bytes<32>): Uint<128> {
|
|
|
212
212
|
return Treasury_getSentTotal(color);
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
//
|
|
215
|
+
// ISigner
|
|
216
216
|
|
|
217
217
|
export circuit getSignerCount(): Uint<8> {
|
|
218
218
|
return Signer_getSignerCount();
|