@unicitylabs/sphere-sdk 0.17.2-dev.2 → 0.17.2-dev.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.
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 +48 -21
  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 +48 -21
  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 +67 -21
  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 +64 -21
  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
package/dist/index.cjs CHANGED
@@ -50,6 +50,7 @@ __export(index_exports, {
50
50
  LIMITS: () => LIMITS,
51
51
  MarketModule: () => MarketModule,
52
52
  NETWORKS: () => NETWORKS,
53
+ NFT_DOCUMENT_MEDIA_TYPE: () => NFT_DOCUMENT_MEDIA_TYPE,
53
54
  NFT_LINK_TAG: () => NFT_LINK_TAG,
54
55
  NFT_MAX_PAYLOAD_BYTES: () => NFT_MAX_PAYLOAD_BYTES,
55
56
  NFT_MEDIA_TAG: () => NFT_MEDIA_TAG,
@@ -128,6 +129,7 @@ __export(index_exports, {
128
129
  identityFromMnemonicSync: () => identityFromMnemonicSync,
129
130
  initSphere: () => initSphere,
130
131
  isKnownToken: () => isKnownToken,
132
+ isNftDocumentLink: () => isNftDocumentLink,
131
133
  isPhoneNumber: () => import_nostr_js_sdk5.isPhoneNumber,
132
134
  isPossiblyCommittedSendOutcome: () => isPossiblyCommittedSendOutcome,
133
135
  isSphereError: () => isSphereError,
@@ -145,6 +147,7 @@ __export(index_exports, {
145
147
  normalizeNametag: () => import_nostr_js_sdk5.normalizeNametag,
146
148
  parseAddress: () => parseAddress,
147
149
  parseAndDecryptWalletText: () => parseAndDecryptWalletText,
150
+ parseNftDocument: () => parseNftDocument,
148
151
  parseNftPayload: () => parseNftPayload,
149
152
  parseTokenAmount: () => parseTokenAmount,
150
153
  parseWalletText: () => parseWalletText,
@@ -9937,7 +9940,8 @@ var NFT_MEDIA_TAG = 39053n;
9937
9940
  var NFT_LINK_TAG = 39054n;
9938
9941
  var NFT_SIGNED_TAG = 39055n;
9939
9942
  var NFT_FORMAT_VERSION = 1n;
9940
- var METADATA_ARITY = 8;
9943
+ var NFT_DOCUMENT_MEDIA_TYPE = "application/vnd.unicity.nft+cbor";
9944
+ var METADATA_ARITY = 9;
9941
9945
  var MEDIA_ARITY = 3;
9942
9946
  var LINK_ARITY = 4;
9943
9947
  var SIGNED_ARITY = 4;
@@ -9945,12 +9949,15 @@ var CREATOR_BYTES = 33;
9945
9949
  var SIGNATURE_BYTES = 65;
9946
9950
  var MAX_URI_LENGTH = 2048;
9947
9951
  var LINK_SCHEMES = ["https://", "ipfs://", "ar://"];
9948
- var SIGNED_DOMAIN = "NftSigned";
9952
+ var SIGNED_DOMAIN = new TextEncoder().encode("UNICITY_NFT_SIGNED");
9949
9953
  var MEDIA_TYPE_PATTERN = /^[a-z0-9][a-z0-9!#$&^_.+-]{0,126}\/[a-z0-9][a-z0-9!#$&^_.+-]{0,126}$/;
9950
9954
  var SHA256_HEX_PATTERN = /^[0-9a-f]{64}$/i;
9955
+ var COLLECTION_ID_PATTERN = /^(?:[0-9a-f]{2}){1,64}$/i;
9951
9956
  var LONE_SURROGATE = /\p{Cs}/u;
9952
9957
  var CBOR_MAJOR_TYPE_MASK = 224;
9953
9958
  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";
9959
+ var COLLECTION_ID_RULE = "must be null or 1\u201364 bytes as even-length hex (2\u2013128 characters)";
9960
+ var DOCUMENT_AS_MEDIA_RULE = `must not be ${NFT_DOCUMENT_MEDIA_TYPE}: a document link is a token's content, never media`;
9954
9961
  var UTF8 = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
9955
9962
  function refuse(field, rule, cause) {
9956
9963
  return new SphereError(`Invalid NFT ${field}: ${rule}`, "VALIDATION_ERROR", cause);
@@ -9962,6 +9969,7 @@ var fieldName = (prefix, name) => prefix === "" ? name : `${prefix}.${name}`;
9962
9969
  var isText = (value) => typeof value === "string" && value.length > 0 && !LONE_SURROGATE.test(value);
9963
9970
  var isBytes3 = (value, length) => value instanceof Uint8Array && (length === void 0 ? value.length > 0 : value.length === length);
9964
9971
  var isMediaType = (value) => typeof value === "string" && MEDIA_TYPE_PATTERN.test(value);
9972
+ var isCollectionId = (value) => typeof value === "string" && COLLECTION_ID_PATTERN.test(value);
9965
9973
  var isAttributeValue = (value) => typeof value === "number" ? Number.isSafeInteger(value) : isText(value);
9966
9974
  function hasSpaceOrControl(value) {
9967
9975
  for (let i = 0; i < value.length; i++) {
@@ -10004,8 +10012,9 @@ function checkLink(prefix, link) {
10004
10012
  function checkMediaRef(field, ref) {
10005
10013
  if (ref === null) return;
10006
10014
  if (ref?.kind === "media") return checkMedia(field, ref);
10007
- if (ref?.kind === "link") return checkLink(field, ref);
10008
- throw refuse(field, "must be an NftMedia, an NftLink or null");
10015
+ if (ref?.kind !== "link") throw refuse(field, "must be an NftMedia, an NftLink or null");
10016
+ checkLink(field, ref);
10017
+ ensure(!isNftDocumentLink(ref), fieldName(field, "media_type"), DOCUMENT_AS_MEDIA_RULE);
10009
10018
  }
10010
10019
  function checkAttributes(attributes) {
10011
10020
  ensure(Array.isArray(attributes), "attributes", "must be an array \u2014 [] when there are none");
@@ -10022,6 +10031,7 @@ function checkMetadata(metadata) {
10022
10031
  checkOptionalText("external_url", metadata.external_url);
10023
10032
  checkAttributes(metadata.attributes);
10024
10033
  checkOptionalText("collection", metadata.collection);
10034
+ ensure(metadata.collection_id === null || isCollectionId(metadata.collection_id), "collection_id", COLLECTION_ID_RULE);
10025
10035
  }
10026
10036
  function checkContent(content) {
10027
10037
  switch (content?.kind) {
@@ -10079,6 +10089,7 @@ function arrayCbor(items) {
10079
10089
  }
10080
10090
  return out;
10081
10091
  }
10092
+ var optionalHexBytes = (value) => import_CborSerializer.CborSerializer.encodeNullable(value, (hex) => import_CborSerializer.CborSerializer.encodeByteString(import_HexConverter.HexConverter.decode(hex.toLowerCase())));
10082
10093
  function metadataCbor(metadata) {
10083
10094
  return versioned(
10084
10095
  NFT_METADATA_TAG,
@@ -10088,7 +10099,8 @@ function metadataCbor(metadata) {
10088
10099
  mediaRefCbor(metadata.animation_url),
10089
10100
  optionalText(metadata.external_url),
10090
10101
  arrayCbor(metadata.attributes.map(attributeCbor)),
10091
- optionalText(metadata.collection)
10102
+ optionalText(metadata.collection),
10103
+ optionalHexBytes(metadata.collection_id)
10092
10104
  );
10093
10105
  }
10094
10106
  function encodeNftContent(content) {
@@ -10146,13 +10158,14 @@ function decodeMedia(body) {
10146
10158
  const [, mediaType, bytes] = versionedFields(body, MEDIA_ARITY);
10147
10159
  return { kind: "media", media_type: decodeText(mediaType), bytes: import_CborDeserializer.CborDeserializer.decodeByteString(bytes) };
10148
10160
  }
10161
+ var decodeHex = (item) => import_HexConverter.HexConverter.encode(import_CborDeserializer.CborDeserializer.decodeByteString(item));
10149
10162
  function decodeLink(body) {
10150
10163
  const [, mediaType, uri, digest] = versionedFields(body, LINK_ARITY);
10151
10164
  return {
10152
10165
  kind: "link",
10153
10166
  media_type: decodeText(mediaType),
10154
10167
  uri: decodeText(uri),
10155
- sha256: import_HexConverter.HexConverter.encode(import_CborDeserializer.CborDeserializer.decodeByteString(digest))
10168
+ sha256: decodeHex(digest)
10156
10169
  };
10157
10170
  }
10158
10171
  function decodeMetadata(body) {
@@ -10165,7 +10178,8 @@ function decodeMetadata(body) {
10165
10178
  animation_url: decodeMediaRef("animation_url", fields[4]),
10166
10179
  external_url: decodeOptionalText(fields[5]),
10167
10180
  attributes: decodeAttributes(fields[6]),
10168
- collection: decodeOptionalText(fields[7])
10181
+ collection: decodeOptionalText(fields[7]),
10182
+ collection_id: import_CborDeserializer.CborDeserializer.decodeNullable(fields[8], decodeHex)
10169
10183
  };
10170
10184
  }
10171
10185
  var MEDIA_REF_DECODERS = /* @__PURE__ */ new Map([
@@ -10177,6 +10191,10 @@ var UNSIGNED_DECODERS = /* @__PURE__ */ new Map([
10177
10191
  [NFT_MEDIA_TAG, decodeMedia],
10178
10192
  [NFT_LINK_TAG, decodeLink]
10179
10193
  ]);
10194
+ var DOCUMENT_DECODERS = /* @__PURE__ */ new Map([
10195
+ [NFT_METADATA_TAG, decodeMetadata],
10196
+ [NFT_MEDIA_TAG, decodeMedia]
10197
+ ]);
10180
10198
  function decodeBody(decoders, field, tag, body) {
10181
10199
  const decode = decoders.get(tag);
10182
10200
  if (decode === void 0) throw refuse(field, `tag ${tag} is not allowed here`);
@@ -10211,20 +10229,35 @@ function parseNftPayload(data) {
10211
10229
  return null;
10212
10230
  }
10213
10231
  }
10214
- function nftSignedDigest(tokenIdCbor, recipientPredicateCbor, payload) {
10232
+ function parseNftDocument(bytes) {
10233
+ try {
10234
+ const document = decodeItem(DOCUMENT_DECODERS, "document", bytes);
10235
+ checkContent(document);
10236
+ return document;
10237
+ } catch {
10238
+ return null;
10239
+ }
10240
+ }
10241
+ function isNftDocumentLink(content) {
10242
+ return content?.kind === "link" && content.media_type === NFT_DOCUMENT_MEDIA_TYPE;
10243
+ }
10244
+ function nftSignedDigest(context, payload) {
10215
10245
  const preimage = import_CborSerializer.CborSerializer.encodeArray(
10216
- import_CborSerializer.CborSerializer.encodeTextString(SIGNED_DOMAIN),
10217
- tokenIdCbor,
10218
- recipientPredicateCbor,
10246
+ import_CborSerializer.CborSerializer.encodeByteString(SIGNED_DOMAIN),
10247
+ import_CborSerializer.CborSerializer.encodeUnsignedInteger(NFT_FORMAT_VERSION),
10248
+ import_CborSerializer.CborSerializer.encodeUnsignedInteger(import_NetworkId.NetworkId.fromId(context.networkId).id),
10249
+ import_CborSerializer.CborSerializer.encodeByteString(context.recipientPredicate),
10250
+ import_CborSerializer.CborSerializer.encodeByteString(context.tokenId),
10251
+ import_CborSerializer.CborSerializer.encodeByteString(context.tokenType),
10219
10252
  import_CborSerializer.CborSerializer.encodeByteString(payload)
10220
10253
  );
10221
10254
  return new import_DataHasher.DataHasher(import_HashAlgorithm.HashAlgorithm.SHA256).update(preimage).digest();
10222
10255
  }
10223
- async function verifyNftSignature(signed, tokenIdCbor, recipientPredicateCbor) {
10256
+ async function verifyNftSignature(signed, context) {
10224
10257
  try {
10225
10258
  if (!import_SigningService.SigningService.isPublicKeyValid(signed.creator)) return "invalid";
10226
10259
  const signature = import_Signature.Signature.decode(signed.signature);
10227
- const digest = await nftSignedDigest(tokenIdCbor, recipientPredicateCbor, signed.payload);
10260
+ const digest = await nftSignedDigest(context, signed.payload);
10228
10261
  return await import_SigningService.SigningService.verifyWithPublicKey(digest, signature, signed.creator) ? "valid" : "invalid";
10229
10262
  } catch {
10230
10263
  return "invalid";
@@ -10358,8 +10391,8 @@ function assertMintableData(data) {
10358
10391
  function nftRecipientCbor(pubkey) {
10359
10392
  return import_EncodedPredicate.EncodedPredicate.fromPredicate(import_SignaturePredicate.SignaturePredicate.create(pubkey)).toCBOR();
10360
10393
  }
10361
- async function signPayload(deps, payload, tokenId, recipientPubkey) {
10362
- const digest = await nftSignedDigest(tokenId.toCBOR(), nftRecipientCbor(recipientPubkey), payload);
10394
+ async function signPayload(deps, payload, context) {
10395
+ const digest = await nftSignedDigest(context, payload);
10363
10396
  const signature = await deps.signingService.sign(digest);
10364
10397
  return encodeNftSigned(deps.signingService.publicKey, payload, signature.encode());
10365
10398
  }
@@ -10368,31 +10401,41 @@ async function planNftMint(deps, params) {
10368
10401
  const tokenType = new import_TokenType.TokenType(params.tokenType).bytes;
10369
10402
  const salt = import_TokenSalt.TokenSalt.generate();
10370
10403
  const tokenId = await import_TokenId.TokenId.fromSalt(deps.networkId, salt);
10371
- const data = params.sign ? await signPayload(deps, payload, tokenId, params.recipientPubkey) : payload;
10404
+ const data = params.sign ? await signPayload(deps, payload, {
10405
+ networkId: deps.networkId.id,
10406
+ recipientPredicate: nftRecipientCbor(params.recipientPubkey),
10407
+ tokenId: tokenId.bytes,
10408
+ tokenType
10409
+ }) : payload;
10372
10410
  assertMintableData(data);
10373
10411
  return { data, salt: salt.toBytes(), tokenType, tokenId: import_HexConverter.HexConverter.encode(tokenId.bytes) };
10374
10412
  }
10375
- async function verifyAgainst(signed, tokenIdCbor, genesisRecipientCbor) {
10413
+ async function verifyAgainst(signed, genesisContext) {
10376
10414
  try {
10377
- return await verifyNftSignature(signed, tokenIdCbor, genesisRecipientCbor());
10415
+ return await verifyNftSignature(signed, genesisContext());
10378
10416
  } catch {
10379
10417
  return "invalid";
10380
10418
  }
10381
10419
  }
10382
- async function readNftData(data, tokenIdCbor, genesisRecipientCbor) {
10420
+ async function readNftData(data, genesisContext) {
10383
10421
  const parsed = parseNftPayload(data);
10384
10422
  if (parsed === null) return null;
10385
10423
  if (parsed.signed === null) return { content: parsed.content, creator: null, signature: "unsigned" };
10386
10424
  return {
10387
10425
  content: parsed.content,
10388
10426
  creator: import_HexConverter.HexConverter.encode(parsed.signed.creator),
10389
- signature: await verifyAgainst(parsed.signed, tokenIdCbor, genesisRecipientCbor)
10427
+ signature: await verifyAgainst(parsed.signed, genesisContext)
10390
10428
  };
10391
10429
  }
10392
10430
  async function readTokenNft(token) {
10393
10431
  try {
10394
10432
  const { genesis } = token.sdkToken;
10395
- return await readNftData(genesis.data, genesis.tokenId.toCBOR(), () => genesis.recipient.toCBOR());
10433
+ return await readNftData(genesis.data, () => ({
10434
+ networkId: genesis.networkId.id,
10435
+ recipientPredicate: genesis.recipient.toCBOR(),
10436
+ tokenId: genesis.tokenId.bytes,
10437
+ tokenType: genesis.tokenType.bytes
10438
+ }));
10396
10439
  } catch {
10397
10440
  return null;
10398
10441
  }
@@ -20853,6 +20896,7 @@ function addressesMatch(a, b) {
20853
20896
  LIMITS,
20854
20897
  MarketModule,
20855
20898
  NETWORKS,
20899
+ NFT_DOCUMENT_MEDIA_TYPE,
20856
20900
  NFT_LINK_TAG,
20857
20901
  NFT_MAX_PAYLOAD_BYTES,
20858
20902
  NFT_MEDIA_TAG,
@@ -20931,6 +20975,7 @@ function addressesMatch(a, b) {
20931
20975
  identityFromMnemonicSync,
20932
20976
  initSphere,
20933
20977
  isKnownToken,
20978
+ isNftDocumentLink,
20934
20979
  isPhoneNumber,
20935
20980
  isPossiblyCommittedSendOutcome,
20936
20981
  isSphereError,
@@ -20948,6 +20993,7 @@ function addressesMatch(a, b) {
20948
20993
  normalizeNametag,
20949
20994
  parseAddress,
20950
20995
  parseAndDecryptWalletText,
20996
+ parseNftDocument,
20951
20997
  parseNftPayload,
20952
20998
  parseTokenAmount,
20953
20999
  parseWalletText,