@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.
Files changed (39) hide show
  1. package/dist/connect/index.cjs +7 -4
  2. package/dist/connect/index.cjs.map +1 -1
  3. package/dist/connect/index.d.cts +4 -0
  4. package/dist/connect/index.d.ts +4 -0
  5. package/dist/connect/index.js +7 -4
  6. package/dist/connect/index.js.map +1 -1
  7. package/dist/core/index.cjs +69 -22
  8. package/dist/core/index.cjs.map +1 -1
  9. package/dist/core/index.d.cts +2 -0
  10. package/dist/core/index.d.ts +2 -0
  11. package/dist/core/index.js +69 -22
  12. package/dist/core/index.js.map +1 -1
  13. package/dist/impl/browser/connect/index.cjs +1 -1
  14. package/dist/impl/browser/connect/index.cjs.map +1 -1
  15. package/dist/impl/browser/connect/index.js +1 -1
  16. package/dist/impl/browser/connect/index.js.map +1 -1
  17. package/dist/impl/wallet-api-v2/index.cjs +3 -0
  18. package/dist/impl/wallet-api-v2/index.cjs.map +1 -1
  19. package/dist/impl/wallet-api-v2/index.js +3 -0
  20. package/dist/impl/wallet-api-v2/index.js.map +1 -1
  21. package/dist/index.cjs +88 -22
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.cts +22 -1
  24. package/dist/index.d.ts +22 -1
  25. package/dist/index.js +85 -22
  26. package/dist/index.js.map +1 -1
  27. package/dist/modules/payments-v2/index.cjs +40 -6
  28. package/dist/modules/payments-v2/index.cjs.map +1 -1
  29. package/dist/modules/payments-v2/index.d.cts +22 -1
  30. package/dist/modules/payments-v2/index.d.ts +22 -1
  31. package/dist/modules/payments-v2/index.js +37 -6
  32. package/dist/modules/payments-v2/index.js.map +1 -1
  33. package/dist/token-engine/index.cjs +67 -21
  34. package/dist/token-engine/index.cjs.map +1 -1
  35. package/dist/token-engine/index.d.cts +28 -4
  36. package/dist/token-engine/index.d.ts +28 -4
  37. package/dist/token-engine/index.js +64 -21
  38. package/dist/token-engine/index.js.map +1 -1
  39. 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
- /** SHA-256 of CBOR `["NftSigned", tokenId, genesis recipient predicate, bstr(payload)]`. */
60
- declare function nftSignedDigest(tokenIdCbor: Uint8Array, recipientPredicateCbor: Uint8Array, payload: Uint8Array): Promise<DataHash>;
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']>, tokenIdCbor: Uint8Array, recipientPredicateCbor: Uint8Array): Promise<Exclude<NftSignatureStatus, 'unsigned'>>;
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
- /** SHA-256 of CBOR `["NftSigned", tokenId, genesis recipient predicate, bstr(payload)]`. */
60
- declare function nftSignedDigest(tokenIdCbor: Uint8Array, recipientPredicateCbor: Uint8Array, payload: Uint8Array): Promise<DataHash>;
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']>, tokenIdCbor: Uint8Array, recipientPredicateCbor: Uint8Array): Promise<Exclude<NftSignatureStatus, 'unsigned'>>;
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 METADATA_ARITY = 8;
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 = "NftSigned";
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 === "link") return checkLink(field, ref);
998
- throw refuse(field, "must be an NftMedia, an NftLink or null");
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: HexConverter.encode(CborDeserializer.decodeByteString(digest))
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 nftSignedDigest(tokenIdCbor, recipientPredicateCbor, payload) {
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.encodeTextString(SIGNED_DOMAIN),
1207
- tokenIdCbor,
1208
- recipientPredicateCbor,
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, tokenIdCbor, recipientPredicateCbor) {
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(tokenIdCbor, recipientPredicateCbor, signed.payload);
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, tokenId, recipientPubkey) {
1349
- const digest = await nftSignedDigest(tokenId.toCBOR(), nftRecipientCbor(recipientPubkey), payload);
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, tokenId, params.recipientPubkey) : 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, tokenIdCbor, genesisRecipientCbor) {
1397
+ async function verifyAgainst(signed, genesisContext) {
1363
1398
  try {
1364
- return await verifyNftSignature(signed, tokenIdCbor, genesisRecipientCbor());
1399
+ return await verifyNftSignature(signed, genesisContext());
1365
1400
  } catch {
1366
1401
  return "invalid";
1367
1402
  }
1368
1403
  }
1369
- async function readNftData(data, tokenIdCbor, genesisRecipientCbor) {
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, tokenIdCbor, genesisRecipientCbor)
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, genesis.tokenId.toCBOR(), () => genesis.recipient.toCBOR());
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,