@openzeppelin/compact-contracts 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -13
- package/access/AccessControl.compact +1 -1
- package/access/Ownable.compact +43 -10
- package/access/ShieldedAccessControl.compact +42 -9
- package/access/ZOwnablePK.compact +44 -9
- package/package.json +7 -5
- package/security/Initializable.compact +17 -1
- package/security/Pausable.compact +1 -1
- package/token/FungibleToken.compact +56 -22
- package/token/MultiToken.compact +47 -14
- package/token/NonFungibleToken.compact +61 -28
- package/utils/Utils.compact +1 -1
package/README.md
CHANGED
|
@@ -199,7 +199,6 @@ make sure to check out the [contribution guide](CONTRIBUTING.md) in advance.
|
|
|
199
199
|
>
|
|
200
200
|
> - [Node.js](https://nodejs.org/)
|
|
201
201
|
> - [Yarn](https://yarnpkg.com/getting-started/install)
|
|
202
|
-
> - [Turbo](https://turborepo.com/docs/getting-started/installation)
|
|
203
202
|
> - [Compact](https://docs.midnight.network/blog/compact-developer-tools)
|
|
204
203
|
|
|
205
204
|
### Set up the project
|
|
@@ -215,20 +214,20 @@ git clone git@github.com:OpenZeppelin/compact-contracts.git
|
|
|
215
214
|
```bash
|
|
216
215
|
nvm install && \
|
|
217
216
|
yarn && \
|
|
218
|
-
|
|
217
|
+
yarn compact
|
|
219
218
|
```
|
|
220
219
|
|
|
221
220
|
### Run tests
|
|
222
221
|
|
|
223
222
|
```bash
|
|
224
|
-
|
|
223
|
+
yarn test
|
|
225
224
|
```
|
|
226
225
|
|
|
227
226
|
### Check/apply Biome formatter
|
|
228
227
|
|
|
229
228
|
```bash
|
|
230
|
-
|
|
231
|
-
|
|
229
|
+
yarn fmt-and-lint
|
|
230
|
+
yarn fmt-and-lint:fix
|
|
232
231
|
```
|
|
233
232
|
|
|
234
233
|
### Advanced
|
|
@@ -236,8 +235,8 @@ turbo fmt-and-lint:fix
|
|
|
236
235
|
#### Targeted compilation
|
|
237
236
|
|
|
238
237
|
```bash
|
|
239
|
-
|
|
240
|
-
|
|
238
|
+
yarn compact:access
|
|
239
|
+
yarn compact:archive
|
|
241
240
|
...
|
|
242
241
|
```
|
|
243
242
|
|
|
@@ -246,11 +245,11 @@ turbo compact:archive
|
|
|
246
245
|
ZK key generation is slow and usually unnecessary during development.
|
|
247
246
|
|
|
248
247
|
```bash
|
|
249
|
-
# Individual module compilation (recommended for development)
|
|
250
|
-
turbo compact:token --filter=@openzeppelin/compact-contracts -- --skip-zk
|
|
251
|
-
|
|
252
248
|
# Full compilation with skip-zk (use environment variable)
|
|
253
|
-
SKIP_ZK=true
|
|
249
|
+
SKIP_ZK=true yarn compact
|
|
250
|
+
|
|
251
|
+
# Access compilation with skip-zk (this compiles security first as a dependency)
|
|
252
|
+
SKIP_ZK=true yarn compact:access
|
|
254
253
|
```
|
|
255
254
|
|
|
256
255
|
#### Clean environment
|
|
@@ -258,13 +257,13 @@ SKIP_ZK=true turbo compact
|
|
|
258
257
|
```bash
|
|
259
258
|
# WARNING!
|
|
260
259
|
# These are destructive commands
|
|
261
|
-
|
|
260
|
+
yarn clean
|
|
262
261
|
rm -rf .turbo/
|
|
263
262
|
```
|
|
264
263
|
|
|
265
264
|
### Troubleshooting
|
|
266
265
|
|
|
267
|
-
- **Issues with turbo's cache?** Try cleaning: `
|
|
266
|
+
- **Issues with turbo's cache?** Try cleaning: `yarn clean && rm -rf .turbo/`
|
|
268
267
|
- **Node version issues?** Use `nvm use` to switch to the correct version
|
|
269
268
|
|
|
270
269
|
## Security
|
package/access/Ownable.compact
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.2.0 (access/Ownable.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.21.0;
|
|
5
5
|
|
|
@@ -85,9 +85,15 @@ pragma language_version >= 0.21.0;
|
|
|
85
85
|
*/
|
|
86
86
|
module Ownable {
|
|
87
87
|
import CompactStandardLibrary;
|
|
88
|
-
import "../security/Initializable" prefix Initializable_;
|
|
89
88
|
import "../utils/Utils" prefix Utils_;
|
|
90
89
|
|
|
90
|
+
/**
|
|
91
|
+
* @description Initialization flag, tracked per-module to avoid the compiler's
|
|
92
|
+
* shared transitive-dependency state bug (LFDT-Minokawa/compact#270). See the
|
|
93
|
+
* Initializable module for rationale.
|
|
94
|
+
*/
|
|
95
|
+
export ledger _isInitialized: Boolean;
|
|
96
|
+
|
|
91
97
|
export ledger _owner: Either<Bytes<32>, ContractAddress>;
|
|
92
98
|
|
|
93
99
|
/**
|
|
@@ -117,11 +123,38 @@ module Ownable {
|
|
|
117
123
|
* @returns {[]} Empty tuple.
|
|
118
124
|
*/
|
|
119
125
|
export circuit initialize(initialOwner: Either<Bytes<32>, ContractAddress>): [] {
|
|
120
|
-
|
|
126
|
+
assertNotInitialized();
|
|
127
|
+
_isInitialized = true;
|
|
121
128
|
assert(!_isTargetZero(initialOwner), "Ownable: invalid initial owner");
|
|
122
129
|
_transferOwnership(initialOwner);
|
|
123
130
|
}
|
|
124
131
|
|
|
132
|
+
/**
|
|
133
|
+
* @description Asserts that the contract has been initialized, throwing an error if not.
|
|
134
|
+
*
|
|
135
|
+
* Requirements:
|
|
136
|
+
*
|
|
137
|
+
* - Contract must be initialized.
|
|
138
|
+
*
|
|
139
|
+
* @return {[]} - Empty tuple.
|
|
140
|
+
*/
|
|
141
|
+
circuit assertInitialized(): [] {
|
|
142
|
+
assert(_isInitialized, "Ownable: contract not initialized");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @description Asserts that the contract has not been initialized, throwing an error if it has.
|
|
147
|
+
*
|
|
148
|
+
* Requirements:
|
|
149
|
+
*
|
|
150
|
+
* - Contract must not be initialized.
|
|
151
|
+
*
|
|
152
|
+
* @return {[]} - Empty tuple.
|
|
153
|
+
*/
|
|
154
|
+
circuit assertNotInitialized(): [] {
|
|
155
|
+
assert(!_isInitialized, "Ownable: contract already initialized");
|
|
156
|
+
}
|
|
157
|
+
|
|
125
158
|
/**
|
|
126
159
|
* @description Returns the current contract owner.
|
|
127
160
|
*
|
|
@@ -134,7 +167,7 @@ module Ownable {
|
|
|
134
167
|
* @returns {Either<Bytes<32>, ContractAddress>} - The contract owner.
|
|
135
168
|
*/
|
|
136
169
|
export circuit owner(): Either<Bytes<32>, ContractAddress> {
|
|
137
|
-
|
|
170
|
+
assertInitialized();
|
|
138
171
|
return _owner;
|
|
139
172
|
}
|
|
140
173
|
|
|
@@ -158,7 +191,7 @@ module Ownable {
|
|
|
158
191
|
* @returns {[]} Empty tuple.
|
|
159
192
|
*/
|
|
160
193
|
export circuit transferOwnership(newOwner: Either<Bytes<32>, ContractAddress>): [] {
|
|
161
|
-
|
|
194
|
+
assertInitialized();
|
|
162
195
|
const isContractAddr = !newOwner.is_left;
|
|
163
196
|
assert(!isContractAddr, "Ownable: unsafe ownership transfer");
|
|
164
197
|
_unsafeTransferOwnership(newOwner);
|
|
@@ -184,7 +217,7 @@ module Ownable {
|
|
|
184
217
|
* @returns {[]} Empty tuple.
|
|
185
218
|
*/
|
|
186
219
|
export circuit _unsafeTransferOwnership(newOwner: Either<Bytes<32>, ContractAddress>): [] {
|
|
187
|
-
|
|
220
|
+
assertInitialized();
|
|
188
221
|
assertOnlyOwner();
|
|
189
222
|
assert(!_isTargetZero(newOwner), "Ownable: invalid new owner");
|
|
190
223
|
_unsafeUncheckedTransferOwnership(newOwner);
|
|
@@ -205,7 +238,7 @@ module Ownable {
|
|
|
205
238
|
* @returns {[]} Empty tuple.
|
|
206
239
|
*/
|
|
207
240
|
export circuit renounceOwnership(): [] {
|
|
208
|
-
|
|
241
|
+
assertInitialized();
|
|
209
242
|
assertOnlyOwner();
|
|
210
243
|
|
|
211
244
|
// We use `left` here because `assertOnlyOwner` would return a misleading
|
|
@@ -233,7 +266,7 @@ module Ownable {
|
|
|
233
266
|
* @returns {[]} Empty tuple.
|
|
234
267
|
*/
|
|
235
268
|
export circuit assertOnlyOwner(): [] {
|
|
236
|
-
|
|
269
|
+
assertInitialized();
|
|
237
270
|
if (_owner.is_left) {
|
|
238
271
|
assert(_computeAccountId() == _owner.left, "Ownable: caller is not the owner");
|
|
239
272
|
} else {
|
|
@@ -260,7 +293,7 @@ module Ownable {
|
|
|
260
293
|
* @returns {[]} Empty tuple.
|
|
261
294
|
*/
|
|
262
295
|
export circuit _transferOwnership(newOwner: Either<Bytes<32>, ContractAddress>): [] {
|
|
263
|
-
|
|
296
|
+
assertInitialized();
|
|
264
297
|
const isContractAddr = !newOwner.is_left;
|
|
265
298
|
assert(!isContractAddr, "Ownable: unsafe ownership transfer");
|
|
266
299
|
_unsafeUncheckedTransferOwnership(newOwner);
|
|
@@ -286,7 +319,7 @@ module Ownable {
|
|
|
286
319
|
export circuit _unsafeUncheckedTransferOwnership(
|
|
287
320
|
newOwner: Either<Bytes<32>, ContractAddress>
|
|
288
321
|
): [] {
|
|
289
|
-
|
|
322
|
+
assertInitialized();
|
|
290
323
|
const canonAcct = Utils_canonicalize<Bytes<32>, ContractAddress>(newOwner);
|
|
291
324
|
_owner = disclose(canonAcct);
|
|
292
325
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.2.0 (access/ShieldedAccessControl.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.21.0;
|
|
5
5
|
|
|
@@ -168,7 +168,6 @@ pragma language_version >= 0.21.0;
|
|
|
168
168
|
module ShieldedAccessControl {
|
|
169
169
|
import CompactStandardLibrary;
|
|
170
170
|
import "../utils/Utils" prefix Utils_;
|
|
171
|
-
import "../security/Initializable" prefix Initializable_;
|
|
172
171
|
|
|
173
172
|
export enum UpdateType {
|
|
174
173
|
Grant,
|
|
@@ -181,6 +180,13 @@ module ShieldedAccessControl {
|
|
|
181
180
|
export new type AccountIdentifier = Bytes<32>;
|
|
182
181
|
export new type RoleNullifier = Bytes<32>;
|
|
183
182
|
|
|
183
|
+
/**
|
|
184
|
+
* @description Initialization flag, tracked per-module to avoid the compiler's
|
|
185
|
+
* shared transitive-dependency state bug (LFDT-Minokawa/compact#270). See the
|
|
186
|
+
* Initializable module for rationale.
|
|
187
|
+
*/
|
|
188
|
+
export ledger _isInitialized: Boolean;
|
|
189
|
+
|
|
184
190
|
/**
|
|
185
191
|
* @ledger _operatorRoles
|
|
186
192
|
* @description A Merkle tree of role commitments stored as SHA256(role | accountId | instanceSalt | commitmentDomain)
|
|
@@ -259,11 +265,38 @@ module ShieldedAccessControl {
|
|
|
259
265
|
*/
|
|
260
266
|
export circuit initialize(instanceSalt: Bytes<32>): [] {
|
|
261
267
|
assert(instanceSalt != default<Bytes<32>>, "ShieldedAccessControl: Instance salt must not be 0");
|
|
262
|
-
|
|
268
|
+
assertNotInitialized();
|
|
269
|
+
_isInitialized = true;
|
|
263
270
|
|
|
264
271
|
_instanceSalt = disclose(instanceSalt);
|
|
265
272
|
}
|
|
266
273
|
|
|
274
|
+
/**
|
|
275
|
+
* @description Asserts that the contract has been initialized, throwing an error if not.
|
|
276
|
+
*
|
|
277
|
+
* Requirements:
|
|
278
|
+
*
|
|
279
|
+
* - Contract must be initialized.
|
|
280
|
+
*
|
|
281
|
+
* @return {[]} - Empty tuple.
|
|
282
|
+
*/
|
|
283
|
+
circuit assertInitialized(): [] {
|
|
284
|
+
assert(_isInitialized, "ShieldedAccessControl: contract not initialized");
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* @description Asserts that the contract has not been initialized, throwing an error if it has.
|
|
289
|
+
*
|
|
290
|
+
* Requirements:
|
|
291
|
+
*
|
|
292
|
+
* - Contract must not be initialized.
|
|
293
|
+
*
|
|
294
|
+
* @return {[]} - Empty tuple.
|
|
295
|
+
*/
|
|
296
|
+
circuit assertNotInitialized(): [] {
|
|
297
|
+
assert(!_isInitialized, "ShieldedAccessControl: contract already initialized");
|
|
298
|
+
}
|
|
299
|
+
|
|
267
300
|
/**
|
|
268
301
|
* @description The default admin role for all roles. Only accounts with this role will be able to grant or revoke other roles
|
|
269
302
|
* unless custom admin roles are created.
|
|
@@ -295,7 +328,7 @@ module ShieldedAccessControl {
|
|
|
295
328
|
* @return {[]} - Empty tuple.
|
|
296
329
|
*/
|
|
297
330
|
export circuit assertOnlyRole(role: RoleIdentifier): [] {
|
|
298
|
-
|
|
331
|
+
assertInitialized();
|
|
299
332
|
assert(_uncheckedCanProveRole(role), "ShieldedAccessControl: unauthorized account");
|
|
300
333
|
}
|
|
301
334
|
|
|
@@ -320,7 +353,7 @@ module ShieldedAccessControl {
|
|
|
320
353
|
* @return {Boolean} - A boolean determining if a caller successfully proved ownership of `role`
|
|
321
354
|
*/
|
|
322
355
|
export circuit canProveRole(role: RoleIdentifier): Boolean {
|
|
323
|
-
|
|
356
|
+
assertInitialized();
|
|
324
357
|
return _uncheckedCanProveRole(role);
|
|
325
358
|
}
|
|
326
359
|
|
|
@@ -417,7 +450,7 @@ module ShieldedAccessControl {
|
|
|
417
450
|
* @return {[]} - Empty tuple.
|
|
418
451
|
*/
|
|
419
452
|
export circuit _grantRole(role: RoleIdentifier, accountId: AccountIdentifier): [] {
|
|
420
|
-
|
|
453
|
+
assertInitialized();
|
|
421
454
|
_updateRole(role, accountId, UpdateType.Grant);
|
|
422
455
|
}
|
|
423
456
|
|
|
@@ -449,7 +482,7 @@ module ShieldedAccessControl {
|
|
|
449
482
|
* @return {[]} - Empty tuple.
|
|
450
483
|
*/
|
|
451
484
|
export circuit renounceRole(role: RoleIdentifier, accountIdConfirmation: AccountIdentifier): [] {
|
|
452
|
-
|
|
485
|
+
assertInitialized();
|
|
453
486
|
|
|
454
487
|
assert(accountIdConfirmation == _computeAccountId(),
|
|
455
488
|
"ShieldedAccessControl: bad confirmation"
|
|
@@ -515,7 +548,7 @@ module ShieldedAccessControl {
|
|
|
515
548
|
* @return {[]} - Empty tuple.
|
|
516
549
|
*/
|
|
517
550
|
export circuit _revokeRole(role: RoleIdentifier, accountId: AccountIdentifier): [] {
|
|
518
|
-
|
|
551
|
+
assertInitialized();
|
|
519
552
|
_updateRole(role, accountId, UpdateType.Revoke);
|
|
520
553
|
}
|
|
521
554
|
|
|
@@ -599,7 +632,7 @@ module ShieldedAccessControl {
|
|
|
599
632
|
* @return {[]} - Empty tuple.
|
|
600
633
|
*/
|
|
601
634
|
export circuit _setRoleAdmin(role: RoleIdentifier, adminId: RoleIdentifier): [] {
|
|
602
|
-
|
|
635
|
+
assertInitialized();
|
|
603
636
|
_adminRoles.insert(disclose(role), disclose(adminId));
|
|
604
637
|
}
|
|
605
638
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.2.0 (access/ZOwnablePK.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.21.0;
|
|
5
5
|
|
|
@@ -45,7 +45,13 @@ pragma language_version >= 0.21.0;
|
|
|
45
45
|
*/
|
|
46
46
|
module ZOwnablePK {
|
|
47
47
|
import CompactStandardLibrary;
|
|
48
|
-
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @description Initialization flag, tracked per-module to avoid the compiler's
|
|
51
|
+
* shared transitive-dependency state bug (LFDT-Minokawa/compact#270). See the
|
|
52
|
+
* Initializable module for rationale.
|
|
53
|
+
*/
|
|
54
|
+
export ledger _isInitialized: Boolean;
|
|
49
55
|
|
|
50
56
|
/**
|
|
51
57
|
* @ledger _ownerCommitment
|
|
@@ -56,6 +62,7 @@ module ZOwnablePK {
|
|
|
56
62
|
* A commitment of `default<Bytes<32>>` (i.e., zero) indicates the contract is unowned.
|
|
57
63
|
*/
|
|
58
64
|
export ledger _ownerCommitment: Bytes<32>;
|
|
65
|
+
|
|
59
66
|
/**
|
|
60
67
|
* @ledger _counter
|
|
61
68
|
* @description Internal transfer counter used to prevent commitment reuse.
|
|
@@ -64,6 +71,7 @@ module ZOwnablePK {
|
|
|
64
71
|
* `instanceSalt` to compute unique owner commitments over time.
|
|
65
72
|
*/
|
|
66
73
|
export ledger _counter: Counter;
|
|
74
|
+
|
|
67
75
|
/**
|
|
68
76
|
* @sealed @ledger _instanceSalt
|
|
69
77
|
* @description A per-instance value provided at initialization used to namespace
|
|
@@ -104,13 +112,40 @@ module ZOwnablePK {
|
|
|
104
112
|
* @returns {[]} Empty tuple.
|
|
105
113
|
*/
|
|
106
114
|
export circuit initialize(ownerId: Bytes<32>, instanceSalt: Bytes<32>): [] {
|
|
107
|
-
|
|
115
|
+
assertNotInitialized();
|
|
116
|
+
_isInitialized = true;
|
|
108
117
|
|
|
109
118
|
assert(ownerId != default<Bytes<32>>, "ZOwnablePK: invalid id");
|
|
110
119
|
_instanceSalt = disclose(instanceSalt);
|
|
111
120
|
_transferOwnership(ownerId);
|
|
112
121
|
}
|
|
113
122
|
|
|
123
|
+
/**
|
|
124
|
+
* @description Asserts that the contract has been initialized, throwing an error if not.
|
|
125
|
+
*
|
|
126
|
+
* Requirements:
|
|
127
|
+
*
|
|
128
|
+
* - Contract must be initialized.
|
|
129
|
+
*
|
|
130
|
+
* @return {[]} - Empty tuple.
|
|
131
|
+
*/
|
|
132
|
+
circuit assertInitialized(): [] {
|
|
133
|
+
assert(_isInitialized, "ZOwnablePK: contract not initialized");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @description Asserts that the contract has not been initialized, throwing an error if it has.
|
|
138
|
+
*
|
|
139
|
+
* Requirements:
|
|
140
|
+
*
|
|
141
|
+
* - Contract must not be initialized.
|
|
142
|
+
*
|
|
143
|
+
* @return {[]} - Empty tuple.
|
|
144
|
+
*/
|
|
145
|
+
circuit assertNotInitialized(): [] {
|
|
146
|
+
assert(!_isInitialized, "ZOwnablePK: contract already initialized");
|
|
147
|
+
}
|
|
148
|
+
|
|
114
149
|
/**
|
|
115
150
|
* @description Returns the current commitment representing the contract owner.
|
|
116
151
|
* The full commitment is: `SHA256(SHA256(pk, nonce), instanceSalt, counter, domain)`.
|
|
@@ -124,7 +159,7 @@ module ZOwnablePK {
|
|
|
124
159
|
* @returns {Bytes<32>} The current owner's commitment.
|
|
125
160
|
*/
|
|
126
161
|
export circuit owner(): Bytes<32> {
|
|
127
|
-
|
|
162
|
+
assertInitialized();
|
|
128
163
|
return _ownerCommitment;
|
|
129
164
|
}
|
|
130
165
|
|
|
@@ -144,7 +179,7 @@ module ZOwnablePK {
|
|
|
144
179
|
* @returns {[]} Empty tuple.
|
|
145
180
|
*/
|
|
146
181
|
export circuit transferOwnership(newOwnerId: Bytes<32>): [] {
|
|
147
|
-
|
|
182
|
+
assertInitialized();
|
|
148
183
|
|
|
149
184
|
assertOnlyOwner();
|
|
150
185
|
assert(newOwnerId != default<Bytes<32>>, "ZOwnablePK: invalid id");
|
|
@@ -166,7 +201,7 @@ module ZOwnablePK {
|
|
|
166
201
|
* @returns {[]} Empty tuple.
|
|
167
202
|
*/
|
|
168
203
|
export circuit renounceOwnership(): [] {
|
|
169
|
-
|
|
204
|
+
assertInitialized();
|
|
170
205
|
|
|
171
206
|
assertOnlyOwner();
|
|
172
207
|
_ownerCommitment.resetToDefault();
|
|
@@ -188,7 +223,7 @@ module ZOwnablePK {
|
|
|
188
223
|
* @returns {[]} Empty tuple.
|
|
189
224
|
*/
|
|
190
225
|
export circuit assertOnlyOwner(): [] {
|
|
191
|
-
|
|
226
|
+
assertInitialized();
|
|
192
227
|
|
|
193
228
|
const nonce = wit_secretNonce();
|
|
194
229
|
const callerAsEither =
|
|
@@ -234,7 +269,7 @@ module ZOwnablePK {
|
|
|
234
269
|
* @returns {Bytes<32>} The commitment derived from `id` and `counter`.
|
|
235
270
|
*/
|
|
236
271
|
export circuit _computeOwnerCommitment(id: Bytes<32>, counter: Uint<64>,): Bytes<32> {
|
|
237
|
-
|
|
272
|
+
assertInitialized();
|
|
238
273
|
return persistentHash<Vector<4, Bytes<32>>>(
|
|
239
274
|
[id, _instanceSalt, counter as Field as Bytes<32>, pad(32, "ZOwnablePK:shield:")]
|
|
240
275
|
);
|
|
@@ -296,7 +331,7 @@ module ZOwnablePK {
|
|
|
296
331
|
* @returns {[]} Empty tuple.
|
|
297
332
|
*/
|
|
298
333
|
export circuit _transferOwnership(newOwnerId: Bytes<32>): [] {
|
|
299
|
-
|
|
334
|
+
assertInitialized();
|
|
300
335
|
|
|
301
336
|
_counter.increment(1);
|
|
302
337
|
_ownerCommitment = disclose(_computeOwnerCommitment(newOwnerId, _counter));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openzeppelin/compact-contracts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "OpenZeppelin Compact contract library",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"openzeppelin",
|
|
@@ -33,6 +33,8 @@
|
|
|
33
33
|
"build": "compact-builder --hierarchical --out dist --clean-dist --exclude '*/archive/*' --exclude 'Mock*' --exclude '*.mock.compact' --copy package.json --copy ../README.md && find dist -type d -empty -delete",
|
|
34
34
|
"test": "SKIP_ZK=true yarn run compact && vitest run",
|
|
35
35
|
"test:coverage": "SKIP_ZK=true yarn run compact && vitest run --coverage",
|
|
36
|
+
"compact:integration": "SKIP_ZK=true compact compile test/integration/_mocks/SharedInitCollision.compact artifacts/SharedInitCollision && SKIP_ZK=true compact compile test/integration/_mocks/ComposedTokens.compact artifacts/ComposedTokens",
|
|
37
|
+
"test:integration": "yarn run compact:integration && vitest run --config vitest.integration.config.ts",
|
|
36
38
|
"types": "tsc -p tsconfig.json --noEmit",
|
|
37
39
|
"clean": "git clean -fXd"
|
|
38
40
|
},
|
|
@@ -45,10 +47,10 @@
|
|
|
45
47
|
"devDependencies": {
|
|
46
48
|
"@openzeppelin/compact-simulator": "^0.0.1",
|
|
47
49
|
"@tsconfig/node24": "^24.0.4",
|
|
48
|
-
"@types/node": "25.
|
|
49
|
-
"@vitest/coverage-v8": "^4.1.
|
|
50
|
+
"@types/node": "25.9.1",
|
|
51
|
+
"@vitest/coverage-v8": "^4.1.8",
|
|
50
52
|
"ts-node": "^10.9.2",
|
|
51
|
-
"typescript": "^
|
|
52
|
-
"vitest": "^4.1.
|
|
53
|
+
"typescript": "^6.0.3",
|
|
54
|
+
"vitest": "^4.1.8"
|
|
53
55
|
}
|
|
54
56
|
}
|
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.2.0 (security/Initializable.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.21.0;
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @module Initializable
|
|
8
8
|
* @description Initializable provides a simple mechanism that mimics the functionality of a constructor.
|
|
9
|
+
*
|
|
10
|
+
* @notice ⚠️ TECHNICAL WARNING: only use this module at the top contract layer, and
|
|
11
|
+
* import it into at most one module per contract.
|
|
12
|
+
*
|
|
13
|
+
* The Compact compiler deduplicates a shared transitive dependency's ledger state
|
|
14
|
+
* inconsistently depending on directory layout
|
|
15
|
+
* (https://github.com/LFDT-Minokawa/compact/issues/270, reproduced on compiler
|
|
16
|
+
* 0.29.0–0.31.0). When two modules in the SAME directory both import this module,
|
|
17
|
+
* their `_isInitialized` flags collapse into a SINGLE shared ledger slot: calling
|
|
18
|
+
* `initialize()` on one marks the other as initialized, and the other can never be
|
|
19
|
+
* initialized independently. Modules in different directories get separate slots.
|
|
20
|
+
* The result is unpredictable and a security hazard for composed contracts.
|
|
21
|
+
*
|
|
22
|
+
* For this reason the OpenZeppelin modules no longer use this module: each tracks
|
|
23
|
+
* its own `_isInitialized` ledger flag inline. This module is kept for direct,
|
|
24
|
+
* single-import use and can be reintroduced across modules once #270 is resolved.
|
|
9
25
|
*/
|
|
10
26
|
module Initializable {
|
|
11
27
|
import CompactStandardLibrary;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.2.0 (token/FungibleToken.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.21.0;
|
|
5
5
|
|
|
@@ -64,9 +64,15 @@ pragma language_version >= 0.21.0;
|
|
|
64
64
|
*/
|
|
65
65
|
module FungibleToken {
|
|
66
66
|
import CompactStandardLibrary;
|
|
67
|
-
import "../security/Initializable" prefix Initializable_;
|
|
68
67
|
import "../utils/Utils" prefix Utils_;
|
|
69
68
|
|
|
69
|
+
/**
|
|
70
|
+
* @description Initialization flag, tracked per-module to avoid the compiler's
|
|
71
|
+
* shared transitive-dependency state bug (LFDT-Minokawa/compact#270). See the
|
|
72
|
+
* Initializable module for rationale.
|
|
73
|
+
*/
|
|
74
|
+
export ledger _isInitialized: Boolean;
|
|
75
|
+
|
|
70
76
|
/**
|
|
71
77
|
* @description Mapping from account addresses to their token balances.
|
|
72
78
|
* @type {Either<Bytes<32>, ContractAddress>} account - The account address.
|
|
@@ -75,6 +81,7 @@ module FungibleToken {
|
|
|
75
81
|
* @type {Map<Either<Bytes<32>, ContractAddress>, Uint<128>>} _balances
|
|
76
82
|
*/
|
|
77
83
|
export ledger _balances: Map<Either<Bytes<32>, ContractAddress>, Uint<128>>;
|
|
84
|
+
|
|
78
85
|
/**
|
|
79
86
|
* @description Mapping from owner accounts to spender accounts and their allowances.
|
|
80
87
|
* @type {Either<Bytes<32>, ContractAddress>} account - The owner account address.
|
|
@@ -134,12 +141,39 @@ module FungibleToken {
|
|
|
134
141
|
symbol_: Opaque<"string">,
|
|
135
142
|
decimals_: Uint<8>
|
|
136
143
|
): [] {
|
|
137
|
-
|
|
144
|
+
assertNotInitialized();
|
|
145
|
+
_isInitialized = true;
|
|
138
146
|
_name = disclose(name_);
|
|
139
147
|
_symbol = disclose(symbol_);
|
|
140
148
|
_decimals = disclose(decimals_);
|
|
141
149
|
}
|
|
142
150
|
|
|
151
|
+
/**
|
|
152
|
+
* @description Asserts that the contract has been initialized, throwing an error if not.
|
|
153
|
+
*
|
|
154
|
+
* Requirements:
|
|
155
|
+
*
|
|
156
|
+
* - Contract must be initialized.
|
|
157
|
+
*
|
|
158
|
+
* @return {[]} - Empty tuple.
|
|
159
|
+
*/
|
|
160
|
+
circuit assertInitialized(): [] {
|
|
161
|
+
assert(_isInitialized, "FungibleToken: contract not initialized");
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* @description Asserts that the contract has not been initialized, throwing an error if it has.
|
|
166
|
+
*
|
|
167
|
+
* Requirements:
|
|
168
|
+
*
|
|
169
|
+
* - Contract must not be initialized.
|
|
170
|
+
*
|
|
171
|
+
* @return {[]} - Empty tuple.
|
|
172
|
+
*/
|
|
173
|
+
circuit assertNotInitialized(): [] {
|
|
174
|
+
assert(!_isInitialized, "FungibleToken: contract already initialized");
|
|
175
|
+
}
|
|
176
|
+
|
|
143
177
|
/**
|
|
144
178
|
* @description Returns the token name.
|
|
145
179
|
*
|
|
@@ -152,7 +186,7 @@ module FungibleToken {
|
|
|
152
186
|
* @return {Opaque<"string">} - The token name.
|
|
153
187
|
*/
|
|
154
188
|
export circuit name(): Opaque<"string"> {
|
|
155
|
-
|
|
189
|
+
assertInitialized();
|
|
156
190
|
return _name;
|
|
157
191
|
}
|
|
158
192
|
|
|
@@ -168,7 +202,7 @@ module FungibleToken {
|
|
|
168
202
|
* @return {Opaque<"string">} - The token name.
|
|
169
203
|
*/
|
|
170
204
|
export circuit symbol(): Opaque<"string"> {
|
|
171
|
-
|
|
205
|
+
assertInitialized();
|
|
172
206
|
return _symbol;
|
|
173
207
|
}
|
|
174
208
|
|
|
@@ -184,7 +218,7 @@ module FungibleToken {
|
|
|
184
218
|
* @return {Uint<8>} - The account's token balance.
|
|
185
219
|
*/
|
|
186
220
|
export circuit decimals(): Uint<8> {
|
|
187
|
-
|
|
221
|
+
assertInitialized();
|
|
188
222
|
return _decimals;
|
|
189
223
|
}
|
|
190
224
|
|
|
@@ -200,7 +234,7 @@ module FungibleToken {
|
|
|
200
234
|
* @return {Uint<128>} - The total supply of tokens.
|
|
201
235
|
*/
|
|
202
236
|
export circuit totalSupply(): Uint<128> {
|
|
203
|
-
|
|
237
|
+
assertInitialized();
|
|
204
238
|
return _totalSupply;
|
|
205
239
|
}
|
|
206
240
|
|
|
@@ -219,7 +253,7 @@ module FungibleToken {
|
|
|
219
253
|
* @return {Uint<128>} - The account's token balance.
|
|
220
254
|
*/
|
|
221
255
|
export circuit balanceOf(account: Either<Bytes<32>, ContractAddress>): Uint<128> {
|
|
222
|
-
|
|
256
|
+
assertInitialized();
|
|
223
257
|
const canonAcct = Utils_canonicalize<Bytes<32>, ContractAddress>(account);
|
|
224
258
|
|
|
225
259
|
if (!_balances.member(disclose(canonAcct))) {
|
|
@@ -253,7 +287,7 @@ module FungibleToken {
|
|
|
253
287
|
to: Either<Bytes<32>, ContractAddress>,
|
|
254
288
|
value: Uint<128>
|
|
255
289
|
): Boolean {
|
|
256
|
-
|
|
290
|
+
assertInitialized();
|
|
257
291
|
const isContractAddr = !to.is_left;
|
|
258
292
|
assert(!isContractAddr, "FungibleToken: unsafe transfer");
|
|
259
293
|
|
|
@@ -283,7 +317,7 @@ module FungibleToken {
|
|
|
283
317
|
to: Either<Bytes<32>, ContractAddress>,
|
|
284
318
|
value: Uint<128>
|
|
285
319
|
): Boolean {
|
|
286
|
-
|
|
320
|
+
assertInitialized();
|
|
287
321
|
const owner = left<Bytes<32>, ContractAddress>(_computeAccountId());
|
|
288
322
|
_unsafeUncheckedTransfer(owner, to, value);
|
|
289
323
|
return true;
|
|
@@ -309,7 +343,7 @@ module FungibleToken {
|
|
|
309
343
|
owner: Either<Bytes<32>, ContractAddress>,
|
|
310
344
|
spender: Either<Bytes<32>, ContractAddress>
|
|
311
345
|
): Uint<128> {
|
|
312
|
-
|
|
346
|
+
assertInitialized();
|
|
313
347
|
const canonOwner = Utils_canonicalize<Bytes<32>, ContractAddress>(owner);
|
|
314
348
|
const canonSpender = Utils_canonicalize<Bytes<32>, ContractAddress>(spender);
|
|
315
349
|
|
|
@@ -337,7 +371,7 @@ module FungibleToken {
|
|
|
337
371
|
export circuit approve(spender: Either<Bytes<32>, ContractAddress>,
|
|
338
372
|
value: Uint<128>
|
|
339
373
|
): Boolean {
|
|
340
|
-
|
|
374
|
+
assertInitialized();
|
|
341
375
|
|
|
342
376
|
const owner = left<Bytes<32>, ContractAddress>(_computeAccountId());
|
|
343
377
|
_approve(owner, spender, value);
|
|
@@ -373,7 +407,7 @@ module FungibleToken {
|
|
|
373
407
|
to: Either<Bytes<32>, ContractAddress>,
|
|
374
408
|
value: Uint<128>
|
|
375
409
|
): Boolean {
|
|
376
|
-
|
|
410
|
+
assertInitialized();
|
|
377
411
|
const isContractAddr = !to.is_left;
|
|
378
412
|
assert(!isContractAddr, "FungibleToken: unsafe transfer");
|
|
379
413
|
return _unsafeTransferFrom(fromAddress, to, value);
|
|
@@ -406,7 +440,7 @@ module FungibleToken {
|
|
|
406
440
|
to: Either<Bytes<32>, ContractAddress>,
|
|
407
441
|
value: Uint<128>
|
|
408
442
|
): Boolean {
|
|
409
|
-
|
|
443
|
+
assertInitialized();
|
|
410
444
|
|
|
411
445
|
const spender = left<Bytes<32>, ContractAddress>(_computeAccountId());
|
|
412
446
|
_spendAllowance(fromAddress, spender, value);
|
|
@@ -443,7 +477,7 @@ module FungibleToken {
|
|
|
443
477
|
to: Either<Bytes<32>, ContractAddress>,
|
|
444
478
|
value: Uint<128>
|
|
445
479
|
): [] {
|
|
446
|
-
|
|
480
|
+
assertInitialized();
|
|
447
481
|
const isContractAddr = !to.is_left;
|
|
448
482
|
assert(!isContractAddr, "FungibleToken: unsafe transfer");
|
|
449
483
|
_unsafeUncheckedTransfer(fromAddress, to, value);
|
|
@@ -474,7 +508,7 @@ module FungibleToken {
|
|
|
474
508
|
to: Either<Bytes<32>, ContractAddress>,
|
|
475
509
|
value: Uint<128>
|
|
476
510
|
): [] {
|
|
477
|
-
|
|
511
|
+
assertInitialized();
|
|
478
512
|
assert(!_isTargetZero(fromAddress), "FungibleToken: invalid sender");
|
|
479
513
|
assert(!_isTargetZero(to), "FungibleToken: invalid receiver");
|
|
480
514
|
|
|
@@ -501,7 +535,7 @@ module FungibleToken {
|
|
|
501
535
|
to: Either<Bytes<32>, ContractAddress>,
|
|
502
536
|
value: Uint<128>
|
|
503
537
|
): [] {
|
|
504
|
-
|
|
538
|
+
assertInitialized();
|
|
505
539
|
const canonFrom = Utils_canonicalize<Bytes<32>, ContractAddress>(fromAddress);
|
|
506
540
|
const canonTo = Utils_canonicalize<Bytes<32>, ContractAddress>(to);
|
|
507
541
|
|
|
@@ -547,7 +581,7 @@ module FungibleToken {
|
|
|
547
581
|
* @return {[]} - Empty tuple.
|
|
548
582
|
*/
|
|
549
583
|
export circuit _mint(account: Either<Bytes<32>, ContractAddress>, value: Uint<128>): [] {
|
|
550
|
-
|
|
584
|
+
assertInitialized();
|
|
551
585
|
const isContractAddr = !account.is_left;
|
|
552
586
|
assert(!isContractAddr, "FungibleToken: unsafe transfer");
|
|
553
587
|
_unsafeMint(account, value);
|
|
@@ -575,7 +609,7 @@ module FungibleToken {
|
|
|
575
609
|
account: Either<Bytes<32>, ContractAddress>,
|
|
576
610
|
value: Uint<128>
|
|
577
611
|
): [] {
|
|
578
|
-
|
|
612
|
+
assertInitialized();
|
|
579
613
|
assert(!_isTargetZero(account), "FungibleToken: invalid receiver");
|
|
580
614
|
_update(ZERO(), account, value);
|
|
581
615
|
}
|
|
@@ -597,7 +631,7 @@ module FungibleToken {
|
|
|
597
631
|
* @return {[]} - Empty tuple.
|
|
598
632
|
*/
|
|
599
633
|
export circuit _burn(account: Either<Bytes<32>, ContractAddress>, value: Uint<128>): [] {
|
|
600
|
-
|
|
634
|
+
assertInitialized();
|
|
601
635
|
assert(!_isTargetZero(account), "FungibleToken: invalid sender");
|
|
602
636
|
_update(account, ZERO(), value);
|
|
603
637
|
}
|
|
@@ -625,7 +659,7 @@ module FungibleToken {
|
|
|
625
659
|
spender: Either<Bytes<32>, ContractAddress>,
|
|
626
660
|
value: Uint<128>
|
|
627
661
|
): [] {
|
|
628
|
-
|
|
662
|
+
assertInitialized();
|
|
629
663
|
const canonOwner = Utils_canonicalize<Bytes<32>, ContractAddress>(owner);
|
|
630
664
|
const canonSpender = Utils_canonicalize<Bytes<32>, ContractAddress>(spender);
|
|
631
665
|
|
|
@@ -663,7 +697,7 @@ module FungibleToken {
|
|
|
663
697
|
spender: Either<Bytes<32>, ContractAddress>,
|
|
664
698
|
value: Uint<128>
|
|
665
699
|
): [] {
|
|
666
|
-
|
|
700
|
+
assertInitialized();
|
|
667
701
|
const canonOwner = Utils_canonicalize<Bytes<32>, ContractAddress>(owner);
|
|
668
702
|
const canonSpender = Utils_canonicalize<Bytes<32>, ContractAddress>(spender);
|
|
669
703
|
|
package/token/MultiToken.compact
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.2.0 (token/MultiToken.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.21.0;
|
|
5
5
|
|
|
@@ -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.
|
|
@@ -199,10 +205,37 @@ module MultiToken {
|
|
|
199
205
|
* @return {[]} - Empty tuple.
|
|
200
206
|
*/
|
|
201
207
|
export circuit initialize(uri_: Opaque<"string">): [] {
|
|
202
|
-
|
|
208
|
+
assertNotInitialized();
|
|
209
|
+
_isInitialized = true;
|
|
203
210
|
_setURI(uri_);
|
|
204
211
|
}
|
|
205
212
|
|
|
213
|
+
/**
|
|
214
|
+
* @description Asserts that the contract has been initialized, throwing an error if not.
|
|
215
|
+
*
|
|
216
|
+
* Requirements:
|
|
217
|
+
*
|
|
218
|
+
* - Contract must be initialized.
|
|
219
|
+
*
|
|
220
|
+
* @return {[]} - Empty tuple.
|
|
221
|
+
*/
|
|
222
|
+
circuit assertInitialized(): [] {
|
|
223
|
+
assert(_isInitialized, "MultiToken: contract not initialized");
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* @description Asserts that the contract has not been initialized, throwing an error if it has.
|
|
228
|
+
*
|
|
229
|
+
* Requirements:
|
|
230
|
+
*
|
|
231
|
+
* - Contract must not be initialized.
|
|
232
|
+
*
|
|
233
|
+
* @return {[]} - Empty tuple.
|
|
234
|
+
*/
|
|
235
|
+
circuit assertNotInitialized(): [] {
|
|
236
|
+
assert(!_isInitialized, "MultiToken: contract already initialized");
|
|
237
|
+
}
|
|
238
|
+
|
|
206
239
|
/**
|
|
207
240
|
* @description This implementation returns the same URI for *all* token types. It relies
|
|
208
241
|
* on the token type ID substitution mechanism defined in the EIP:
|
|
@@ -220,7 +253,7 @@ module MultiToken {
|
|
|
220
253
|
* @return {Opaque<"string">} - The base URI for all tokens.
|
|
221
254
|
*/
|
|
222
255
|
export circuit uri(id: Uint<128>): Opaque<"string"> {
|
|
223
|
-
|
|
256
|
+
assertInitialized();
|
|
224
257
|
return _uri;
|
|
225
258
|
}
|
|
226
259
|
|
|
@@ -241,7 +274,7 @@ module MultiToken {
|
|
|
241
274
|
account: Either<Bytes<32>, ContractAddress>,
|
|
242
275
|
id: Uint<128>
|
|
243
276
|
): Uint<128> {
|
|
244
|
-
|
|
277
|
+
assertInitialized();
|
|
245
278
|
const canonAcct = Utils_canonicalize<Bytes<32>, ContractAddress>(account);
|
|
246
279
|
|
|
247
280
|
if (!_balances.member(disclose(id)) || !_balances.lookup(id).member(disclose(canonAcct))) {
|
|
@@ -273,7 +306,7 @@ module MultiToken {
|
|
|
273
306
|
operator: Either<Bytes<32>, ContractAddress>,
|
|
274
307
|
approved: Boolean
|
|
275
308
|
): [] {
|
|
276
|
-
|
|
309
|
+
assertInitialized();
|
|
277
310
|
const caller = left<Bytes<32>, ContractAddress>(_computeAccountId());
|
|
278
311
|
_setApprovalForAll(caller, operator, approved);
|
|
279
312
|
}
|
|
@@ -295,7 +328,7 @@ module MultiToken {
|
|
|
295
328
|
account: Either<Bytes<32>, ContractAddress>,
|
|
296
329
|
operator: Either<Bytes<32>, ContractAddress>
|
|
297
330
|
): Boolean {
|
|
298
|
-
|
|
331
|
+
assertInitialized();
|
|
299
332
|
const canonAcct = Utils_canonicalize<Bytes<32>, ContractAddress>(account);
|
|
300
333
|
const canonOp = Utils_canonicalize<Bytes<32>, ContractAddress>(operator);
|
|
301
334
|
|
|
@@ -404,7 +437,7 @@ module MultiToken {
|
|
|
404
437
|
id: Uint<128>,
|
|
405
438
|
value: Uint<128>
|
|
406
439
|
): [] {
|
|
407
|
-
|
|
440
|
+
assertInitialized();
|
|
408
441
|
const canonFrom = Utils_canonicalize<Bytes<32>, ContractAddress>(fromAddress);
|
|
409
442
|
const canonTo = Utils_canonicalize<Bytes<32>, ContractAddress>(to);
|
|
410
443
|
|
|
@@ -464,7 +497,7 @@ module MultiToken {
|
|
|
464
497
|
id: Uint<128>,
|
|
465
498
|
value: Uint<128>
|
|
466
499
|
): [] {
|
|
467
|
-
|
|
500
|
+
assertInitialized();
|
|
468
501
|
|
|
469
502
|
const caller = left<Bytes<32>, ContractAddress>(_computeAccountId());
|
|
470
503
|
const canonFrom = Utils_canonicalize<Bytes<32>, ContractAddress>(fromAddress);
|
|
@@ -505,7 +538,7 @@ module MultiToken {
|
|
|
505
538
|
id: Uint<128>,
|
|
506
539
|
value: Uint<128>
|
|
507
540
|
): [] {
|
|
508
|
-
|
|
541
|
+
assertInitialized();
|
|
509
542
|
|
|
510
543
|
assert(!_isTargetZero(fromAddress), "MultiToken: invalid sender");
|
|
511
544
|
assert(!_isTargetZero(to), "MultiToken: invalid receiver");
|
|
@@ -536,7 +569,7 @@ module MultiToken {
|
|
|
536
569
|
* @return {[]} - Empty tuple.
|
|
537
570
|
*/
|
|
538
571
|
export circuit _setURI(newURI: Opaque<"string">): [] {
|
|
539
|
-
|
|
572
|
+
assertInitialized();
|
|
540
573
|
_uri = disclose(newURI);
|
|
541
574
|
}
|
|
542
575
|
|
|
@@ -593,7 +626,7 @@ module MultiToken {
|
|
|
593
626
|
id: Uint<128>,
|
|
594
627
|
value: Uint<128>
|
|
595
628
|
): [] {
|
|
596
|
-
|
|
629
|
+
assertInitialized();
|
|
597
630
|
assert(!_isTargetZero(to), "MultiToken: invalid receiver");
|
|
598
631
|
_update(ZERO(), to, id, value);
|
|
599
632
|
}
|
|
@@ -618,7 +651,7 @@ module MultiToken {
|
|
|
618
651
|
id: Uint<128>,
|
|
619
652
|
value: Uint<128>
|
|
620
653
|
): [] {
|
|
621
|
-
|
|
654
|
+
assertInitialized();
|
|
622
655
|
assert(!_isTargetZero(fromAddress), "MultiToken: invalid sender");
|
|
623
656
|
_update(fromAddress, ZERO(), id, value);
|
|
624
657
|
}
|
|
@@ -649,7 +682,7 @@ module MultiToken {
|
|
|
649
682
|
operator: Either<Bytes<32>, ContractAddress>,
|
|
650
683
|
approved: Boolean
|
|
651
684
|
): [] {
|
|
652
|
-
|
|
685
|
+
assertInitialized();
|
|
653
686
|
const canonOwner = Utils_canonicalize<Bytes<32>, ContractAddress>(owner);
|
|
654
687
|
assert(!_isTargetZero(canonOwner), "MultiToken: invalid owner");
|
|
655
688
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
|
-
// OpenZeppelin Compact Contracts v0.
|
|
2
|
+
// OpenZeppelin Compact Contracts v0.2.0 (token/NonFungibleToken.compact)
|
|
3
3
|
|
|
4
4
|
pragma language_version >= 0.21.0;
|
|
5
5
|
|
|
@@ -83,13 +83,19 @@ pragma language_version >= 0.21.0;
|
|
|
83
83
|
|
|
84
84
|
module NonFungibleToken {
|
|
85
85
|
import CompactStandardLibrary;
|
|
86
|
-
import "../security/Initializable" prefix Initializable_;
|
|
87
86
|
import "../utils/Utils" prefix Utils_;
|
|
88
87
|
|
|
89
88
|
/// Public state
|
|
90
89
|
export sealed ledger _name: Opaque<"string">;
|
|
91
90
|
export sealed ledger _symbol: Opaque<"string">;
|
|
92
91
|
|
|
92
|
+
/**
|
|
93
|
+
* @description Initialization flag, tracked per-module to avoid the compiler's
|
|
94
|
+
* shared transitive-dependency state bug (LFDT-Minokawa/compact#270). See the
|
|
95
|
+
* Initializable module for rationale.
|
|
96
|
+
*/
|
|
97
|
+
export ledger _isInitialized: Boolean;
|
|
98
|
+
|
|
93
99
|
/**
|
|
94
100
|
* @description Mapping from token IDs to their owner addresses.
|
|
95
101
|
* @type {Uint<128>} tokenId - The unique identifier for a token.
|
|
@@ -177,11 +183,38 @@ module NonFungibleToken {
|
|
|
177
183
|
* @return {[]} - Empty tuple.
|
|
178
184
|
*/
|
|
179
185
|
export circuit initialize(name_: Opaque<"string">, symbol_: Opaque<"string">): [] {
|
|
180
|
-
|
|
186
|
+
assertNotInitialized();
|
|
187
|
+
_isInitialized = true;
|
|
181
188
|
_name = disclose(name_);
|
|
182
189
|
_symbol = disclose(symbol_);
|
|
183
190
|
}
|
|
184
191
|
|
|
192
|
+
/**
|
|
193
|
+
* @description Asserts that the contract has been initialized, throwing an error if not.
|
|
194
|
+
*
|
|
195
|
+
* Requirements:
|
|
196
|
+
*
|
|
197
|
+
* - Contract must be initialized.
|
|
198
|
+
*
|
|
199
|
+
* @return {[]} - Empty tuple.
|
|
200
|
+
*/
|
|
201
|
+
circuit assertInitialized(): [] {
|
|
202
|
+
assert(_isInitialized, "NonFungibleToken: contract not initialized");
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* @description Asserts that the contract has not been initialized, throwing an error if it has.
|
|
207
|
+
*
|
|
208
|
+
* Requirements:
|
|
209
|
+
*
|
|
210
|
+
* - Contract must not be initialized.
|
|
211
|
+
*
|
|
212
|
+
* @return {[]} - Empty tuple.
|
|
213
|
+
*/
|
|
214
|
+
circuit assertNotInitialized(): [] {
|
|
215
|
+
assert(!_isInitialized, "NonFungibleToken: contract already initialized");
|
|
216
|
+
}
|
|
217
|
+
|
|
185
218
|
/**
|
|
186
219
|
* @description Returns the number of tokens in `owner`'s account.
|
|
187
220
|
*
|
|
@@ -195,7 +228,7 @@ module NonFungibleToken {
|
|
|
195
228
|
* @return {Uint<128>} - The number of tokens in `owner`'s account.
|
|
196
229
|
*/
|
|
197
230
|
export circuit balanceOf(owner: Either<Bytes<32>, ContractAddress>): Uint<128> {
|
|
198
|
-
|
|
231
|
+
assertInitialized();
|
|
199
232
|
const canonOwner = Utils_canonicalize<Bytes<32>, ContractAddress>(owner);
|
|
200
233
|
|
|
201
234
|
if (!_balances.member(disclose(canonOwner))) {
|
|
@@ -219,7 +252,7 @@ module NonFungibleToken {
|
|
|
219
252
|
* @return {Either<Bytes<32>, ContractAddress>} - The account that owns the token.
|
|
220
253
|
*/
|
|
221
254
|
export circuit ownerOf(tokenId: Uint<128>): Either<Bytes<32>, ContractAddress> {
|
|
222
|
-
|
|
255
|
+
assertInitialized();
|
|
223
256
|
return _requireOwned(tokenId);
|
|
224
257
|
}
|
|
225
258
|
|
|
@@ -235,7 +268,7 @@ module NonFungibleToken {
|
|
|
235
268
|
* @return {Opaque<"string">} - The token name.
|
|
236
269
|
*/
|
|
237
270
|
export circuit name(): Opaque<"string"> {
|
|
238
|
-
|
|
271
|
+
assertInitialized();
|
|
239
272
|
return _name;
|
|
240
273
|
}
|
|
241
274
|
|
|
@@ -251,7 +284,7 @@ module NonFungibleToken {
|
|
|
251
284
|
* @return {Opaque<"string">} - The token symbol.
|
|
252
285
|
*/
|
|
253
286
|
export circuit symbol(): Opaque<"string"> {
|
|
254
|
-
|
|
287
|
+
assertInitialized();
|
|
255
288
|
return _symbol;
|
|
256
289
|
}
|
|
257
290
|
|
|
@@ -275,7 +308,7 @@ module NonFungibleToken {
|
|
|
275
308
|
* @return {Opaque<"string">} - the token id's URI.
|
|
276
309
|
*/
|
|
277
310
|
export circuit tokenURI(tokenId: Uint<128>): Opaque<"string"> {
|
|
278
|
-
|
|
311
|
+
assertInitialized();
|
|
279
312
|
_requireOwned(tokenId);
|
|
280
313
|
|
|
281
314
|
if (!_tokenURIs.member(disclose(tokenId))) {
|
|
@@ -302,7 +335,7 @@ module NonFungibleToken {
|
|
|
302
335
|
* @return {[]} - Empty tuple.
|
|
303
336
|
*/
|
|
304
337
|
export circuit _setTokenURI(tokenId: Uint<128>, tokenURI: Opaque<"string">): [] {
|
|
305
|
-
|
|
338
|
+
assertInitialized();
|
|
306
339
|
_requireOwned(tokenId);
|
|
307
340
|
|
|
308
341
|
return _tokenURIs.insert(disclose(tokenId), disclose(tokenURI));
|
|
@@ -330,7 +363,7 @@ module NonFungibleToken {
|
|
|
330
363
|
* @return {[]} - Empty tuple.
|
|
331
364
|
*/
|
|
332
365
|
export circuit approve(to: Either<Bytes<32>, ContractAddress>, tokenId: Uint<128>): [] {
|
|
333
|
-
|
|
366
|
+
assertInitialized();
|
|
334
367
|
const auth = left<Bytes<32>, ContractAddress>(_computeAccountId());
|
|
335
368
|
_approve(to, tokenId, auth);
|
|
336
369
|
}
|
|
@@ -349,7 +382,7 @@ module NonFungibleToken {
|
|
|
349
382
|
* @return {Either<Bytes<32>, ContractAddress>} - The account approved to manage the token
|
|
350
383
|
*/
|
|
351
384
|
export circuit getApproved(tokenId: Uint<128>): Either<Bytes<32>, ContractAddress> {
|
|
352
|
-
|
|
385
|
+
assertInitialized();
|
|
353
386
|
_requireOwned(tokenId);
|
|
354
387
|
|
|
355
388
|
return _getApproved(tokenId);
|
|
@@ -377,7 +410,7 @@ module NonFungibleToken {
|
|
|
377
410
|
operator: Either<Bytes<32>, ContractAddress>,
|
|
378
411
|
approved: Boolean
|
|
379
412
|
): [] {
|
|
380
|
-
|
|
413
|
+
assertInitialized();
|
|
381
414
|
const owner = left<Bytes<32>, ContractAddress>(_computeAccountId());
|
|
382
415
|
_setApprovalForAll(owner, operator, approved);
|
|
383
416
|
}
|
|
@@ -399,7 +432,7 @@ module NonFungibleToken {
|
|
|
399
432
|
owner: Either<Bytes<32>, ContractAddress>,
|
|
400
433
|
operator: Either<Bytes<32>, ContractAddress>
|
|
401
434
|
): Boolean {
|
|
402
|
-
|
|
435
|
+
assertInitialized();
|
|
403
436
|
const canonOwner = Utils_canonicalize<Bytes<32>, ContractAddress>(owner);
|
|
404
437
|
const canonOp = Utils_canonicalize<Bytes<32>, ContractAddress>(operator);
|
|
405
438
|
|
|
@@ -439,7 +472,7 @@ module NonFungibleToken {
|
|
|
439
472
|
to: Either<Bytes<32>, ContractAddress>,
|
|
440
473
|
tokenId: Uint<128>
|
|
441
474
|
): [] {
|
|
442
|
-
|
|
475
|
+
assertInitialized();
|
|
443
476
|
const isContractAddr = !to.is_left;
|
|
444
477
|
assert(!isContractAddr, "NonFungibleToken: unsafe transfer");
|
|
445
478
|
|
|
@@ -476,7 +509,7 @@ module NonFungibleToken {
|
|
|
476
509
|
to: Either<Bytes<32>, ContractAddress>,
|
|
477
510
|
tokenId: Uint<128>
|
|
478
511
|
): [] {
|
|
479
|
-
|
|
512
|
+
assertInitialized();
|
|
480
513
|
assert(!_isTargetZero(to), "NonFungibleToken: invalid receiver");
|
|
481
514
|
// Setting an "auth" argument enables the `_isAuthorized` check which verifies that the token exists
|
|
482
515
|
// (fromAddress != 0). Therefore, it is not needed to verify that the return value is not 0 here.
|
|
@@ -500,7 +533,7 @@ module NonFungibleToken {
|
|
|
500
533
|
* @return {Either<Bytes<32>, ContractAddress>} - The owner of the token
|
|
501
534
|
*/
|
|
502
535
|
export circuit _ownerOf(tokenId: Uint<128>): Either<Bytes<32>, ContractAddress> {
|
|
503
|
-
|
|
536
|
+
assertInitialized();
|
|
504
537
|
if (!_owners.member(disclose(tokenId))) {
|
|
505
538
|
return ZERO();
|
|
506
539
|
}
|
|
@@ -521,7 +554,7 @@ module NonFungibleToken {
|
|
|
521
554
|
* @return {Either<Bytes<32>, ContractAddress>} - An account approved to spend `tokenId`
|
|
522
555
|
*/
|
|
523
556
|
export circuit _getApproved(tokenId: Uint<128>): Either<Bytes<32>, ContractAddress> {
|
|
524
|
-
|
|
557
|
+
assertInitialized();
|
|
525
558
|
if (!_tokenApprovals.member(disclose(tokenId))) {
|
|
526
559
|
return ZERO();
|
|
527
560
|
}
|
|
@@ -551,7 +584,7 @@ module NonFungibleToken {
|
|
|
551
584
|
spender: Either<Bytes<32>, ContractAddress>,
|
|
552
585
|
tokenId: Uint<128>
|
|
553
586
|
): Boolean {
|
|
554
|
-
|
|
587
|
+
assertInitialized();
|
|
555
588
|
const canonOwner = Utils_canonicalize<Bytes<32>, ContractAddress>(owner);
|
|
556
589
|
const canonSpender = Utils_canonicalize<Bytes<32>, ContractAddress>(spender);
|
|
557
590
|
|
|
@@ -584,7 +617,7 @@ module NonFungibleToken {
|
|
|
584
617
|
spender: Either<Bytes<32>, ContractAddress>,
|
|
585
618
|
tokenId: Uint<128>
|
|
586
619
|
): [] {
|
|
587
|
-
|
|
620
|
+
assertInitialized();
|
|
588
621
|
if (!_isAuthorized(owner, spender, tokenId)) {
|
|
589
622
|
assert(!_isTargetZero(owner), "NonFungibleToken: nonexistent token");
|
|
590
623
|
assert(false, "NonFungibleToken: insufficient approval");
|
|
@@ -610,7 +643,7 @@ module NonFungibleToken {
|
|
|
610
643
|
tokenId: Uint<128>,
|
|
611
644
|
auth: Either<Bytes<32>, ContractAddress>
|
|
612
645
|
): Either<Bytes<32>, ContractAddress> {
|
|
613
|
-
|
|
646
|
+
assertInitialized();
|
|
614
647
|
const canonTo = Utils_canonicalize<Bytes<32>, ContractAddress>(to);
|
|
615
648
|
const canonAuth = Utils_canonicalize<Bytes<32>, ContractAddress>(auth);
|
|
616
649
|
const fromAddress = _ownerOf(tokenId);
|
|
@@ -659,7 +692,7 @@ module NonFungibleToken {
|
|
|
659
692
|
* @return {[]} - Empty tuple.
|
|
660
693
|
*/
|
|
661
694
|
export circuit _mint(to: Either<Bytes<32>, ContractAddress>, tokenId: Uint<128>): [] {
|
|
662
|
-
|
|
695
|
+
assertInitialized();
|
|
663
696
|
const isContractAddr = !to.is_left;
|
|
664
697
|
assert(!isContractAddr, "NonFungibleToken: unsafe transfer");
|
|
665
698
|
|
|
@@ -689,7 +722,7 @@ module NonFungibleToken {
|
|
|
689
722
|
to: Either<Bytes<32>, ContractAddress>,
|
|
690
723
|
tokenId: Uint<128>
|
|
691
724
|
): [] {
|
|
692
|
-
|
|
725
|
+
assertInitialized();
|
|
693
726
|
assert(!_isTargetZero(to), "NonFungibleToken: invalid receiver");
|
|
694
727
|
|
|
695
728
|
const previousOwner = _update(to, tokenId, ZERO());
|
|
@@ -713,7 +746,7 @@ module NonFungibleToken {
|
|
|
713
746
|
* @return {[]} - Empty tuple.
|
|
714
747
|
*/
|
|
715
748
|
export circuit _burn(tokenId: Uint<128>): [] {
|
|
716
|
-
|
|
749
|
+
assertInitialized();
|
|
717
750
|
const previousOwner = _update(ZERO(), tokenId, ZERO());
|
|
718
751
|
assert(!_isTargetZero(previousOwner), "NonFungibleToken: invalid sender");
|
|
719
752
|
_tokenURIs.remove(disclose(tokenId));
|
|
@@ -746,7 +779,7 @@ module NonFungibleToken {
|
|
|
746
779
|
to: Either<Bytes<32>, ContractAddress>,
|
|
747
780
|
tokenId: Uint<128>
|
|
748
781
|
): [] {
|
|
749
|
-
|
|
782
|
+
assertInitialized();
|
|
750
783
|
const isContractAddr = !to.is_left;
|
|
751
784
|
assert(!isContractAddr, "NonFungibleToken: unsafe transfer");
|
|
752
785
|
|
|
@@ -780,7 +813,7 @@ module NonFungibleToken {
|
|
|
780
813
|
to: Either<Bytes<32>, ContractAddress>,
|
|
781
814
|
tokenId: Uint<128>
|
|
782
815
|
): [] {
|
|
783
|
-
|
|
816
|
+
assertInitialized();
|
|
784
817
|
assert(!_isTargetZero(to), "NonFungibleToken: invalid receiver");
|
|
785
818
|
|
|
786
819
|
const previousOwner = _update(to, tokenId, ZERO());
|
|
@@ -812,7 +845,7 @@ module NonFungibleToken {
|
|
|
812
845
|
tokenId: Uint<128>,
|
|
813
846
|
auth: Either<Bytes<32>, ContractAddress>
|
|
814
847
|
): [] {
|
|
815
|
-
|
|
848
|
+
assertInitialized();
|
|
816
849
|
// Canonicalize + normalize `to`
|
|
817
850
|
const canonTo = Utils_canonicalize<Bytes<32>, ContractAddress>(to);
|
|
818
851
|
const normalizedTo = _isTargetZero(canonTo) ? ZERO() : canonTo;
|
|
@@ -853,7 +886,7 @@ module NonFungibleToken {
|
|
|
853
886
|
operator: Either<Bytes<32>, ContractAddress>,
|
|
854
887
|
approved: Boolean
|
|
855
888
|
): [] {
|
|
856
|
-
|
|
889
|
+
assertInitialized();
|
|
857
890
|
const canonOwner = Utils_canonicalize<Bytes<32>, ContractAddress>(owner);
|
|
858
891
|
assert(!_isTargetZero(canonOwner), "NonFungibleToken: invalid owner");
|
|
859
892
|
|
|
@@ -885,7 +918,7 @@ module NonFungibleToken {
|
|
|
885
918
|
* @return {Either<Bytes<32>, ContractAddress>} - The owner of `tokenId`
|
|
886
919
|
*/
|
|
887
920
|
export circuit _requireOwned(tokenId: Uint<128>): Either<Bytes<32>, ContractAddress> {
|
|
888
|
-
|
|
921
|
+
assertInitialized();
|
|
889
922
|
const owner = _ownerOf(tokenId);
|
|
890
923
|
|
|
891
924
|
assert(!_isTargetZero(owner), "NonFungibleToken: nonexistent token");
|
package/utils/Utils.compact
CHANGED