@silvana-one/nft 0.3.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/node/admin/advanced.d.ts +5 -1
- package/dist/node/contracts/admin.d.ts +100 -10
- package/dist/node/contracts/admin.js +130 -29
- package/dist/node/contracts/admin.js.map +1 -1
- package/dist/node/contracts/collection.d.ts +56 -35
- package/dist/node/contracts/collection.js +162 -71
- package/dist/node/contracts/collection.js.map +1 -1
- package/dist/node/contracts/nft.js +3 -0
- package/dist/node/contracts/nft.js.map +1 -1
- package/dist/node/contracts.d.ts +51 -33
- package/dist/node/index.cjs +447 -182
- package/dist/node/interfaces/collection.d.ts +5 -5
- package/dist/node/interfaces/events.d.ts +306 -16
- package/dist/node/interfaces/events.js +42 -4
- package/dist/node/interfaces/events.js.map +1 -1
- package/dist/node/interfaces/ownable.d.ts +7 -7
- package/dist/node/interfaces/ownable.js.map +1 -1
- package/dist/node/interfaces/pausable.d.ts +3 -3
- package/dist/node/interfaces/pausable.js.map +1 -1
- package/dist/node/interfaces/types.d.ts +182 -27
- package/dist/node/interfaces/types.js +120 -71
- package/dist/node/interfaces/types.js.map +1 -1
- package/dist/node/marketplace/auction.d.ts +7 -3
- package/dist/node/marketplace/auction.js +4 -4
- package/dist/node/marketplace/auction.js.map +1 -1
- package/dist/node/marketplace/bid.d.ts +5 -1
- package/dist/node/marketplace/bid.js +3 -5
- package/dist/node/marketplace/bid.js.map +1 -1
- package/dist/node/marketplace/nft-shares.d.ts +17 -5
- package/dist/node/marketplace/offer.d.ts +5 -1
- package/dist/node/util/div.js +10 -5
- package/dist/node/util/div.js.map +1 -1
- package/dist/node/vk.js +6 -6
- package/dist/node/vk.js.map +1 -1
- package/dist/node/zkprogram-example/game.d.ts +6 -17
- package/dist/node/zkprogram-example/update.d.ts +6 -17
- package/dist/tsconfig.node.tsbuildinfo +1 -1
- package/dist/tsconfig.web.tsbuildinfo +1 -1
- package/dist/web/admin/advanced.d.ts +5 -1
- package/dist/web/contracts/admin.d.ts +100 -10
- package/dist/web/contracts/admin.js +130 -29
- package/dist/web/contracts/admin.js.map +1 -1
- package/dist/web/contracts/collection.d.ts +56 -35
- package/dist/web/contracts/collection.js +162 -71
- package/dist/web/contracts/collection.js.map +1 -1
- package/dist/web/contracts/nft.js +3 -0
- package/dist/web/contracts/nft.js.map +1 -1
- package/dist/web/contracts.d.ts +51 -33
- package/dist/web/interfaces/collection.d.ts +5 -5
- package/dist/web/interfaces/events.d.ts +306 -16
- package/dist/web/interfaces/events.js +42 -4
- package/dist/web/interfaces/events.js.map +1 -1
- package/dist/web/interfaces/ownable.d.ts +7 -7
- package/dist/web/interfaces/ownable.js.map +1 -1
- package/dist/web/interfaces/pausable.d.ts +3 -3
- package/dist/web/interfaces/pausable.js.map +1 -1
- package/dist/web/interfaces/types.d.ts +182 -27
- package/dist/web/interfaces/types.js +120 -71
- package/dist/web/interfaces/types.js.map +1 -1
- package/dist/web/marketplace/auction.d.ts +7 -3
- package/dist/web/marketplace/auction.js +4 -4
- package/dist/web/marketplace/auction.js.map +1 -1
- package/dist/web/marketplace/bid.d.ts +5 -1
- package/dist/web/marketplace/bid.js +3 -5
- package/dist/web/marketplace/bid.js.map +1 -1
- package/dist/web/marketplace/nft-shares.d.ts +17 -5
- package/dist/web/marketplace/offer.d.ts +5 -1
- package/dist/web/util/div.js +10 -5
- package/dist/web/util/div.js.map +1 -1
- package/dist/web/vk.js +6 -6
- package/dist/web/vk.js.map +1 -1
- package/dist/web/zkprogram-example/game.d.ts +6 -17
- package/dist/web/zkprogram-example/update.d.ts +6 -17
- package/package.json +13 -13
- package/src/contracts/admin.ts +137 -24
- package/src/contracts/collection.ts +188 -96
- package/src/contracts/nft.ts +3 -0
- package/src/interfaces/collection.ts +11 -5
- package/src/interfaces/events.ts +47 -3
- package/src/interfaces/ownable.ts +1 -1
- package/src/interfaces/pausable.ts +1 -1
- package/src/interfaces/types.ts +152 -78
- package/src/marketplace/auction.ts +5 -4
- package/src/marketplace/bid.ts +4 -6
- package/src/util/div.ts +12 -5
- package/src/vk.ts +6 -6
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { PublicKey, TokenContract } from "o1js";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
TransferBySignatureParams,
|
|
4
|
+
TransferByProofParams,
|
|
5
|
+
NFTStateStruct,
|
|
6
|
+
} from "./types.js";
|
|
3
7
|
import { NFTOwnerContractConstructor } from "./owner.js";
|
|
4
8
|
import { NFTApprovalContractConstructor } from "./approval.js";
|
|
5
9
|
import { NFTUpdateContractConstructor } from "./update.js";
|
|
@@ -20,26 +24,28 @@ type NFTCollectionBase = TokenContract & {
|
|
|
20
24
|
*
|
|
21
25
|
* @param params - The transfer details.
|
|
22
26
|
*/
|
|
23
|
-
transferByProof(params:
|
|
27
|
+
transferByProof(params: TransferByProofParams): Promise<void>;
|
|
24
28
|
/**
|
|
25
29
|
* Transfers ownership of an NFT from contract without admin approval.
|
|
26
30
|
*
|
|
27
31
|
* @param params - The transfer details.
|
|
28
32
|
*/
|
|
29
|
-
transferBySignature(params:
|
|
33
|
+
transferBySignature(params: TransferBySignatureParams): Promise<void>;
|
|
30
34
|
|
|
31
35
|
/**
|
|
32
36
|
* Transfers ownership of an NFT from contract without admin approval using a proof.
|
|
33
37
|
*
|
|
34
38
|
* @param params - The transfer details.
|
|
35
39
|
*/
|
|
36
|
-
|
|
40
|
+
adminApprovedTransferByProof(params: TransferByProofParams): Promise<void>;
|
|
37
41
|
/**
|
|
38
42
|
* Transfers ownership of an NFT from contract without admin approval.
|
|
39
43
|
*
|
|
40
44
|
* @param params - The transfer details.
|
|
41
45
|
*/
|
|
42
|
-
|
|
46
|
+
adminApprovedTransferBySignature(
|
|
47
|
+
params: TransferBySignatureParams
|
|
48
|
+
): Promise<void>;
|
|
43
49
|
|
|
44
50
|
/**
|
|
45
51
|
* Returns the state of an NFT.
|
package/src/interfaces/events.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { PublicKey, Struct, UInt32, Field, Bool } from "o1js";
|
|
1
|
+
import { PublicKey, Struct, UInt32, Field, Bool, UInt64 } from "o1js";
|
|
2
2
|
import { Storage } from "@silvana-one/storage";
|
|
3
3
|
import { NFTStateStruct, UInt64Option } from "./types.js";
|
|
4
4
|
|
|
5
5
|
export {
|
|
6
6
|
MintEvent,
|
|
7
|
+
NFTUpdateEvent,
|
|
7
8
|
UpdateEvent,
|
|
8
9
|
TransferEvent,
|
|
9
10
|
UpgradeVerificationKeyEvent,
|
|
@@ -11,6 +12,11 @@ export {
|
|
|
11
12
|
LimitMintingEvent,
|
|
12
13
|
PauseNFTEvent,
|
|
13
14
|
ApproveEvent,
|
|
15
|
+
SetNameEvent,
|
|
16
|
+
SetBaseURLEvent,
|
|
17
|
+
SetRoyaltyFeeEvent,
|
|
18
|
+
SetTransferFeeEvent,
|
|
19
|
+
SetAdminEvent,
|
|
14
20
|
};
|
|
15
21
|
|
|
16
22
|
/**
|
|
@@ -21,6 +27,14 @@ class MintEvent extends Struct({
|
|
|
21
27
|
initialState: NFTStateStruct,
|
|
22
28
|
/** The public key address of the minted NFT. */
|
|
23
29
|
address: PublicKey,
|
|
30
|
+
/** The token ID of the minted NFT. */
|
|
31
|
+
tokenId: Field,
|
|
32
|
+
/** The fee paid for the minting.
|
|
33
|
+
* This fee is controlled by the admin contract
|
|
34
|
+
* and is not checked by the Collection contract
|
|
35
|
+
* Please check the admin contract code before using this fee
|
|
36
|
+
*/
|
|
37
|
+
fee: UInt64,
|
|
24
38
|
}) {}
|
|
25
39
|
|
|
26
40
|
/**
|
|
@@ -38,7 +52,7 @@ class UpdateEvent extends Struct({
|
|
|
38
52
|
/** The approved address of the NFT after the update. */
|
|
39
53
|
approved: PublicKey,
|
|
40
54
|
/** The version number of the NFT state. */
|
|
41
|
-
version:
|
|
55
|
+
version: UInt64,
|
|
42
56
|
/** Indicates whether the NFT is paused after the update. */
|
|
43
57
|
isPaused: Bool,
|
|
44
58
|
/** The hash of the verification key used for metadata proofs. */
|
|
@@ -95,7 +109,7 @@ class UpgradeVerificationKeyEvent extends Struct({
|
|
|
95
109
|
verificationKeyHash: Field,
|
|
96
110
|
/** The public key address of the NFT whose verification key is upgraded. */
|
|
97
111
|
address: PublicKey,
|
|
98
|
-
/** The
|
|
112
|
+
/** The tokenId of the upgraded contract */
|
|
99
113
|
tokenId: Field,
|
|
100
114
|
}) {}
|
|
101
115
|
|
|
@@ -113,3 +127,33 @@ class LimitMintingEvent extends Struct({
|
|
|
113
127
|
/** Indicates whether minting is limited (`true`) or not (`false`). */
|
|
114
128
|
mintingLimited: Bool,
|
|
115
129
|
}) {}
|
|
130
|
+
|
|
131
|
+
class NFTUpdateEvent extends Struct({
|
|
132
|
+
/** The public key address of the NFT. */
|
|
133
|
+
address: PublicKey,
|
|
134
|
+
}) {}
|
|
135
|
+
|
|
136
|
+
class SetNameEvent extends Struct({
|
|
137
|
+
/** The updated name of the Collection. */
|
|
138
|
+
name: Field,
|
|
139
|
+
}) {}
|
|
140
|
+
|
|
141
|
+
class SetBaseURLEvent extends Struct({
|
|
142
|
+
/** The updated base URL of the Collection. */
|
|
143
|
+
baseURL: Field,
|
|
144
|
+
}) {}
|
|
145
|
+
|
|
146
|
+
class SetRoyaltyFeeEvent extends Struct({
|
|
147
|
+
/** The updated royalty fee of the Collection. */
|
|
148
|
+
royaltyFee: UInt32,
|
|
149
|
+
}) {}
|
|
150
|
+
|
|
151
|
+
class SetTransferFeeEvent extends Struct({
|
|
152
|
+
/** The updated transfer fee of the Collection. */
|
|
153
|
+
transferFee: UInt64,
|
|
154
|
+
}) {}
|
|
155
|
+
|
|
156
|
+
class SetAdminEvent extends Struct({
|
|
157
|
+
/** The updated admin contract of the Collection. */
|
|
158
|
+
admin: PublicKey,
|
|
159
|
+
}) {}
|
package/src/interfaces/types.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
FeatureFlags,
|
|
11
11
|
Option,
|
|
12
12
|
Account,
|
|
13
|
+
Gadgets,
|
|
13
14
|
} from "o1js";
|
|
14
15
|
import { Storage } from "@silvana-one/storage";
|
|
15
16
|
export {
|
|
@@ -24,7 +25,8 @@ export {
|
|
|
24
25
|
NFTUpdateProof,
|
|
25
26
|
NFTStateStruct,
|
|
26
27
|
UInt64Option,
|
|
27
|
-
|
|
28
|
+
TransferBySignatureParams,
|
|
29
|
+
TransferByProofParams,
|
|
28
30
|
MAX_ROYALTY_FEE,
|
|
29
31
|
NFTTransactionContext,
|
|
30
32
|
TransferExtendedParams,
|
|
@@ -109,7 +111,10 @@ class NFTImmutableState extends Struct({
|
|
|
109
111
|
address: PublicKey, // readonly
|
|
110
112
|
/** The token ID associated with the NFT (readonly). */
|
|
111
113
|
tokenId: Field, // readonly
|
|
112
|
-
/** The
|
|
114
|
+
/** The identifier of the NFT within the collection to be used off-chain(readonly).
|
|
115
|
+
* It can be set to any value chosen by the creator for the new NFTs
|
|
116
|
+
* and by default is set to 0. To uniquely identify the NFT, use the pair (NFT address, tokenId) or (collection address, NFT address)
|
|
117
|
+
*/
|
|
113
118
|
id: UInt64, // readonly
|
|
114
119
|
}) {
|
|
115
120
|
/**
|
|
@@ -193,7 +198,7 @@ class NFTState extends Struct({
|
|
|
193
198
|
/** The off-chain storage information (e.g., IPFS hash). */
|
|
194
199
|
storage: Storage,
|
|
195
200
|
/** The version number of the NFT state. */
|
|
196
|
-
version:
|
|
201
|
+
version: UInt64,
|
|
197
202
|
/** Indicates whether the NFT contract is currently paused. */
|
|
198
203
|
isPaused: Bool,
|
|
199
204
|
/** The hash of the verification key used for metadata proofs. */
|
|
@@ -223,6 +228,7 @@ class NFTState extends Struct({
|
|
|
223
228
|
a.metadataVerificationKeyHash.assertEquals(b.metadataVerificationKeyHash);
|
|
224
229
|
a.creator.assertEquals(b.creator);
|
|
225
230
|
NFTTransactionContext.assertEqual(a.context, b.context);
|
|
231
|
+
a.oracleAddress.assertEquals(b.oracleAddress);
|
|
226
232
|
}
|
|
227
233
|
|
|
228
234
|
/**
|
|
@@ -282,14 +288,22 @@ class NFTData extends Struct({
|
|
|
282
288
|
/** The approved address of the NFT. */
|
|
283
289
|
approved: PublicKey,
|
|
284
290
|
/** The version number of the NFT state. */
|
|
285
|
-
version:
|
|
291
|
+
version: UInt64,
|
|
286
292
|
/** The unique identifier of the NFT within the collection. */
|
|
287
293
|
id: UInt64,
|
|
288
|
-
/** Determines whether the NFT's ownership can be changed via a zero-knowledge proof (readonly).
|
|
294
|
+
/** Determines whether the NFT's ownership can be changed via a zero-knowledge proof (readonly).
|
|
295
|
+
*
|
|
296
|
+
* It can be used only with update() and updateWithOracle() methods and
|
|
297
|
+
* in this case overrides both canTransfer and canApprove flags used in the transfer methods
|
|
298
|
+
*/
|
|
289
299
|
canChangeOwnerByProof: Bool, // readonly
|
|
290
|
-
/** Specifies if the NFT's ownership can be transferred (readonly).
|
|
300
|
+
/** Specifies if the NFT's ownership can be transferred (readonly). Applies
|
|
301
|
+
* to transfer methods and can be bypassed by the update() and updateWithOracle() methods
|
|
302
|
+
*/
|
|
291
303
|
canTransfer: Bool, // readonly
|
|
292
|
-
/** Specifies if the NFT's approved address can be changed (readonly).
|
|
304
|
+
/** Specifies if the NFT's approved address can be changed (readonly). Transfer methods reset approved address to PublicKey.empty()
|
|
305
|
+
* on transfer independently from the canApprove flag value
|
|
306
|
+
*/
|
|
293
307
|
canApprove: Bool, // readonly
|
|
294
308
|
/** Indicates whether the NFT's metadata can be updated (readonly). */
|
|
295
309
|
canChangeMetadata: Bool, // readonly
|
|
@@ -314,7 +328,7 @@ class NFTData extends Struct({
|
|
|
314
328
|
static new(params: {
|
|
315
329
|
owner: string | PublicKey;
|
|
316
330
|
approved?: string | PublicKey;
|
|
317
|
-
version?: number;
|
|
331
|
+
version?: number | bigint | string;
|
|
318
332
|
id?: bigint | string;
|
|
319
333
|
canChangeOwnerByProof?: boolean;
|
|
320
334
|
canTransfer?: boolean;
|
|
@@ -350,7 +364,7 @@ class NFTData extends Struct({
|
|
|
350
364
|
? PublicKey.fromBase58(approved)
|
|
351
365
|
: approved
|
|
352
366
|
: PublicKey.empty(),
|
|
353
|
-
version:
|
|
367
|
+
version: UInt64.from(BigInt(version ?? 0)),
|
|
354
368
|
id: UInt64.from(BigInt(id ?? 0)),
|
|
355
369
|
canChangeOwnerByProof: Bool(canChangeOwnerByProof ?? false),
|
|
356
370
|
canTransfer: Bool(canTransfer ?? true),
|
|
@@ -374,14 +388,10 @@ class NFTData extends Struct({
|
|
|
374
388
|
* @returns The packed Field representation of the NFTData.
|
|
375
389
|
*/
|
|
376
390
|
pack(): NFTDataPacked {
|
|
377
|
-
const id = this.id.value.toBits(64);
|
|
378
|
-
const version = this.version.value.toBits(32);
|
|
379
391
|
return new NFTDataPacked({
|
|
380
392
|
ownerX: this.owner.x,
|
|
381
393
|
approvedX: this.approved.x,
|
|
382
394
|
data: Field.fromBits([
|
|
383
|
-
...id,
|
|
384
|
-
...version,
|
|
385
395
|
this.canChangeOwnerByProof,
|
|
386
396
|
this.canTransfer,
|
|
387
397
|
this.canApprove,
|
|
@@ -394,7 +404,9 @@ class NFTData extends Struct({
|
|
|
394
404
|
this.requireOwnerAuthorizationToUpgrade,
|
|
395
405
|
this.owner.isOdd,
|
|
396
406
|
this.approved.isOdd,
|
|
397
|
-
])
|
|
407
|
+
])
|
|
408
|
+
.add(Field(this.id.value).mul(Field(2 ** 12)))
|
|
409
|
+
.add(Field(this.version.value).mul(Field(2 ** (12 + 64)))),
|
|
398
410
|
});
|
|
399
411
|
}
|
|
400
412
|
|
|
@@ -404,45 +416,70 @@ class NFTData extends Struct({
|
|
|
404
416
|
* @returns A new NFTData instance.
|
|
405
417
|
*/
|
|
406
418
|
static unpack(packed: NFTDataPacked): NFTData {
|
|
407
|
-
const
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
419
|
+
const unpacked = Provable.witness(NFTData, () => {
|
|
420
|
+
const bits = Gadgets.and(packed.data, Field(0xfffn), 12 + 64 + 64).toBits(
|
|
421
|
+
12
|
|
422
|
+
);
|
|
423
|
+
const idField = Gadgets.and(
|
|
424
|
+
packed.data,
|
|
425
|
+
Field(0xffffffffffffffff000n),
|
|
426
|
+
12 + 64 + 64
|
|
427
|
+
);
|
|
428
|
+
const idBits = idField.toBits(64 + 12);
|
|
429
|
+
// the next line relies on the constants 0xffffffffffffffff000n and 12 + 64 + 64 above
|
|
430
|
+
const id = UInt64.Unsafe.fromField(
|
|
431
|
+
Field.fromBits(idBits.slice(12, 64 + 12))
|
|
432
|
+
);
|
|
433
|
+
id.value.mul(Field(2 ** 12)).assertEquals(idField);
|
|
412
434
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
435
|
+
const versionField = Gadgets.and(
|
|
436
|
+
packed.data,
|
|
437
|
+
Field(0xffffffffffffffff0000000000000000000n),
|
|
438
|
+
64 + 64 + 12
|
|
439
|
+
);
|
|
440
|
+
const versionBits = versionField.toBits(12 + 64 + 64);
|
|
441
|
+
// the next line relies on the constants 0xffffffffffffffff0000000000000000000n and 12 + 64 + 64 above
|
|
442
|
+
const version = UInt64.Unsafe.fromField(
|
|
443
|
+
Field.fromBits(versionBits.slice(12 + 64, 12 + 64 + 64))
|
|
444
|
+
);
|
|
445
|
+
version.value.mul(Field(2 ** (12 + 64))).assertEquals(versionField);
|
|
446
|
+
|
|
447
|
+
const canChangeOwnerByProof = bits[0];
|
|
448
|
+
const canTransfer = bits[1];
|
|
449
|
+
const canApprove = bits[2];
|
|
450
|
+
const canChangeMetadata = bits[3];
|
|
451
|
+
const canChangeStorage = bits[4];
|
|
452
|
+
const canChangeName = bits[5];
|
|
453
|
+
const canChangeMetadataVerificationKeyHash = bits[6];
|
|
454
|
+
const canPause = bits[7];
|
|
455
|
+
const isPaused = bits[8];
|
|
456
|
+
const requireOwnerAuthorizationToUpgrade = bits[9];
|
|
457
|
+
const ownerIsOdd = bits[10];
|
|
458
|
+
const approvedIsOdd = bits[11];
|
|
459
|
+
const owner = PublicKey.from({ x: packed.ownerX, isOdd: ownerIsOdd });
|
|
460
|
+
const approved = PublicKey.from({
|
|
461
|
+
x: packed.approvedX,
|
|
462
|
+
isOdd: approvedIsOdd,
|
|
463
|
+
});
|
|
464
|
+
return new NFTData({
|
|
465
|
+
owner,
|
|
466
|
+
approved,
|
|
467
|
+
id,
|
|
468
|
+
version,
|
|
469
|
+
canChangeOwnerByProof,
|
|
470
|
+
canTransfer,
|
|
471
|
+
canApprove,
|
|
472
|
+
canChangeMetadata,
|
|
473
|
+
canChangeStorage,
|
|
474
|
+
canChangeName,
|
|
475
|
+
canChangeMetadataVerificationKeyHash,
|
|
476
|
+
canPause,
|
|
477
|
+
isPaused,
|
|
478
|
+
requireOwnerAuthorizationToUpgrade,
|
|
479
|
+
});
|
|
445
480
|
});
|
|
481
|
+
NFTDataPacked.assertEqual(unpacked.pack(), packed);
|
|
482
|
+
return unpacked;
|
|
446
483
|
}
|
|
447
484
|
}
|
|
448
485
|
|
|
@@ -462,6 +499,8 @@ class CollectionData extends Struct({
|
|
|
462
499
|
mintingIsLimited: Bool,
|
|
463
500
|
/** Indicates whether the collection is currently paused. */
|
|
464
501
|
isPaused: Bool,
|
|
502
|
+
/** The public key part (isOdd) of the pending creator. The x field is written to the contract state as pendingCreatorX */
|
|
503
|
+
pendingCreatorIsOdd: Bool,
|
|
465
504
|
}) {
|
|
466
505
|
/**
|
|
467
506
|
* Creates a new CollectionData instance with specified parameters.
|
|
@@ -488,6 +527,7 @@ class CollectionData extends Struct({
|
|
|
488
527
|
requireTransferApproval: Bool(requireTransferApproval ?? false),
|
|
489
528
|
mintingIsLimited: Bool(mintingIsLimited ?? false),
|
|
490
529
|
isPaused: Bool(isPaused ?? false),
|
|
530
|
+
pendingCreatorIsOdd: Bool(PublicKey.empty().isOdd),
|
|
491
531
|
});
|
|
492
532
|
}
|
|
493
533
|
|
|
@@ -500,9 +540,10 @@ class CollectionData extends Struct({
|
|
|
500
540
|
this.isPaused,
|
|
501
541
|
this.requireTransferApproval,
|
|
502
542
|
this.mintingIsLimited,
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
543
|
+
this.pendingCreatorIsOdd,
|
|
544
|
+
])
|
|
545
|
+
.add(Field(this.royaltyFee.value).mul(Field(2 ** 4)))
|
|
546
|
+
.add(Field(this.transferFee.value).mul(Field(2 ** (4 + 32))));
|
|
506
547
|
}
|
|
507
548
|
|
|
508
549
|
/**
|
|
@@ -511,36 +552,55 @@ class CollectionData extends Struct({
|
|
|
511
552
|
* @returns A new CollectionData instance.
|
|
512
553
|
*/
|
|
513
554
|
static unpack(packed: Field) {
|
|
514
|
-
const
|
|
515
|
-
|
|
516
|
-
Field.fromBits(bits.slice(3, 3 + 32))
|
|
517
|
-
);
|
|
518
|
-
const transferFee = UInt64.Unsafe.fromField(
|
|
519
|
-
Field.fromBits(bits.slice(3 + 32, 3 + 32 + 64))
|
|
520
|
-
);
|
|
555
|
+
const unpacked = Provable.witness(CollectionData, () => {
|
|
556
|
+
const bits = Gadgets.and(packed, Field(0xfn), 4 + 32 + 64).toBits(4);
|
|
521
557
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
558
|
+
const royaltyFeeField = Gadgets.and(
|
|
559
|
+
packed,
|
|
560
|
+
Field(0xffffffff0n),
|
|
561
|
+
4 + 32 + 64
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
const royaltyFeeBits = royaltyFeeField.toBits(4 + 32);
|
|
565
|
+
// The next line relies on the constants 0xffffffff0n and 4 + 32 + 64 above
|
|
566
|
+
const royaltyFee = UInt32.Unsafe.fromField(
|
|
567
|
+
Field.fromBits(royaltyFeeBits.slice(4, 4 + 32))
|
|
568
|
+
);
|
|
569
|
+
royaltyFee.value.mul(Field(2 ** 4)).assertEquals(royaltyFeeField);
|
|
570
|
+
|
|
571
|
+
const transferFeeField = Gadgets.and(
|
|
572
|
+
packed,
|
|
573
|
+
Field(0xffffffffffffffff000000000n),
|
|
574
|
+
4 + 32 + 64
|
|
575
|
+
);
|
|
576
|
+
const transferFeeBits = transferFeeField.toBits(4 + 32 + 64);
|
|
577
|
+
// The next line relies on the constants 0xffffffffffffffff000000000n and 4 + 32 + 64 above
|
|
578
|
+
const transferFee = UInt64.Unsafe.fromField(
|
|
579
|
+
Field.fromBits(transferFeeBits.slice(4 + 32, 4 + 32 + 64))
|
|
580
|
+
);
|
|
581
|
+
transferFee.value
|
|
582
|
+
.mul(Field(2 ** (4 + 32)))
|
|
583
|
+
.assertEquals(transferFeeField);
|
|
584
|
+
|
|
585
|
+
return new CollectionData({
|
|
586
|
+
isPaused: bits[0],
|
|
587
|
+
requireTransferApproval: bits[1],
|
|
588
|
+
mintingIsLimited: bits[2],
|
|
589
|
+
pendingCreatorIsOdd: bits[3],
|
|
590
|
+
royaltyFee,
|
|
591
|
+
transferFee,
|
|
592
|
+
});
|
|
528
593
|
});
|
|
594
|
+
unpacked.pack().assertEquals(packed);
|
|
595
|
+
return unpacked;
|
|
529
596
|
}
|
|
530
597
|
|
|
531
598
|
static isPaused(packed: Field) {
|
|
532
|
-
return packed.toBits(
|
|
599
|
+
return packed.toBits(4 + 32 + 64)[0];
|
|
533
600
|
}
|
|
534
601
|
|
|
535
602
|
static requireTransferApproval(packed: Field) {
|
|
536
|
-
return packed.toBits(
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
static mintingIsLimited(packed: Field) {
|
|
540
|
-
const bits = packed.toBits(3 + 32 + 64);
|
|
541
|
-
const isPaused = bits[0];
|
|
542
|
-
const mintingIsLimited = bits[2];
|
|
543
|
-
return isPaused.or(mintingIsLimited);
|
|
603
|
+
return packed.toBits(4 + 32 + 64)[1];
|
|
544
604
|
}
|
|
545
605
|
}
|
|
546
606
|
|
|
@@ -586,9 +646,23 @@ class MintRequest extends Struct({
|
|
|
586
646
|
}) {}
|
|
587
647
|
|
|
588
648
|
/**
|
|
589
|
-
* Represents the parameters required for transferring an NFT.
|
|
649
|
+
* Represents the parameters required for transferring an NFT using a signature.
|
|
650
|
+
*/
|
|
651
|
+
class TransferBySignatureParams extends Struct({
|
|
652
|
+
/** The address of the NFT contract. */
|
|
653
|
+
address: PublicKey,
|
|
654
|
+
/** The receiver's public key. */
|
|
655
|
+
to: PublicKey,
|
|
656
|
+
/** Optional price for the transfer. */
|
|
657
|
+
price: UInt64Option,
|
|
658
|
+
/** Custom value that can be interpreted by the owner or approved contract. */
|
|
659
|
+
context: NFTTransactionContext,
|
|
660
|
+
}) {}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Represents the parameters required for transferring an NFT using a proof.
|
|
590
664
|
*/
|
|
591
|
-
class
|
|
665
|
+
class TransferByProofParams extends Struct({
|
|
592
666
|
/** The address of the NFT contract. */
|
|
593
667
|
address: PublicKey,
|
|
594
668
|
/** The sender's public key. */
|
|
@@ -23,7 +23,8 @@ import {
|
|
|
23
23
|
NFTCollectionBase,
|
|
24
24
|
NFTTransactionContext,
|
|
25
25
|
TransferExtendedParams,
|
|
26
|
-
|
|
26
|
+
TransferBySignatureParams,
|
|
27
|
+
TransferByProofParams,
|
|
27
28
|
} from "../interfaces/index.js";
|
|
28
29
|
import { mulDiv } from "../util/index.js";
|
|
29
30
|
|
|
@@ -231,7 +232,7 @@ export function AuctionFactory(params: {
|
|
|
231
232
|
|
|
232
233
|
events = {
|
|
233
234
|
bid: AuctionBidEvent,
|
|
234
|
-
settleAuction:
|
|
235
|
+
settleAuction: TransferByProofParams,
|
|
235
236
|
canTransfer: TransferEvent,
|
|
236
237
|
settlePayment: UInt64,
|
|
237
238
|
settleAuctioneerPayment: UInt64,
|
|
@@ -337,7 +338,7 @@ export function AuctionFactory(params: {
|
|
|
337
338
|
"Bidder does not have enough balance"
|
|
338
339
|
);
|
|
339
340
|
const collection = this.getCollectionContract(auction.collection);
|
|
340
|
-
const transferParams = new
|
|
341
|
+
const transferParams = new TransferByProofParams({
|
|
341
342
|
address: nftAddress,
|
|
342
343
|
from: this.address,
|
|
343
344
|
to: auction.bidder,
|
|
@@ -366,7 +367,7 @@ export function AuctionFactory(params: {
|
|
|
366
367
|
const nftAddress = auction.nft;
|
|
367
368
|
|
|
368
369
|
const collection = this.getCollectionContract(auction.collection);
|
|
369
|
-
const transferParams = new
|
|
370
|
+
const transferParams = new TransferByProofParams({
|
|
370
371
|
address: nftAddress,
|
|
371
372
|
from: this.address,
|
|
372
373
|
to: auction.owner,
|
package/src/marketplace/bid.ts
CHANGED
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
import {
|
|
26
26
|
NFTCollectionBase,
|
|
27
27
|
NFTCollectionContractConstructor,
|
|
28
|
-
|
|
28
|
+
TransferBySignatureParams,
|
|
29
29
|
UInt64Option,
|
|
30
30
|
NFTTransactionContext,
|
|
31
31
|
} from "../interfaces/index.js";
|
|
@@ -178,9 +178,8 @@ export function BidFactory(params: {
|
|
|
178
178
|
const Collection = collectionContract();
|
|
179
179
|
const collection = new Collection(nftAddress.collection);
|
|
180
180
|
await collection.transferBySignature(
|
|
181
|
-
new
|
|
181
|
+
new TransferBySignatureParams({
|
|
182
182
|
address: nftAddress.nft,
|
|
183
|
-
from: PublicKey.empty(),
|
|
184
183
|
to: buyer,
|
|
185
184
|
price: UInt64Option.fromValue(price),
|
|
186
185
|
context: new NFTTransactionContext({
|
|
@@ -195,10 +194,9 @@ export function BidFactory(params: {
|
|
|
195
194
|
const buyer = this.buyer.getAndRequireEquals();
|
|
196
195
|
const Collection = collectionContract();
|
|
197
196
|
const collection = new Collection(nftAddress.collection);
|
|
198
|
-
await collection.
|
|
199
|
-
new
|
|
197
|
+
await collection.adminApprovedTransferBySignature(
|
|
198
|
+
new TransferBySignatureParams({
|
|
200
199
|
address: nftAddress.nft,
|
|
201
|
-
from: PublicKey.empty(),
|
|
202
200
|
to: buyer,
|
|
203
201
|
price: UInt64Option.fromValue(price),
|
|
204
202
|
context: new NFTTransactionContext({
|
package/src/util/div.ts
CHANGED
|
@@ -30,15 +30,22 @@ export function mulDiv(params: {
|
|
|
30
30
|
valueBigInt * multiplierBigInt - result * denominatorBigInt;
|
|
31
31
|
return { result: Field.from(result), remainder: Field.from(remainder) };
|
|
32
32
|
});
|
|
33
|
+
// We check that the result and remainder are in the correct range using Gadgets.rangeCheck64 before using Unsafe methods
|
|
34
|
+
// The next four lines should always be used together and cannot be separated
|
|
33
35
|
Gadgets.rangeCheck64(fields.result);
|
|
36
|
+
const result = UInt64.Unsafe.fromField(fields.result);
|
|
34
37
|
Gadgets.rangeCheck64(fields.remainder);
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
const remainder = UInt64.Unsafe.fromField(fields.remainder);
|
|
39
|
+
|
|
40
|
+
remainder.assertLessThan(denominator); // should fail in case the denominator is zero
|
|
41
|
+
|
|
42
|
+
// We use the Field representations of the values to avoid overflows
|
|
43
|
+
result.value
|
|
37
44
|
.mul(denominator.value)
|
|
38
|
-
.add(
|
|
45
|
+
.add(remainder.value)
|
|
39
46
|
.assertEquals(value.value.mul(multiplier.value)); // should fail in case the denominator is zero
|
|
40
47
|
return {
|
|
41
|
-
result
|
|
42
|
-
remainder
|
|
48
|
+
result,
|
|
49
|
+
remainder,
|
|
43
50
|
};
|
|
44
51
|
}
|
package/src/vk.ts
CHANGED
|
@@ -42,21 +42,21 @@ export const nftVerificationKeys: {
|
|
|
42
42
|
};
|
|
43
43
|
} = {
|
|
44
44
|
devnet: {
|
|
45
|
-
o1js: "2.
|
|
45
|
+
o1js: "2.3.0",
|
|
46
46
|
vk: {
|
|
47
47
|
NFT: {
|
|
48
|
-
hash: "
|
|
49
|
-
data: "AADUTaZ5kJK+C2TL7P/tc4MlgEq5zWOLFDtgDU/u9ry3Es1Ek79TcLqIWg8s6TJJcXzM0D/6xz1y8FQn2tGjjcspfNtNRAmG3FdldAatVpnkTwS6Otpm88gl7lOPX8bRJjhHfEtdvEsQ0OudcDzB5iCqu268zqkBvXrXT3xaNN+sIIqLTtxltMz4RS/2layxzL6mg1J+kkTsNIJsg6MufeMI6Xn5pAYOaWFqgo0N0WZsnF3EYcYq1LcDucyyFS2RqRninioewrlEDzjY8y6rmf9+GibQasJCE+mkbfB4wCOuFMiSrRIN/73BODz9siBxs/bU/p7xffJsOL8JvitK7ngRyG3PfGGdW22njv9MYxNhb/YhKnPA0qPTOQjxg1a/Pg8NyjB9RM7eypPJNLFaWFzNM4JRxjI7wGVVOfE0D7DUAL32SzQ1Jmr4mILqDhnDREu2ETq0Lb+c1cxPgb4x1nYbWcSgdAOtKJBvXHkWs7JlJdL1q9yiRrzYb1kPMPNGACnSB3N3Omm//FhxitOOM4yucxZyKpKst/otZu51/gGBDW5tIwKYpfl5ETSNvDFY+9rLUHv+LxSz+
|
|
48
|
+
hash: "9783798445384315170525620826037823720996093317060506623212107011216804106668",
|
|
49
|
+
data: "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",
|
|
50
50
|
type: "nft",
|
|
51
51
|
},
|
|
52
52
|
},
|
|
53
53
|
},
|
|
54
54
|
mainnet: {
|
|
55
|
-
o1js: "2.
|
|
55
|
+
o1js: "2.3.0",
|
|
56
56
|
vk: {
|
|
57
57
|
NFT: {
|
|
58
|
-
hash: "
|
|
59
|
-
data: "AADUTaZ5kJK+C2TL7P/tc4MlgEq5zWOLFDtgDU/u9ry3Es1Ek79TcLqIWg8s6TJJcXzM0D/6xz1y8FQn2tGjjcspfNtNRAmG3FdldAatVpnkTwS6Otpm88gl7lOPX8bRJjhHfEtdvEsQ0OudcDzB5iCqu268zqkBvXrXT3xaNN+sIIqLTtxltMz4RS/2layxzL6mg1J+kkTsNIJsg6MufeMI6Xn5pAYOaWFqgo0N0WZsnF3EYcYq1LcDucyyFS2RqRninioewrlEDzjY8y6rmf9+GibQasJCE+mkbfB4wCOuFMiSrRIN/73BODz9siBxs/bU/p7xffJsOL8JvitK7ngRyG3PfGGdW22njv9MYxNhb/YhKnPA0qPTOQjxg1a/Pg8NyjB9RM7eypPJNLFaWFzNM4JRxjI7wGVVOfE0D7DUAL32SzQ1Jmr4mILqDhnDREu2ETq0Lb+c1cxPgb4x1nYbWcSgdAOtKJBvXHkWs7JlJdL1q9yiRrzYb1kPMPNGACnSB3N3Omm//FhxitOOM4yucxZyKpKst/otZu51/gGBDW5tIwKYpfl5ETSNvDFY+9rLUHv+LxSz+
|
|
58
|
+
hash: "22175359608429605539570936386638550990144108869927626059283446793366639887120",
|
|
59
|
+
data: "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",
|
|
60
60
|
type: "nft",
|
|
61
61
|
},
|
|
62
62
|
},
|