@unicitylabs/sphere-sdk 0.17.2-dev.1 → 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.
- package/dist/connect/index.cjs +202 -8
- package/dist/connect/index.cjs.map +1 -1
- package/dist/connect/index.d.cts +81 -4
- package/dist/connect/index.d.ts +81 -4
- package/dist/connect/index.js +202 -8
- package/dist/connect/index.js.map +1 -1
- package/dist/core/index.cjs +48 -21
- 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 +48 -21
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/connect/index.cjs +17 -5
- package/dist/impl/browser/connect/index.cjs.map +1 -1
- package/dist/impl/browser/connect/index.d.cts +3 -1
- package/dist/impl/browser/connect/index.d.ts +3 -1
- package/dist/impl/browser/connect/index.js +17 -5
- package/dist/impl/browser/connect/index.js.map +1 -1
- package/dist/impl/nodejs/connect/index.cjs +16 -4
- package/dist/impl/nodejs/connect/index.cjs.map +1 -1
- package/dist/impl/nodejs/connect/index.d.cts +1 -1
- package/dist/impl/nodejs/connect/index.d.ts +1 -1
- package/dist/impl/nodejs/connect/index.js +16 -4
- package/dist/impl/nodejs/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 +67 -21
- 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 +64 -21
- 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
package/dist/core/index.d.cts
CHANGED
|
@@ -112,6 +112,8 @@ interface NftMetadata {
|
|
|
112
112
|
readonly external_url: string | null;
|
|
113
113
|
readonly attributes: readonly NftAttribute[];
|
|
114
114
|
readonly collection: string | null;
|
|
115
|
+
/** The collection the item claims: 1–64 bytes as even-length hex (decoded lowercase). Never verified here. */
|
|
116
|
+
readonly collection_id: string | null;
|
|
115
117
|
}
|
|
116
118
|
type NftContent = NftMetadata | NftMedia | NftLink;
|
|
117
119
|
type NftSignatureStatus = 'unsigned' | 'valid' | 'invalid';
|
package/dist/core/index.d.ts
CHANGED
|
@@ -112,6 +112,8 @@ interface NftMetadata {
|
|
|
112
112
|
readonly external_url: string | null;
|
|
113
113
|
readonly attributes: readonly NftAttribute[];
|
|
114
114
|
readonly collection: string | null;
|
|
115
|
+
/** The collection the item claims: 1–64 bytes as even-length hex (decoded lowercase). Never verified here. */
|
|
116
|
+
readonly collection_id: string | null;
|
|
115
117
|
}
|
|
116
118
|
type NftContent = NftMetadata | NftMedia | NftLink;
|
|
117
119
|
type NftSignatureStatus = 'unsigned' | 'valid' | 'invalid';
|
package/dist/core/index.js
CHANGED
|
@@ -9784,7 +9784,8 @@ var NFT_MEDIA_TAG = 39053n;
|
|
|
9784
9784
|
var NFT_LINK_TAG = 39054n;
|
|
9785
9785
|
var NFT_SIGNED_TAG = 39055n;
|
|
9786
9786
|
var NFT_FORMAT_VERSION = 1n;
|
|
9787
|
-
var
|
|
9787
|
+
var NFT_DOCUMENT_MEDIA_TYPE = "application/vnd.unicity.nft+cbor";
|
|
9788
|
+
var METADATA_ARITY = 9;
|
|
9788
9789
|
var MEDIA_ARITY = 3;
|
|
9789
9790
|
var LINK_ARITY = 4;
|
|
9790
9791
|
var SIGNED_ARITY = 4;
|
|
@@ -9792,12 +9793,15 @@ var CREATOR_BYTES = 33;
|
|
|
9792
9793
|
var SIGNATURE_BYTES = 65;
|
|
9793
9794
|
var MAX_URI_LENGTH = 2048;
|
|
9794
9795
|
var LINK_SCHEMES = ["https://", "ipfs://", "ar://"];
|
|
9795
|
-
var SIGNED_DOMAIN = "
|
|
9796
|
+
var SIGNED_DOMAIN = new TextEncoder().encode("UNICITY_NFT_SIGNED");
|
|
9796
9797
|
var MEDIA_TYPE_PATTERN = /^[a-z0-9][a-z0-9!#$&^_.+-]{0,126}\/[a-z0-9][a-z0-9!#$&^_.+-]{0,126}$/;
|
|
9797
9798
|
var SHA256_HEX_PATTERN = /^[0-9a-f]{64}$/i;
|
|
9799
|
+
var COLLECTION_ID_PATTERN = /^(?:[0-9a-f]{2}){1,64}$/i;
|
|
9798
9800
|
var LONE_SURROGATE = /\p{Cs}/u;
|
|
9799
9801
|
var CBOR_MAJOR_TYPE_MASK = 224;
|
|
9800
9802
|
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";
|
|
9803
|
+
var COLLECTION_ID_RULE = "must be null or 1\u201364 bytes as even-length hex (2\u2013128 characters)";
|
|
9804
|
+
var DOCUMENT_AS_MEDIA_RULE = `must not be ${NFT_DOCUMENT_MEDIA_TYPE}: a document link is a token's content, never media`;
|
|
9801
9805
|
var UTF8 = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true });
|
|
9802
9806
|
function refuse(field, rule, cause) {
|
|
9803
9807
|
return new SphereError(`Invalid NFT ${field}: ${rule}`, "VALIDATION_ERROR", cause);
|
|
@@ -9809,6 +9813,7 @@ var fieldName = (prefix, name) => prefix === "" ? name : `${prefix}.${name}`;
|
|
|
9809
9813
|
var isText = (value) => typeof value === "string" && value.length > 0 && !LONE_SURROGATE.test(value);
|
|
9810
9814
|
var isBytes3 = (value, length) => value instanceof Uint8Array && (length === void 0 ? value.length > 0 : value.length === length);
|
|
9811
9815
|
var isMediaType = (value) => typeof value === "string" && MEDIA_TYPE_PATTERN.test(value);
|
|
9816
|
+
var isCollectionId = (value) => typeof value === "string" && COLLECTION_ID_PATTERN.test(value);
|
|
9812
9817
|
var isAttributeValue = (value) => typeof value === "number" ? Number.isSafeInteger(value) : isText(value);
|
|
9813
9818
|
function hasSpaceOrControl(value) {
|
|
9814
9819
|
for (let i = 0; i < value.length; i++) {
|
|
@@ -9851,8 +9856,9 @@ function checkLink(prefix, link) {
|
|
|
9851
9856
|
function checkMediaRef(field, ref) {
|
|
9852
9857
|
if (ref === null) return;
|
|
9853
9858
|
if (ref?.kind === "media") return checkMedia(field, ref);
|
|
9854
|
-
if (ref?.kind
|
|
9855
|
-
|
|
9859
|
+
if (ref?.kind !== "link") throw refuse(field, "must be an NftMedia, an NftLink or null");
|
|
9860
|
+
checkLink(field, ref);
|
|
9861
|
+
ensure(!isNftDocumentLink(ref), fieldName(field, "media_type"), DOCUMENT_AS_MEDIA_RULE);
|
|
9856
9862
|
}
|
|
9857
9863
|
function checkAttributes(attributes) {
|
|
9858
9864
|
ensure(Array.isArray(attributes), "attributes", "must be an array \u2014 [] when there are none");
|
|
@@ -9869,6 +9875,7 @@ function checkMetadata(metadata) {
|
|
|
9869
9875
|
checkOptionalText("external_url", metadata.external_url);
|
|
9870
9876
|
checkAttributes(metadata.attributes);
|
|
9871
9877
|
checkOptionalText("collection", metadata.collection);
|
|
9878
|
+
ensure(metadata.collection_id === null || isCollectionId(metadata.collection_id), "collection_id", COLLECTION_ID_RULE);
|
|
9872
9879
|
}
|
|
9873
9880
|
function checkContent(content) {
|
|
9874
9881
|
switch (content?.kind) {
|
|
@@ -9926,6 +9933,7 @@ function arrayCbor(items) {
|
|
|
9926
9933
|
}
|
|
9927
9934
|
return out;
|
|
9928
9935
|
}
|
|
9936
|
+
var optionalHexBytes = (value) => CborSerializer.encodeNullable(value, (hex) => CborSerializer.encodeByteString(HexConverter.decode(hex.toLowerCase())));
|
|
9929
9937
|
function metadataCbor(metadata) {
|
|
9930
9938
|
return versioned(
|
|
9931
9939
|
NFT_METADATA_TAG,
|
|
@@ -9935,7 +9943,8 @@ function metadataCbor(metadata) {
|
|
|
9935
9943
|
mediaRefCbor(metadata.animation_url),
|
|
9936
9944
|
optionalText(metadata.external_url),
|
|
9937
9945
|
arrayCbor(metadata.attributes.map(attributeCbor)),
|
|
9938
|
-
optionalText(metadata.collection)
|
|
9946
|
+
optionalText(metadata.collection),
|
|
9947
|
+
optionalHexBytes(metadata.collection_id)
|
|
9939
9948
|
);
|
|
9940
9949
|
}
|
|
9941
9950
|
function encodeNftContent(content) {
|
|
@@ -9993,13 +10002,14 @@ function decodeMedia(body) {
|
|
|
9993
10002
|
const [, mediaType, bytes] = versionedFields(body, MEDIA_ARITY);
|
|
9994
10003
|
return { kind: "media", media_type: decodeText(mediaType), bytes: CborDeserializer.decodeByteString(bytes) };
|
|
9995
10004
|
}
|
|
10005
|
+
var decodeHex = (item) => HexConverter.encode(CborDeserializer.decodeByteString(item));
|
|
9996
10006
|
function decodeLink(body) {
|
|
9997
10007
|
const [, mediaType, uri, digest] = versionedFields(body, LINK_ARITY);
|
|
9998
10008
|
return {
|
|
9999
10009
|
kind: "link",
|
|
10000
10010
|
media_type: decodeText(mediaType),
|
|
10001
10011
|
uri: decodeText(uri),
|
|
10002
|
-
sha256:
|
|
10012
|
+
sha256: decodeHex(digest)
|
|
10003
10013
|
};
|
|
10004
10014
|
}
|
|
10005
10015
|
function decodeMetadata(body) {
|
|
@@ -10012,7 +10022,8 @@ function decodeMetadata(body) {
|
|
|
10012
10022
|
animation_url: decodeMediaRef("animation_url", fields[4]),
|
|
10013
10023
|
external_url: decodeOptionalText(fields[5]),
|
|
10014
10024
|
attributes: decodeAttributes(fields[6]),
|
|
10015
|
-
collection: decodeOptionalText(fields[7])
|
|
10025
|
+
collection: decodeOptionalText(fields[7]),
|
|
10026
|
+
collection_id: CborDeserializer.decodeNullable(fields[8], decodeHex)
|
|
10016
10027
|
};
|
|
10017
10028
|
}
|
|
10018
10029
|
var MEDIA_REF_DECODERS = /* @__PURE__ */ new Map([
|
|
@@ -10058,20 +10069,26 @@ function parseNftPayload(data) {
|
|
|
10058
10069
|
return null;
|
|
10059
10070
|
}
|
|
10060
10071
|
}
|
|
10061
|
-
function
|
|
10072
|
+
function isNftDocumentLink(content) {
|
|
10073
|
+
return content?.kind === "link" && content.media_type === NFT_DOCUMENT_MEDIA_TYPE;
|
|
10074
|
+
}
|
|
10075
|
+
function nftSignedDigest(context, payload) {
|
|
10062
10076
|
const preimage = CborSerializer.encodeArray(
|
|
10063
|
-
CborSerializer.
|
|
10064
|
-
|
|
10065
|
-
|
|
10077
|
+
CborSerializer.encodeByteString(SIGNED_DOMAIN),
|
|
10078
|
+
CborSerializer.encodeUnsignedInteger(NFT_FORMAT_VERSION),
|
|
10079
|
+
CborSerializer.encodeUnsignedInteger(NetworkId.fromId(context.networkId).id),
|
|
10080
|
+
CborSerializer.encodeByteString(context.recipientPredicate),
|
|
10081
|
+
CborSerializer.encodeByteString(context.tokenId),
|
|
10082
|
+
CborSerializer.encodeByteString(context.tokenType),
|
|
10066
10083
|
CborSerializer.encodeByteString(payload)
|
|
10067
10084
|
);
|
|
10068
10085
|
return new DataHasher(HashAlgorithm.SHA256).update(preimage).digest();
|
|
10069
10086
|
}
|
|
10070
|
-
async function verifyNftSignature(signed,
|
|
10087
|
+
async function verifyNftSignature(signed, context) {
|
|
10071
10088
|
try {
|
|
10072
10089
|
if (!SigningService.isPublicKeyValid(signed.creator)) return "invalid";
|
|
10073
10090
|
const signature = Signature.decode(signed.signature);
|
|
10074
|
-
const digest = await nftSignedDigest(
|
|
10091
|
+
const digest = await nftSignedDigest(context, signed.payload);
|
|
10075
10092
|
return await SigningService.verifyWithPublicKey(digest, signature, signed.creator) ? "valid" : "invalid";
|
|
10076
10093
|
} catch {
|
|
10077
10094
|
return "invalid";
|
|
@@ -10202,8 +10219,8 @@ function assertMintableData(data) {
|
|
|
10202
10219
|
function nftRecipientCbor(pubkey) {
|
|
10203
10220
|
return EncodedPredicate.fromPredicate(SignaturePredicate.create(pubkey)).toCBOR();
|
|
10204
10221
|
}
|
|
10205
|
-
async function signPayload(deps, payload,
|
|
10206
|
-
const digest = await nftSignedDigest(
|
|
10222
|
+
async function signPayload(deps, payload, context) {
|
|
10223
|
+
const digest = await nftSignedDigest(context, payload);
|
|
10207
10224
|
const signature = await deps.signingService.sign(digest);
|
|
10208
10225
|
return encodeNftSigned(deps.signingService.publicKey, payload, signature.encode());
|
|
10209
10226
|
}
|
|
@@ -10212,31 +10229,41 @@ async function planNftMint(deps, params) {
|
|
|
10212
10229
|
const tokenType = new TokenType(params.tokenType).bytes;
|
|
10213
10230
|
const salt = TokenSalt.generate();
|
|
10214
10231
|
const tokenId = await TokenId.fromSalt(deps.networkId, salt);
|
|
10215
|
-
const data = params.sign ? await signPayload(deps, payload,
|
|
10232
|
+
const data = params.sign ? await signPayload(deps, payload, {
|
|
10233
|
+
networkId: deps.networkId.id,
|
|
10234
|
+
recipientPredicate: nftRecipientCbor(params.recipientPubkey),
|
|
10235
|
+
tokenId: tokenId.bytes,
|
|
10236
|
+
tokenType
|
|
10237
|
+
}) : payload;
|
|
10216
10238
|
assertMintableData(data);
|
|
10217
10239
|
return { data, salt: salt.toBytes(), tokenType, tokenId: HexConverter.encode(tokenId.bytes) };
|
|
10218
10240
|
}
|
|
10219
|
-
async function verifyAgainst(signed,
|
|
10241
|
+
async function verifyAgainst(signed, genesisContext) {
|
|
10220
10242
|
try {
|
|
10221
|
-
return await verifyNftSignature(signed,
|
|
10243
|
+
return await verifyNftSignature(signed, genesisContext());
|
|
10222
10244
|
} catch {
|
|
10223
10245
|
return "invalid";
|
|
10224
10246
|
}
|
|
10225
10247
|
}
|
|
10226
|
-
async function readNftData(data,
|
|
10248
|
+
async function readNftData(data, genesisContext) {
|
|
10227
10249
|
const parsed = parseNftPayload(data);
|
|
10228
10250
|
if (parsed === null) return null;
|
|
10229
10251
|
if (parsed.signed === null) return { content: parsed.content, creator: null, signature: "unsigned" };
|
|
10230
10252
|
return {
|
|
10231
10253
|
content: parsed.content,
|
|
10232
10254
|
creator: HexConverter.encode(parsed.signed.creator),
|
|
10233
|
-
signature: await verifyAgainst(parsed.signed,
|
|
10255
|
+
signature: await verifyAgainst(parsed.signed, genesisContext)
|
|
10234
10256
|
};
|
|
10235
10257
|
}
|
|
10236
10258
|
async function readTokenNft(token) {
|
|
10237
10259
|
try {
|
|
10238
10260
|
const { genesis } = token.sdkToken;
|
|
10239
|
-
return await readNftData(genesis.data,
|
|
10261
|
+
return await readNftData(genesis.data, () => ({
|
|
10262
|
+
networkId: genesis.networkId.id,
|
|
10263
|
+
recipientPredicate: genesis.recipient.toCBOR(),
|
|
10264
|
+
tokenId: genesis.tokenId.bytes,
|
|
10265
|
+
tokenType: genesis.tokenType.bytes
|
|
10266
|
+
}));
|
|
10240
10267
|
} catch {
|
|
10241
10268
|
return null;
|
|
10242
10269
|
}
|