@silvana-one/nft 0.1.1 → 0.1.3
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/dist/node/contracts/collection.d.ts +6 -1
- package/dist/node/contracts/collection.js +6 -4
- package/dist/node/contracts/collection.js.map +1 -1
- package/dist/node/index.cjs +834 -590
- package/dist/node/index.d.ts +1 -0
- package/dist/node/index.js +1 -0
- package/dist/node/index.js.map +1 -1
- package/dist/node/interfaces/owner.d.ts +1 -1
- package/dist/node/interfaces/types.d.ts +2 -2
- package/dist/node/interfaces/types.js +2 -2
- package/dist/node/interfaces/types.js.map +1 -1
- package/dist/node/marketplace/auction.js +11 -2
- package/dist/node/marketplace/auction.js.map +1 -1
- package/dist/node/marketplace/nft-shares.js +6 -5
- package/dist/node/marketplace/nft-shares.js.map +1 -1
- package/dist/node/metadata/address.d.ts +38 -0
- package/dist/node/metadata/address.js +39 -0
- package/dist/node/metadata/address.js.map +1 -0
- package/dist/node/metadata/index.d.ts +1 -0
- package/dist/node/metadata/index.js +1 -0
- package/dist/node/metadata/index.js.map +1 -1
- package/dist/node/metadata/metadata.d.ts +37 -6
- package/dist/node/metadata/metadata.js +147 -4
- package/dist/node/metadata/metadata.js.map +1 -1
- package/dist/node/metadata/pin.d.ts +10 -0
- package/dist/node/metadata/pin.js +18 -0
- package/dist/node/metadata/pin.js.map +1 -0
- package/dist/node/metadata/tree.d.ts +1 -1
- package/dist/node/metadata/tree.js +2 -2
- package/dist/node/metadata/tree.js.map +1 -1
- package/dist/node/util/div.d.ts +62 -0
- package/dist/node/util/div.js +39 -0
- package/dist/node/util/div.js.map +1 -0
- package/dist/node/util/index.d.ts +1 -0
- package/dist/node/util/index.js +2 -0
- package/dist/node/util/index.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/tsconfig.web.tsbuildinfo +1 -1
- package/dist/web/contracts/collection.d.ts +6 -1
- package/dist/web/contracts/collection.js +6 -4
- package/dist/web/contracts/collection.js.map +1 -1
- package/dist/web/index.d.ts +1 -0
- package/dist/web/index.js +1 -0
- package/dist/web/index.js.map +1 -1
- package/dist/web/interfaces/owner.d.ts +1 -1
- package/dist/web/interfaces/types.d.ts +2 -2
- package/dist/web/interfaces/types.js +2 -2
- package/dist/web/interfaces/types.js.map +1 -1
- package/dist/web/marketplace/auction.js +11 -2
- package/dist/web/marketplace/auction.js.map +1 -1
- package/dist/web/marketplace/nft-shares.js +6 -5
- package/dist/web/marketplace/nft-shares.js.map +1 -1
- package/dist/web/metadata/address.d.ts +38 -0
- package/dist/web/metadata/address.js +39 -0
- package/dist/web/metadata/address.js.map +1 -0
- package/dist/web/metadata/index.d.ts +1 -0
- package/dist/web/metadata/index.js +1 -0
- package/dist/web/metadata/index.js.map +1 -1
- package/dist/web/metadata/metadata.d.ts +37 -6
- package/dist/web/metadata/metadata.js +147 -4
- package/dist/web/metadata/metadata.js.map +1 -1
- package/dist/web/metadata/pin.d.ts +10 -0
- package/dist/web/metadata/pin.js +18 -0
- package/dist/web/metadata/pin.js.map +1 -0
- package/dist/web/metadata/tree.d.ts +1 -1
- package/dist/web/metadata/tree.js +2 -2
- package/dist/web/metadata/tree.js.map +1 -1
- package/dist/web/util/div.d.ts +62 -0
- package/dist/web/util/div.js +39 -0
- package/dist/web/util/div.js.map +1 -0
- package/dist/web/util/index.d.ts +1 -0
- package/dist/web/util/index.js +2 -0
- package/dist/web/util/index.js.map +1 -0
- package/package.json +8 -8
- package/src/contracts/collection.ts +6 -4
- package/src/index.ts +1 -0
- package/src/interfaces/owner.ts +1 -1
- package/src/interfaces/types.ts +4 -4
- package/src/marketplace/auction.ts +13 -2
- package/src/marketplace/nft-shares.ts +7 -8
- package/src/metadata/address.ts +50 -0
- package/src/metadata/index.ts +1 -0
- package/src/metadata/metadata.ts +236 -11
- package/src/metadata/pin.ts +30 -0
- package/src/metadata/tree.ts +4 -3
- package/src/util/div.ts +44 -0
- package/src/util/index.ts +1 -0
package/src/interfaces/types.ts
CHANGED
|
@@ -315,7 +315,7 @@ class NFTData extends Struct({
|
|
|
315
315
|
owner: string | PublicKey;
|
|
316
316
|
approved?: string | PublicKey;
|
|
317
317
|
version?: number;
|
|
318
|
-
id?: bigint;
|
|
318
|
+
id?: bigint | string;
|
|
319
319
|
canChangeOwnerByProof?: boolean;
|
|
320
320
|
canTransfer?: boolean;
|
|
321
321
|
canApprove?: boolean;
|
|
@@ -351,7 +351,7 @@ class NFTData extends Struct({
|
|
|
351
351
|
: approved
|
|
352
352
|
: PublicKey.empty(),
|
|
353
353
|
version: UInt32.from(version ?? 0),
|
|
354
|
-
id: UInt64.from(id ?? 0),
|
|
354
|
+
id: UInt64.from(BigInt(id ?? 0)),
|
|
355
355
|
canChangeOwnerByProof: Bool(canChangeOwnerByProof ?? false),
|
|
356
356
|
canTransfer: Bool(canTransfer ?? true),
|
|
357
357
|
canApprove: Bool(canApprove ?? true),
|
|
@@ -470,7 +470,7 @@ class CollectionData extends Struct({
|
|
|
470
470
|
*/
|
|
471
471
|
static new(params: {
|
|
472
472
|
royaltyFee?: number;
|
|
473
|
-
transferFee?: number;
|
|
473
|
+
transferFee?: number | bigint | string;
|
|
474
474
|
requireTransferApproval?: boolean;
|
|
475
475
|
mintingIsLimited?: boolean;
|
|
476
476
|
isPaused?: boolean;
|
|
@@ -484,7 +484,7 @@ class CollectionData extends Struct({
|
|
|
484
484
|
} = params;
|
|
485
485
|
return new CollectionData({
|
|
486
486
|
royaltyFee: UInt32.from(royaltyFee ?? 0),
|
|
487
|
-
transferFee: UInt64.from(transferFee ?? 0),
|
|
487
|
+
transferFee: UInt64.from(BigInt(transferFee ?? 0)),
|
|
488
488
|
requireTransferApproval: Bool(requireTransferApproval ?? false),
|
|
489
489
|
mintingIsLimited: Bool(mintingIsLimited ?? false),
|
|
490
490
|
isPaused: Bool(isPaused ?? false),
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
TransferExtendedParams,
|
|
26
26
|
TransferParams,
|
|
27
27
|
} from "../interfaces/index.js";
|
|
28
|
+
import { mulDiv } from "../util/index.js";
|
|
28
29
|
|
|
29
30
|
const MAX_SALE_FEE = 100000;
|
|
30
31
|
const MIN_STEP = 10; // 1% to previous bid
|
|
@@ -252,7 +253,11 @@ export function AuctionFactory(params: {
|
|
|
252
253
|
UInt32.from(MAX_SALE_FEE),
|
|
253
254
|
"Sale fee is too high"
|
|
254
255
|
);
|
|
255
|
-
return
|
|
256
|
+
return mulDiv({
|
|
257
|
+
value: price,
|
|
258
|
+
multiplier: UInt64.from(saleFee),
|
|
259
|
+
denominator: UInt64.from(MAX_SALE_FEE),
|
|
260
|
+
}).result;
|
|
256
261
|
}
|
|
257
262
|
// anyone can call this method to bid, paying the bid amount for the bidder
|
|
258
263
|
@method.returns(Auction)
|
|
@@ -268,7 +273,13 @@ export function AuctionFactory(params: {
|
|
|
268
273
|
"Bid should be greater or equal than the minimum price"
|
|
269
274
|
);
|
|
270
275
|
price.assertGreaterThan(
|
|
271
|
-
bidAmount.add(
|
|
276
|
+
bidAmount.add(
|
|
277
|
+
mulDiv({
|
|
278
|
+
value: bidAmount,
|
|
279
|
+
multiplier: UInt64.from(MIN_STEP),
|
|
280
|
+
denominator: UInt64.from(1000),
|
|
281
|
+
}).result
|
|
282
|
+
),
|
|
272
283
|
"Bid should be greater than the existing bid plus the minimum step"
|
|
273
284
|
);
|
|
274
285
|
this.network.globalSlotSinceGenesis.requireBetween(
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
} from "@silvana-one/token";
|
|
22
22
|
import { NFTOwnerBase, TransferExtendedParams } from "../interfaces/index.js";
|
|
23
23
|
import { Auction, AuctionFactory } from "./auction.js";
|
|
24
|
+
import { mulDiv } from "../util/index.js";
|
|
24
25
|
|
|
25
26
|
export interface NFTSharesAdminDeployProps
|
|
26
27
|
extends Exclude<DeployArgs, undefined> {
|
|
@@ -275,14 +276,12 @@ export function NFTSharesFactory(params: {
|
|
|
275
276
|
balance
|
|
276
277
|
.equals(UInt64.zero)
|
|
277
278
|
.assertFalse("Balance is zero, nothing to withdraw");
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
);
|
|
285
|
-
const amountInMina = UInt64.Unsafe.fromField(amountInMinaField);
|
|
279
|
+
const amountInMina = mulDiv({
|
|
280
|
+
value: shares,
|
|
281
|
+
multiplier: balance,
|
|
282
|
+
denominator: sharesOutstanding,
|
|
283
|
+
}).result;
|
|
284
|
+
|
|
286
285
|
const sender = this.sender.getUnconstrained();
|
|
287
286
|
const senderUpdate = AccountUpdate.createSigned(sender);
|
|
288
287
|
senderUpdate.balance.addInPlace(amountInMina);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Field, PublicKey, Poseidon } from "o1js";
|
|
2
|
+
export { MinaAddress };
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
/**
|
|
6
|
+
* The `MinaAddress` class represents a Mina address in the form of a Merkle tree. The address is converted to its
|
|
7
|
+
* hash and stored as a leaf in the Merkle tree. The root of the tree can be used as a compact representation
|
|
8
|
+
* of the address data in cryptographic proofs.
|
|
9
|
+
*/
|
|
10
|
+
class MinaAddress {
|
|
11
|
+
/**
|
|
12
|
+
* The original address.
|
|
13
|
+
*/
|
|
14
|
+
readonly address: PublicKey;
|
|
15
|
+
/**
|
|
16
|
+
* The hash of the address.
|
|
17
|
+
*/
|
|
18
|
+
readonly hash: Field;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Constructs a new `MinaAddress` instance by creating a Merkle tree from the given address.
|
|
22
|
+
* The address is converted to its hash and stored as a leaf in the tree.
|
|
23
|
+
*
|
|
24
|
+
* @param address - The address to be represented.
|
|
25
|
+
* @throws Will throw an error if the address is not a valid Mina address.
|
|
26
|
+
*/
|
|
27
|
+
constructor(address: PublicKey | string) {
|
|
28
|
+
this.address =
|
|
29
|
+
typeof address === "string" ? PublicKey.fromBase58(address) : address;
|
|
30
|
+
this.hash = Poseidon.hashPacked(PublicKey, this.address);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Returns the original address.
|
|
35
|
+
*
|
|
36
|
+
* @returns The public key.
|
|
37
|
+
*/
|
|
38
|
+
public toPublicKey(): PublicKey {
|
|
39
|
+
return this.address;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Returns the base58 representation of the address.
|
|
44
|
+
*
|
|
45
|
+
* @returns The base58 representation of the address.
|
|
46
|
+
*/
|
|
47
|
+
public toString(): string {
|
|
48
|
+
return this.address.toBase58();
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/metadata/index.ts
CHANGED
package/src/metadata/metadata.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Field, Poseidon, Struct, Experimental } from "o1js";
|
|
1
|
+
import { Field, Poseidon, Struct, Experimental, PublicKey, UInt64 } from "o1js";
|
|
2
2
|
import { fieldFromString } from "../interfaces/index.js";
|
|
3
3
|
import { Text } from "./text.js";
|
|
4
|
+
import { MinaAddress } from "./address.js";
|
|
4
5
|
import { MetadataTree } from "./tree.js";
|
|
5
6
|
export {
|
|
6
7
|
Metadata,
|
|
@@ -31,7 +32,9 @@ type MetadataFieldType =
|
|
|
31
32
|
| "text"
|
|
32
33
|
| "image"
|
|
33
34
|
| "url"
|
|
34
|
-
| "field"
|
|
35
|
+
| "field" // Field
|
|
36
|
+
| "number" // UInt64
|
|
37
|
+
| "address" // PublicKey
|
|
35
38
|
| "map"
|
|
36
39
|
| "tree";
|
|
37
40
|
|
|
@@ -50,7 +53,7 @@ class MetadataValue extends Struct({
|
|
|
50
53
|
* @returns A new MetadataValue.
|
|
51
54
|
*/
|
|
52
55
|
static new(params: {
|
|
53
|
-
value: Field | Text | Metadata | MetadataTree;
|
|
56
|
+
value: Field | Text | Metadata | MetadataTree | PublicKey | UInt64;
|
|
54
57
|
type: MetadataFieldType;
|
|
55
58
|
}) {
|
|
56
59
|
const { value, type } = params;
|
|
@@ -74,6 +77,18 @@ class MetadataValue extends Struct({
|
|
|
74
77
|
if (!(value instanceof Field)) throw new Error(`Invalid value type`);
|
|
75
78
|
valueField = value;
|
|
76
79
|
break;
|
|
80
|
+
case "number":
|
|
81
|
+
if (!(value instanceof UInt64)) throw new Error(`Invalid value type`);
|
|
82
|
+
valueField = value.value;
|
|
83
|
+
break;
|
|
84
|
+
case "address":
|
|
85
|
+
if (!(value instanceof PublicKey))
|
|
86
|
+
throw new Error(`Invalid value type`);
|
|
87
|
+
const address = new MinaAddress(value);
|
|
88
|
+
valueField = address.hash;
|
|
89
|
+
length = Field(2);
|
|
90
|
+
height = Field(0);
|
|
91
|
+
break;
|
|
77
92
|
case "map":
|
|
78
93
|
if (!(value instanceof Metadata)) throw new Error(`Invalid value type`);
|
|
79
94
|
valueField = value.map.root;
|
|
@@ -271,7 +286,14 @@ class Metadata {
|
|
|
271
286
|
traits: {
|
|
272
287
|
[key: string]: {
|
|
273
288
|
type: string;
|
|
274
|
-
value:
|
|
289
|
+
value:
|
|
290
|
+
| string
|
|
291
|
+
| Field
|
|
292
|
+
| Metadata
|
|
293
|
+
| MetadataTree
|
|
294
|
+
| UInt64
|
|
295
|
+
| PublicKey
|
|
296
|
+
| unknown;
|
|
275
297
|
isPrivate: boolean;
|
|
276
298
|
};
|
|
277
299
|
} = {};
|
|
@@ -328,7 +350,16 @@ class Metadata {
|
|
|
328
350
|
addTrait(params: {
|
|
329
351
|
key: string;
|
|
330
352
|
type: string;
|
|
331
|
-
value:
|
|
353
|
+
value:
|
|
354
|
+
| string
|
|
355
|
+
| Field
|
|
356
|
+
| Metadata
|
|
357
|
+
| MetadataTree
|
|
358
|
+
| UInt64
|
|
359
|
+
| PublicKey
|
|
360
|
+
| bigint
|
|
361
|
+
| number
|
|
362
|
+
| unknown;
|
|
332
363
|
isPrivate?: boolean;
|
|
333
364
|
}): {
|
|
334
365
|
key: Field;
|
|
@@ -340,7 +371,13 @@ class Metadata {
|
|
|
340
371
|
let canonicalRepresentation: unknown = value;
|
|
341
372
|
|
|
342
373
|
if (type in MetadataFieldTypeValues) {
|
|
343
|
-
let valueObject:
|
|
374
|
+
let valueObject:
|
|
375
|
+
| Field
|
|
376
|
+
| Text
|
|
377
|
+
| Metadata
|
|
378
|
+
| MetadataTree
|
|
379
|
+
| UInt64
|
|
380
|
+
| PublicKey;
|
|
344
381
|
switch (type) {
|
|
345
382
|
case "string":
|
|
346
383
|
if (typeof value !== "string")
|
|
@@ -355,9 +392,34 @@ class Metadata {
|
|
|
355
392
|
valueObject = new Text(value);
|
|
356
393
|
break;
|
|
357
394
|
case "field":
|
|
358
|
-
if (
|
|
395
|
+
if (
|
|
396
|
+
!(
|
|
397
|
+
value instanceof Field ||
|
|
398
|
+
typeof value === "bigint" ||
|
|
399
|
+
typeof value === "number" ||
|
|
400
|
+
typeof value === "string"
|
|
401
|
+
)
|
|
402
|
+
)
|
|
359
403
|
throw new Error(`Invalid trait value type`);
|
|
360
|
-
valueObject = value;
|
|
404
|
+
valueObject = Field(value);
|
|
405
|
+
break;
|
|
406
|
+
case "number":
|
|
407
|
+
if (
|
|
408
|
+
!(
|
|
409
|
+
value instanceof UInt64 ||
|
|
410
|
+
typeof value === "bigint" ||
|
|
411
|
+
typeof value === "number" ||
|
|
412
|
+
typeof value === "string"
|
|
413
|
+
)
|
|
414
|
+
)
|
|
415
|
+
throw new Error(`Invalid trait value type`);
|
|
416
|
+
valueObject = UInt64.from(value);
|
|
417
|
+
break;
|
|
418
|
+
case "address":
|
|
419
|
+
if (!(value instanceof PublicKey || typeof value === "string"))
|
|
420
|
+
throw new Error(`Invalid trait value type`);
|
|
421
|
+
valueObject =
|
|
422
|
+
typeof value === "string" ? PublicKey.fromBase58(value) : value;
|
|
361
423
|
break;
|
|
362
424
|
case "map":
|
|
363
425
|
if (!(value instanceof Metadata))
|
|
@@ -431,6 +493,16 @@ class Metadata {
|
|
|
431
493
|
throw new Error(`Invalid trait value type`);
|
|
432
494
|
jsonValue = value.toJSON();
|
|
433
495
|
break;
|
|
496
|
+
case "number":
|
|
497
|
+
if (!(value instanceof UInt64))
|
|
498
|
+
throw new Error(`Invalid trait value type`);
|
|
499
|
+
jsonValue = value.toJSON();
|
|
500
|
+
break;
|
|
501
|
+
case "address":
|
|
502
|
+
if (!(value instanceof PublicKey))
|
|
503
|
+
throw new Error(`Invalid trait value type`);
|
|
504
|
+
jsonValue = value.toBase58();
|
|
505
|
+
break;
|
|
434
506
|
case "map":
|
|
435
507
|
if (!(value instanceof Metadata))
|
|
436
508
|
throw new Error(`Invalid trait value type`);
|
|
@@ -470,7 +542,7 @@ class Metadata {
|
|
|
470
542
|
description?: string;
|
|
471
543
|
banner?: string;
|
|
472
544
|
metadataRoot: string;
|
|
473
|
-
traits
|
|
545
|
+
traits?: {
|
|
474
546
|
key: string;
|
|
475
547
|
type: string;
|
|
476
548
|
value: string | object;
|
|
@@ -481,7 +553,14 @@ class Metadata {
|
|
|
481
553
|
plugins?: MetadataPlugin[];
|
|
482
554
|
}): Metadata {
|
|
483
555
|
const { json, checkRoot = false, plugins } = params;
|
|
484
|
-
const {
|
|
556
|
+
const {
|
|
557
|
+
name,
|
|
558
|
+
description,
|
|
559
|
+
image,
|
|
560
|
+
banner,
|
|
561
|
+
metadataRoot,
|
|
562
|
+
traits = [],
|
|
563
|
+
} = json;
|
|
485
564
|
if (!name) throw new Error(`Metadata name is required`);
|
|
486
565
|
if (typeof name !== "string") throw new Error(`Invalid metadata name`);
|
|
487
566
|
if (!image || typeof image !== "string")
|
|
@@ -511,7 +590,14 @@ class Metadata {
|
|
|
511
590
|
plugins,
|
|
512
591
|
});
|
|
513
592
|
for (const { key, type, value, isPrivate } of traits) {
|
|
514
|
-
let valueField:
|
|
593
|
+
let valueField:
|
|
594
|
+
| string
|
|
595
|
+
| Field
|
|
596
|
+
| Metadata
|
|
597
|
+
| MetadataTree
|
|
598
|
+
| UInt64
|
|
599
|
+
| PublicKey
|
|
600
|
+
| unknown;
|
|
515
601
|
switch (type) {
|
|
516
602
|
case "string":
|
|
517
603
|
case "text":
|
|
@@ -526,6 +612,16 @@ class Metadata {
|
|
|
526
612
|
throw new Error(`Invalid trait value type`);
|
|
527
613
|
valueField = Field.fromJSON(value);
|
|
528
614
|
break;
|
|
615
|
+
case "number":
|
|
616
|
+
if (typeof value !== "string")
|
|
617
|
+
throw new Error(`Invalid trait value type`);
|
|
618
|
+
valueField = UInt64.fromJSON(value);
|
|
619
|
+
break;
|
|
620
|
+
case "address":
|
|
621
|
+
if (typeof value !== "string")
|
|
622
|
+
throw new Error(`Invalid trait value type`);
|
|
623
|
+
valueField = PublicKey.fromBase58(value);
|
|
624
|
+
break;
|
|
529
625
|
case "map":
|
|
530
626
|
if (typeof value !== "object")
|
|
531
627
|
throw new Error(`Invalid trait value type`);
|
|
@@ -587,6 +683,133 @@ class Metadata {
|
|
|
587
683
|
|
|
588
684
|
return metadata;
|
|
589
685
|
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* Constructs a Metadata instance from OpenAPI JSON data (without calculated root).
|
|
689
|
+
* @param params - The parameters including json data, checkRoot flag, and plugins.
|
|
690
|
+
* @returns A new Metadata instance.
|
|
691
|
+
*/
|
|
692
|
+
static fromOpenApiJSON(params: {
|
|
693
|
+
json: {
|
|
694
|
+
name: string;
|
|
695
|
+
image: string;
|
|
696
|
+
description?: string;
|
|
697
|
+
banner?: string;
|
|
698
|
+
traits?: {
|
|
699
|
+
key: string;
|
|
700
|
+
type: string;
|
|
701
|
+
value: string | object;
|
|
702
|
+
isPrivate?: boolean;
|
|
703
|
+
}[];
|
|
704
|
+
};
|
|
705
|
+
plugins?: MetadataPlugin[];
|
|
706
|
+
}): Metadata {
|
|
707
|
+
const { json, plugins } = params;
|
|
708
|
+
const { name, description, image, banner, traits = [] } = json;
|
|
709
|
+
if (!name) throw new Error(`Metadata name is required`);
|
|
710
|
+
if (typeof name !== "string") throw new Error(`Invalid metadata name`);
|
|
711
|
+
if (!image || typeof image !== "string")
|
|
712
|
+
throw new Error(`Invalid metadata image`);
|
|
713
|
+
if (description && typeof description !== "string")
|
|
714
|
+
throw new Error(`Invalid metadata description`);
|
|
715
|
+
if (banner && typeof banner !== "string")
|
|
716
|
+
throw new Error(`Invalid metadata banner`);
|
|
717
|
+
if (!traits || !Array.isArray(traits))
|
|
718
|
+
throw new Error(`Metadata traits are required`);
|
|
719
|
+
for (const { key, type, value, isPrivate } of traits) {
|
|
720
|
+
if (!key || typeof key !== "string") throw new Error(`Invalid trait key`);
|
|
721
|
+
if (!type || typeof type !== "string")
|
|
722
|
+
throw new Error(`Invalid trait type`);
|
|
723
|
+
if (!value || (typeof value !== "string" && typeof value !== "object"))
|
|
724
|
+
throw new Error(`Invalid trait value`);
|
|
725
|
+
if (isPrivate && typeof isPrivate !== "boolean")
|
|
726
|
+
throw new Error(`Invalid trait isPrivate`);
|
|
727
|
+
}
|
|
728
|
+
const metadata = new Metadata({
|
|
729
|
+
name,
|
|
730
|
+
description,
|
|
731
|
+
image,
|
|
732
|
+
banner,
|
|
733
|
+
plugins,
|
|
734
|
+
});
|
|
735
|
+
for (const { key, type, value, isPrivate } of traits) {
|
|
736
|
+
let valueField:
|
|
737
|
+
| string
|
|
738
|
+
| Field
|
|
739
|
+
| Metadata
|
|
740
|
+
| MetadataTree
|
|
741
|
+
| UInt64
|
|
742
|
+
| PublicKey
|
|
743
|
+
| unknown;
|
|
744
|
+
switch (type) {
|
|
745
|
+
case "string":
|
|
746
|
+
case "text":
|
|
747
|
+
case "image":
|
|
748
|
+
case "url":
|
|
749
|
+
if (typeof value !== "string")
|
|
750
|
+
throw new Error(`Invalid trait value type`);
|
|
751
|
+
valueField = value;
|
|
752
|
+
break;
|
|
753
|
+
case "field":
|
|
754
|
+
if (typeof value !== "string")
|
|
755
|
+
throw new Error(`Invalid trait value type`);
|
|
756
|
+
valueField = Field.fromJSON(value);
|
|
757
|
+
break;
|
|
758
|
+
case "number":
|
|
759
|
+
if (typeof value !== "string")
|
|
760
|
+
throw new Error(`Invalid trait value type`);
|
|
761
|
+
valueField = UInt64.fromJSON(value);
|
|
762
|
+
break;
|
|
763
|
+
case "address":
|
|
764
|
+
if (typeof value !== "string")
|
|
765
|
+
throw new Error(`Invalid trait value type`);
|
|
766
|
+
valueField = PublicKey.fromBase58(value);
|
|
767
|
+
break;
|
|
768
|
+
case "map":
|
|
769
|
+
if (typeof value !== "object")
|
|
770
|
+
throw new Error(`Invalid trait value type`);
|
|
771
|
+
valueField = Metadata.fromOpenApiJSON({
|
|
772
|
+
json: value as unknown as {
|
|
773
|
+
name: string;
|
|
774
|
+
image: string;
|
|
775
|
+
description?: string;
|
|
776
|
+
traits: {
|
|
777
|
+
key: string;
|
|
778
|
+
type: string;
|
|
779
|
+
value: string | object;
|
|
780
|
+
isPrivate?: boolean;
|
|
781
|
+
}[];
|
|
782
|
+
},
|
|
783
|
+
});
|
|
784
|
+
break;
|
|
785
|
+
case "tree":
|
|
786
|
+
if (typeof value !== "object")
|
|
787
|
+
throw new Error(`Invalid trait value type`);
|
|
788
|
+
valueField = MetadataTree.fromJSON(
|
|
789
|
+
value as unknown as {
|
|
790
|
+
height: number;
|
|
791
|
+
root: string;
|
|
792
|
+
values: { key: string; value: string }[];
|
|
793
|
+
}
|
|
794
|
+
);
|
|
795
|
+
break;
|
|
796
|
+
default:
|
|
797
|
+
const plugin = metadata.plugins.find(
|
|
798
|
+
(plugin) => plugin.name === type
|
|
799
|
+
);
|
|
800
|
+
if (!plugin) throw new Error(`Unknown trait type`);
|
|
801
|
+
valueField = plugin.fromJSON(value);
|
|
802
|
+
}
|
|
803
|
+
metadata.addTrait({
|
|
804
|
+
key,
|
|
805
|
+
type,
|
|
806
|
+
value: valueField,
|
|
807
|
+
isPrivate: isPrivate ?? false,
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
return metadata;
|
|
812
|
+
}
|
|
590
813
|
}
|
|
591
814
|
|
|
592
815
|
/**
|
|
@@ -600,4 +823,6 @@ const MetadataFieldTypeValues = {
|
|
|
600
823
|
field: { code: 5n, inputType: Field, storedType: Field }, // Field
|
|
601
824
|
map: { code: 6n, inputType: Metadata, storedType: Metadata }, // Metadata
|
|
602
825
|
tree: { code: 7n, inputType: MetadataTree, storedType: MetadataTree }, // MetadataTree
|
|
826
|
+
number: { code: 8n, inputType: UInt64, storedType: UInt64 }, // UInt64
|
|
827
|
+
address: { code: 9n, inputType: PublicKey, storedType: MinaAddress }, // MinaAddress
|
|
603
828
|
} as const;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Metadata } from "./metadata.js";
|
|
2
|
+
import {
|
|
3
|
+
pinJSON,
|
|
4
|
+
serializeIndexedMap,
|
|
5
|
+
IndexedMapSerialized,
|
|
6
|
+
} from "@silvana-one/storage";
|
|
7
|
+
import { Field } from "o1js";
|
|
8
|
+
|
|
9
|
+
export async function pinMetadata(metadata: Metadata): Promise<{
|
|
10
|
+
name: string;
|
|
11
|
+
ipfsHash: string;
|
|
12
|
+
metadataRoot: Field;
|
|
13
|
+
privateMetadata: string;
|
|
14
|
+
serializedMap: IndexedMapSerialized;
|
|
15
|
+
}> {
|
|
16
|
+
const privateMetadata = JSON.stringify(metadata.toJSON(true), null, 2);
|
|
17
|
+
const ipfsHash: string | undefined = await pinJSON({
|
|
18
|
+
data: metadata.toJSON(false),
|
|
19
|
+
name: "nft-metadata",
|
|
20
|
+
});
|
|
21
|
+
if (!ipfsHash) throw new Error("Failed to pin metadata");
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
name: metadata.name,
|
|
25
|
+
ipfsHash,
|
|
26
|
+
metadataRoot: metadata.map.root,
|
|
27
|
+
privateMetadata,
|
|
28
|
+
serializedMap: serializeIndexedMap(metadata.map),
|
|
29
|
+
};
|
|
30
|
+
}
|
package/src/metadata/tree.ts
CHANGED
|
@@ -94,7 +94,7 @@ export class MetadataTree {
|
|
|
94
94
|
*/
|
|
95
95
|
static fromJSON(json: {
|
|
96
96
|
height: number;
|
|
97
|
-
root
|
|
97
|
+
root?: string;
|
|
98
98
|
values: { key: string; value: string }[];
|
|
99
99
|
}): MetadataTree {
|
|
100
100
|
const { height, values, root } = json;
|
|
@@ -102,7 +102,7 @@ export class MetadataTree {
|
|
|
102
102
|
if (typeof height !== "number" || height < 1 || height > 254)
|
|
103
103
|
throw new Error(`Invalid tree height`);
|
|
104
104
|
|
|
105
|
-
if (
|
|
105
|
+
if (root && typeof root !== "string") throw new Error(`Invalid tree root`);
|
|
106
106
|
|
|
107
107
|
if (!values || !Array.isArray(values))
|
|
108
108
|
throw new Error(`Tree values are required`);
|
|
@@ -121,7 +121,8 @@ export class MetadataTree {
|
|
|
121
121
|
}))
|
|
122
122
|
);
|
|
123
123
|
|
|
124
|
-
if (tree.root.toJSON() !== root)
|
|
124
|
+
if (root && tree.root.toJSON() !== root)
|
|
125
|
+
throw new Error("Invalid tree json");
|
|
125
126
|
|
|
126
127
|
return tree;
|
|
127
128
|
}
|
package/src/util/div.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Field, Provable, UInt64, Gadgets, Struct } from "o1js";
|
|
2
|
+
|
|
3
|
+
export class MulDivResult extends Struct({
|
|
4
|
+
result: UInt64,
|
|
5
|
+
remainder: UInt64,
|
|
6
|
+
}) {}
|
|
7
|
+
|
|
8
|
+
class MulDivResultInternal extends Struct({
|
|
9
|
+
result: Field,
|
|
10
|
+
remainder: Field,
|
|
11
|
+
}) {}
|
|
12
|
+
|
|
13
|
+
export function mulDiv(params: {
|
|
14
|
+
value: UInt64;
|
|
15
|
+
multiplier: UInt64;
|
|
16
|
+
denominator: UInt64;
|
|
17
|
+
}): MulDivResult {
|
|
18
|
+
const { value, multiplier, denominator } = params;
|
|
19
|
+
denominator.equals(UInt64.zero).assertFalse("division by zero"); // should fail in case the denominator is zero
|
|
20
|
+
const fields = Provable.witness(MulDivResultInternal, () => {
|
|
21
|
+
const valueBigInt = value.toBigInt();
|
|
22
|
+
const multiplierBigInt = multiplier.toBigInt();
|
|
23
|
+
const denominatorBigInt = denominator.toBigInt();
|
|
24
|
+
// handle division by zero for first pass of the prover that can pass zero instead of the real value
|
|
25
|
+
if (denominatorBigInt === 0n) {
|
|
26
|
+
return { result: Field.from(0n), remainder: Field.from(0n) };
|
|
27
|
+
}
|
|
28
|
+
const result = (valueBigInt * multiplierBigInt) / denominatorBigInt;
|
|
29
|
+
const remainder =
|
|
30
|
+
valueBigInt * multiplierBigInt - result * denominatorBigInt;
|
|
31
|
+
return { result: Field.from(result), remainder: Field.from(remainder) };
|
|
32
|
+
});
|
|
33
|
+
Gadgets.rangeCheck64(fields.result);
|
|
34
|
+
Gadgets.rangeCheck64(fields.remainder);
|
|
35
|
+
fields.remainder.assertLessThan(denominator.value); // should fail in case the denominator is zero
|
|
36
|
+
fields.result
|
|
37
|
+
.mul(denominator.value)
|
|
38
|
+
.add(fields.remainder)
|
|
39
|
+
.assertEquals(value.value.mul(multiplier.value)); // should fail in case the denominator is zero
|
|
40
|
+
return {
|
|
41
|
+
result: UInt64.Unsafe.fromField(fields.result),
|
|
42
|
+
remainder: UInt64.Unsafe.fromField(fields.remainder),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./div.js";
|