@unicitylabs/sphere-sdk 0.9.0-dev.0 → 0.9.0-dev.1

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.
@@ -0,0 +1,647 @@
1
+ // token-engine/sdk.ts
2
+ import { StateTransitionClient } from "@unicitylabs/state-transition-sdk/lib/StateTransitionClient.js";
3
+ import { AggregatorClient } from "@unicitylabs/state-transition-sdk/lib/api/AggregatorClient.js";
4
+ import { NetworkId } from "@unicitylabs/state-transition-sdk/lib/api/NetworkId.js";
5
+ import { CertificationData } from "@unicitylabs/state-transition-sdk/lib/api/CertificationData.js";
6
+ import { CertificationResponse, CertificationStatus } from "@unicitylabs/state-transition-sdk/lib/api/CertificationResponse.js";
7
+ import { StateId } from "@unicitylabs/state-transition-sdk/lib/api/StateId.js";
8
+ import { InclusionProof } from "@unicitylabs/state-transition-sdk/lib/api/InclusionProof.js";
9
+ import { InclusionProofResponse } from "@unicitylabs/state-transition-sdk/lib/api/InclusionProofResponse.js";
10
+ import { RootTrustBase } from "@unicitylabs/state-transition-sdk/lib/api/bft/RootTrustBase.js";
11
+ import { waitInclusionProof } from "@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils.js";
12
+ import { Token } from "@unicitylabs/state-transition-sdk/lib/transaction/Token.js";
13
+ import { MintTransaction } from "@unicitylabs/state-transition-sdk/lib/transaction/MintTransaction.js";
14
+ import { TransferTransaction } from "@unicitylabs/state-transition-sdk/lib/transaction/TransferTransaction.js";
15
+ import { CertifiedMintTransaction } from "@unicitylabs/state-transition-sdk/lib/transaction/CertifiedMintTransaction.js";
16
+ import { CertifiedTransferTransaction } from "@unicitylabs/state-transition-sdk/lib/transaction/CertifiedTransferTransaction.js";
17
+ import { TokenId } from "@unicitylabs/state-transition-sdk/lib/transaction/TokenId.js";
18
+ import { TokenType } from "@unicitylabs/state-transition-sdk/lib/transaction/TokenType.js";
19
+ import { TokenSalt } from "@unicitylabs/state-transition-sdk/lib/transaction/TokenSalt.js";
20
+ import { MintJustificationVerifierService } from "@unicitylabs/state-transition-sdk/lib/transaction/verification/MintJustificationVerifierService.js";
21
+ import { EncodedPredicate } from "@unicitylabs/state-transition-sdk/lib/predicate/EncodedPredicate.js";
22
+ import { SignaturePredicate } from "@unicitylabs/state-transition-sdk/lib/predicate/builtin/SignaturePredicate.js";
23
+ import { SignaturePredicateUnlockScript } from "@unicitylabs/state-transition-sdk/lib/predicate/builtin/SignaturePredicateUnlockScript.js";
24
+ import { BurnPredicate } from "@unicitylabs/state-transition-sdk/lib/predicate/builtin/BurnPredicate.js";
25
+ import { PredicateVerifierService } from "@unicitylabs/state-transition-sdk/lib/predicate/verification/PredicateVerifierService.js";
26
+ import { SigningService } from "@unicitylabs/state-transition-sdk/lib/crypto/secp256k1/SigningService.js";
27
+ import { Signature } from "@unicitylabs/state-transition-sdk/lib/crypto/secp256k1/Signature.js";
28
+ import { MintSigningService } from "@unicitylabs/state-transition-sdk/lib/crypto/MintSigningService.js";
29
+ import { HashAlgorithm } from "@unicitylabs/state-transition-sdk/lib/crypto/hash/HashAlgorithm.js";
30
+ import { DataHash } from "@unicitylabs/state-transition-sdk/lib/crypto/hash/DataHash.js";
31
+ import { DataHasher } from "@unicitylabs/state-transition-sdk/lib/crypto/hash/DataHasher.js";
32
+ import { DataHasherFactory } from "@unicitylabs/state-transition-sdk/lib/crypto/hash/DataHasherFactory.js";
33
+ import { CborSerializer } from "@unicitylabs/state-transition-sdk/lib/serialization/cbor/CborSerializer.js";
34
+ import { CborDeserializer } from "@unicitylabs/state-transition-sdk/lib/serialization/cbor/CborDeserializer.js";
35
+ import { CborError } from "@unicitylabs/state-transition-sdk/lib/serialization/cbor/CborError.js";
36
+ import { Asset } from "@unicitylabs/state-transition-sdk/lib/payment/asset/Asset.js";
37
+ import { AssetId } from "@unicitylabs/state-transition-sdk/lib/payment/asset/AssetId.js";
38
+ import { PaymentAssetCollection } from "@unicitylabs/state-transition-sdk/lib/payment/asset/PaymentAssetCollection.js";
39
+ import { TokenSplit } from "@unicitylabs/state-transition-sdk/lib/payment/TokenSplit.js";
40
+ import { SplitTokenRequest } from "@unicitylabs/state-transition-sdk/lib/payment/SplitTokenRequest.js";
41
+ import { SplitToken } from "@unicitylabs/state-transition-sdk/lib/payment/SplitToken.js";
42
+ import { SplitAssetProof } from "@unicitylabs/state-transition-sdk/lib/payment/SplitAssetProof.js";
43
+ import { SplitMintJustification } from "@unicitylabs/state-transition-sdk/lib/payment/SplitMintJustification.js";
44
+ import { SplitMintJustificationVerifier } from "@unicitylabs/state-transition-sdk/lib/payment/SplitMintJustificationVerifier.js";
45
+ import { VerificationStatus } from "@unicitylabs/state-transition-sdk/lib/verification/VerificationStatus.js";
46
+ import { VerificationResult } from "@unicitylabs/state-transition-sdk/lib/verification/VerificationResult.js";
47
+ import { UnicityId } from "@unicitylabs/state-transition-sdk/lib/unicity-id/UnicityId.js";
48
+ import { UnicityIdMintTransaction } from "@unicitylabs/state-transition-sdk/lib/unicity-id/UnicityIdMintTransaction.js";
49
+ import { CertifiedUnicityIdMintTransaction } from "@unicitylabs/state-transition-sdk/lib/unicity-id/CertifiedUnicityIdMintTransaction.js";
50
+ import { UnicityIdToken } from "@unicitylabs/state-transition-sdk/lib/unicity-id/UnicityIdToken.js";
51
+ import { HexConverter } from "@unicitylabs/state-transition-sdk/lib/util/HexConverter.js";
52
+ import { BigintConverter } from "@unicitylabs/state-transition-sdk/lib/util/BigintConverter.js";
53
+ import { BitString } from "@unicitylabs/state-transition-sdk/lib/util/BitString.js";
54
+
55
+ // token-engine/identity.ts
56
+ var UNICITY_TOKEN_TYPE_HEX = "f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509";
57
+ var SIGNING_ALGORITHM = "secp256k1";
58
+ var EMBEDDED_PREDICATE_UNMASKED = 0;
59
+ var HASH_ALGORITHM_SHA256 = 0n;
60
+ var SHA256_IMPRINT_PREFIX = new Uint8Array([0, 0]);
61
+ function hexToBytes(hex) {
62
+ const bytes = new Uint8Array(hex.length / 2);
63
+ for (let i = 0; i < bytes.length; i++) {
64
+ bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
65
+ }
66
+ return bytes;
67
+ }
68
+ function sha256(data) {
69
+ return new DataHasher(HashAlgorithm.SHA256).update(data).digest().then((h) => h.data);
70
+ }
71
+ async function deriveDirectAddress(publicKey) {
72
+ const tokenTypeCbor = CborSerializer.encodeByteString(hexToBytes(UNICITY_TOKEN_TYPE_HEX));
73
+ const reference = CborSerializer.encodeArray(
74
+ CborSerializer.encodeByteString(new Uint8Array([EMBEDDED_PREDICATE_UNMASKED])),
75
+ CborSerializer.encodeByteString(tokenTypeCbor),
76
+ CborSerializer.encodeTextString(SIGNING_ALGORITHM),
77
+ CborSerializer.encodeUnsignedInteger(HASH_ALGORITHM_SHA256),
78
+ CborSerializer.encodeByteString(publicKey)
79
+ );
80
+ const refHash = await sha256(reference);
81
+ const imprint = new Uint8Array([...SHA256_IMPRINT_PREFIX, ...refHash]);
82
+ const checksum = (await sha256(imprint)).slice(0, 4);
83
+ return `DIRECT://${HexConverter.encode(imprint)}${HexConverter.encode(checksum)}`;
84
+ }
85
+
86
+ // core/errors.ts
87
+ var SphereError = class extends Error {
88
+ code;
89
+ cause;
90
+ constructor(message, code, cause) {
91
+ super(message);
92
+ this.name = "SphereError";
93
+ this.code = code;
94
+ this.cause = cause;
95
+ }
96
+ };
97
+
98
+ // core/logger.ts
99
+ var LOGGER_KEY = "__sphere_sdk_logger__";
100
+ function getState() {
101
+ const g = globalThis;
102
+ if (!g[LOGGER_KEY]) {
103
+ g[LOGGER_KEY] = { debug: false, tags: {}, handler: null };
104
+ }
105
+ return g[LOGGER_KEY];
106
+ }
107
+ function isEnabled(tag) {
108
+ const state = getState();
109
+ if (tag in state.tags) return state.tags[tag];
110
+ return state.debug;
111
+ }
112
+ var logger = {
113
+ /**
114
+ * Configure the logger. Can be called multiple times (last write wins).
115
+ * Typically called by createBrowserProviders(), createNodeProviders(), or Sphere.init().
116
+ */
117
+ configure(config) {
118
+ const state = getState();
119
+ if (config.debug !== void 0) state.debug = config.debug;
120
+ if (config.handler !== void 0) state.handler = config.handler;
121
+ },
122
+ /**
123
+ * Enable/disable debug logging for a specific tag.
124
+ * Per-tag setting overrides the global debug flag.
125
+ *
126
+ * @example
127
+ * ```ts
128
+ * logger.setTagDebug('Nostr', true); // enable only Nostr logs
129
+ * logger.setTagDebug('Nostr', false); // disable Nostr logs even if global debug=true
130
+ * ```
131
+ */
132
+ setTagDebug(tag, enabled) {
133
+ getState().tags[tag] = enabled;
134
+ },
135
+ /**
136
+ * Clear per-tag override, falling back to global debug flag.
137
+ */
138
+ clearTagDebug(tag) {
139
+ delete getState().tags[tag];
140
+ },
141
+ /** Returns true if debug mode is enabled for the given tag (or globally). */
142
+ isDebugEnabled(tag) {
143
+ if (tag) return isEnabled(tag);
144
+ return getState().debug;
145
+ },
146
+ /**
147
+ * Debug-level log. Only shown when debug is enabled (globally or for this tag).
148
+ * Use for detailed operational information.
149
+ */
150
+ debug(tag, message, ...args) {
151
+ if (!isEnabled(tag)) return;
152
+ const state = getState();
153
+ if (state.handler) {
154
+ state.handler("debug", tag, message, ...args);
155
+ } else {
156
+ console.log(`[${tag}]`, message, ...args);
157
+ }
158
+ },
159
+ /**
160
+ * Warning-level log. ALWAYS shown regardless of debug flag.
161
+ * Use for important but non-critical issues (timeouts, retries, degraded state).
162
+ */
163
+ warn(tag, message, ...args) {
164
+ const state = getState();
165
+ if (state.handler) {
166
+ state.handler("warn", tag, message, ...args);
167
+ } else {
168
+ console.warn(`[${tag}]`, message, ...args);
169
+ }
170
+ },
171
+ /**
172
+ * Error-level log. ALWAYS shown regardless of debug flag.
173
+ * Use for critical failures that should never be silenced.
174
+ */
175
+ error(tag, message, ...args) {
176
+ const state = getState();
177
+ if (state.handler) {
178
+ state.handler("error", tag, message, ...args);
179
+ } else {
180
+ console.error(`[${tag}]`, message, ...args);
181
+ }
182
+ },
183
+ /** Reset all logger state (debug flag, tags, handler). Primarily for tests. */
184
+ reset() {
185
+ const g = globalThis;
186
+ delete g[LOGGER_KEY];
187
+ }
188
+ };
189
+
190
+ // token-engine/SpherePaymentData.ts
191
+ var COIN_ID_PATTERN = /^([0-9a-f]{2})+$/;
192
+ function assertAsset(coinId, amount) {
193
+ if (!COIN_ID_PATTERN.test(coinId)) {
194
+ throw new SphereError(`Invalid coin id (expected even-length lowercase hex): "${coinId}"`, "VALIDATION_ERROR");
195
+ }
196
+ if (amount < 0n) {
197
+ throw new SphereError(`Asset amount must be non-negative: ${amount.toString()}`, "VALIDATION_ERROR");
198
+ }
199
+ }
200
+ function sphereAssetToSdk(coinId, amount) {
201
+ assertAsset(coinId, amount);
202
+ return new Asset(new AssetId(HexConverter.decode(coinId)), amount);
203
+ }
204
+ var SpherePaymentData = class _SpherePaymentData {
205
+ constructor(assets, _memo = null) {
206
+ this.assets = assets;
207
+ this._memo = _memo;
208
+ }
209
+ /** Sphere-private CBOR tag (verified free in the v2 SDK tag space). */
210
+ static CBOR_TAG = 39050n;
211
+ /** Envelope version; bump when the structure changes. */
212
+ static VERSION = 1n;
213
+ /** Opaque, app-defined memo carried alongside the value (e.g. invoice attribution). */
214
+ get memo() {
215
+ return this._memo ? new Uint8Array(this._memo) : null;
216
+ }
217
+ /** Wrap an existing SDK asset collection (+ optional opaque memo). */
218
+ static create(assets, memo = null) {
219
+ return new _SpherePaymentData(assets, memo);
220
+ }
221
+ /** Build from a sphere-domain value (hex coin id → bigint amount) + optional opaque memo. */
222
+ static fromValue(value, memo = null) {
223
+ const assets = value.assets.map((a) => sphereAssetToSdk(a.coinId, a.amount));
224
+ return new _SpherePaymentData(PaymentAssetCollection.create(...assets), memo);
225
+ }
226
+ /** Decode from the CBOR envelope produced by {@link encode}. */
227
+ static fromCBOR(bytes) {
228
+ const tag = CborDeserializer.decodeTag(bytes);
229
+ if (tag.tag !== _SpherePaymentData.CBOR_TAG) {
230
+ throw new CborError(`Invalid SpherePaymentData tag: ${tag.tag}`);
231
+ }
232
+ const fields = CborDeserializer.decodeArray(tag.data, 3);
233
+ const version = CborDeserializer.decodeUnsignedInteger(fields[0]);
234
+ if (version !== _SpherePaymentData.VERSION) {
235
+ throw new CborError(`Unsupported SpherePaymentData version: ${version}`);
236
+ }
237
+ const memo = CborDeserializer.decodeNullable(fields[2], CborDeserializer.decodeByteString);
238
+ return new _SpherePaymentData(PaymentAssetCollection.fromCBOR(fields[1]), memo);
239
+ }
240
+ /** Deterministic, versioned, tagged CBOR: `tag(39050)[ version, assets, memo? ]`. */
241
+ encode() {
242
+ return Promise.resolve(
243
+ CborSerializer.encodeTag(
244
+ _SpherePaymentData.CBOR_TAG,
245
+ CborSerializer.encodeArray(
246
+ CborSerializer.encodeUnsignedInteger(_SpherePaymentData.VERSION),
247
+ this.assets.toCBOR(),
248
+ CborSerializer.encodeNullable(this._memo, CborSerializer.encodeByteString)
249
+ )
250
+ )
251
+ );
252
+ }
253
+ /** Project to a sphere-domain value (hex coin id + bigint amount), preserving order. */
254
+ toValue() {
255
+ return {
256
+ assets: this.assets.toArray().map((a) => ({
257
+ coinId: HexConverter.encode(a.id.bytes),
258
+ amount: a.value
259
+ }))
260
+ };
261
+ }
262
+ /** Balance of a single coin within this payload (0n when absent). */
263
+ balanceOf(coinId) {
264
+ if (!COIN_ID_PATTERN.test(coinId)) {
265
+ throw new SphereError(`Invalid coin id (expected even-length lowercase hex): "${coinId}"`, "VALIDATION_ERROR");
266
+ }
267
+ const asset = this.assets.get(new AssetId(HexConverter.decode(coinId)));
268
+ return asset ? asset.value : 0n;
269
+ }
270
+ };
271
+ function decodeSpherePaymentData(bytes) {
272
+ return Promise.resolve(SpherePaymentData.fromCBOR(bytes));
273
+ }
274
+
275
+ // token-engine/token-blob.ts
276
+ var TOKEN_BLOB_VERSION = 1;
277
+
278
+ // token-engine/SphereTokenEngine.ts
279
+ var SphereTokenEngine = class {
280
+ constructor(deps) {
281
+ this.deps = deps;
282
+ }
283
+ // ── identity ────────────────────────────────────────────────────────────────
284
+ getIdentity() {
285
+ return { chainPubkey: new Uint8Array(this.deps.signingService.publicKey) };
286
+ }
287
+ /** Legacy DIRECT:// address (Path A). Async: the derivation hashes via the SDK. */
288
+ deriveIdentityAddress(pubkey) {
289
+ return deriveDirectAddress(pubkey ?? this.deps.signingService.publicKey);
290
+ }
291
+ // ── value (read) ─────────────────────────────────────────────────────────────
292
+ readValue(token) {
293
+ return token.value;
294
+ }
295
+ balanceOf(token, coinId) {
296
+ let sum = 0n;
297
+ for (const asset of token.value?.assets ?? []) {
298
+ if (asset.coinId === coinId) sum += asset.amount;
299
+ }
300
+ return sum;
301
+ }
302
+ tokenId(token) {
303
+ return token.blob.tokenId;
304
+ }
305
+ readMemo(token) {
306
+ const sdkToken = token.sdkToken;
307
+ if (sdkToken.transactions.length > 0) {
308
+ return sdkToken.latestTransaction.data;
309
+ }
310
+ const data = sdkToken.genesis.data;
311
+ if (data && this.isSpherePaymentData(data)) {
312
+ return SpherePaymentData.fromCBOR(data).memo;
313
+ }
314
+ return null;
315
+ }
316
+ readTokenData(token) {
317
+ const data = token.sdkToken.genesis.data;
318
+ return data ? new Uint8Array(data) : null;
319
+ }
320
+ // ── lifecycle ────────────────────────────────────────────────────────────────
321
+ async mint(params, options) {
322
+ const recipient = SignaturePredicate.create(params.recipientPubkey);
323
+ const data = params.value ? await SpherePaymentData.fromValue(params.value).encode() : null;
324
+ const mintTx = await MintTransaction.create(this.deps.networkId, recipient, data);
325
+ const certificationData = await CertificationData.fromMintTransaction(mintTx);
326
+ const response = await this.deps.client.submitCertificationRequest(certificationData);
327
+ if (response.status !== CertificationStatus.SUCCESS) {
328
+ throw new SphereError(`Mint certification failed: ${response.status}`, "AGGREGATOR_ERROR");
329
+ }
330
+ const proof = await waitInclusionProof(
331
+ this.deps.client,
332
+ this.deps.trustBase,
333
+ this.deps.predicateVerifier,
334
+ mintTx,
335
+ options?.signal
336
+ );
337
+ const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
338
+ const token = await Token.mint(
339
+ this.deps.trustBase,
340
+ this.deps.predicateVerifier,
341
+ this.deps.mintJustificationVerifier,
342
+ certified
343
+ );
344
+ return this.wrapToken(token);
345
+ }
346
+ async mintDataToken(params, options) {
347
+ const recipient = SignaturePredicate.create(params.recipientPubkey);
348
+ const tokenType = params.tokenType ? new TokenType(params.tokenType) : TokenType.generate();
349
+ const salt = params.salt ? TokenSalt.fromBytes(params.salt) : TokenSalt.generate();
350
+ const mintTx = await MintTransaction.create(this.deps.networkId, recipient, params.data, tokenType, salt);
351
+ const certificationData = await CertificationData.fromMintTransaction(mintTx);
352
+ const response = await this.deps.client.submitCertificationRequest(certificationData);
353
+ if (response.status !== CertificationStatus.SUCCESS) {
354
+ throw new SphereError(`Data-token mint failed: ${response.status}`, "AGGREGATOR_ERROR");
355
+ }
356
+ const proof = await waitInclusionProof(
357
+ this.deps.client,
358
+ this.deps.trustBase,
359
+ this.deps.predicateVerifier,
360
+ mintTx,
361
+ options?.signal
362
+ );
363
+ const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
364
+ const token = await Token.mint(
365
+ this.deps.trustBase,
366
+ this.deps.predicateVerifier,
367
+ this.deps.mintJustificationVerifier,
368
+ certified
369
+ );
370
+ return this.wrapToken(token);
371
+ }
372
+ async transfer(params, options) {
373
+ this.assertOwned(params.token);
374
+ const recipient = SignaturePredicate.create(params.recipientPubkey);
375
+ const stateMask = crypto.getRandomValues(new Uint8Array(32));
376
+ const transferTx = await TransferTransaction.create(params.token.sdkToken, recipient, stateMask, params.data ?? null);
377
+ const unlockScript = await SignaturePredicateUnlockScript.create(transferTx, this.deps.signingService);
378
+ const certificationData = await CertificationData.fromTransaction(transferTx, unlockScript);
379
+ const response = await this.deps.client.submitCertificationRequest(certificationData);
380
+ if (response.status !== CertificationStatus.SUCCESS) {
381
+ throw new SphereError(`Transfer certification failed: ${response.status}`, "TRANSFER_FAILED");
382
+ }
383
+ const proof = await waitInclusionProof(
384
+ this.deps.client,
385
+ this.deps.trustBase,
386
+ this.deps.predicateVerifier,
387
+ transferTx,
388
+ options?.signal
389
+ );
390
+ const certified = await transferTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
391
+ const transferred = await params.token.sdkToken.transfer(this.deps.trustBase, this.deps.predicateVerifier, certified);
392
+ return this.wrapToken(transferred);
393
+ }
394
+ async split(params, options) {
395
+ this.assertOwned(params.token);
396
+ if (params.outputs.length === 0) {
397
+ throw new SphereError("Split requires at least one output", "VALIDATION_ERROR");
398
+ }
399
+ const requests = params.outputs.map(
400
+ (o) => SplitTokenRequest.create(
401
+ SignaturePredicate.create(o.recipientPubkey),
402
+ PaymentAssetCollection.create(sphereAssetToSdk(o.coinId, o.amount))
403
+ )
404
+ );
405
+ const split = await TokenSplit.split(params.token.sdkToken, decodeSpherePaymentData, requests);
406
+ const burnUnlock = await SignaturePredicateUnlockScript.create(split.burn.transaction, this.deps.signingService);
407
+ const burnCert = await CertificationData.fromTransaction(split.burn.transaction, burnUnlock);
408
+ const burnResponse = await this.deps.client.submitCertificationRequest(burnCert);
409
+ if (burnResponse.status !== CertificationStatus.SUCCESS) {
410
+ throw new SphereError(`Split burn failed: ${burnResponse.status}`, "TRANSFER_FAILED");
411
+ }
412
+ const burnProof = await waitInclusionProof(
413
+ this.deps.client,
414
+ this.deps.trustBase,
415
+ this.deps.predicateVerifier,
416
+ split.burn.transaction,
417
+ options?.signal
418
+ );
419
+ const burnCertified = await split.burn.transaction.toCertifiedTransaction(
420
+ this.deps.trustBase,
421
+ this.deps.predicateVerifier,
422
+ burnProof
423
+ );
424
+ const burntToken = await params.token.sdkToken.transfer(
425
+ this.deps.trustBase,
426
+ this.deps.predicateVerifier,
427
+ burnCertified
428
+ );
429
+ const outputs = [];
430
+ for (let i = 0; i < split.tokens.length; i++) {
431
+ const splitToken = split.tokens[i];
432
+ const data = await SpherePaymentData.create(splitToken.assets, params.outputs[i].data ?? null).encode();
433
+ const justification = SplitMintJustification.create(burntToken, splitToken.proofs).toCBOR();
434
+ const mintTx = await MintTransaction.create(
435
+ splitToken.networkId,
436
+ splitToken.recipient,
437
+ data,
438
+ splitToken.tokenType,
439
+ splitToken.salt,
440
+ justification
441
+ );
442
+ const certData = await CertificationData.fromMintTransaction(mintTx);
443
+ const response = await this.deps.client.submitCertificationRequest(certData);
444
+ if (response.status !== CertificationStatus.SUCCESS) {
445
+ throw new SphereError(`Split mint failed: ${response.status}`, "AGGREGATOR_ERROR");
446
+ }
447
+ const proof = await waitInclusionProof(
448
+ this.deps.client,
449
+ this.deps.trustBase,
450
+ this.deps.predicateVerifier,
451
+ mintTx,
452
+ options?.signal
453
+ );
454
+ const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);
455
+ const token = await Token.mint(
456
+ this.deps.trustBase,
457
+ this.deps.predicateVerifier,
458
+ this.deps.mintJustificationVerifier,
459
+ certified
460
+ );
461
+ outputs.push(this.wrapToken(token));
462
+ }
463
+ return { outputs };
464
+ }
465
+ // ── verification ─────────────────────────────────────────────────────────────
466
+ async verify(token, _options) {
467
+ const result = await token.sdkToken.verify(
468
+ this.deps.trustBase,
469
+ this.deps.predicateVerifier,
470
+ this.deps.mintJustificationVerifier
471
+ );
472
+ return result.status === VerificationStatus.OK ? { ok: true } : { ok: false, reason: String(result.status) };
473
+ }
474
+ isOwnedBy(token, pubkey) {
475
+ const owner = token.sdkToken.latestTransaction.recipient;
476
+ const claimed = EncodedPredicate.fromPredicate(SignaturePredicate.create(pubkey));
477
+ return EncodedPredicate.equals(owner, claimed);
478
+ }
479
+ async isSpent(token, _options) {
480
+ const probe = await TransferTransaction.create(
481
+ token.sdkToken,
482
+ SignaturePredicate.create(this.deps.signingService.publicKey),
483
+ new Uint8Array(32)
484
+ );
485
+ const stateId = await StateId.fromTransaction(probe);
486
+ const response = await this.deps.client.getInclusionProof(stateId);
487
+ return response.inclusionProof.inclusionCertificate !== null;
488
+ }
489
+ // ── serialization ────────────────────────────────────────────────────────────
490
+ encodeToken(token) {
491
+ return token.blob;
492
+ }
493
+ async decodeToken(blob) {
494
+ const sdkToken = await Token.fromCBOR(blob.token);
495
+ if (sdkToken.genesis.networkId.id !== this.deps.networkId.id) {
496
+ throw new SphereError(
497
+ `Token network mismatch: token is on network ${sdkToken.genesis.networkId.id}, engine on ${this.deps.networkId.id}`,
498
+ "VALIDATION_ERROR"
499
+ );
500
+ }
501
+ return this.wrapToken(sdkToken);
502
+ }
503
+ // ── internals ────────────────────────────────────────────────────────────────
504
+ /** Fail fast if this engine's key does not own the token's current state. */
505
+ assertOwned(token) {
506
+ if (!this.isOwnedBy(token, this.deps.signingService.publicKey)) {
507
+ throw new SphereError("Cannot transfer a token not owned by this engine identity", "VALIDATION_ERROR");
508
+ }
509
+ }
510
+ /** Wrap an SDK token into a SphereToken: cache its blob (incl. stable tokenId) + decoded value. */
511
+ wrapToken(sdkToken) {
512
+ const data = sdkToken.genesis.data;
513
+ let value = null;
514
+ if (data && this.isSpherePaymentData(data)) {
515
+ try {
516
+ value = SpherePaymentData.fromCBOR(data).toValue();
517
+ } catch (err) {
518
+ throw new SphereError(
519
+ `Failed to decode token payment data: ${err instanceof Error ? err.message : String(err)}`,
520
+ "VALIDATION_ERROR"
521
+ );
522
+ }
523
+ }
524
+ const blob = {
525
+ v: TOKEN_BLOB_VERSION,
526
+ network: sdkToken.genesis.networkId.id,
527
+ tokenId: HexConverter.encode(sdkToken.id.bytes),
528
+ token: sdkToken.toCBOR()
529
+ };
530
+ return { sdkToken, blob, value };
531
+ }
532
+ /** True if the bytes are a SpherePaymentData envelope (value token) vs a raw data token. */
533
+ isSpherePaymentData(data) {
534
+ try {
535
+ return CborDeserializer.decodeTag(data).tag === SpherePaymentData.CBOR_TAG;
536
+ } catch {
537
+ return false;
538
+ }
539
+ }
540
+ };
541
+
542
+ // token-engine/factory.ts
543
+ async function createSphereTokenEngine(config) {
544
+ if (config.trustBaseJson == null) {
545
+ throw new SphereError("Engine config requires a trust base (trustBaseJson)", "INVALID_CONFIG");
546
+ }
547
+ if (!config.apiKey) {
548
+ logger.warn(
549
+ "TokenEngine",
550
+ "No aggregator apiKey \u2014 pass config.oracle.apiKey (testnet2 value in .env.example; mainnet from a secret env var). Gateway requests will be unauthenticated."
551
+ );
552
+ }
553
+ const trustBase = RootTrustBase.fromJSON(config.trustBaseJson);
554
+ const predicateVerifier = PredicateVerifierService.create();
555
+ const mintJustificationVerifier = new MintJustificationVerifierService();
556
+ mintJustificationVerifier.register(
557
+ new SplitMintJustificationVerifier(trustBase, predicateVerifier, decodeSpherePaymentData)
558
+ );
559
+ const deps = {
560
+ client: new StateTransitionClient(new AggregatorClient(config.aggregatorUrl, config.apiKey ?? null)),
561
+ trustBase,
562
+ predicateVerifier,
563
+ mintJustificationVerifier,
564
+ signingService: new SigningService(config.privateKey),
565
+ // The trust base is the single source of truth for the network id (it carries
566
+ // NetworkId.fromId, so any id works — e.g. testnet2 = 4 — with no enum entry).
567
+ networkId: trustBase.networkId
568
+ };
569
+ return new SphereTokenEngine(deps);
570
+ }
571
+
572
+ // token-engine/unicity-id.ts
573
+ var SelfIssuedUnicityIdMinter = class {
574
+ constructor(client, trustBase, predicateVerifier, signingService) {
575
+ this.client = client;
576
+ this.trustBase = trustBase;
577
+ this.predicateVerifier = predicateVerifier;
578
+ this.signingService = signingService;
579
+ }
580
+ async mintUnicityIdToken(name, options) {
581
+ const unicityId = new UnicityId(name);
582
+ const self = SignaturePredicate.fromSigningService(this.signingService);
583
+ const mintTx = await UnicityIdMintTransaction.create(
584
+ self,
585
+ // issuer lock script (self-issued)
586
+ self,
587
+ // recipient — locks the minted token state to this wallet
588
+ unicityId,
589
+ new TokenType(HexConverter.decode(UNICITY_TOKEN_TYPE_HEX)),
590
+ self
591
+ // target predicate the unicity id resolves to
592
+ );
593
+ const certificationData = await CertificationData.fromTransaction(
594
+ mintTx,
595
+ await SignaturePredicateUnlockScript.create(mintTx, this.signingService)
596
+ );
597
+ const response = await this.client.submitCertificationRequest(certificationData);
598
+ if (response.status !== CertificationStatus.SUCCESS) {
599
+ logger.warn(
600
+ "UnicityIdMinter",
601
+ `Certification request returned ${response.status} for "@${name}" \u2014 attempting proof recovery (idempotent re-mint).`
602
+ );
603
+ }
604
+ try {
605
+ const proof = await waitInclusionProof(
606
+ this.client,
607
+ this.trustBase,
608
+ this.predicateVerifier,
609
+ mintTx,
610
+ options?.signal
611
+ );
612
+ const certified = await mintTx.toCertifiedTransaction(this.trustBase, this.predicateVerifier, proof);
613
+ const token = await UnicityIdToken.mint(this.trustBase, this.predicateVerifier, certified);
614
+ return {
615
+ tokenCborHex: HexConverter.encode(token.toCBOR()),
616
+ tokenId: HexConverter.encode(token.id.bytes)
617
+ };
618
+ } catch (err) {
619
+ const detail = err instanceof Error ? err.message : String(err);
620
+ throw new SphereError(
621
+ `Unicity ID mint failed for "@${name}": ${detail}` + (response.status !== CertificationStatus.SUCCESS ? ` (certification status: ${response.status})` : ""),
622
+ "AGGREGATOR_ERROR"
623
+ );
624
+ }
625
+ }
626
+ };
627
+ function createUnicityIdMinter(config) {
628
+ if (config.trustBaseJson == null) {
629
+ throw new SphereError("Unicity ID minter requires a trust base (trustBaseJson)", "INVALID_CONFIG");
630
+ }
631
+ const trustBase = RootTrustBase.fromJSON(config.trustBaseJson);
632
+ return new SelfIssuedUnicityIdMinter(
633
+ new StateTransitionClient(new AggregatorClient(config.aggregatorUrl, config.apiKey ?? null)),
634
+ trustBase,
635
+ PredicateVerifierService.create(),
636
+ new SigningService(config.privateKey)
637
+ );
638
+ }
639
+ export {
640
+ SpherePaymentData,
641
+ createSphereTokenEngine,
642
+ createUnicityIdMinter,
643
+ decodeSpherePaymentData,
644
+ deriveDirectAddress,
645
+ sphereAssetToSdk
646
+ };
647
+ //# sourceMappingURL=index.js.map