@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
|
@@ -22,6 +22,7 @@ var token_engine_exports = {};
|
|
|
22
22
|
__export(token_engine_exports, {
|
|
23
23
|
CheckpointPersistFailedError: () => CheckpointPersistFailedError,
|
|
24
24
|
CheckpointTrustbaseMismatchError: () => CheckpointTrustbaseMismatchError,
|
|
25
|
+
NFT_DOCUMENT_MEDIA_TYPE: () => NFT_DOCUMENT_MEDIA_TYPE,
|
|
25
26
|
NFT_FORMAT_VERSION: () => NFT_FORMAT_VERSION,
|
|
26
27
|
NFT_LINK_TAG: () => NFT_LINK_TAG,
|
|
27
28
|
NFT_MEDIA_TAG: () => NFT_MEDIA_TAG,
|
|
@@ -36,7 +37,9 @@ __export(token_engine_exports, {
|
|
|
36
37
|
deriveDirectAddress: () => deriveDirectAddress,
|
|
37
38
|
encodeNftContent: () => encodeNftContent,
|
|
38
39
|
encodeNftSigned: () => encodeNftSigned,
|
|
40
|
+
isNftDocumentLink: () => isNftDocumentLink,
|
|
39
41
|
nftSignedDigest: () => nftSignedDigest,
|
|
42
|
+
parseNftDocument: () => parseNftDocument,
|
|
40
43
|
parseNftPayload: () => parseNftPayload,
|
|
41
44
|
sphereAssetToSdk: () => sphereAssetToSdk,
|
|
42
45
|
verifyNftLinkContent: () => verifyNftLinkContent,
|
|
@@ -973,7 +976,8 @@ var NFT_MEDIA_TAG = 39053n;
|
|
|
973
976
|
var NFT_LINK_TAG = 39054n;
|
|
974
977
|
var NFT_SIGNED_TAG = 39055n;
|
|
975
978
|
var NFT_FORMAT_VERSION = 1n;
|
|
976
|
-
var
|
|
979
|
+
var NFT_DOCUMENT_MEDIA_TYPE = "application/vnd.unicity.nft+cbor";
|
|
980
|
+
var METADATA_ARITY = 9;
|
|
977
981
|
var MEDIA_ARITY = 3;
|
|
978
982
|
var LINK_ARITY = 4;
|
|
979
983
|
var SIGNED_ARITY = 4;
|
|
@@ -981,12 +985,15 @@ var CREATOR_BYTES = 33;
|
|
|
981
985
|
var SIGNATURE_BYTES = 65;
|
|
982
986
|
var MAX_URI_LENGTH = 2048;
|
|
983
987
|
var LINK_SCHEMES = ["https://", "ipfs://", "ar://"];
|
|
984
|
-
var SIGNED_DOMAIN = "
|
|
988
|
+
var SIGNED_DOMAIN = new TextEncoder().encode("UNICITY_NFT_SIGNED");
|
|
985
989
|
var MEDIA_TYPE_PATTERN = /^[a-z0-9][a-z0-9!#$&^_.+-]{0,126}\/[a-z0-9][a-z0-9!#$&^_.+-]{0,126}$/;
|
|
986
990
|
var SHA256_HEX_PATTERN = /^[0-9a-f]{64}$/i;
|
|
991
|
+
var COLLECTION_ID_PATTERN = /^(?:[0-9a-f]{2}){1,64}$/i;
|
|
987
992
|
var LONE_SURROGATE = /\p{Cs}/u;
|
|
988
993
|
var CBOR_MAJOR_TYPE_MASK = 224;
|
|
989
994
|
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";
|
|
995
|
+
var COLLECTION_ID_RULE = "must be null or 1\u201364 bytes as even-length hex (2\u2013128 characters)";
|
|
996
|
+
var DOCUMENT_AS_MEDIA_RULE = `must not be ${NFT_DOCUMENT_MEDIA_TYPE}: a document link is a token's content, never media`;
|
|
990
997
|
var UTF8 = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
|
|
991
998
|
function refuse(field, rule, cause) {
|
|
992
999
|
return new SphereError(`Invalid NFT ${field}: ${rule}`, "VALIDATION_ERROR", cause);
|
|
@@ -998,6 +1005,7 @@ var fieldName = (prefix, name) => prefix === "" ? name : `${prefix}.${name}`;
|
|
|
998
1005
|
var isText = (value) => typeof value === "string" && value.length > 0 && !LONE_SURROGATE.test(value);
|
|
999
1006
|
var isBytes2 = (value, length) => value instanceof Uint8Array && (length === void 0 ? value.length > 0 : value.length === length);
|
|
1000
1007
|
var isMediaType = (value) => typeof value === "string" && MEDIA_TYPE_PATTERN.test(value);
|
|
1008
|
+
var isCollectionId = (value) => typeof value === "string" && COLLECTION_ID_PATTERN.test(value);
|
|
1001
1009
|
var isAttributeValue = (value) => typeof value === "number" ? Number.isSafeInteger(value) : isText(value);
|
|
1002
1010
|
function hasSpaceOrControl(value) {
|
|
1003
1011
|
for (let i = 0; i < value.length; i++) {
|
|
@@ -1040,8 +1048,9 @@ function checkLink(prefix, link) {
|
|
|
1040
1048
|
function checkMediaRef(field, ref) {
|
|
1041
1049
|
if (ref === null) return;
|
|
1042
1050
|
if (ref?.kind === "media") return checkMedia(field, ref);
|
|
1043
|
-
if (ref?.kind
|
|
1044
|
-
|
|
1051
|
+
if (ref?.kind !== "link") throw refuse(field, "must be an NftMedia, an NftLink or null");
|
|
1052
|
+
checkLink(field, ref);
|
|
1053
|
+
ensure(!isNftDocumentLink(ref), fieldName(field, "media_type"), DOCUMENT_AS_MEDIA_RULE);
|
|
1045
1054
|
}
|
|
1046
1055
|
function checkAttributes(attributes) {
|
|
1047
1056
|
ensure(Array.isArray(attributes), "attributes", "must be an array \u2014 [] when there are none");
|
|
@@ -1058,6 +1067,7 @@ function checkMetadata(metadata) {
|
|
|
1058
1067
|
checkOptionalText("external_url", metadata.external_url);
|
|
1059
1068
|
checkAttributes(metadata.attributes);
|
|
1060
1069
|
checkOptionalText("collection", metadata.collection);
|
|
1070
|
+
ensure(metadata.collection_id === null || isCollectionId(metadata.collection_id), "collection_id", COLLECTION_ID_RULE);
|
|
1061
1071
|
}
|
|
1062
1072
|
function checkContent(content) {
|
|
1063
1073
|
switch (content?.kind) {
|
|
@@ -1115,6 +1125,7 @@ function arrayCbor(items) {
|
|
|
1115
1125
|
}
|
|
1116
1126
|
return out;
|
|
1117
1127
|
}
|
|
1128
|
+
var optionalHexBytes = (value) => import_CborSerializer.CborSerializer.encodeNullable(value, (hex) => import_CborSerializer.CborSerializer.encodeByteString(import_HexConverter.HexConverter.decode(hex.toLowerCase())));
|
|
1118
1129
|
function metadataCbor(metadata) {
|
|
1119
1130
|
return versioned(
|
|
1120
1131
|
NFT_METADATA_TAG,
|
|
@@ -1124,7 +1135,8 @@ function metadataCbor(metadata) {
|
|
|
1124
1135
|
mediaRefCbor(metadata.animation_url),
|
|
1125
1136
|
optionalText(metadata.external_url),
|
|
1126
1137
|
arrayCbor(metadata.attributes.map(attributeCbor)),
|
|
1127
|
-
optionalText(metadata.collection)
|
|
1138
|
+
optionalText(metadata.collection),
|
|
1139
|
+
optionalHexBytes(metadata.collection_id)
|
|
1128
1140
|
);
|
|
1129
1141
|
}
|
|
1130
1142
|
function encodeNftContent(content) {
|
|
@@ -1182,13 +1194,14 @@ function decodeMedia(body) {
|
|
|
1182
1194
|
const [, mediaType, bytes] = versionedFields(body, MEDIA_ARITY);
|
|
1183
1195
|
return { kind: "media", media_type: decodeText(mediaType), bytes: import_CborDeserializer.CborDeserializer.decodeByteString(bytes) };
|
|
1184
1196
|
}
|
|
1197
|
+
var decodeHex = (item) => import_HexConverter.HexConverter.encode(import_CborDeserializer.CborDeserializer.decodeByteString(item));
|
|
1185
1198
|
function decodeLink(body) {
|
|
1186
1199
|
const [, mediaType, uri, digest] = versionedFields(body, LINK_ARITY);
|
|
1187
1200
|
return {
|
|
1188
1201
|
kind: "link",
|
|
1189
1202
|
media_type: decodeText(mediaType),
|
|
1190
1203
|
uri: decodeText(uri),
|
|
1191
|
-
sha256:
|
|
1204
|
+
sha256: decodeHex(digest)
|
|
1192
1205
|
};
|
|
1193
1206
|
}
|
|
1194
1207
|
function decodeMetadata(body) {
|
|
@@ -1201,7 +1214,8 @@ function decodeMetadata(body) {
|
|
|
1201
1214
|
animation_url: decodeMediaRef("animation_url", fields[4]),
|
|
1202
1215
|
external_url: decodeOptionalText(fields[5]),
|
|
1203
1216
|
attributes: decodeAttributes(fields[6]),
|
|
1204
|
-
collection: decodeOptionalText(fields[7])
|
|
1217
|
+
collection: decodeOptionalText(fields[7]),
|
|
1218
|
+
collection_id: import_CborDeserializer.CborDeserializer.decodeNullable(fields[8], decodeHex)
|
|
1205
1219
|
};
|
|
1206
1220
|
}
|
|
1207
1221
|
var MEDIA_REF_DECODERS = /* @__PURE__ */ new Map([
|
|
@@ -1213,6 +1227,10 @@ var UNSIGNED_DECODERS = /* @__PURE__ */ new Map([
|
|
|
1213
1227
|
[NFT_MEDIA_TAG, decodeMedia],
|
|
1214
1228
|
[NFT_LINK_TAG, decodeLink]
|
|
1215
1229
|
]);
|
|
1230
|
+
var DOCUMENT_DECODERS = /* @__PURE__ */ new Map([
|
|
1231
|
+
[NFT_METADATA_TAG, decodeMetadata],
|
|
1232
|
+
[NFT_MEDIA_TAG, decodeMedia]
|
|
1233
|
+
]);
|
|
1216
1234
|
function decodeBody(decoders, field, tag, body) {
|
|
1217
1235
|
const decode = decoders.get(tag);
|
|
1218
1236
|
if (decode === void 0) throw refuse(field, `tag ${tag} is not allowed here`);
|
|
@@ -1247,20 +1265,35 @@ function parseNftPayload(data) {
|
|
|
1247
1265
|
return null;
|
|
1248
1266
|
}
|
|
1249
1267
|
}
|
|
1250
|
-
function
|
|
1268
|
+
function parseNftDocument(bytes) {
|
|
1269
|
+
try {
|
|
1270
|
+
const document = decodeItem(DOCUMENT_DECODERS, "document", bytes);
|
|
1271
|
+
checkContent(document);
|
|
1272
|
+
return document;
|
|
1273
|
+
} catch {
|
|
1274
|
+
return null;
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
function isNftDocumentLink(content) {
|
|
1278
|
+
return content?.kind === "link" && content.media_type === NFT_DOCUMENT_MEDIA_TYPE;
|
|
1279
|
+
}
|
|
1280
|
+
function nftSignedDigest(context, payload) {
|
|
1251
1281
|
const preimage = import_CborSerializer.CborSerializer.encodeArray(
|
|
1252
|
-
import_CborSerializer.CborSerializer.
|
|
1253
|
-
|
|
1254
|
-
|
|
1282
|
+
import_CborSerializer.CborSerializer.encodeByteString(SIGNED_DOMAIN),
|
|
1283
|
+
import_CborSerializer.CborSerializer.encodeUnsignedInteger(NFT_FORMAT_VERSION),
|
|
1284
|
+
import_CborSerializer.CborSerializer.encodeUnsignedInteger(import_NetworkId.NetworkId.fromId(context.networkId).id),
|
|
1285
|
+
import_CborSerializer.CborSerializer.encodeByteString(context.recipientPredicate),
|
|
1286
|
+
import_CborSerializer.CborSerializer.encodeByteString(context.tokenId),
|
|
1287
|
+
import_CborSerializer.CborSerializer.encodeByteString(context.tokenType),
|
|
1255
1288
|
import_CborSerializer.CborSerializer.encodeByteString(payload)
|
|
1256
1289
|
);
|
|
1257
1290
|
return new import_DataHasher.DataHasher(import_HashAlgorithm.HashAlgorithm.SHA256).update(preimage).digest();
|
|
1258
1291
|
}
|
|
1259
|
-
async function verifyNftSignature(signed,
|
|
1292
|
+
async function verifyNftSignature(signed, context) {
|
|
1260
1293
|
try {
|
|
1261
1294
|
if (!import_SigningService.SigningService.isPublicKeyValid(signed.creator)) return "invalid";
|
|
1262
1295
|
const signature = import_Signature.Signature.decode(signed.signature);
|
|
1263
|
-
const digest = await nftSignedDigest(
|
|
1296
|
+
const digest = await nftSignedDigest(context, signed.payload);
|
|
1264
1297
|
return await import_SigningService.SigningService.verifyWithPublicKey(digest, signature, signed.creator) ? "valid" : "invalid";
|
|
1265
1298
|
} catch {
|
|
1266
1299
|
return "invalid";
|
|
@@ -1391,8 +1424,8 @@ function assertMintableData(data) {
|
|
|
1391
1424
|
function nftRecipientCbor(pubkey) {
|
|
1392
1425
|
return import_EncodedPredicate.EncodedPredicate.fromPredicate(import_SignaturePredicate.SignaturePredicate.create(pubkey)).toCBOR();
|
|
1393
1426
|
}
|
|
1394
|
-
async function signPayload(deps, payload,
|
|
1395
|
-
const digest = await nftSignedDigest(
|
|
1427
|
+
async function signPayload(deps, payload, context) {
|
|
1428
|
+
const digest = await nftSignedDigest(context, payload);
|
|
1396
1429
|
const signature = await deps.signingService.sign(digest);
|
|
1397
1430
|
return encodeNftSigned(deps.signingService.publicKey, payload, signature.encode());
|
|
1398
1431
|
}
|
|
@@ -1401,31 +1434,41 @@ async function planNftMint(deps, params) {
|
|
|
1401
1434
|
const tokenType = new import_TokenType.TokenType(params.tokenType).bytes;
|
|
1402
1435
|
const salt = import_TokenSalt.TokenSalt.generate();
|
|
1403
1436
|
const tokenId = await import_TokenId.TokenId.fromSalt(deps.networkId, salt);
|
|
1404
|
-
const data = params.sign ? await signPayload(deps, payload,
|
|
1437
|
+
const data = params.sign ? await signPayload(deps, payload, {
|
|
1438
|
+
networkId: deps.networkId.id,
|
|
1439
|
+
recipientPredicate: nftRecipientCbor(params.recipientPubkey),
|
|
1440
|
+
tokenId: tokenId.bytes,
|
|
1441
|
+
tokenType
|
|
1442
|
+
}) : payload;
|
|
1405
1443
|
assertMintableData(data);
|
|
1406
1444
|
return { data, salt: salt.toBytes(), tokenType, tokenId: import_HexConverter.HexConverter.encode(tokenId.bytes) };
|
|
1407
1445
|
}
|
|
1408
|
-
async function verifyAgainst(signed,
|
|
1446
|
+
async function verifyAgainst(signed, genesisContext) {
|
|
1409
1447
|
try {
|
|
1410
|
-
return await verifyNftSignature(signed,
|
|
1448
|
+
return await verifyNftSignature(signed, genesisContext());
|
|
1411
1449
|
} catch {
|
|
1412
1450
|
return "invalid";
|
|
1413
1451
|
}
|
|
1414
1452
|
}
|
|
1415
|
-
async function readNftData(data,
|
|
1453
|
+
async function readNftData(data, genesisContext) {
|
|
1416
1454
|
const parsed = parseNftPayload(data);
|
|
1417
1455
|
if (parsed === null) return null;
|
|
1418
1456
|
if (parsed.signed === null) return { content: parsed.content, creator: null, signature: "unsigned" };
|
|
1419
1457
|
return {
|
|
1420
1458
|
content: parsed.content,
|
|
1421
1459
|
creator: import_HexConverter.HexConverter.encode(parsed.signed.creator),
|
|
1422
|
-
signature: await verifyAgainst(parsed.signed,
|
|
1460
|
+
signature: await verifyAgainst(parsed.signed, genesisContext)
|
|
1423
1461
|
};
|
|
1424
1462
|
}
|
|
1425
1463
|
async function readTokenNft(token) {
|
|
1426
1464
|
try {
|
|
1427
1465
|
const { genesis } = token.sdkToken;
|
|
1428
|
-
return await readNftData(genesis.data,
|
|
1466
|
+
return await readNftData(genesis.data, () => ({
|
|
1467
|
+
networkId: genesis.networkId.id,
|
|
1468
|
+
recipientPredicate: genesis.recipient.toCBOR(),
|
|
1469
|
+
tokenId: genesis.tokenId.bytes,
|
|
1470
|
+
tokenType: genesis.tokenType.bytes
|
|
1471
|
+
}));
|
|
1429
1472
|
} catch {
|
|
1430
1473
|
return null;
|
|
1431
1474
|
}
|
|
@@ -2074,6 +2117,7 @@ async function createSphereTokenEngine(config) {
|
|
|
2074
2117
|
0 && (module.exports = {
|
|
2075
2118
|
CheckpointPersistFailedError,
|
|
2076
2119
|
CheckpointTrustbaseMismatchError,
|
|
2120
|
+
NFT_DOCUMENT_MEDIA_TYPE,
|
|
2077
2121
|
NFT_FORMAT_VERSION,
|
|
2078
2122
|
NFT_LINK_TAG,
|
|
2079
2123
|
NFT_MEDIA_TAG,
|
|
@@ -2088,7 +2132,9 @@ async function createSphereTokenEngine(config) {
|
|
|
2088
2132
|
deriveDirectAddress,
|
|
2089
2133
|
encodeNftContent,
|
|
2090
2134
|
encodeNftSigned,
|
|
2135
|
+
isNftDocumentLink,
|
|
2091
2136
|
nftSignedDigest,
|
|
2137
|
+
parseNftDocument,
|
|
2092
2138
|
parseNftPayload,
|
|
2093
2139
|
sphereAssetToSdk,
|
|
2094
2140
|
verifyNftLinkContent,
|