@unicitylabs/sphere-sdk 0.17.2-dev.2 → 0.17.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/connect/index.cjs +7 -4
- package/dist/connect/index.cjs.map +1 -1
- package/dist/connect/index.d.cts +4 -0
- package/dist/connect/index.d.ts +4 -0
- package/dist/connect/index.js +7 -4
- package/dist/connect/index.js.map +1 -1
- package/dist/core/index.cjs +69 -22
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +2 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +69 -22
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/connect/index.cjs +1 -1
- package/dist/impl/browser/connect/index.cjs.map +1 -1
- package/dist/impl/browser/connect/index.js +1 -1
- package/dist/impl/browser/connect/index.js.map +1 -1
- package/dist/impl/wallet-api-v2/index.cjs +3 -0
- package/dist/impl/wallet-api-v2/index.cjs.map +1 -1
- package/dist/impl/wallet-api-v2/index.js +3 -0
- package/dist/impl/wallet-api-v2/index.js.map +1 -1
- package/dist/index.cjs +88 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -1
- package/dist/index.d.ts +22 -1
- package/dist/index.js +85 -22
- package/dist/index.js.map +1 -1
- package/dist/modules/payments-v2/index.cjs +40 -6
- package/dist/modules/payments-v2/index.cjs.map +1 -1
- package/dist/modules/payments-v2/index.d.cts +22 -1
- package/dist/modules/payments-v2/index.d.ts +22 -1
- package/dist/modules/payments-v2/index.js +37 -6
- package/dist/modules/payments-v2/index.js.map +1 -1
- package/dist/token-engine/index.cjs +67 -21
- package/dist/token-engine/index.cjs.map +1 -1
- package/dist/token-engine/index.d.cts +28 -4
- package/dist/token-engine/index.d.ts +28 -4
- package/dist/token-engine/index.js +64 -21
- package/dist/token-engine/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -9,6 +9,8 @@ declare const NFT_MEDIA_TAG = 39053n;
|
|
|
9
9
|
declare const NFT_LINK_TAG = 39054n;
|
|
10
10
|
declare const NFT_SIGNED_TAG = 39055n;
|
|
11
11
|
declare const NFT_FORMAT_VERSION = 1n;
|
|
12
|
+
/** The media type of an NftLink that points at a metadata document instead of a media file. */
|
|
13
|
+
declare const NFT_DOCUMENT_MEDIA_TYPE = "application/vnd.unicity.nft+cbor";
|
|
12
14
|
/** An inline file. */
|
|
13
15
|
interface NftMedia {
|
|
14
16
|
readonly kind: 'media';
|
|
@@ -38,6 +40,8 @@ interface NftMetadata {
|
|
|
38
40
|
readonly external_url: string | null;
|
|
39
41
|
readonly attributes: readonly NftAttribute[];
|
|
40
42
|
readonly collection: string | null;
|
|
43
|
+
/** The collection the item claims: 1–64 bytes as even-length hex (decoded lowercase). Never verified here. */
|
|
44
|
+
readonly collection_id: string | null;
|
|
41
45
|
}
|
|
42
46
|
type NftContent = NftMetadata | NftMedia | NftLink;
|
|
43
47
|
type NftSignatureStatus = 'unsigned' | 'valid' | 'invalid';
|
|
@@ -50,16 +54,36 @@ interface ParsedNft {
|
|
|
50
54
|
readonly signature: Uint8Array;
|
|
51
55
|
} | null;
|
|
52
56
|
}
|
|
57
|
+
/** What an NftSigned signature binds besides its payload, exactly as the token's mint records it. */
|
|
58
|
+
interface NftSignatureContext {
|
|
59
|
+
/** The network identifier α (`NetworkId.id`). */
|
|
60
|
+
readonly networkId: number | bigint;
|
|
61
|
+
/** The genesis recipient predicate, as `EncodedPredicate.toCBOR()` bytes. */
|
|
62
|
+
readonly recipientPredicate: Uint8Array;
|
|
63
|
+
/** The 32 raw token id bytes. */
|
|
64
|
+
readonly tokenId: Uint8Array;
|
|
65
|
+
readonly tokenType: Uint8Array;
|
|
66
|
+
}
|
|
53
67
|
/** Encode one unsigned item. Throws SphereError('…', 'VALIDATION_ERROR') naming the offending field. */
|
|
54
68
|
declare function encodeNftContent(content: NftContent): Uint8Array;
|
|
55
69
|
/** Wrap an encoded unsigned item; `signature` is `Signature.encode()` over {@link nftSignedDigest}. */
|
|
56
70
|
declare function encodeNftSigned(creator: Uint8Array, payload: Uint8Array, signature: Uint8Array): Uint8Array;
|
|
57
71
|
/** Structural parse of a genesis payload. NEVER throws; null = not a recognised NFT. */
|
|
58
72
|
declare function parseNftPayload(data: Uint8Array | null): ParsedNft | null;
|
|
59
|
-
/**
|
|
60
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Parse the file a document link points at: exactly one NftMetadata or NftMedia item.
|
|
75
|
+
* NEVER throws; null = not a document. Check the bytes with verifyNftLinkContent first.
|
|
76
|
+
*/
|
|
77
|
+
declare function parseNftDocument(bytes: Uint8Array): NftMetadata | NftMedia | null;
|
|
78
|
+
/** Is this an NftLink to a metadata document ({@link NFT_DOCUMENT_MEDIA_TYPE}), resolved with parseNftDocument? */
|
|
79
|
+
declare function isNftDocumentLink(content: NftContent): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* SHA-256 of CBOR `[h'UNICITY_NFT_SIGNED', 1, α, h(recipient predicate), h(token id), h(token type), h(payload)]`:
|
|
82
|
+
* every term a byte string except the version and α, as in the yellowpaper genesis commitment.
|
|
83
|
+
*/
|
|
84
|
+
declare function nftSignedDigest(context: NftSignatureContext, payload: Uint8Array): Promise<DataHash>;
|
|
61
85
|
/** Recovering verification: binds the recovery byte and refuses high-s. Never throws. */
|
|
62
|
-
declare function verifyNftSignature(signed: NonNullable<ParsedNft['signed']>,
|
|
86
|
+
declare function verifyNftSignature(signed: NonNullable<ParsedNft['signed']>, context: NftSignatureContext): Promise<Exclude<NftSignatureStatus, 'unsigned'>>;
|
|
63
87
|
/** Does `bytes` match the link's sha256? Pure, sync. */
|
|
64
88
|
declare function verifyNftLinkContent(link: NftLink, bytes: Uint8Array): boolean;
|
|
65
89
|
|
|
@@ -656,4 +680,4 @@ declare class SpherePaymentData implements IPaymentData {
|
|
|
656
680
|
*/
|
|
657
681
|
declare function decodeSpherePaymentData(bytes: Uint8Array): Promise<IPaymentData>;
|
|
658
682
|
|
|
659
|
-
export { type BuildNftMintParams, CheckpointPersistFailedError, CheckpointTrustbaseMismatchError, type CoinId, type CreateTokenEngine, type EngineConfig, type EngineIdentity, type EngineOpOptions, type EngineVerifyResult, type ITokenEngine, type MintDataTokenParams, type MintParams, NFT_FORMAT_VERSION, NFT_LINK_TAG, NFT_MEDIA_TAG, NFT_METADATA_TAG, NFT_SIGNED_TAG, type NftAttribute, type NftContent, type NftLink, type NftMedia, type NftMediaRef, type NftMetadata, type NftMintPlan, type NftReading, type NftSignatureStatus, type ParsedNft, ProofUnconfirmedError, type SphereAsset, type SphereNetwork, SpherePaymentData, type SphereToken, type SphereValue, SplitCheckpointLostError, type SplitCheckpointStore, type SplitOutput, type SplitParams, type SplitResult, type TokenBlob, TransferConflictError, type TransferParams, type VerificationWorker, type VerificationWorkerConfig, createSphereTokenEngine, decodeSpherePaymentData, deriveDirectAddress, encodeNftContent, encodeNftSigned, nftSignedDigest, parseNftPayload, sphereAssetToSdk, verifyNftLinkContent, verifyNftSignature };
|
|
683
|
+
export { type BuildNftMintParams, CheckpointPersistFailedError, CheckpointTrustbaseMismatchError, type CoinId, type CreateTokenEngine, type EngineConfig, type EngineIdentity, type EngineOpOptions, type EngineVerifyResult, type ITokenEngine, type MintDataTokenParams, type MintParams, NFT_DOCUMENT_MEDIA_TYPE, NFT_FORMAT_VERSION, NFT_LINK_TAG, NFT_MEDIA_TAG, NFT_METADATA_TAG, NFT_SIGNED_TAG, type NftAttribute, type NftContent, type NftLink, type NftMedia, type NftMediaRef, type NftMetadata, type NftMintPlan, type NftReading, type NftSignatureContext, type NftSignatureStatus, type ParsedNft, ProofUnconfirmedError, type SphereAsset, type SphereNetwork, SpherePaymentData, type SphereToken, type SphereValue, SplitCheckpointLostError, type SplitCheckpointStore, type SplitOutput, type SplitParams, type SplitResult, type TokenBlob, TransferConflictError, type TransferParams, type VerificationWorker, type VerificationWorkerConfig, createSphereTokenEngine, decodeSpherePaymentData, deriveDirectAddress, encodeNftContent, encodeNftSigned, isNftDocumentLink, nftSignedDigest, parseNftDocument, parseNftPayload, sphereAssetToSdk, verifyNftLinkContent, verifyNftSignature };
|
|
@@ -9,6 +9,8 @@ declare const NFT_MEDIA_TAG = 39053n;
|
|
|
9
9
|
declare const NFT_LINK_TAG = 39054n;
|
|
10
10
|
declare const NFT_SIGNED_TAG = 39055n;
|
|
11
11
|
declare const NFT_FORMAT_VERSION = 1n;
|
|
12
|
+
/** The media type of an NftLink that points at a metadata document instead of a media file. */
|
|
13
|
+
declare const NFT_DOCUMENT_MEDIA_TYPE = "application/vnd.unicity.nft+cbor";
|
|
12
14
|
/** An inline file. */
|
|
13
15
|
interface NftMedia {
|
|
14
16
|
readonly kind: 'media';
|
|
@@ -38,6 +40,8 @@ interface NftMetadata {
|
|
|
38
40
|
readonly external_url: string | null;
|
|
39
41
|
readonly attributes: readonly NftAttribute[];
|
|
40
42
|
readonly collection: string | null;
|
|
43
|
+
/** The collection the item claims: 1–64 bytes as even-length hex (decoded lowercase). Never verified here. */
|
|
44
|
+
readonly collection_id: string | null;
|
|
41
45
|
}
|
|
42
46
|
type NftContent = NftMetadata | NftMedia | NftLink;
|
|
43
47
|
type NftSignatureStatus = 'unsigned' | 'valid' | 'invalid';
|
|
@@ -50,16 +54,36 @@ interface ParsedNft {
|
|
|
50
54
|
readonly signature: Uint8Array;
|
|
51
55
|
} | null;
|
|
52
56
|
}
|
|
57
|
+
/** What an NftSigned signature binds besides its payload, exactly as the token's mint records it. */
|
|
58
|
+
interface NftSignatureContext {
|
|
59
|
+
/** The network identifier α (`NetworkId.id`). */
|
|
60
|
+
readonly networkId: number | bigint;
|
|
61
|
+
/** The genesis recipient predicate, as `EncodedPredicate.toCBOR()` bytes. */
|
|
62
|
+
readonly recipientPredicate: Uint8Array;
|
|
63
|
+
/** The 32 raw token id bytes. */
|
|
64
|
+
readonly tokenId: Uint8Array;
|
|
65
|
+
readonly tokenType: Uint8Array;
|
|
66
|
+
}
|
|
53
67
|
/** Encode one unsigned item. Throws SphereError('…', 'VALIDATION_ERROR') naming the offending field. */
|
|
54
68
|
declare function encodeNftContent(content: NftContent): Uint8Array;
|
|
55
69
|
/** Wrap an encoded unsigned item; `signature` is `Signature.encode()` over {@link nftSignedDigest}. */
|
|
56
70
|
declare function encodeNftSigned(creator: Uint8Array, payload: Uint8Array, signature: Uint8Array): Uint8Array;
|
|
57
71
|
/** Structural parse of a genesis payload. NEVER throws; null = not a recognised NFT. */
|
|
58
72
|
declare function parseNftPayload(data: Uint8Array | null): ParsedNft | null;
|
|
59
|
-
/**
|
|
60
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Parse the file a document link points at: exactly one NftMetadata or NftMedia item.
|
|
75
|
+
* NEVER throws; null = not a document. Check the bytes with verifyNftLinkContent first.
|
|
76
|
+
*/
|
|
77
|
+
declare function parseNftDocument(bytes: Uint8Array): NftMetadata | NftMedia | null;
|
|
78
|
+
/** Is this an NftLink to a metadata document ({@link NFT_DOCUMENT_MEDIA_TYPE}), resolved with parseNftDocument? */
|
|
79
|
+
declare function isNftDocumentLink(content: NftContent): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* SHA-256 of CBOR `[h'UNICITY_NFT_SIGNED', 1, α, h(recipient predicate), h(token id), h(token type), h(payload)]`:
|
|
82
|
+
* every term a byte string except the version and α, as in the yellowpaper genesis commitment.
|
|
83
|
+
*/
|
|
84
|
+
declare function nftSignedDigest(context: NftSignatureContext, payload: Uint8Array): Promise<DataHash>;
|
|
61
85
|
/** Recovering verification: binds the recovery byte and refuses high-s. Never throws. */
|
|
62
|
-
declare function verifyNftSignature(signed: NonNullable<ParsedNft['signed']>,
|
|
86
|
+
declare function verifyNftSignature(signed: NonNullable<ParsedNft['signed']>, context: NftSignatureContext): Promise<Exclude<NftSignatureStatus, 'unsigned'>>;
|
|
63
87
|
/** Does `bytes` match the link's sha256? Pure, sync. */
|
|
64
88
|
declare function verifyNftLinkContent(link: NftLink, bytes: Uint8Array): boolean;
|
|
65
89
|
|
|
@@ -656,4 +680,4 @@ declare class SpherePaymentData implements IPaymentData {
|
|
|
656
680
|
*/
|
|
657
681
|
declare function decodeSpherePaymentData(bytes: Uint8Array): Promise<IPaymentData>;
|
|
658
682
|
|
|
659
|
-
export { type BuildNftMintParams, CheckpointPersistFailedError, CheckpointTrustbaseMismatchError, type CoinId, type CreateTokenEngine, type EngineConfig, type EngineIdentity, type EngineOpOptions, type EngineVerifyResult, type ITokenEngine, type MintDataTokenParams, type MintParams, NFT_FORMAT_VERSION, NFT_LINK_TAG, NFT_MEDIA_TAG, NFT_METADATA_TAG, NFT_SIGNED_TAG, type NftAttribute, type NftContent, type NftLink, type NftMedia, type NftMediaRef, type NftMetadata, type NftMintPlan, type NftReading, type NftSignatureStatus, type ParsedNft, ProofUnconfirmedError, type SphereAsset, type SphereNetwork, SpherePaymentData, type SphereToken, type SphereValue, SplitCheckpointLostError, type SplitCheckpointStore, type SplitOutput, type SplitParams, type SplitResult, type TokenBlob, TransferConflictError, type TransferParams, type VerificationWorker, type VerificationWorkerConfig, createSphereTokenEngine, decodeSpherePaymentData, deriveDirectAddress, encodeNftContent, encodeNftSigned, nftSignedDigest, parseNftPayload, sphereAssetToSdk, verifyNftLinkContent, verifyNftSignature };
|
|
683
|
+
export { type BuildNftMintParams, CheckpointPersistFailedError, CheckpointTrustbaseMismatchError, type CoinId, type CreateTokenEngine, type EngineConfig, type EngineIdentity, type EngineOpOptions, type EngineVerifyResult, type ITokenEngine, type MintDataTokenParams, type MintParams, NFT_DOCUMENT_MEDIA_TYPE, NFT_FORMAT_VERSION, NFT_LINK_TAG, NFT_MEDIA_TAG, NFT_METADATA_TAG, NFT_SIGNED_TAG, type NftAttribute, type NftContent, type NftLink, type NftMedia, type NftMediaRef, type NftMetadata, type NftMintPlan, type NftReading, type NftSignatureContext, type NftSignatureStatus, type ParsedNft, ProofUnconfirmedError, type SphereAsset, type SphereNetwork, SpherePaymentData, type SphereToken, type SphereValue, SplitCheckpointLostError, type SplitCheckpointStore, type SplitOutput, type SplitParams, type SplitResult, type TokenBlob, TransferConflictError, type TransferParams, type VerificationWorker, type VerificationWorkerConfig, createSphereTokenEngine, decodeSpherePaymentData, deriveDirectAddress, encodeNftContent, encodeNftSigned, isNftDocumentLink, nftSignedDigest, parseNftDocument, parseNftPayload, sphereAssetToSdk, verifyNftLinkContent, verifyNftSignature };
|
|
@@ -927,7 +927,8 @@ var NFT_MEDIA_TAG = 39053n;
|
|
|
927
927
|
var NFT_LINK_TAG = 39054n;
|
|
928
928
|
var NFT_SIGNED_TAG = 39055n;
|
|
929
929
|
var NFT_FORMAT_VERSION = 1n;
|
|
930
|
-
var
|
|
930
|
+
var NFT_DOCUMENT_MEDIA_TYPE = "application/vnd.unicity.nft+cbor";
|
|
931
|
+
var METADATA_ARITY = 9;
|
|
931
932
|
var MEDIA_ARITY = 3;
|
|
932
933
|
var LINK_ARITY = 4;
|
|
933
934
|
var SIGNED_ARITY = 4;
|
|
@@ -935,12 +936,15 @@ var CREATOR_BYTES = 33;
|
|
|
935
936
|
var SIGNATURE_BYTES = 65;
|
|
936
937
|
var MAX_URI_LENGTH = 2048;
|
|
937
938
|
var LINK_SCHEMES = ["https://", "ipfs://", "ar://"];
|
|
938
|
-
var SIGNED_DOMAIN = "
|
|
939
|
+
var SIGNED_DOMAIN = new TextEncoder().encode("UNICITY_NFT_SIGNED");
|
|
939
940
|
var MEDIA_TYPE_PATTERN = /^[a-z0-9][a-z0-9!#$&^_.+-]{0,126}\/[a-z0-9][a-z0-9!#$&^_.+-]{0,126}$/;
|
|
940
941
|
var SHA256_HEX_PATTERN = /^[0-9a-f]{64}$/i;
|
|
942
|
+
var COLLECTION_ID_PATTERN = /^(?:[0-9a-f]{2}){1,64}$/i;
|
|
941
943
|
var LONE_SURROGATE = /\p{Cs}/u;
|
|
942
944
|
var CBOR_MAJOR_TYPE_MASK = 224;
|
|
943
945
|
var ATTRIBUTE_VALUE_RULE = "must be non-empty text or an integer within \xB1(2^53 \u2212 1) \u2014 write decimals and larger numbers as text";
|
|
946
|
+
var COLLECTION_ID_RULE = "must be null or 1\u201364 bytes as even-length hex (2\u2013128 characters)";
|
|
947
|
+
var DOCUMENT_AS_MEDIA_RULE = `must not be ${NFT_DOCUMENT_MEDIA_TYPE}: a document link is a token's content, never media`;
|
|
944
948
|
var UTF8 = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
|
|
945
949
|
function refuse(field, rule, cause) {
|
|
946
950
|
return new SphereError(`Invalid NFT ${field}: ${rule}`, "VALIDATION_ERROR", cause);
|
|
@@ -952,6 +956,7 @@ var fieldName = (prefix, name) => prefix === "" ? name : `${prefix}.${name}`;
|
|
|
952
956
|
var isText = (value) => typeof value === "string" && value.length > 0 && !LONE_SURROGATE.test(value);
|
|
953
957
|
var isBytes2 = (value, length) => value instanceof Uint8Array && (length === void 0 ? value.length > 0 : value.length === length);
|
|
954
958
|
var isMediaType = (value) => typeof value === "string" && MEDIA_TYPE_PATTERN.test(value);
|
|
959
|
+
var isCollectionId = (value) => typeof value === "string" && COLLECTION_ID_PATTERN.test(value);
|
|
955
960
|
var isAttributeValue = (value) => typeof value === "number" ? Number.isSafeInteger(value) : isText(value);
|
|
956
961
|
function hasSpaceOrControl(value) {
|
|
957
962
|
for (let i = 0; i < value.length; i++) {
|
|
@@ -994,8 +999,9 @@ function checkLink(prefix, link) {
|
|
|
994
999
|
function checkMediaRef(field, ref) {
|
|
995
1000
|
if (ref === null) return;
|
|
996
1001
|
if (ref?.kind === "media") return checkMedia(field, ref);
|
|
997
|
-
if (ref?.kind
|
|
998
|
-
|
|
1002
|
+
if (ref?.kind !== "link") throw refuse(field, "must be an NftMedia, an NftLink or null");
|
|
1003
|
+
checkLink(field, ref);
|
|
1004
|
+
ensure(!isNftDocumentLink(ref), fieldName(field, "media_type"), DOCUMENT_AS_MEDIA_RULE);
|
|
999
1005
|
}
|
|
1000
1006
|
function checkAttributes(attributes) {
|
|
1001
1007
|
ensure(Array.isArray(attributes), "attributes", "must be an array \u2014 [] when there are none");
|
|
@@ -1012,6 +1018,7 @@ function checkMetadata(metadata) {
|
|
|
1012
1018
|
checkOptionalText("external_url", metadata.external_url);
|
|
1013
1019
|
checkAttributes(metadata.attributes);
|
|
1014
1020
|
checkOptionalText("collection", metadata.collection);
|
|
1021
|
+
ensure(metadata.collection_id === null || isCollectionId(metadata.collection_id), "collection_id", COLLECTION_ID_RULE);
|
|
1015
1022
|
}
|
|
1016
1023
|
function checkContent(content) {
|
|
1017
1024
|
switch (content?.kind) {
|
|
@@ -1069,6 +1076,7 @@ function arrayCbor(items) {
|
|
|
1069
1076
|
}
|
|
1070
1077
|
return out;
|
|
1071
1078
|
}
|
|
1079
|
+
var optionalHexBytes = (value) => CborSerializer.encodeNullable(value, (hex) => CborSerializer.encodeByteString(HexConverter.decode(hex.toLowerCase())));
|
|
1072
1080
|
function metadataCbor(metadata) {
|
|
1073
1081
|
return versioned(
|
|
1074
1082
|
NFT_METADATA_TAG,
|
|
@@ -1078,7 +1086,8 @@ function metadataCbor(metadata) {
|
|
|
1078
1086
|
mediaRefCbor(metadata.animation_url),
|
|
1079
1087
|
optionalText(metadata.external_url),
|
|
1080
1088
|
arrayCbor(metadata.attributes.map(attributeCbor)),
|
|
1081
|
-
optionalText(metadata.collection)
|
|
1089
|
+
optionalText(metadata.collection),
|
|
1090
|
+
optionalHexBytes(metadata.collection_id)
|
|
1082
1091
|
);
|
|
1083
1092
|
}
|
|
1084
1093
|
function encodeNftContent(content) {
|
|
@@ -1136,13 +1145,14 @@ function decodeMedia(body) {
|
|
|
1136
1145
|
const [, mediaType, bytes] = versionedFields(body, MEDIA_ARITY);
|
|
1137
1146
|
return { kind: "media", media_type: decodeText(mediaType), bytes: CborDeserializer.decodeByteString(bytes) };
|
|
1138
1147
|
}
|
|
1148
|
+
var decodeHex = (item) => HexConverter.encode(CborDeserializer.decodeByteString(item));
|
|
1139
1149
|
function decodeLink(body) {
|
|
1140
1150
|
const [, mediaType, uri, digest] = versionedFields(body, LINK_ARITY);
|
|
1141
1151
|
return {
|
|
1142
1152
|
kind: "link",
|
|
1143
1153
|
media_type: decodeText(mediaType),
|
|
1144
1154
|
uri: decodeText(uri),
|
|
1145
|
-
sha256:
|
|
1155
|
+
sha256: decodeHex(digest)
|
|
1146
1156
|
};
|
|
1147
1157
|
}
|
|
1148
1158
|
function decodeMetadata(body) {
|
|
@@ -1155,7 +1165,8 @@ function decodeMetadata(body) {
|
|
|
1155
1165
|
animation_url: decodeMediaRef("animation_url", fields[4]),
|
|
1156
1166
|
external_url: decodeOptionalText(fields[5]),
|
|
1157
1167
|
attributes: decodeAttributes(fields[6]),
|
|
1158
|
-
collection: decodeOptionalText(fields[7])
|
|
1168
|
+
collection: decodeOptionalText(fields[7]),
|
|
1169
|
+
collection_id: CborDeserializer.decodeNullable(fields[8], decodeHex)
|
|
1159
1170
|
};
|
|
1160
1171
|
}
|
|
1161
1172
|
var MEDIA_REF_DECODERS = /* @__PURE__ */ new Map([
|
|
@@ -1167,6 +1178,10 @@ var UNSIGNED_DECODERS = /* @__PURE__ */ new Map([
|
|
|
1167
1178
|
[NFT_MEDIA_TAG, decodeMedia],
|
|
1168
1179
|
[NFT_LINK_TAG, decodeLink]
|
|
1169
1180
|
]);
|
|
1181
|
+
var DOCUMENT_DECODERS = /* @__PURE__ */ new Map([
|
|
1182
|
+
[NFT_METADATA_TAG, decodeMetadata],
|
|
1183
|
+
[NFT_MEDIA_TAG, decodeMedia]
|
|
1184
|
+
]);
|
|
1170
1185
|
function decodeBody(decoders, field, tag, body) {
|
|
1171
1186
|
const decode = decoders.get(tag);
|
|
1172
1187
|
if (decode === void 0) throw refuse(field, `tag ${tag} is not allowed here`);
|
|
@@ -1201,20 +1216,35 @@ function parseNftPayload(data) {
|
|
|
1201
1216
|
return null;
|
|
1202
1217
|
}
|
|
1203
1218
|
}
|
|
1204
|
-
function
|
|
1219
|
+
function parseNftDocument(bytes) {
|
|
1220
|
+
try {
|
|
1221
|
+
const document = decodeItem(DOCUMENT_DECODERS, "document", bytes);
|
|
1222
|
+
checkContent(document);
|
|
1223
|
+
return document;
|
|
1224
|
+
} catch {
|
|
1225
|
+
return null;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
function isNftDocumentLink(content) {
|
|
1229
|
+
return content?.kind === "link" && content.media_type === NFT_DOCUMENT_MEDIA_TYPE;
|
|
1230
|
+
}
|
|
1231
|
+
function nftSignedDigest(context, payload) {
|
|
1205
1232
|
const preimage = CborSerializer.encodeArray(
|
|
1206
|
-
CborSerializer.
|
|
1207
|
-
|
|
1208
|
-
|
|
1233
|
+
CborSerializer.encodeByteString(SIGNED_DOMAIN),
|
|
1234
|
+
CborSerializer.encodeUnsignedInteger(NFT_FORMAT_VERSION),
|
|
1235
|
+
CborSerializer.encodeUnsignedInteger(NetworkId.fromId(context.networkId).id),
|
|
1236
|
+
CborSerializer.encodeByteString(context.recipientPredicate),
|
|
1237
|
+
CborSerializer.encodeByteString(context.tokenId),
|
|
1238
|
+
CborSerializer.encodeByteString(context.tokenType),
|
|
1209
1239
|
CborSerializer.encodeByteString(payload)
|
|
1210
1240
|
);
|
|
1211
1241
|
return new DataHasher(HashAlgorithm.SHA256).update(preimage).digest();
|
|
1212
1242
|
}
|
|
1213
|
-
async function verifyNftSignature(signed,
|
|
1243
|
+
async function verifyNftSignature(signed, context) {
|
|
1214
1244
|
try {
|
|
1215
1245
|
if (!SigningService.isPublicKeyValid(signed.creator)) return "invalid";
|
|
1216
1246
|
const signature = Signature.decode(signed.signature);
|
|
1217
|
-
const digest = await nftSignedDigest(
|
|
1247
|
+
const digest = await nftSignedDigest(context, signed.payload);
|
|
1218
1248
|
return await SigningService.verifyWithPublicKey(digest, signature, signed.creator) ? "valid" : "invalid";
|
|
1219
1249
|
} catch {
|
|
1220
1250
|
return "invalid";
|
|
@@ -1345,8 +1375,8 @@ function assertMintableData(data) {
|
|
|
1345
1375
|
function nftRecipientCbor(pubkey) {
|
|
1346
1376
|
return EncodedPredicate.fromPredicate(SignaturePredicate.create(pubkey)).toCBOR();
|
|
1347
1377
|
}
|
|
1348
|
-
async function signPayload(deps, payload,
|
|
1349
|
-
const digest = await nftSignedDigest(
|
|
1378
|
+
async function signPayload(deps, payload, context) {
|
|
1379
|
+
const digest = await nftSignedDigest(context, payload);
|
|
1350
1380
|
const signature = await deps.signingService.sign(digest);
|
|
1351
1381
|
return encodeNftSigned(deps.signingService.publicKey, payload, signature.encode());
|
|
1352
1382
|
}
|
|
@@ -1355,31 +1385,41 @@ async function planNftMint(deps, params) {
|
|
|
1355
1385
|
const tokenType = new TokenType(params.tokenType).bytes;
|
|
1356
1386
|
const salt = TokenSalt.generate();
|
|
1357
1387
|
const tokenId = await TokenId.fromSalt(deps.networkId, salt);
|
|
1358
|
-
const data = params.sign ? await signPayload(deps, payload,
|
|
1388
|
+
const data = params.sign ? await signPayload(deps, payload, {
|
|
1389
|
+
networkId: deps.networkId.id,
|
|
1390
|
+
recipientPredicate: nftRecipientCbor(params.recipientPubkey),
|
|
1391
|
+
tokenId: tokenId.bytes,
|
|
1392
|
+
tokenType
|
|
1393
|
+
}) : payload;
|
|
1359
1394
|
assertMintableData(data);
|
|
1360
1395
|
return { data, salt: salt.toBytes(), tokenType, tokenId: HexConverter.encode(tokenId.bytes) };
|
|
1361
1396
|
}
|
|
1362
|
-
async function verifyAgainst(signed,
|
|
1397
|
+
async function verifyAgainst(signed, genesisContext) {
|
|
1363
1398
|
try {
|
|
1364
|
-
return await verifyNftSignature(signed,
|
|
1399
|
+
return await verifyNftSignature(signed, genesisContext());
|
|
1365
1400
|
} catch {
|
|
1366
1401
|
return "invalid";
|
|
1367
1402
|
}
|
|
1368
1403
|
}
|
|
1369
|
-
async function readNftData(data,
|
|
1404
|
+
async function readNftData(data, genesisContext) {
|
|
1370
1405
|
const parsed = parseNftPayload(data);
|
|
1371
1406
|
if (parsed === null) return null;
|
|
1372
1407
|
if (parsed.signed === null) return { content: parsed.content, creator: null, signature: "unsigned" };
|
|
1373
1408
|
return {
|
|
1374
1409
|
content: parsed.content,
|
|
1375
1410
|
creator: HexConverter.encode(parsed.signed.creator),
|
|
1376
|
-
signature: await verifyAgainst(parsed.signed,
|
|
1411
|
+
signature: await verifyAgainst(parsed.signed, genesisContext)
|
|
1377
1412
|
};
|
|
1378
1413
|
}
|
|
1379
1414
|
async function readTokenNft(token) {
|
|
1380
1415
|
try {
|
|
1381
1416
|
const { genesis } = token.sdkToken;
|
|
1382
|
-
return await readNftData(genesis.data,
|
|
1417
|
+
return await readNftData(genesis.data, () => ({
|
|
1418
|
+
networkId: genesis.networkId.id,
|
|
1419
|
+
recipientPredicate: genesis.recipient.toCBOR(),
|
|
1420
|
+
tokenId: genesis.tokenId.bytes,
|
|
1421
|
+
tokenType: genesis.tokenType.bytes
|
|
1422
|
+
}));
|
|
1383
1423
|
} catch {
|
|
1384
1424
|
return null;
|
|
1385
1425
|
}
|
|
@@ -2027,6 +2067,7 @@ async function createSphereTokenEngine(config) {
|
|
|
2027
2067
|
export {
|
|
2028
2068
|
CheckpointPersistFailedError,
|
|
2029
2069
|
CheckpointTrustbaseMismatchError,
|
|
2070
|
+
NFT_DOCUMENT_MEDIA_TYPE,
|
|
2030
2071
|
NFT_FORMAT_VERSION,
|
|
2031
2072
|
NFT_LINK_TAG,
|
|
2032
2073
|
NFT_MEDIA_TAG,
|
|
@@ -2041,7 +2082,9 @@ export {
|
|
|
2041
2082
|
deriveDirectAddress,
|
|
2042
2083
|
encodeNftContent,
|
|
2043
2084
|
encodeNftSigned,
|
|
2085
|
+
isNftDocumentLink,
|
|
2044
2086
|
nftSignedDigest,
|
|
2087
|
+
parseNftDocument,
|
|
2045
2088
|
parseNftPayload,
|
|
2046
2089
|
sphereAssetToSdk,
|
|
2047
2090
|
verifyNftLinkContent,
|