@openzeppelin/compact-contracts 0.2.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 +3 -3
- 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/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 +9 -5
- 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/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/utils/Utils.compact +86 -2
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.3.0-alpha (multisig/Signer.compact)
|
|
3
|
+
|
|
4
|
+
pragma language_version >= 0.23.0;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @module Signer<T>
|
|
8
|
+
* @description Manages signer registry, threshold enforcement, and signer
|
|
9
|
+
* validation for multisig governance contracts.
|
|
10
|
+
*
|
|
11
|
+
* Parameterized over the signer identity type `T`, allowing the consuming
|
|
12
|
+
* contract to choose the identity mechanism at import time. Common
|
|
13
|
+
* instantiations include:
|
|
14
|
+
*
|
|
15
|
+
* - `Bytes<32>` for commitment-based identity (e.g., hash of ECDSA public key)
|
|
16
|
+
* - `JubjubPoint` for Schnorr/MuSig aggregated key
|
|
17
|
+
*
|
|
18
|
+
* The Signer module does not resolve caller identity. It receives a validated
|
|
19
|
+
* caller from the contract layer and checks it against the registry.
|
|
20
|
+
* This separation allows the identity mechanism to change without
|
|
21
|
+
* modifying the module.
|
|
22
|
+
*
|
|
23
|
+
* The signer count and threshold are of type `Uint<8>`, limiting the
|
|
24
|
+
* maximum number of signers and threshold to 255. This is sufficient
|
|
25
|
+
* for any practical multisig use case. For large-scale governance
|
|
26
|
+
* requiring more signers, consider a Merkle tree-based variant.
|
|
27
|
+
*
|
|
28
|
+
* Multi-step signer reconfigurations (e.g., removing a signer and
|
|
29
|
+
* lowering the threshold) may produce intermediate states where the
|
|
30
|
+
* module's invariants temporarily hold but the contract's intended
|
|
31
|
+
* configuration is incomplete. This is a contract-layer concern.
|
|
32
|
+
* Contracts should either perform reconfigurations atomically in a
|
|
33
|
+
* single circuit or use a configuration nonce to invalidate proposals
|
|
34
|
+
* created under a stale signer set.
|
|
35
|
+
*
|
|
36
|
+
* Underscore-prefixed circuits (_addSigner, _removeSigner,
|
|
37
|
+
* _changeThreshold) have no access control enforcement. The consuming
|
|
38
|
+
* contract must gate these behind its own authorization policy.
|
|
39
|
+
*
|
|
40
|
+
* Contracts may handle their own initialization and this module
|
|
41
|
+
* supports custom flows. Thus, contracts may choose to not
|
|
42
|
+
* call `initialize` in the contract's constructor. Contracts MUST NOT
|
|
43
|
+
* call `initialize` outside of the constructor context because
|
|
44
|
+
* this could corrupt the signer set and threshold configuration.
|
|
45
|
+
*/
|
|
46
|
+
module Signer<T> {
|
|
47
|
+
import CompactStandardLibrary;
|
|
48
|
+
|
|
49
|
+
// ─── State ──────────────────────────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
export ledger _signers: Set<T>;
|
|
52
|
+
export ledger _signerCount: Uint<8>;
|
|
53
|
+
export ledger _threshold: Uint<8>;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @description Initialization flag, tracked per-module to avoid the compiler's
|
|
57
|
+
* shared transitive-dependency state bug (LFDT-Minokawa/compact#270). See the
|
|
58
|
+
* Initializable module for rationale.
|
|
59
|
+
*/
|
|
60
|
+
export ledger _isInitialized: Boolean;
|
|
61
|
+
|
|
62
|
+
// ─── Initialization ─────────────────────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @description Initializes the signer module with the given threshold
|
|
66
|
+
* and an initial set of signers.
|
|
67
|
+
* If used, it should only be called in the contract's constructor.
|
|
68
|
+
*
|
|
69
|
+
* @circuitInfo k=11, rows=1815
|
|
70
|
+
*
|
|
71
|
+
* Requirements:
|
|
72
|
+
*
|
|
73
|
+
* - If used, can only be called once (in the constructor).
|
|
74
|
+
* - `thresh` must not be zero.
|
|
75
|
+
* - `thresh` must not exceed the number of `signers`.
|
|
76
|
+
* - `signers` must not contain duplicates.
|
|
77
|
+
*
|
|
78
|
+
* @param {Vector<n, T>} signers - The initial signer set.
|
|
79
|
+
* @param {Uint<8>} thresh - The minimum number of approvals required.
|
|
80
|
+
* @returns {[]} Empty tuple.
|
|
81
|
+
*/
|
|
82
|
+
export circuit initialize<#n>(
|
|
83
|
+
signers: Vector<n, T>,
|
|
84
|
+
thresh: Uint<8>
|
|
85
|
+
): [] {
|
|
86
|
+
assertNotInitialized();
|
|
87
|
+
_isInitialized = true;
|
|
88
|
+
|
|
89
|
+
for (const signer of signers) {
|
|
90
|
+
_addSigner(signer);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
_changeThreshold(thresh);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ─── Guards ─────────────────────────────────────────────────────────────
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @description Asserts that the given caller is an active signer.
|
|
100
|
+
*
|
|
101
|
+
* @circuitInfo k=10, rows=585
|
|
102
|
+
*
|
|
103
|
+
* Requirements:
|
|
104
|
+
*
|
|
105
|
+
* - Contract must be initialized.
|
|
106
|
+
* - `caller` must be a member of the signers registry.
|
|
107
|
+
*
|
|
108
|
+
* @param {T} caller - The identity to validate.
|
|
109
|
+
* @returns {[]} Empty tuple.
|
|
110
|
+
*/
|
|
111
|
+
export circuit assertSigner(caller: T): [] {
|
|
112
|
+
assertInitialized();
|
|
113
|
+
assert(isSigner(caller), "Signer: not a signer");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @description Asserts that the given approval count meets the threshold.
|
|
118
|
+
*
|
|
119
|
+
* @circuitInfo k=9, rows=54
|
|
120
|
+
*
|
|
121
|
+
* Requirements:
|
|
122
|
+
*
|
|
123
|
+
* - Contract must be initialized.
|
|
124
|
+
* - Ledger threshold must be set (not be zero).
|
|
125
|
+
* - `approvalCount` must be >= threshold.
|
|
126
|
+
*
|
|
127
|
+
* @param {Uint<8>} approvalCount - The current number of approvals.
|
|
128
|
+
* @returns {[]} Empty tuple.
|
|
129
|
+
*/
|
|
130
|
+
export circuit assertThresholdMet(approvalCount: Uint<8>): [] {
|
|
131
|
+
assertInitialized();
|
|
132
|
+
assert(_threshold != 0, "Signer: threshold not set");
|
|
133
|
+
assert(approvalCount >= _threshold, "Signer: threshold not met");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ─── View ──────────────────────────────────────────────────────────
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* @description Returns the current signer count.
|
|
140
|
+
*
|
|
141
|
+
* @circuitInfo k=6, rows=26
|
|
142
|
+
*
|
|
143
|
+
* Requirements:
|
|
144
|
+
*
|
|
145
|
+
* - Contract must be initialized.
|
|
146
|
+
*
|
|
147
|
+
* @returns {Uint<8>} The number of active signers.
|
|
148
|
+
*/
|
|
149
|
+
export circuit getSignerCount(): Uint<8> {
|
|
150
|
+
assertInitialized();
|
|
151
|
+
return _signerCount;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* @description Returns the approval threshold.
|
|
156
|
+
*
|
|
157
|
+
* @circuitInfo k=6, rows=26
|
|
158
|
+
*
|
|
159
|
+
* Requirements:
|
|
160
|
+
*
|
|
161
|
+
* - Contract must be initialized.
|
|
162
|
+
*
|
|
163
|
+
* @returns {Uint<8>} The threshold.
|
|
164
|
+
*/
|
|
165
|
+
export circuit getThreshold(): Uint<8> {
|
|
166
|
+
assertInitialized();
|
|
167
|
+
return _threshold;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* @description Returns whether the given account is an active signer.
|
|
172
|
+
*
|
|
173
|
+
* @circuitInfo k=10, rows=605
|
|
174
|
+
*
|
|
175
|
+
* @param {T} account - The account to check.
|
|
176
|
+
* @returns {Boolean} True if the account is an active signer.
|
|
177
|
+
*/
|
|
178
|
+
export circuit isSigner(account: T): Boolean {
|
|
179
|
+
return _signers.member(disclose(account));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ─── Signer Management ─────────────────────────────────────────────────────
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* @description Adds a new signer to the registry.
|
|
186
|
+
*
|
|
187
|
+
* @notice Access control is NOT enforced here.
|
|
188
|
+
* The consuming contract must gate this behind its own
|
|
189
|
+
* authorization policy.
|
|
190
|
+
*
|
|
191
|
+
* @circuitInfo k=10, rows=598
|
|
192
|
+
*
|
|
193
|
+
* Requirements:
|
|
194
|
+
*
|
|
195
|
+
* - `signer` must not already be an active signer.
|
|
196
|
+
*
|
|
197
|
+
* @param {T} signer - The signer to add.
|
|
198
|
+
* @returns {[]} Empty tuple.
|
|
199
|
+
*/
|
|
200
|
+
export circuit _addSigner(signer: T): [] {
|
|
201
|
+
assert(
|
|
202
|
+
!isSigner(signer),
|
|
203
|
+
"Signer: signer already active"
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
_signers.insert(disclose(signer));
|
|
207
|
+
_signerCount = _signerCount + 1 as Uint<8>;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* @description Removes a signer from the registry.
|
|
212
|
+
*
|
|
213
|
+
* @notice Access control is NOT enforced here.
|
|
214
|
+
* The consuming contract must gate this behind its own
|
|
215
|
+
* authorization policy.
|
|
216
|
+
*
|
|
217
|
+
* @circuitInfo k=10, rows=612
|
|
218
|
+
*
|
|
219
|
+
* Requirements:
|
|
220
|
+
*
|
|
221
|
+
* - `signer` must be an active signer.
|
|
222
|
+
* - Removal must not drop signer count below threshold.
|
|
223
|
+
*
|
|
224
|
+
* @param {T} signer - The signer to remove.
|
|
225
|
+
* @returns {[]} Empty tuple.
|
|
226
|
+
*/
|
|
227
|
+
export circuit _removeSigner(signer: T): [] {
|
|
228
|
+
assert(isSigner(signer), "Signer: not a signer");
|
|
229
|
+
|
|
230
|
+
const newCount = _signerCount - 1 as Uint<8>;
|
|
231
|
+
assert(newCount >= _threshold, "Signer: removal would breach threshold");
|
|
232
|
+
|
|
233
|
+
_signers.remove(disclose(signer));
|
|
234
|
+
_signerCount = newCount;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* @description Updates the approval threshold.
|
|
239
|
+
*
|
|
240
|
+
* @notice Access control is NOT enforced here.
|
|
241
|
+
* The consuming contract must gate this behind its own
|
|
242
|
+
* authorization policy.
|
|
243
|
+
*
|
|
244
|
+
* @circuitInfo k=9, rows=53
|
|
245
|
+
*
|
|
246
|
+
* Requirements:
|
|
247
|
+
*
|
|
248
|
+
* - `newThreshold` must not be zero.
|
|
249
|
+
* - `newThreshold` must not exceed the current signer count.
|
|
250
|
+
*
|
|
251
|
+
* @param {Uint<8>} newThreshold - The new minimum number of approvals required.
|
|
252
|
+
* @returns {[]} Empty tuple.
|
|
253
|
+
*/
|
|
254
|
+
export circuit _changeThreshold(newThreshold: Uint<8>): [] {
|
|
255
|
+
assert(newThreshold <= _signerCount, "Signer: threshold exceeds signer count");
|
|
256
|
+
_setThreshold(newThreshold);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* @description Sets the approval threshold without checking
|
|
261
|
+
* against the current signer count.
|
|
262
|
+
*
|
|
263
|
+
* @warning This is intended for use during contract construction
|
|
264
|
+
* or custom setup flows where signers may not yet be registered.
|
|
265
|
+
*
|
|
266
|
+
* @notice Access control is NOT enforced here.
|
|
267
|
+
* The consuming contract must gate this behind its own
|
|
268
|
+
* authorization policy. Use `_changeThreshold` for
|
|
269
|
+
* operational threshold changes with signer count validation.
|
|
270
|
+
*
|
|
271
|
+
* @circuitInfo k=6, rows=40
|
|
272
|
+
*
|
|
273
|
+
* Requirements:
|
|
274
|
+
*
|
|
275
|
+
* - `newThreshold` must not be zero.
|
|
276
|
+
*
|
|
277
|
+
* @param {Uint<8>} newThreshold - The minimum number of approvals required.
|
|
278
|
+
* @returns {[]} Empty tuple.
|
|
279
|
+
*/
|
|
280
|
+
export circuit _setThreshold(newThreshold: Uint<8>): [] {
|
|
281
|
+
assert(newThreshold != 0, "Signer: threshold must not be zero");
|
|
282
|
+
_threshold = disclose(newThreshold);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// ─── Init guards ─────────────────────────────────────────────────────────
|
|
286
|
+
|
|
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
|
+
/**
|
|
301
|
+
* @description Asserts that the contract has not been initialized, throwing an error if it has.
|
|
302
|
+
*
|
|
303
|
+
* Requirements:
|
|
304
|
+
*
|
|
305
|
+
* - Contract must not be initialized.
|
|
306
|
+
*
|
|
307
|
+
* @return {[]} - Empty tuple.
|
|
308
|
+
*/
|
|
309
|
+
circuit assertNotInitialized(): [] {
|
|
310
|
+
assert(!_isInitialized, "Signer: contract already initialized");
|
|
311
|
+
}
|
|
312
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.3.0-alpha (multisig/SignerManager.compact)
|
|
3
|
+
|
|
4
|
+
pragma language_version >= 0.23.0;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @module SignerManager<T>
|
|
8
|
+
* @description Manages signer registry, threshold enforcement, and signer
|
|
9
|
+
* validation for multisig governance contracts.
|
|
10
|
+
*
|
|
11
|
+
* Parameterized over the signer identity type `T`, allowing the consuming
|
|
12
|
+
* contract to choose the identity mechanism at import time. Common
|
|
13
|
+
* instantiations include:
|
|
14
|
+
*
|
|
15
|
+
* - `Either<ZswapCoinPublicKey, ContractAddress>` for ownPublicKey()-based identity
|
|
16
|
+
* - `Bytes<32>` for commitment-based identity (e.g., hash of ECDSA public key)
|
|
17
|
+
* - `NativePoint` for Schnorr/MuSig aggregated key
|
|
18
|
+
*
|
|
19
|
+
* SignerManager does not resolve caller identity. It receives a validated
|
|
20
|
+
* caller from the contract layer and checks it against the registry.
|
|
21
|
+
* This separation allows the identity mechanism to change without
|
|
22
|
+
* modifying the module.
|
|
23
|
+
*
|
|
24
|
+
* Underscore-prefixed circuits (_addSigner, _removeSigner,
|
|
25
|
+
* _changeThreshold) have no access control enforcement. The consuming
|
|
26
|
+
* contract must gate these behind its own authorization policy.
|
|
27
|
+
*/
|
|
28
|
+
module SignerManager<T> {
|
|
29
|
+
import CompactStandardLibrary;
|
|
30
|
+
|
|
31
|
+
// ─── State ──────────────────────────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
export ledger _signers: Set<T>;
|
|
34
|
+
export ledger _signerCount: Uint<8>;
|
|
35
|
+
export ledger _threshold: Uint<8>;
|
|
36
|
+
|
|
37
|
+
// ─── Initialization ─────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @description Initializes the signer manager with the given threshold
|
|
41
|
+
* and an initial set of signers.
|
|
42
|
+
* Must be called in the contract's constructor.
|
|
43
|
+
*
|
|
44
|
+
* Requirements:
|
|
45
|
+
*
|
|
46
|
+
* - `thresh` must be greater than 0.
|
|
47
|
+
* - `signers` must not contain duplicates.
|
|
48
|
+
*
|
|
49
|
+
* @param {Vector<n, T>} signers - The initial signer set.
|
|
50
|
+
* @param {Uint<8>} thresh - The minimum number of approvals required.
|
|
51
|
+
*
|
|
52
|
+
* @returns {[]} Empty tuple.
|
|
53
|
+
*/
|
|
54
|
+
export circuit initialize<#n>(
|
|
55
|
+
signers: Vector<n, T>,
|
|
56
|
+
thresh: Uint<8>
|
|
57
|
+
): [] {
|
|
58
|
+
assert(thresh > 0, "SignerManager: threshold must be > 0");
|
|
59
|
+
_threshold = disclose(thresh);
|
|
60
|
+
|
|
61
|
+
for (const signer of signers) {
|
|
62
|
+
_addSigner(signer);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
assert(_signerCount >= thresh, "SignerManager: threshold exceeds signer count");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ─── Guards ─────────────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @description Asserts that the given caller is an active signer.
|
|
72
|
+
*
|
|
73
|
+
* Requirements:
|
|
74
|
+
*
|
|
75
|
+
* - `caller` must be a member of the signers registry.
|
|
76
|
+
*
|
|
77
|
+
* @param {T} caller - The identity to validate.
|
|
78
|
+
*
|
|
79
|
+
* @returns {[]} Empty tuple.
|
|
80
|
+
*/
|
|
81
|
+
export circuit assertSigner(caller: T): [] {
|
|
82
|
+
assert(isSigner(caller), "SignerManager: not a signer");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @description Asserts that the given approval count meets the threshold.
|
|
87
|
+
*
|
|
88
|
+
* Requirements:
|
|
89
|
+
*
|
|
90
|
+
* - `approvalCount` must be >= threshold.
|
|
91
|
+
*
|
|
92
|
+
* @param {Uint<8>} approvalCount - The current number of approvals.
|
|
93
|
+
*
|
|
94
|
+
* @returns {[]} Empty tuple.
|
|
95
|
+
*/
|
|
96
|
+
export circuit assertThresholdMet(approvalCount: Uint<8>): [] {
|
|
97
|
+
assert(approvalCount >= _threshold, "SignerManager: threshold not met");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ─── View ──────────────────────────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @description Returns the current signer count.
|
|
104
|
+
*
|
|
105
|
+
* @returns {Uint<8>} The number of active signers.
|
|
106
|
+
*/
|
|
107
|
+
export circuit getSignerCount(): Uint<8> {
|
|
108
|
+
return _signerCount;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @description Returns the approval threshold.
|
|
113
|
+
*
|
|
114
|
+
* @returns {Uint<8>} The threshold.
|
|
115
|
+
*/
|
|
116
|
+
export circuit getThreshold(): Uint<8> {
|
|
117
|
+
return _threshold;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @description Returns whether the given account is an active signer.
|
|
122
|
+
*
|
|
123
|
+
* @param {T} account - The account to check.
|
|
124
|
+
*
|
|
125
|
+
* @returns {Boolean} True if the account is an active signer.
|
|
126
|
+
*/
|
|
127
|
+
export circuit isSigner(account: T): Boolean {
|
|
128
|
+
return _signers.member(disclose(account));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ─── Signer Management ─────────────────────────────────────────────────────
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @description Adds a new signer to the registry.
|
|
135
|
+
*
|
|
136
|
+
* @notice Access control is NOT enforced here.
|
|
137
|
+
* The consuming contract must gate this behind its own
|
|
138
|
+
* authorization policy.
|
|
139
|
+
*
|
|
140
|
+
* Requirements:
|
|
141
|
+
*
|
|
142
|
+
* - `signer` must not already be an active signer.
|
|
143
|
+
*
|
|
144
|
+
* @param {T} signer - The signer to add.
|
|
145
|
+
*
|
|
146
|
+
* @returns {[]} Empty tuple.
|
|
147
|
+
*/
|
|
148
|
+
export circuit _addSigner(signer: T): [] {
|
|
149
|
+
assert(
|
|
150
|
+
!isSigner(signer),
|
|
151
|
+
"SignerManager: signer already active"
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
_signers.insert(disclose(signer));
|
|
155
|
+
_signerCount = _signerCount + 1 as Uint<8>;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @description Removes a signer from the registry.
|
|
160
|
+
*
|
|
161
|
+
* @notice Access control is NOT enforced here.
|
|
162
|
+
* The consuming contract must gate this behind its own
|
|
163
|
+
* authorization policy.
|
|
164
|
+
*
|
|
165
|
+
* Requirements:
|
|
166
|
+
*
|
|
167
|
+
* - `signer` must be an active signer.
|
|
168
|
+
* - Removal must not drop signer count below threshold.
|
|
169
|
+
*
|
|
170
|
+
* @param {T} signer - The signer to remove.
|
|
171
|
+
*
|
|
172
|
+
* @returns {[]} Empty tuple.
|
|
173
|
+
*/
|
|
174
|
+
export circuit _removeSigner(signer: T): [] {
|
|
175
|
+
assert(isSigner(signer), "SignerManager: not a signer");
|
|
176
|
+
|
|
177
|
+
const newCount = _signerCount - 1 as Uint<8>;
|
|
178
|
+
assert(newCount >= _threshold, "SignerManager: removal would breach threshold");
|
|
179
|
+
|
|
180
|
+
_signers.remove(disclose(signer));
|
|
181
|
+
_signerCount = newCount;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* @description Updates the approval threshold.
|
|
186
|
+
*
|
|
187
|
+
* @notice Access control is NOT enforced here.
|
|
188
|
+
* The consuming contract must gate this behind its own
|
|
189
|
+
* authorization policy.
|
|
190
|
+
*
|
|
191
|
+
* Requirements:
|
|
192
|
+
*
|
|
193
|
+
* - `newThreshold` must be greater than 0.
|
|
194
|
+
* - `newThreshold` must not exceed the current signer count.
|
|
195
|
+
*
|
|
196
|
+
* @param {Uint<8>} newThreshold - The new minimum number of approvals required.
|
|
197
|
+
*
|
|
198
|
+
* @returns {[]} Empty tuple.
|
|
199
|
+
*/
|
|
200
|
+
export circuit _changeThreshold(newThreshold: Uint<8>): [] {
|
|
201
|
+
assert(newThreshold > 0, "SignerManager: threshold must be > 0");
|
|
202
|
+
assert(newThreshold <= _signerCount, "SignerManager: threshold exceeds signer count");
|
|
203
|
+
_threshold = disclose(newThreshold);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.3.0-alpha (multisig/UnshieldedTreasury.compact)
|
|
3
|
+
|
|
4
|
+
pragma language_version >= 0.23.0;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @module UnshieldedTreasury
|
|
8
|
+
* @description Manages unshielded (transparent) token deposits and
|
|
9
|
+
* transfers for multisig governance contracts.
|
|
10
|
+
*
|
|
11
|
+
* Balances are tracked per token color in a single map. Protocol-level
|
|
12
|
+
* balance comparison circuits (`unshieldedBalanceLte`,
|
|
13
|
+
* `unshieldedBalanceGte`) are used for overflow and sufficiency checks,
|
|
14
|
+
* avoiding the exact-match problem of `unshieldedBalance`.
|
|
15
|
+
*
|
|
16
|
+
* Underscore-prefixed circuits (_deposit, _send) have no access control
|
|
17
|
+
* enforcement. The consuming contract must gate these behind its own
|
|
18
|
+
* authorization policy.
|
|
19
|
+
*/
|
|
20
|
+
module UnshieldedTreasury {
|
|
21
|
+
import CompactStandardLibrary;
|
|
22
|
+
import { UINT128_MAX } from "../utils/Utils" prefix Utils_;
|
|
23
|
+
|
|
24
|
+
// ─── State ──────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
export ledger _balances: Map<Bytes<32>, Uint<128>>;
|
|
27
|
+
|
|
28
|
+
// ─── Deposit ────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @description Receives unshielded tokens into the treasury.
|
|
32
|
+
*
|
|
33
|
+
* The token receive is executed at the protocol level first via
|
|
34
|
+
* `receiveUnshielded`. The balance map is then updated.
|
|
35
|
+
*
|
|
36
|
+
* Zero-value deposits are permitted. While currently a no-op
|
|
37
|
+
* economically, they may serve as signaling mechanisms when events
|
|
38
|
+
* are supported.
|
|
39
|
+
*
|
|
40
|
+
* @notice Access control is NOT enforced here.
|
|
41
|
+
* The consuming contract must gate this behind its own
|
|
42
|
+
* authorization policy.
|
|
43
|
+
*
|
|
44
|
+
* Requirements:
|
|
45
|
+
*
|
|
46
|
+
* - Deposit must not cause balance overflow.
|
|
47
|
+
*
|
|
48
|
+
* @param {Bytes<32>} color - The token type identifier.
|
|
49
|
+
* @param {Uint<128>} amount - The amount to deposit.
|
|
50
|
+
*
|
|
51
|
+
* @returns {[]} Empty tuple.
|
|
52
|
+
*/
|
|
53
|
+
export circuit _deposit(color: Bytes<32>, amount: Uint<128>): [] {
|
|
54
|
+
assert(
|
|
55
|
+
unshieldedBalanceLte(disclose(color), Utils_UINT128_MAX() - disclose(amount)),
|
|
56
|
+
"UnshieldedTreasury: overflow"
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
receiveUnshielded(disclose(color), disclose(amount));
|
|
60
|
+
|
|
61
|
+
const bal = getTokenBalance(color);
|
|
62
|
+
_balances.insert(disclose(color), disclose(bal + amount as Uint<128>));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ─── Send ───────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @description Sends unshielded tokens from the treasury.
|
|
69
|
+
*
|
|
70
|
+
* @notice Access control is NOT enforced here.
|
|
71
|
+
* The consuming contract must gate this behind its own
|
|
72
|
+
* authorization policy.
|
|
73
|
+
*
|
|
74
|
+
* Requirements:
|
|
75
|
+
*
|
|
76
|
+
* - The treasury must hold sufficient balance for the given token color.
|
|
77
|
+
*
|
|
78
|
+
* @param {Either<ContractAddress, UserAddress>} recipient - The recipient address.
|
|
79
|
+
* @param {Bytes<32>} color - The token type identifier.
|
|
80
|
+
* @param {Uint<128>} amount - The amount to send.
|
|
81
|
+
*
|
|
82
|
+
* @returns {[]} Empty tuple.
|
|
83
|
+
*/
|
|
84
|
+
export circuit _send(
|
|
85
|
+
recipient: Either<ContractAddress, UserAddress>,
|
|
86
|
+
color: Bytes<32>,
|
|
87
|
+
amount: Uint<128>
|
|
88
|
+
): [] {
|
|
89
|
+
assert(
|
|
90
|
+
unshieldedBalanceGte(disclose(color), disclose(amount)),
|
|
91
|
+
"UnshieldedTreasury: insufficient balance"
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const bal = getTokenBalance(color);
|
|
95
|
+
_balances.insert(disclose(color), disclose(bal - amount as Uint<128>));
|
|
96
|
+
sendUnshielded(disclose(color), disclose(amount), disclose(recipient));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ─── View ───────────────────────────────────────────────────────
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @description Returns the balance for a given token color.
|
|
103
|
+
*
|
|
104
|
+
* @param {Bytes<32>} color - The token type identifier.
|
|
105
|
+
*
|
|
106
|
+
* @returns {Uint<128>} The current balance.
|
|
107
|
+
*/
|
|
108
|
+
export circuit getTokenBalance(color: Bytes<32>): Uint<128> {
|
|
109
|
+
if (!_balances.member(disclose(color))) {
|
|
110
|
+
return 0;
|
|
111
|
+
}
|
|
112
|
+
return _balances.lookup(disclose(color));
|
|
113
|
+
}
|
|
114
|
+
}
|