@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 @@
1
+ {"version":3,"sources":["../../token-engine/index.ts","../../token-engine/sdk.ts","../../token-engine/identity.ts","../../core/errors.ts","../../core/logger.ts","../../token-engine/SpherePaymentData.ts","../../token-engine/token-blob.ts","../../token-engine/SphereTokenEngine.ts","../../token-engine/factory.ts","../../token-engine/unicity-id.ts"],"sourcesContent":["/**\n * token-engine — public entry point.\n *\n * The wallet's anti-corruption layer over the v2 state-transition SDK. Everything\n * outside this module imports the token engine from here (the ITokenEngine port\n * and sphere-domain types), never the SDK directly.\n *\n * The concrete adapter (`createSphereTokenEngine`) and the in-memory test double\n * (`FakeTokenEngine`) are exported here as they land in Phase 0 / Track A.\n */\n\n// Frozen contract\nexport type {\n ITokenEngine,\n EngineConfig,\n EngineOpOptions,\n CreateTokenEngine,\n} from './engine';\n\nexport type {\n EngineIdentity,\n SphereNetwork,\n CoinId,\n SphereAsset,\n SphereValue,\n TokenBlob,\n SphereToken,\n MintParams,\n MintDataTokenParams,\n TransferParams,\n SplitOutput,\n SplitParams,\n SplitResult,\n EngineVerifyResult,\n} from './types';\n\n// Identity (A6): legacy DIRECT:// address derivation (Path A — XP-invariant).\n// Reused by core/Sphere (B6) and the engine's deriveIdentityAddress.\nexport { deriveDirectAddress } from './identity';\n\n// The concrete adapter factory (A4) — the public way to obtain an ITokenEngine.\nexport { createSphereTokenEngine } from './factory';\n\n// Self-issued Unicity ID (nametag) token mint — the v2 analog of the v1\n// nametag mint, stored at registration but unused at runtime (D5 + user\n// decision 2026-06-10; see unicity-id.ts header).\nexport { createUnicityIdMinter } from './unicity-id';\nexport type { IUnicityIdMinter, UnicityIdMintResult } from './unicity-id';\n\n// The SpherePaymentData codec (CBOR tag 39050) — the value envelope inside\n// Sphere tokens. Exported via the `./token-engine` subpath so server-side\n// consumers (wallet-api deposit validation) can decode token values without\n// pulling the browser/IPFS/Nostr dependency closure of the root entry.\nexport {\n SpherePaymentData,\n decodeSpherePaymentData,\n sphereAssetToSdk,\n} from './SpherePaymentData';\n","/**\n * token-engine/sdk.ts — the SINGLE place that imports @unicitylabs/state-transition-sdk.\n *\n * The v1 cut-over is done: the canonical package name now resolves to the v2 SDK\n * (the migration-era `state-transition-sdk-v2` npm alias is gone, and v1 with it).\n *\n * Everything else in token-engine/ imports SDK symbols from `./sdk`, never from the package directly.\n * ESLint `no-restricted-imports` forbids importing the SDK anywhere except this file.\n *\n * Import path note: subpaths are `…/lib/<path>.js` (no `/src/`); no root barrel.\n */\n\n// ── client / aggregator / proof / network / trust base ──────────────────────\nexport { StateTransitionClient } from '@unicitylabs/state-transition-sdk/lib/StateTransitionClient.js';\nexport { AggregatorClient } from '@unicitylabs/state-transition-sdk/lib/api/AggregatorClient.js';\nexport type { IAggregatorClient } from '@unicitylabs/state-transition-sdk/lib/api/IAggregatorClient.js';\nexport { NetworkId } from '@unicitylabs/state-transition-sdk/lib/api/NetworkId.js';\nexport { CertificationData } from '@unicitylabs/state-transition-sdk/lib/api/CertificationData.js';\nexport { CertificationResponse, CertificationStatus } from '@unicitylabs/state-transition-sdk/lib/api/CertificationResponse.js';\nexport { StateId } from '@unicitylabs/state-transition-sdk/lib/api/StateId.js';\nexport { InclusionProof } from '@unicitylabs/state-transition-sdk/lib/api/InclusionProof.js';\nexport { InclusionProofResponse } from '@unicitylabs/state-transition-sdk/lib/api/InclusionProofResponse.js';\nexport { RootTrustBase } from '@unicitylabs/state-transition-sdk/lib/api/bft/RootTrustBase.js';\nexport { waitInclusionProof } from '@unicitylabs/state-transition-sdk/lib/util/InclusionProofUtils.js';\n\n// ── token / transactions ────────────────────────────────────────────────────\nexport { Token } from '@unicitylabs/state-transition-sdk/lib/transaction/Token.js';\nexport { MintTransaction } from '@unicitylabs/state-transition-sdk/lib/transaction/MintTransaction.js';\nexport { TransferTransaction } from '@unicitylabs/state-transition-sdk/lib/transaction/TransferTransaction.js';\nexport { CertifiedMintTransaction } from '@unicitylabs/state-transition-sdk/lib/transaction/CertifiedMintTransaction.js';\nexport { CertifiedTransferTransaction } from '@unicitylabs/state-transition-sdk/lib/transaction/CertifiedTransferTransaction.js';\nexport { TokenId } from '@unicitylabs/state-transition-sdk/lib/transaction/TokenId.js';\nexport { TokenType } from '@unicitylabs/state-transition-sdk/lib/transaction/TokenType.js';\nexport { TokenSalt } from '@unicitylabs/state-transition-sdk/lib/transaction/TokenSalt.js';\nexport type { ITransaction } from '@unicitylabs/state-transition-sdk/lib/transaction/ITransaction.js';\nexport { MintJustificationVerifierService } from '@unicitylabs/state-transition-sdk/lib/transaction/verification/MintJustificationVerifierService.js';\n\n// ── predicates / unlock scripts ─────────────────────────────────────────────\nexport type { IPredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/IPredicate.js';\nexport type { IUnlockScript } from '@unicitylabs/state-transition-sdk/lib/predicate/IUnlockScript.js';\nexport { EncodedPredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/EncodedPredicate.js';\nexport { SignaturePredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/builtin/SignaturePredicate.js';\nexport { SignaturePredicateUnlockScript } from '@unicitylabs/state-transition-sdk/lib/predicate/builtin/SignaturePredicateUnlockScript.js';\nexport { BurnPredicate } from '@unicitylabs/state-transition-sdk/lib/predicate/builtin/BurnPredicate.js';\nexport { PredicateVerifierService } from '@unicitylabs/state-transition-sdk/lib/predicate/verification/PredicateVerifierService.js';\n\n// ── crypto / hashing ────────────────────────────────────────────────────────\nexport { SigningService } from '@unicitylabs/state-transition-sdk/lib/crypto/secp256k1/SigningService.js';\nexport { Signature } from '@unicitylabs/state-transition-sdk/lib/crypto/secp256k1/Signature.js';\nexport { MintSigningService } from '@unicitylabs/state-transition-sdk/lib/crypto/MintSigningService.js';\nexport { HashAlgorithm } from '@unicitylabs/state-transition-sdk/lib/crypto/hash/HashAlgorithm.js';\nexport { DataHash } from '@unicitylabs/state-transition-sdk/lib/crypto/hash/DataHash.js';\nexport { DataHasher } from '@unicitylabs/state-transition-sdk/lib/crypto/hash/DataHasher.js';\nexport { DataHasherFactory } from '@unicitylabs/state-transition-sdk/lib/crypto/hash/DataHasherFactory.js';\n\n// ── CBOR serialization ──────────────────────────────────────────────────────\nexport { CborSerializer } from '@unicitylabs/state-transition-sdk/lib/serialization/cbor/CborSerializer.js';\nexport { CborDeserializer } from '@unicitylabs/state-transition-sdk/lib/serialization/cbor/CborDeserializer.js';\nexport { CborError } from '@unicitylabs/state-transition-sdk/lib/serialization/cbor/CborError.js';\n\n// ── payment / value / split ─────────────────────────────────────────────────\nexport type { IPaymentData } from '@unicitylabs/state-transition-sdk/lib/payment/IPaymentData.js';\nexport { Asset } from '@unicitylabs/state-transition-sdk/lib/payment/asset/Asset.js';\nexport { AssetId } from '@unicitylabs/state-transition-sdk/lib/payment/asset/AssetId.js';\nexport { PaymentAssetCollection } from '@unicitylabs/state-transition-sdk/lib/payment/asset/PaymentAssetCollection.js';\nexport { TokenSplit } from '@unicitylabs/state-transition-sdk/lib/payment/TokenSplit.js';\nexport { SplitTokenRequest } from '@unicitylabs/state-transition-sdk/lib/payment/SplitTokenRequest.js';\nexport { SplitToken } from '@unicitylabs/state-transition-sdk/lib/payment/SplitToken.js';\nexport { SplitAssetProof } from '@unicitylabs/state-transition-sdk/lib/payment/SplitAssetProof.js';\nexport { SplitMintJustification } from '@unicitylabs/state-transition-sdk/lib/payment/SplitMintJustification.js';\nexport { SplitMintJustificationVerifier } from '@unicitylabs/state-transition-sdk/lib/payment/SplitMintJustificationVerifier.js';\n\n// ── verification result types ───────────────────────────────────────────────\nexport { VerificationStatus } from '@unicitylabs/state-transition-sdk/lib/verification/VerificationStatus.js';\nexport { VerificationResult } from '@unicitylabs/state-transition-sdk/lib/verification/VerificationResult.js';\n\n// ── unicity-id (nametag) ────────────────────────────────────────────────────\nexport { UnicityId } from '@unicitylabs/state-transition-sdk/lib/unicity-id/UnicityId.js';\nexport { UnicityIdMintTransaction } from '@unicitylabs/state-transition-sdk/lib/unicity-id/UnicityIdMintTransaction.js';\nexport { CertifiedUnicityIdMintTransaction } from '@unicitylabs/state-transition-sdk/lib/unicity-id/CertifiedUnicityIdMintTransaction.js';\nexport { UnicityIdToken } from '@unicitylabs/state-transition-sdk/lib/unicity-id/UnicityIdToken.js';\n\n// ── util ────────────────────────────────────────────────────────────────────\nexport { HexConverter } from '@unicitylabs/state-transition-sdk/lib/util/HexConverter.js';\nexport { BigintConverter } from '@unicitylabs/state-transition-sdk/lib/util/BigintConverter.js';\nexport { BitString } from '@unicitylabs/state-transition-sdk/lib/util/BitString.js';\n","/**\n * token-engine/identity.ts — legacy `DIRECT://` identity address (Path A, A6).\n *\n * Reproduces the wallet's stable L3 identity address EXACTLY as the v1\n * `UnmaskedPredicateReference` → `DirectAddress` path did, but VENDORED (no v1 SDK\n * import) so the v1 dependency can be removed at the final cut-over. Quest XP is\n * keyed on this address, so it MUST stay byte-identical — the multi-vector\n * golden test (identity.test.ts) locks it.\n *\n * The recipe (recovered from v1 `UnmaskedPredicateReference.create` + `DirectAddress.create`):\n * ref = SHA-256( CBOR[ bstr([UNMASKED]), bstr(tokenType.toCBOR()),\n * tstr(\"secp256k1\"), uint(SHA256), bstr(publicKey) ] )\n * imprint = 0x0000 ‖ ref (SHA-256 DataHash imprint)\n * address = \"DIRECT://\" ‖ hex(imprint) ‖ hex( SHA-256(imprint)[0:4] )\n *\n * The CBOR + SHA-256 are standard, so v2's CborSerializer/DataHasher produce\n * byte-identical output; the v1 enum values (UNMASKED=0, HashAlgorithm.SHA256=0)\n * and the SHA-256 imprint prefix (0x0000) are pinned as constants.\n */\n\nimport { CborSerializer, DataHasher, HashAlgorithm, HexConverter } from './sdk';\n\n/**\n * Unicity L3 token type used for identity-address derivation (immutable).\n * Also pinned as the fixed token type of self-issued Unicity ID tokens\n * (token-engine/unicity-id.ts) — the same constant the v1 nametag mint used,\n * keeping the mint deterministic per (name, wallet key).\n */\nexport const UNICITY_TOKEN_TYPE_HEX = 'f8aa13834268d29355ff12183066f0cb902003629bbc5eb9ef0efbe397867509';\n/** v1 `SigningService.algorithm` for secp256k1. */\nconst SIGNING_ALGORITHM = 'secp256k1';\n/** v1 `EmbeddedPredicateType.UNMASKED`. */\nconst EMBEDDED_PREDICATE_UNMASKED = 0;\n/** v1 `HashAlgorithm.SHA256` (as encoded in the reference CBOR). */\nconst HASH_ALGORITHM_SHA256 = 0n;\n/** v1 SHA-256 `DataHash` imprint algorithm tag (2-byte big-endian). */\nconst SHA256_IMPRINT_PREFIX = new Uint8Array([0x00, 0x00]);\n\nfunction hexToBytes(hex: string): Uint8Array {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n }\n return bytes;\n}\n\nfunction sha256(data: Uint8Array): Promise<Uint8Array> {\n return new DataHasher(HashAlgorithm.SHA256)\n .update(data)\n .digest()\n .then((h) => h.data);\n}\n\n/**\n * Derive the legacy `DIRECT://` identity address for a compressed (33-byte)\n * secp256k1 public key. Deterministic; byte-identical to the v1 path (Path A).\n */\nexport async function deriveDirectAddress(publicKey: Uint8Array): Promise<string> {\n const tokenTypeCbor = CborSerializer.encodeByteString(hexToBytes(UNICITY_TOKEN_TYPE_HEX)); // v1 TokenType.toCBOR()\n const reference = CborSerializer.encodeArray(\n CborSerializer.encodeByteString(new Uint8Array([EMBEDDED_PREDICATE_UNMASKED])),\n CborSerializer.encodeByteString(tokenTypeCbor),\n CborSerializer.encodeTextString(SIGNING_ALGORITHM),\n CborSerializer.encodeUnsignedInteger(HASH_ALGORITHM_SHA256),\n CborSerializer.encodeByteString(publicKey),\n );\n\n const refHash = await sha256(reference);\n const imprint = new Uint8Array([...SHA256_IMPRINT_PREFIX, ...refHash]);\n const checksum = (await sha256(imprint)).slice(0, 4);\n\n return `DIRECT://${HexConverter.encode(imprint)}${HexConverter.encode(checksum)}`;\n}\n","/**\n * SDK Error Types\n *\n * Structured error codes for programmatic error handling in UI.\n * UI can switch on error.code to show appropriate user-facing messages.\n *\n * @example\n * ```ts\n * import { SphereError } from '@unicitylabs/sphere-sdk';\n *\n * try {\n * await sphere.payments.send({ ... });\n * } catch (err) {\n * if (err instanceof SphereError) {\n * switch (err.code) {\n * case 'INSUFFICIENT_BALANCE': showToast('Not enough funds'); break;\n * case 'INVALID_RECIPIENT': showToast('Recipient not found'); break;\n * case 'TRANSPORT_ERROR': showToast('Network connection issue'); break;\n * case 'TIMEOUT': showToast('Request timed out, try again'); break;\n * default: showToast(err.message);\n * }\n * }\n * }\n * ```\n */\n\nexport type SphereErrorCode =\n | 'NOT_INITIALIZED'\n | 'ALREADY_INITIALIZED'\n | 'INVALID_CONFIG'\n | 'INVALID_IDENTITY'\n | 'INSUFFICIENT_BALANCE'\n | 'INVALID_RECIPIENT'\n | 'TRANSFER_FAILED'\n | 'STORAGE_ERROR'\n | 'TRANSPORT_ERROR'\n | 'AGGREGATOR_ERROR'\n | 'VALIDATION_ERROR'\n | 'NETWORK_ERROR'\n | 'TIMEOUT'\n | 'DECRYPTION_ERROR'\n | 'MODULE_NOT_AVAILABLE'\n | 'SIGNING_ERROR'\n // Token Spend Queue error codes\n | 'SEND_QUEUE_TIMEOUT'\n | 'SEND_INSUFFICIENT_BALANCE'\n | 'SEND_RESERVATION_CANCELLED'\n | 'SEND_QUEUE_FULL'\n | 'MODULE_DESTROYED'\n | 'REENTRANT_GATE'\n // Invoice / Accounting error codes\n | 'INVOICE_NO_TARGETS'\n | 'INVOICE_INVALID_ADDRESS'\n | 'INVOICE_NO_ASSETS'\n | 'INVOICE_INVALID_ASSET'\n | 'INVOICE_INVALID_AMOUNT'\n | 'INVOICE_INVALID_COIN'\n | 'INVOICE_INVALID_NFT'\n | 'INVOICE_PAST_DUE_DATE'\n | 'INVOICE_DUPLICATE_ADDRESS'\n | 'INVOICE_DUPLICATE_COIN'\n | 'INVOICE_DUPLICATE_NFT'\n | 'INVOICE_MINT_FAILED'\n | 'INVOICE_INVALID_PROOF'\n | 'INVOICE_WRONG_TOKEN_TYPE'\n | 'INVOICE_INVALID_DATA'\n | 'INVOICE_ALREADY_EXISTS'\n | 'INVOICE_NOT_FOUND'\n | 'INVOICE_NOT_TARGET'\n | 'INVOICE_ALREADY_CLOSED'\n | 'INVOICE_ALREADY_CANCELLED'\n | 'INVOICE_ORACLE_REQUIRED'\n | 'INVOICE_TERMINATED'\n | 'INVOICE_INVALID_TARGET'\n | 'INVOICE_INVALID_ASSET_INDEX'\n | 'INVOICE_RETURN_EXCEEDS_BALANCE'\n | 'INVOICE_INVALID_DELIVERY_METHOD'\n | 'INVOICE_INVALID_REFUND_ADDRESS'\n | 'INVOICE_INVALID_CONTACT'\n | 'INVOICE_INVALID_ID'\n | 'INVOICE_TOO_MANY_TARGETS'\n | 'INVOICE_TOO_MANY_ASSETS'\n | 'INVOICE_MEMO_TOO_LONG'\n | 'INVOICE_TERMS_TOO_LARGE'\n | 'INVOICE_NOT_TERMINATED'\n | 'INVOICE_NOT_CANCELLED'\n | 'INVOICE_STORAGE_FAILED'\n | 'RATE_LIMITED'\n | 'COMMUNICATIONS_UNAVAILABLE'\n // Swap error codes\n | 'SWAP_INVALID_DEAL'\n | 'SWAP_INVALID_MANIFEST'\n | 'SWAP_NOT_FOUND'\n | 'SWAP_WRONG_STATE'\n | 'SWAP_RESOLVE_FAILED'\n | 'SWAP_DM_SEND_FAILED'\n | 'SWAP_ESCROW_REJECTED'\n | 'SWAP_DEPOSIT_FAILED'\n | 'SWAP_PAYOUT_VERIFICATION_FAILED'\n | 'SWAP_ALREADY_EXISTS'\n | 'SWAP_ALREADY_COMPLETED'\n | 'SWAP_ALREADY_CANCELLED'\n | 'SWAP_TIMEOUT'\n | 'SWAP_LIMIT_EXCEEDED'\n | 'SWAP_ALREADY_INITIALIZED'\n | 'SWAP_MODULE_DESTROYED'\n | 'SWAP_NOT_INITIALIZED';\n\nexport class SphereError extends Error {\n readonly code: SphereErrorCode;\n readonly cause?: unknown;\n\n constructor(message: string, code: SphereErrorCode, cause?: unknown) {\n super(message);\n this.name = 'SphereError';\n this.code = code;\n this.cause = cause;\n }\n}\n\n/**\n * Type guard to check if an error is a SphereError\n */\nexport function isSphereError(err: unknown): err is SphereError {\n return err instanceof SphereError;\n}\n","/**\n * Centralized SDK Logger\n *\n * A lightweight singleton logger that works across all tsup bundles\n * by storing state on globalThis. Supports three log levels:\n * - debug: detailed messages (only shown when debug=true)\n * - warn: important warnings (ALWAYS shown regardless of debug flag)\n * - error: critical errors (ALWAYS shown regardless of debug flag)\n *\n * Global debug flag enables all logging. Per-tag overrides allow\n * granular control (e.g., only transport debug).\n *\n * @example\n * ```ts\n * import { logger } from '@unicitylabs/sphere-sdk';\n *\n * // Enable all debug logging\n * logger.configure({ debug: true });\n *\n * // Enable only specific tags\n * logger.setTagDebug('Nostr', true);\n *\n * // Usage in SDK classes\n * logger.debug('Payments', 'Transfer started', { amount, recipient });\n * logger.warn('Nostr', 'queryEvents timed out after 5s');\n * logger.error('Sphere', 'Critical failure', error);\n * ```\n */\n\nexport type LogLevel = 'debug' | 'warn' | 'error';\n\nexport type LogHandler = (level: LogLevel, tag: string, message: string, ...args: unknown[]) => void;\n\nexport interface LoggerConfig {\n /** Enable debug logging globally (default: false). When false, only warn and error messages are shown. */\n debug?: boolean;\n /** Custom log handler. If provided, replaces console output. Useful for tests or custom log sinks. */\n handler?: LogHandler | null;\n}\n\n// Use a unique symbol-like key on globalThis to share logger state across tsup bundles\nconst LOGGER_KEY = '__sphere_sdk_logger__';\n\ninterface LoggerState {\n debug: boolean;\n tags: Record<string, boolean>;\n handler: LogHandler | null;\n}\n\nfunction getState(): LoggerState {\n const g = globalThis as unknown as Record<string, unknown>;\n if (!g[LOGGER_KEY]) {\n g[LOGGER_KEY] = { debug: false, tags: {}, handler: null } satisfies LoggerState;\n }\n return g[LOGGER_KEY] as LoggerState;\n}\n\nfunction isEnabled(tag: string): boolean {\n const state = getState();\n // Per-tag override takes priority\n if (tag in state.tags) return state.tags[tag];\n // Fall back to global flag\n return state.debug;\n}\n\nexport const logger = {\n /**\n * Configure the logger. Can be called multiple times (last write wins).\n * Typically called by createBrowserProviders(), createNodeProviders(), or Sphere.init().\n */\n configure(config: LoggerConfig): void {\n const state = getState();\n if (config.debug !== undefined) state.debug = config.debug;\n if (config.handler !== undefined) state.handler = config.handler;\n },\n\n /**\n * Enable/disable debug logging for a specific tag.\n * Per-tag setting overrides the global debug flag.\n *\n * @example\n * ```ts\n * logger.setTagDebug('Nostr', true); // enable only Nostr logs\n * logger.setTagDebug('Nostr', false); // disable Nostr logs even if global debug=true\n * ```\n */\n setTagDebug(tag: string, enabled: boolean): void {\n getState().tags[tag] = enabled;\n },\n\n /**\n * Clear per-tag override, falling back to global debug flag.\n */\n clearTagDebug(tag: string): void {\n delete getState().tags[tag];\n },\n\n /** Returns true if debug mode is enabled for the given tag (or globally). */\n isDebugEnabled(tag?: string): boolean {\n if (tag) return isEnabled(tag);\n return getState().debug;\n },\n\n /**\n * Debug-level log. Only shown when debug is enabled (globally or for this tag).\n * Use for detailed operational information.\n */\n debug(tag: string, message: string, ...args: unknown[]): void {\n if (!isEnabled(tag)) return;\n const state = getState();\n if (state.handler) {\n state.handler('debug', tag, message, ...args);\n } else {\n console.log(`[${tag}]`, message, ...args);\n }\n },\n\n /**\n * Warning-level log. ALWAYS shown regardless of debug flag.\n * Use for important but non-critical issues (timeouts, retries, degraded state).\n */\n warn(tag: string, message: string, ...args: unknown[]): void {\n const state = getState();\n if (state.handler) {\n state.handler('warn', tag, message, ...args);\n } else {\n console.warn(`[${tag}]`, message, ...args);\n }\n },\n\n /**\n * Error-level log. ALWAYS shown regardless of debug flag.\n * Use for critical failures that should never be silenced.\n */\n error(tag: string, message: string, ...args: unknown[]): void {\n const state = getState();\n if (state.handler) {\n state.handler('error', tag, message, ...args);\n } else {\n console.error(`[${tag}]`, message, ...args);\n }\n },\n\n /** Reset all logger state (debug flag, tags, handler). Primarily for tests. */\n reset(): void {\n const g = globalThis as unknown as Record<string, unknown>;\n delete g[LOGGER_KEY];\n },\n};\n","/**\n * token-engine/SpherePaymentData.ts — the sphere value model.\n *\n * v2 `Token` carries no coins; value is app-defined and stored in\n * `MintTransaction.data`. `SpherePaymentData` is that payload: it implements the\n * SDK's `IPaymentData` (so `TokenSplit` can read it for value conservation) and\n * encodes a `PaymentAssetCollection` inside a versioned, tagged CBOR envelope.\n *\n * The SDK never inspects our raw bytes — it only calls `decodePaymentData(data)`\n * and reads `.assets` — so the envelope (tag + version) is ours, chosen for\n * forward-compatible storage. `fromValue`/`toValue` bridge sphere-domain values\n * (hex coin id + bigint amount) to/from the SDK asset collection.\n */\n\nimport {\n Asset,\n AssetId,\n CborDeserializer,\n CborError,\n CborSerializer,\n HexConverter,\n type IPaymentData,\n PaymentAssetCollection,\n} from './sdk';\nimport type { CoinId, SphereValue } from './types';\nimport { SphereError } from '../core/errors';\n\n/** Canonical coin id: non-empty, even-length lowercase hex (the form `toValue` emits). */\nconst COIN_ID_PATTERN = /^([0-9a-f]{2})+$/;\n\n/** Guard a sphere-domain asset before it crosses into the SDK value model. */\nfunction assertAsset(coinId: CoinId, amount: bigint): void {\n if (!COIN_ID_PATTERN.test(coinId)) {\n throw new SphereError(`Invalid coin id (expected even-length lowercase hex): \"${coinId}\"`, 'VALIDATION_ERROR');\n }\n if (amount < 0n) {\n // Negative bigints silently encode to an empty byte string (decoding back to 0n)\n // in the SDK's BigintConverter — reject loudly to avoid silent value loss.\n throw new SphereError(`Asset amount must be non-negative: ${amount.toString()}`, 'VALIDATION_ERROR');\n }\n}\n\n/** Validate a sphere-domain asset and build the SDK Asset — the single validation point. */\nexport function sphereAssetToSdk(coinId: CoinId, amount: bigint): Asset {\n assertAsset(coinId, amount);\n return new Asset(new AssetId(HexConverter.decode(coinId)), amount);\n}\n\nexport class SpherePaymentData implements IPaymentData {\n /** Sphere-private CBOR tag (verified free in the v2 SDK tag space). */\n public static readonly CBOR_TAG = 39050n;\n /** Envelope version; bump when the structure changes. */\n public static readonly VERSION = 1n;\n\n private constructor(\n public readonly assets: PaymentAssetCollection,\n private readonly _memo: Uint8Array | null = null,\n ) {}\n\n /** Opaque, app-defined memo carried alongside the value (e.g. invoice attribution). */\n public get memo(): Uint8Array | null {\n return this._memo ? new Uint8Array(this._memo) : null;\n }\n\n /** Wrap an existing SDK asset collection (+ optional opaque memo). */\n public static create(assets: PaymentAssetCollection, memo: Uint8Array | null = null): SpherePaymentData {\n return new SpherePaymentData(assets, memo);\n }\n\n /** Build from a sphere-domain value (hex coin id → bigint amount) + optional opaque memo. */\n public static fromValue(value: SphereValue, memo: Uint8Array | null = null): SpherePaymentData {\n const assets = value.assets.map((a) => sphereAssetToSdk(a.coinId, a.amount));\n return new SpherePaymentData(PaymentAssetCollection.create(...assets), memo);\n }\n\n /** Decode from the CBOR envelope produced by {@link encode}. */\n public static fromCBOR(bytes: Uint8Array): SpherePaymentData {\n const tag = CborDeserializer.decodeTag(bytes);\n if (tag.tag !== SpherePaymentData.CBOR_TAG) {\n throw new CborError(`Invalid SpherePaymentData tag: ${tag.tag}`);\n }\n // Strict structure: exactly [version, assets, memo] (matches encode + the SDK's\n // fixed-shape decoders). Extra/missing fields are corruption, not tolerated.\n const fields = CborDeserializer.decodeArray(tag.data, 3);\n const version = CborDeserializer.decodeUnsignedInteger(fields[0]);\n if (version !== SpherePaymentData.VERSION) {\n throw new CborError(`Unsupported SpherePaymentData version: ${version}`);\n }\n const memo = CborDeserializer.decodeNullable(fields[2], CborDeserializer.decodeByteString);\n return new SpherePaymentData(PaymentAssetCollection.fromCBOR(fields[1]), memo);\n }\n\n /** Deterministic, versioned, tagged CBOR: `tag(39050)[ version, assets, memo? ]`. */\n public encode(): Promise<Uint8Array> {\n return Promise.resolve(\n CborSerializer.encodeTag(\n SpherePaymentData.CBOR_TAG,\n CborSerializer.encodeArray(\n CborSerializer.encodeUnsignedInteger(SpherePaymentData.VERSION),\n this.assets.toCBOR(),\n CborSerializer.encodeNullable(this._memo, CborSerializer.encodeByteString),\n ),\n ),\n );\n }\n\n /** Project to a sphere-domain value (hex coin id + bigint amount), preserving order. */\n public toValue(): SphereValue {\n return {\n assets: this.assets.toArray().map((a) => ({\n coinId: HexConverter.encode(a.id.bytes) as CoinId,\n amount: a.value,\n })),\n };\n }\n\n /** Balance of a single coin within this payload (0n when absent). */\n public balanceOf(coinId: CoinId): bigint {\n if (!COIN_ID_PATTERN.test(coinId)) {\n throw new SphereError(`Invalid coin id (expected even-length lowercase hex): \"${coinId}\"`, 'VALIDATION_ERROR');\n }\n const asset = this.assets.get(new AssetId(HexConverter.decode(coinId)));\n return asset ? asset.value : 0n;\n }\n}\n\n/**\n * Async payment-data decoder matching the SDK's `decodePaymentData` signature.\n * Used by `TokenSplit.split` (value conservation) and `SplitMintJustificationVerifier`.\n */\nexport function decodeSpherePaymentData(bytes: Uint8Array): Promise<IPaymentData> {\n return Promise.resolve(SpherePaymentData.fromCBOR(bytes));\n}\n","/**\n * token-engine/token-blob.ts — storage/transport codec for a wallet token.\n *\n * A {@link TokenBlob} is `{ v, network, tokenId, token }` where `token` is the v2\n * `Token.toCBOR()` bytes and `tokenId` is the genesis-stable id (stored so dedup /\n * listing / tombstone keys need no engine call). This codec wraps it in a\n * sphere-private CBOR envelope so the wallet's own storage format can version\n * independently of the SDK's token CBOR. The decoded value is re-derivable from\n * `token`, so it is NOT stored here.\n */\n\nimport { CborDeserializer, CborError, CborSerializer } from './sdk';\nimport type { TokenBlob } from './types';\n\n/** Sphere-private CBOR tag (distinct from SpherePaymentData's 39050). */\nconst TOKEN_BLOB_TAG = 39051n;\n/** Current blob envelope version. */\nexport const TOKEN_BLOB_VERSION = 1;\n\n/** Encode a blob as `tag(39051)[ v, network, tokenId, token ]`. */\nexport function encodeTokenBlob(blob: TokenBlob): Uint8Array {\n return CborSerializer.encodeTag(\n TOKEN_BLOB_TAG,\n CborSerializer.encodeArray(\n CborSerializer.encodeUnsignedInteger(BigInt(blob.v)),\n CborSerializer.encodeUnsignedInteger(BigInt(blob.network)),\n CborSerializer.encodeTextString(blob.tokenId),\n CborSerializer.encodeByteString(blob.token),\n ),\n );\n}\n\n/** Decode a blob produced by {@link encodeTokenBlob}. */\nexport function decodeTokenBlob(bytes: Uint8Array): TokenBlob {\n const tag = CborDeserializer.decodeTag(bytes);\n if (tag.tag !== TOKEN_BLOB_TAG) {\n throw new CborError(`Invalid TokenBlob tag: ${tag.tag}`);\n }\n const fields = CborDeserializer.decodeArray(tag.data, 4);\n const v = Number(CborDeserializer.decodeUnsignedInteger(fields[0]));\n if (v !== TOKEN_BLOB_VERSION) {\n throw new CborError(`Unsupported TokenBlob version: ${v}`);\n }\n return {\n v,\n network: Number(CborDeserializer.decodeUnsignedInteger(fields[1])),\n tokenId: CborDeserializer.decodeTextString(fields[2]),\n token: CborDeserializer.decodeByteString(fields[3]),\n };\n}\n","/**\n * token-engine/SphereTokenEngine.ts — the real ITokenEngine adapter (Track A).\n *\n * The full sender-driven engine over the v2 SDK: identity, value reads,\n * serialization, verification, mint, transfer, split and spent-status. All\n * crypto/value logic is the SDK's; this class orchestrates build → submit →\n * wait-proof → certify → realize, and maps SDK errors/states to the\n * sphere-domain ITokenEngine port.\n *\n * SDK objects are injected via `EngineDeps` (built by the factory in A4, or by\n * test wiring around TestAggregatorClient), keeping the engine logic independent\n * of how the aggregator/trust base are constructed.\n */\n\nimport { SphereError } from '../core/errors';\nimport { deriveDirectAddress } from './identity';\nimport {\n CborDeserializer,\n CertificationData,\n CertificationStatus,\n EncodedPredicate,\n HexConverter,\n type MintJustificationVerifierService,\n MintTransaction,\n type NetworkId,\n PaymentAssetCollection,\n type PredicateVerifierService,\n type RootTrustBase,\n SignaturePredicate,\n SignaturePredicateUnlockScript,\n type SigningService,\n SplitMintJustification,\n SplitTokenRequest,\n StateId,\n type StateTransitionClient,\n Token,\n TokenSalt,\n TokenSplit,\n TokenType,\n TransferTransaction,\n VerificationStatus,\n waitInclusionProof,\n} from './sdk';\nimport { decodeSpherePaymentData, SpherePaymentData, sphereAssetToSdk } from './SpherePaymentData';\nimport { TOKEN_BLOB_VERSION } from './token-blob';\nimport type { EngineOpOptions, ITokenEngine } from './engine';\nimport type {\n CoinId,\n EngineIdentity,\n EngineVerifyResult,\n MintDataTokenParams,\n MintParams,\n SphereToken,\n SphereValue,\n SplitParams,\n SplitResult,\n TokenBlob,\n TransferParams,\n} from './types';\n\n/** SDK objects the engine operates with; assembled by the factory (A4) or test wiring. */\nexport interface EngineDeps {\n readonly client: StateTransitionClient;\n readonly trustBase: RootTrustBase;\n readonly predicateVerifier: PredicateVerifierService;\n readonly mintJustificationVerifier: MintJustificationVerifierService;\n /** The wallet's signing key (its identity + the spender for transfers it owns). */\n readonly signingService: SigningService;\n readonly networkId: NetworkId;\n}\n\nexport class SphereTokenEngine implements ITokenEngine {\n public constructor(private readonly deps: EngineDeps) {}\n\n // ── identity ────────────────────────────────────────────────────────────────\n\n public getIdentity(): EngineIdentity {\n return { chainPubkey: new Uint8Array(this.deps.signingService.publicKey) };\n }\n\n /** Legacy DIRECT:// address (Path A). Async: the derivation hashes via the SDK. */\n public deriveIdentityAddress(pubkey?: Uint8Array): Promise<string> {\n return deriveDirectAddress(pubkey ?? this.deps.signingService.publicKey);\n }\n\n // ── value (read) ─────────────────────────────────────────────────────────────\n\n public readValue(token: SphereToken): SphereValue | null {\n return token.value;\n }\n\n public balanceOf(token: SphereToken, coinId: CoinId): bigint {\n let sum = 0n;\n for (const asset of token.value?.assets ?? []) {\n if (asset.coinId === coinId) sum += asset.amount;\n }\n return sum;\n }\n\n public tokenId(token: SphereToken): string {\n return token.blob.tokenId;\n }\n\n public readMemo(token: SphereToken): Uint8Array | null {\n const sdkToken = token.sdkToken;\n // A transferred token delivers its memo on the latest transfer.\n if (sdkToken.transactions.length > 0) {\n return sdkToken.latestTransaction.data;\n }\n // A minted output (e.g. a split output) carries the memo in its value envelope.\n const data = sdkToken.genesis.data;\n if (data && this.isSpherePaymentData(data)) {\n return SpherePaymentData.fromCBOR(data).memo;\n }\n return null;\n }\n\n public readTokenData(token: SphereToken): Uint8Array | null {\n const data = token.sdkToken.genesis.data;\n return data ? new Uint8Array(data) : null;\n }\n\n // ── lifecycle ────────────────────────────────────────────────────────────────\n\n public async mint(params: MintParams, options?: EngineOpOptions): Promise<SphereToken> {\n const recipient = SignaturePredicate.create(params.recipientPubkey);\n const data = params.value ? await SpherePaymentData.fromValue(params.value).encode() : null;\n\n const mintTx = await MintTransaction.create(this.deps.networkId, recipient, data);\n const certificationData = await CertificationData.fromMintTransaction(mintTx);\n\n const response = await this.deps.client.submitCertificationRequest(certificationData);\n if (response.status !== CertificationStatus.SUCCESS) {\n throw new SphereError(`Mint certification failed: ${response.status}`, 'AGGREGATOR_ERROR');\n }\n\n const proof = await waitInclusionProof(\n this.deps.client,\n this.deps.trustBase,\n this.deps.predicateVerifier,\n mintTx,\n options?.signal,\n );\n const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);\n const token = await Token.mint(\n this.deps.trustBase,\n this.deps.predicateVerifier,\n this.deps.mintJustificationVerifier,\n certified,\n );\n return this.wrapToken(token);\n }\n\n public async mintDataToken(params: MintDataTokenParams, options?: EngineOpOptions): Promise<SphereToken> {\n const recipient = SignaturePredicate.create(params.recipientPubkey);\n const tokenType = params.tokenType ? new TokenType(params.tokenType) : TokenType.generate();\n // A deterministic salt yields a stable, terms-derived tokenId (TokenId.fromSalt).\n const salt = params.salt ? TokenSalt.fromBytes(params.salt) : TokenSalt.generate();\n\n const mintTx = await MintTransaction.create(this.deps.networkId, recipient, params.data, tokenType, salt);\n const certificationData = await CertificationData.fromMintTransaction(mintTx);\n\n const response = await this.deps.client.submitCertificationRequest(certificationData);\n if (response.status !== CertificationStatus.SUCCESS) {\n throw new SphereError(`Data-token mint failed: ${response.status}`, 'AGGREGATOR_ERROR');\n }\n\n const proof = await waitInclusionProof(\n this.deps.client,\n this.deps.trustBase,\n this.deps.predicateVerifier,\n mintTx,\n options?.signal,\n );\n const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);\n const token = await Token.mint(\n this.deps.trustBase,\n this.deps.predicateVerifier,\n this.deps.mintJustificationVerifier,\n certified,\n );\n return this.wrapToken(token);\n }\n\n public async transfer(params: TransferParams, options?: EngineOpOptions): Promise<SphereToken> {\n this.assertOwned(params.token);\n const recipient = SignaturePredicate.create(params.recipientPubkey);\n const stateMask = crypto.getRandomValues(new Uint8Array(32));\n\n const transferTx = await TransferTransaction.create(params.token.sdkToken, recipient, stateMask, params.data ?? null);\n const unlockScript = await SignaturePredicateUnlockScript.create(transferTx, this.deps.signingService);\n const certificationData = await CertificationData.fromTransaction(transferTx, unlockScript);\n\n const response = await this.deps.client.submitCertificationRequest(certificationData);\n if (response.status !== CertificationStatus.SUCCESS) {\n throw new SphereError(`Transfer certification failed: ${response.status}`, 'TRANSFER_FAILED');\n }\n\n const proof = await waitInclusionProof(\n this.deps.client,\n this.deps.trustBase,\n this.deps.predicateVerifier,\n transferTx,\n options?.signal,\n );\n const certified = await transferTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);\n const transferred = await params.token.sdkToken.transfer(this.deps.trustBase, this.deps.predicateVerifier, certified);\n return this.wrapToken(transferred);\n }\n\n public async split(params: SplitParams, options?: EngineOpOptions): Promise<SplitResult> {\n this.assertOwned(params.token);\n if (params.outputs.length === 0) {\n throw new SphereError('Split requires at least one output', 'VALIDATION_ERROR');\n }\n\n const requests = params.outputs.map((o) =>\n SplitTokenRequest.create(\n SignaturePredicate.create(o.recipientPubkey),\n PaymentAssetCollection.create(sphereAssetToSdk(o.coinId, o.amount)),\n ),\n );\n\n // Value conservation is enforced inside the SDK split (root.value === source value).\n const split = await TokenSplit.split(params.token.sdkToken, decodeSpherePaymentData, requests);\n\n // 1. Burn the source: certify the burn transfer and append it -> burntToken.\n const burnUnlock = await SignaturePredicateUnlockScript.create(split.burn.transaction, this.deps.signingService);\n const burnCert = await CertificationData.fromTransaction(split.burn.transaction, burnUnlock);\n const burnResponse = await this.deps.client.submitCertificationRequest(burnCert);\n if (burnResponse.status !== CertificationStatus.SUCCESS) {\n throw new SphereError(`Split burn failed: ${burnResponse.status}`, 'TRANSFER_FAILED');\n }\n const burnProof = await waitInclusionProof(\n this.deps.client,\n this.deps.trustBase,\n this.deps.predicateVerifier,\n split.burn.transaction,\n options?.signal,\n );\n const burnCertified = await split.burn.transaction.toCertifiedTransaction(\n this.deps.trustBase,\n this.deps.predicateVerifier,\n burnProof,\n );\n const burntToken = await params.token.sdkToken.transfer(\n this.deps.trustBase,\n this.deps.predicateVerifier,\n burnCertified,\n );\n\n // 2. Mint each output, justified by the burnt token + its split proofs.\n const outputs: SphereToken[] = [];\n for (let i = 0; i < split.tokens.length; i++) {\n const splitToken = split.tokens[i];\n // split.tokens preserves requests order, so the per-output memo maps by index.\n const data = await SpherePaymentData.create(splitToken.assets, params.outputs[i].data ?? null).encode();\n const justification = SplitMintJustification.create(burntToken, splitToken.proofs).toCBOR();\n const mintTx = await MintTransaction.create(\n splitToken.networkId,\n splitToken.recipient,\n data,\n splitToken.tokenType,\n splitToken.salt,\n justification,\n );\n const certData = await CertificationData.fromMintTransaction(mintTx);\n const response = await this.deps.client.submitCertificationRequest(certData);\n if (response.status !== CertificationStatus.SUCCESS) {\n throw new SphereError(`Split mint failed: ${response.status}`, 'AGGREGATOR_ERROR');\n }\n const proof = await waitInclusionProof(\n this.deps.client,\n this.deps.trustBase,\n this.deps.predicateVerifier,\n mintTx,\n options?.signal,\n );\n const certified = await mintTx.toCertifiedTransaction(this.deps.trustBase, this.deps.predicateVerifier, proof);\n const token = await Token.mint(\n this.deps.trustBase,\n this.deps.predicateVerifier,\n this.deps.mintJustificationVerifier,\n certified,\n );\n outputs.push(this.wrapToken(token));\n }\n\n return { outputs };\n }\n\n // ── verification ─────────────────────────────────────────────────────────────\n\n public async verify(token: SphereToken, _options?: EngineOpOptions): Promise<EngineVerifyResult> {\n const result = await token.sdkToken.verify(\n this.deps.trustBase,\n this.deps.predicateVerifier,\n this.deps.mintJustificationVerifier,\n );\n return result.status === VerificationStatus.OK ? { ok: true } : { ok: false, reason: String(result.status) };\n }\n\n public isOwnedBy(token: SphereToken, pubkey: Uint8Array): boolean {\n const owner = token.sdkToken.latestTransaction.recipient;\n const claimed = EncodedPredicate.fromPredicate(SignaturePredicate.create(pubkey));\n return EncodedPredicate.equals(owner, claimed);\n }\n\n public async isSpent(token: SphereToken, _options?: EngineOpOptions): Promise<boolean> {\n // The token's current state id = what a future transfer would spend. Derive it via a\n // probe transfer (never submitted) — StateId depends only on the source's lock script\n // and state hash, not on the recipient or state mask.\n const probe = await TransferTransaction.create(\n token.sdkToken,\n SignaturePredicate.create(this.deps.signingService.publicKey),\n new Uint8Array(32),\n );\n const stateId = await StateId.fromTransaction(probe);\n const response = await this.deps.client.getInclusionProof(stateId);\n // A present inclusion certificate means the state has already been consumed on-chain.\n return response.inclusionProof.inclusionCertificate !== null;\n }\n\n // ── serialization ────────────────────────────────────────────────────────────\n\n public encodeToken(token: SphereToken): TokenBlob {\n return token.blob;\n }\n\n public async decodeToken(blob: TokenBlob): Promise<SphereToken> {\n const sdkToken = await Token.fromCBOR(blob.token);\n if (sdkToken.genesis.networkId.id !== this.deps.networkId.id) {\n throw new SphereError(\n `Token network mismatch: token is on network ${sdkToken.genesis.networkId.id}, ` +\n `engine on ${this.deps.networkId.id}`,\n 'VALIDATION_ERROR',\n );\n }\n return this.wrapToken(sdkToken);\n }\n\n // ── internals ────────────────────────────────────────────────────────────────\n\n /** Fail fast if this engine's key does not own the token's current state. */\n private assertOwned(token: SphereToken): void {\n if (!this.isOwnedBy(token, this.deps.signingService.publicKey)) {\n throw new SphereError('Cannot transfer a token not owned by this engine identity', 'VALIDATION_ERROR');\n }\n }\n\n /** Wrap an SDK token into a SphereToken: cache its blob (incl. stable tokenId) + decoded value. */\n private wrapToken(sdkToken: Token): SphereToken {\n const data = sdkToken.genesis.data;\n let value: SphereValue | null = null;\n // Only value tokens carry a SpherePaymentData envelope; data tokens (e.g. invoices)\n // leave value === null. A corrupt value envelope still errors loudly.\n if (data && this.isSpherePaymentData(data)) {\n try {\n value = SpherePaymentData.fromCBOR(data).toValue();\n } catch (err) {\n throw new SphereError(\n `Failed to decode token payment data: ${err instanceof Error ? err.message : String(err)}`,\n 'VALIDATION_ERROR',\n );\n }\n }\n const blob: TokenBlob = {\n v: TOKEN_BLOB_VERSION,\n network: sdkToken.genesis.networkId.id,\n tokenId: HexConverter.encode(sdkToken.id.bytes),\n token: sdkToken.toCBOR(),\n };\n return { sdkToken, blob, value };\n }\n\n /** True if the bytes are a SpherePaymentData envelope (value token) vs a raw data token. */\n private isSpherePaymentData(data: Uint8Array): boolean {\n try {\n return CborDeserializer.decodeTag(data).tag === SpherePaymentData.CBOR_TAG;\n } catch {\n return false;\n }\n }\n}\n","/**\n * token-engine/factory.ts — the real engine constructor (A4).\n *\n * `createSphereTokenEngine` is the public way to obtain an ITokenEngine. It maps\n * the sphere-domain EngineConfig to the SDK objects the engine needs: the\n * aggregator client (from `aggregatorUrl`), the trust base (parsed from\n * `trustBaseJson`), the wallet signing key (from `privateKey`), the network id,\n * and a mint-justification verifier with the split verifier registered (so\n * split-output tokens verify).\n *\n * Loading the trust base per environment (browser fetch / node file) stays with\n * the caller (impl/<env>/oracle, reusing the existing trust-base loaders); it\n * passes the parsed JSON in via `trustBaseJson`, keeping this factory env-agnostic.\n */\n\nimport { SphereError } from '../core/errors';\nimport { logger } from '../core/logger';\nimport {\n AggregatorClient,\n MintJustificationVerifierService,\n PredicateVerifierService,\n RootTrustBase,\n SigningService,\n SplitMintJustificationVerifier,\n StateTransitionClient,\n} from './sdk';\nimport { decodeSpherePaymentData } from './SpherePaymentData';\nimport { type EngineDeps, SphereTokenEngine } from './SphereTokenEngine';\nimport type { EngineConfig, ITokenEngine } from './engine';\n\nexport async function createSphereTokenEngine(config: EngineConfig): Promise<ITokenEngine> {\n if (config.trustBaseJson == null) {\n throw new SphereError('Engine config requires a trust base (trustBaseJson)', 'INVALID_CONFIG');\n }\n\n if (!config.apiKey) {\n logger.warn(\n 'TokenEngine',\n 'No aggregator apiKey — pass config.oracle.apiKey (testnet2 value in .env.example; mainnet from a secret env var). Gateway requests will be unauthenticated.',\n );\n }\n\n const trustBase = RootTrustBase.fromJSON(config.trustBaseJson);\n const predicateVerifier = PredicateVerifierService.create();\n const mintJustificationVerifier = new MintJustificationVerifierService();\n mintJustificationVerifier.register(\n new SplitMintJustificationVerifier(trustBase, predicateVerifier, decodeSpherePaymentData),\n );\n\n const deps: EngineDeps = {\n client: new StateTransitionClient(new AggregatorClient(config.aggregatorUrl, config.apiKey ?? null)),\n trustBase,\n predicateVerifier,\n mintJustificationVerifier,\n signingService: new SigningService(config.privateKey),\n // The trust base is the single source of truth for the network id (it carries\n // NetworkId.fromId, so any id works — e.g. testnet2 = 4 — with no enum entry).\n networkId: trustBase.networkId,\n };\n\n return new SphereTokenEngine(deps);\n}\n","/**\n * token-engine/unicity-id.ts — self-issued v2 UnicityIdToken mint (the v2 analog\n * of the v1 nametag-token mint).\n *\n * User decision 2026-06-10: the on-chain Unicity ID claim is minted AND STORED\n * again at nametag registration (it was retired with v1 in Track B / D5), but it\n * is NOT used at runtime — name resolution stays Nostr-binding-only, receive\n * stays SignaturePredicate(chainPubkey), and no PROXY semantics return. The\n * token is kept for the future (e.g. an issuer/verification model).\n *\n * Trust model: SELF-ISSUED. The wallet's own key is the issuer lock script, the\n * recipient AND the target predicate (the v2 local-mint path — the issuer pin is\n * only meaningful when verifying third-party tokens). Because the issuer lock\n * script is part of the StateId, on-chain uniqueness is per-issuer only; GLOBAL\n * name uniqueness remains the Nostr first-seen-wins binding's job (unchanged).\n *\n * Determinism / idempotency: tokenId = SHA256(CBOR[\"NAMETAG_\", null, name]),\n * tokenType is pinned (UNICITY_TOKEN_TYPE_HEX), and all predicates derive from\n * the wallet key — the whole mint transaction is reproducible byte-for-byte, so\n * a re-mint (e.g. after the token was lost from local storage) re-certifies the\n * same state and yields the identical token (the v1 REQUEST_ID_EXISTS recovery\n * analog).\n */\n\nimport { SphereError } from '../core/errors';\nimport { logger } from '../core/logger';\nimport {\n AggregatorClient,\n CertificationData,\n CertificationStatus,\n HexConverter,\n PredicateVerifierService,\n RootTrustBase,\n SignaturePredicate,\n SignaturePredicateUnlockScript,\n SigningService,\n StateTransitionClient,\n TokenType,\n UnicityId,\n UnicityIdMintTransaction,\n UnicityIdToken,\n waitInclusionProof,\n} from './sdk';\nimport { UNICITY_TOKEN_TYPE_HEX } from './identity';\nimport type { EngineConfig, EngineOpOptions } from './engine';\n\nexport interface UnicityIdMintResult {\n /** UnicityIdToken CBOR, hex-encoded — the storable form (UnicityIdToken.fromCBOR round-trips it). */\n readonly tokenCborHex: string;\n /** 64-char hex token id, derived from the name (stable across re-mints). */\n readonly tokenId: string;\n}\n\n/** Self-issued Unicity ID (nametag) token minter. */\nexport interface IUnicityIdMinter {\n /**\n * Mint (or idempotently re-certify) the UnicityIdToken for `name`,\n * self-issued by this wallet's key. Network-bound: submits the certification\n * request and waits for the inclusion proof.\n */\n mintUnicityIdToken(name: string, options?: EngineOpOptions): Promise<UnicityIdMintResult>;\n}\n\nclass SelfIssuedUnicityIdMinter implements IUnicityIdMinter {\n public constructor(\n private readonly client: StateTransitionClient,\n private readonly trustBase: RootTrustBase,\n private readonly predicateVerifier: PredicateVerifierService,\n private readonly signingService: SigningService,\n ) {}\n\n public async mintUnicityIdToken(name: string, options?: EngineOpOptions): Promise<UnicityIdMintResult> {\n const unicityId = new UnicityId(name);\n const self = SignaturePredicate.fromSigningService(this.signingService);\n\n // Deterministic per (name, wallet key): pinned token type, self predicates.\n const mintTx = await UnicityIdMintTransaction.create(\n self, // issuer lock script (self-issued)\n self, // recipient — locks the minted token state to this wallet\n unicityId,\n new TokenType(HexConverter.decode(UNICITY_TOKEN_TYPE_HEX)),\n self, // target predicate the unicity id resolves to\n );\n\n const certificationData = await CertificationData.fromTransaction(\n mintTx,\n await SignaturePredicateUnlockScript.create(mintTx, this.signingService),\n );\n\n const response = await this.client.submitCertificationRequest(certificationData);\n if (response.status !== CertificationStatus.SUCCESS) {\n // The transaction is deterministic: when this wallet minted the name\n // before, its certification is already on-chain and the proof below\n // still verifies (idempotent re-mint). Any genuine failure surfaces as\n // a proof-wait timeout/verification error instead.\n logger.warn(\n 'UnicityIdMinter',\n `Certification request returned ${response.status} for \"@${name}\" — attempting proof recovery (idempotent re-mint).`,\n );\n }\n\n try {\n const proof = await waitInclusionProof(\n this.client,\n this.trustBase,\n this.predicateVerifier,\n mintTx,\n options?.signal,\n );\n const certified = await mintTx.toCertifiedTransaction(this.trustBase, this.predicateVerifier, proof);\n const token = await UnicityIdToken.mint(this.trustBase, this.predicateVerifier, certified);\n return {\n tokenCborHex: HexConverter.encode(token.toCBOR()),\n tokenId: HexConverter.encode(token.id.bytes),\n };\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n throw new SphereError(\n `Unicity ID mint failed for \"@${name}\": ${detail}` +\n (response.status !== CertificationStatus.SUCCESS ? ` (certification status: ${response.status})` : ''),\n 'AGGREGATOR_ERROR',\n );\n }\n }\n}\n\n/** SDK objects the minter operates with (test seam; mirrors EngineDeps). */\nexport interface UnicityIdMinterDeps {\n readonly client: StateTransitionClient;\n readonly trustBase: RootTrustBase;\n readonly predicateVerifier: PredicateVerifierService;\n readonly signingService: SigningService;\n}\n\n/** Build the minter from pre-constructed SDK objects (tests / shared wiring). */\nexport function createUnicityIdMinterFromDeps(deps: UnicityIdMinterDeps): IUnicityIdMinter {\n return new SelfIssuedUnicityIdMinter(deps.client, deps.trustBase, deps.predicateVerifier, deps.signingService);\n}\n\n/**\n * Build the self-issued Unicity ID minter from the same config the token engine\n * uses (trust base JSON + gateway URL + API key + wallet key).\n */\nexport function createUnicityIdMinter(config: EngineConfig): IUnicityIdMinter {\n if (config.trustBaseJson == null) {\n throw new SphereError('Unicity ID minter requires a trust base (trustBaseJson)', 'INVALID_CONFIG');\n }\n const trustBase = RootTrustBase.fromJSON(config.trustBaseJson);\n return new SelfIssuedUnicityIdMinter(\n new StateTransitionClient(new AggregatorClient(config.aggregatorUrl, config.apiKey ?? null)),\n trustBase,\n PredicateVerifierService.create(),\n new SigningService(config.privateKey),\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,363 @@
1
+ import { Token } from '@unicitylabs/state-transition-sdk/lib/transaction/Token.js';
2
+ import { IPaymentData } from '@unicitylabs/state-transition-sdk/lib/payment/IPaymentData.js';
3
+ import { Asset } from '@unicitylabs/state-transition-sdk/lib/payment/asset/Asset.js';
4
+ import { PaymentAssetCollection } from '@unicitylabs/state-transition-sdk/lib/payment/asset/PaymentAssetCollection.js';
5
+
6
+ /**
7
+ * token-engine/types.ts — the FROZEN, sphere-domain contract surface.
8
+ *
9
+ * Design rule (anti-corruption): the public ITokenEngine port speaks ONLY
10
+ * sphere-domain types — `Uint8Array` pubkeys, `string` coin ids, `bigint`
11
+ * amounts, plain enums. The v2 state-transition SDK has exactly ONE foothold
12
+ * here: `SphereToken.sdkToken`, an OPAQUE handle. Callers must treat it as
13
+ * opaque (store it, hand it back to the engine) and never call methods on it —
14
+ * they cannot, since the ESLint boundary forbids them importing the SDK.
15
+ *
16
+ * Both migration tracks freeze against this file:
17
+ * Track A implements it (token-engine internals).
18
+ * Track B codes callers against it (using FakeTokenEngine until A lands).
19
+ */
20
+
21
+ /** The wallet identity at the engine boundary. The private key never appears in a DTO. */
22
+ interface EngineIdentity {
23
+ /** 33-byte compressed secp256k1 public key (stable across the migration — Path A). */
24
+ readonly chainPubkey: Uint8Array;
25
+ }
26
+ /** Which Unicity network a token/engine lives on. Maps to the SDK NetworkId inside the engine. */
27
+ type SphereNetwork = 'mainnet' | 'testnet' | 'local';
28
+ /**
29
+ * Coin identifier. Canonical form is the lowercase hex of the v2 AssetId;
30
+ * human symbols (e.g. "ALPHA") are resolved to hex via the registry before use.
31
+ */
32
+ type CoinId = string;
33
+ /** One fungible position inside a token. */
34
+ interface SphereAsset {
35
+ readonly coinId: CoinId;
36
+ readonly amount: bigint;
37
+ }
38
+ /** The decoded, app-defined value carried by a token (v2 Token itself is value-less). */
39
+ interface SphereValue {
40
+ readonly assets: readonly SphereAsset[];
41
+ }
42
+ /**
43
+ * Storage-and-display token. Format version + network let storage migrate
44
+ * independently of the SDK's own CBOR. The decoded value is re-derivable from
45
+ * `token`, so it is NOT stored — only cached at runtime on SphereToken.value.
46
+ */
47
+ interface TokenBlob {
48
+ /** Blob format version (sphere storage migrations; independent of SDK CBOR). */
49
+ readonly v: number;
50
+ /** NetworkId.id the token belongs to (mainnet=1 / testnet=2 / local=3). */
51
+ readonly network: number;
52
+ /**
53
+ * Genesis-stable token id — 64-char lowercase hex of the v2 `TokenId.bytes`
54
+ * (same across every state of the token). Stored on the blob so dedup / listing
55
+ * / tombstone keys need no engine call. `createTokenStateKey = ${tokenId}_${hash}`.
56
+ */
57
+ readonly tokenId: string;
58
+ /** CBOR bytes of the v2 Token (`Token.toCBOR()`). */
59
+ readonly token: Uint8Array;
60
+ }
61
+ /**
62
+ * A wallet token. `sdkToken` is the OPAQUE engine handle (see file header) —
63
+ * present for the engine to operate on, never to be touched by callers.
64
+ */
65
+ interface SphereToken {
66
+ /** Opaque v2 SDK handle. Do not call methods on this outside token-engine/. */
67
+ readonly sdkToken: Token;
68
+ /** Serializable form for storage/transport. */
69
+ readonly blob: TokenBlob;
70
+ /** Decoded value (cached); null when the token carries no sphere payment data. */
71
+ readonly value: SphereValue | null;
72
+ }
73
+ interface MintParams {
74
+ /** Recipient's 33-byte compressed chain pubkey; engine derives the predicate. */
75
+ readonly recipientPubkey: Uint8Array;
76
+ /** Value to embed in the mint; null mints a value-less token. */
77
+ readonly value?: SphereValue | null;
78
+ }
79
+ /**
80
+ * Mint a NON-value (data) token: arbitrary opaque `data` (e.g. serialized invoice
81
+ * terms), a custom `tokenType`, and a deterministic `salt` → a stable,
82
+ * terms-derived `tokenId`. The minted token has `value === null` (it carries data,
83
+ * not coins); read the bytes back with `readTokenData`.
84
+ */
85
+ interface MintDataTokenParams {
86
+ readonly recipientPubkey: Uint8Array;
87
+ /** Opaque token payload (the engine does not interpret it). */
88
+ readonly data: Uint8Array;
89
+ /** Token type bytes; defaults to a random type when omitted. */
90
+ readonly tokenType?: Uint8Array;
91
+ /** Salt bytes; deterministic salt → deterministic (terms-derived) tokenId. */
92
+ readonly salt?: Uint8Array;
93
+ }
94
+ interface TransferParams {
95
+ /** The token to spend (must be owned by this engine's identity). */
96
+ readonly token: SphereToken;
97
+ /** Recipient's 33-byte compressed chain pubkey. */
98
+ readonly recipientPubkey: Uint8Array;
99
+ /** Optional opaque on-chain memo carried on the transfer (read back via `readMemo`). */
100
+ readonly data?: Uint8Array;
101
+ }
102
+ /**
103
+ * One split output = one single-coin token. To split a multi-coin token, emit
104
+ * one output per coin (the recipient receives the value as several tokens; the
105
+ * SDK enforces per-coin conservation). If a single multi-coin output token is
106
+ * ever needed, generalize this to `assets: readonly SphereAsset[]` (additive).
107
+ */
108
+ interface SplitOutput {
109
+ readonly recipientPubkey: Uint8Array;
110
+ readonly coinId: CoinId;
111
+ readonly amount: bigint;
112
+ /** Optional opaque memo carried in this output's value envelope (read back via `readMemo`). */
113
+ readonly data?: Uint8Array;
114
+ }
115
+ interface SplitParams {
116
+ /** The token to split (its total per coin must equal the sum of outputs). */
117
+ readonly token: SphereToken;
118
+ /** Desired outputs; value conservation is enforced by the SDK split. */
119
+ readonly outputs: readonly SplitOutput[];
120
+ }
121
+ interface SplitResult {
122
+ /**
123
+ * One minted token per requested output, **index-aligned with
124
+ * `SplitParams.outputs`** — `outputs[i]` is the token for `params.outputs[i]`
125
+ * (so a payee/change split can rely on positional order). Guaranteed by both
126
+ * the real engine and FakeTokenEngine.
127
+ */
128
+ readonly outputs: readonly SphereToken[];
129
+ }
130
+ /** Verification outcome, flattened to sphere-domain (no SDK status enum leaks). */
131
+ interface EngineVerifyResult {
132
+ readonly ok: boolean;
133
+ /** Human-readable reason when `ok` is false (mapped from the SDK verification status). */
134
+ readonly reason?: string;
135
+ }
136
+
137
+ /**
138
+ * token-engine/engine.ts — the FROZEN public port (ITokenEngine) + its config.
139
+ *
140
+ * This is the contract both migration tracks build against. It is sphere-domain
141
+ * only (see types.ts). The granular, SDK-typed steps (buildMint, submit,
142
+ * awaitProof, certify, …) are an INTERNAL concern of the real adapter and are
143
+ * intentionally NOT part of this public interface.
144
+ */
145
+
146
+ /** Options common to the long-running, network-bound operations. */
147
+ interface EngineOpOptions {
148
+ /** Cancels the operation (including inclusion-proof polling). */
149
+ readonly signal?: AbortSignal;
150
+ }
151
+ /**
152
+ * The token engine port. The wallet's secp256k1 identity, the target network,
153
+ * the aggregator client and the trust base are all bound at construction
154
+ * (see EngineConfig); operations below take only sphere-domain arguments.
155
+ */
156
+ interface ITokenEngine {
157
+ /** This engine's wallet identity (chain pubkey). Synchronous. */
158
+ getIdentity(): EngineIdentity;
159
+ /**
160
+ * Legacy `DIRECT://` address for the given pubkey (defaults to this engine's
161
+ * identity). This is the ONLY "address" in v2 and is kept stable across the
162
+ * migration (Path A) so Quest XP / Unicity IDs keyed on it survive. Async —
163
+ * the derivation hashes via the SDK.
164
+ */
165
+ deriveIdentityAddress(pubkey?: Uint8Array): Promise<string>;
166
+ /**
167
+ * Genesis-stable token id — 64-char lowercase hex of the v2 TokenId (same
168
+ * across every state). Use for dedup / history / tombstone keys. Synchronous.
169
+ */
170
+ tokenId(token: SphereToken): string;
171
+ /** Decoded value of a token (cached). Synchronous. */
172
+ readValue(token: SphereToken): SphereValue | null;
173
+ /** Balance of a single coin within a token. Synchronous. */
174
+ balanceOf(token: SphereToken, coinId: CoinId): bigint;
175
+ /**
176
+ * The opaque on-chain memo delivered with this token: the latest transfer's
177
+ * data for a transferred token, else the memo in a minted output's value
178
+ * envelope (split). Returns `null` when there is no memo — including for data
179
+ * tokens (no value envelope; use `readTokenData`) and memo-less value tokens.
180
+ * To tell a data token from a value token, check `readValue` (null ⇒
181
+ * data/value-less token). Synchronous.
182
+ */
183
+ readMemo(token: SphereToken): Uint8Array | null;
184
+ /** Raw genesis data of a token (e.g. a data-token's terms). `null` when absent. Synchronous. */
185
+ readTokenData(token: SphereToken): Uint8Array | null;
186
+ /**
187
+ * Mint (issue) a new token to a recipient pubkey. NOT a wallet end-user flow —
188
+ * this is the issuer/developer capability: an app issuing its own tokens
189
+ * (rewards, in-app currency, tickets) to users, or seeding test balances. v2
190
+ * makes standalone mint first-class (Token.mint accepts a genesis with a null
191
+ * justification). Split's per-output mint is a separate, internal path; the
192
+ * Unicity-ID/nametag mint is a distinct identity surface (see migration plan §4.4).
193
+ */
194
+ mint(params: MintParams, options?: EngineOpOptions): Promise<SphereToken>;
195
+ /**
196
+ * Mint a NON-value (data) token: opaque `data` + custom `tokenType` + deterministic
197
+ * `salt` → a stable, terms-derived `tokenId`. The result has `value === null`;
198
+ * read its bytes via `readTokenData`. (Used e.g. for on-chain invoice tokens.)
199
+ */
200
+ mintDataToken(params: MintDataTokenParams, options?: EngineOpOptions): Promise<SphereToken>;
201
+ /** Spend a token wholesale to a recipient pubkey; returns the recipient's finished token. */
202
+ transfer(params: TransferParams, options?: EngineOpOptions): Promise<SphereToken>;
203
+ /** Split a token into N value-conserving outputs (burn source + internally mint each output). */
204
+ split(params: SplitParams, options?: EngineOpOptions): Promise<SplitResult>;
205
+ /** Fully verify a token against the trust base. */
206
+ verify(token: SphereToken, options?: EngineOpOptions): Promise<EngineVerifyResult>;
207
+ /** Whether the token's current state has already been spent on the network. */
208
+ isSpent(token: SphereToken, options?: EngineOpOptions): Promise<boolean>;
209
+ /**
210
+ * Whether the token's CURRENT state is locked to `SignaturePredicate(pubkey)`.
211
+ * Local + synchronous (predicate byte-compare, no network). The receive path
212
+ * uses it to reject tokens that are not actually addressed to this wallet.
213
+ */
214
+ isOwnedBy(token: SphereToken, pubkey: Uint8Array): boolean;
215
+ /** Serialize a token for storage/transport. Synchronous. */
216
+ encodeToken(token: SphereToken): TokenBlob;
217
+ /** Reconstruct a token from its blob (decodes embedded payment data). */
218
+ decodeToken(blob: TokenBlob): Promise<SphereToken>;
219
+ }
220
+ /**
221
+ * Engine construction config. Sphere-domain inputs only: the factory maps
222
+ * `network` → SDK NetworkId, builds the aggregator client from `aggregatorUrl`,
223
+ * the signing service from `privateKey`, and loads the trust base internally.
224
+ *
225
+ * NOTE: trust-base sourcing + proof-policy defaults are finalized in Phase 0.8;
226
+ * this shape may gain fields there without affecting the ITokenEngine contract.
227
+ */
228
+ interface EngineConfig {
229
+ /** Aggregator (gateway) base URL the StateTransitionClient talks to. */
230
+ readonly aggregatorUrl: string;
231
+ /** Optional gateway API key (some gateways, e.g. testnet2, require it for auth). */
232
+ readonly apiKey?: string;
233
+ /** Wallet signing key (secp256k1 private scalar, 32 bytes). Held inside the engine only. */
234
+ readonly privateKey: Uint8Array;
235
+ /**
236
+ * Root-trust-base JSON. The single source of truth for the network — the engine's
237
+ * NetworkId is taken from it (`RootTrustBase.networkId` via `NetworkId.fromId`), so
238
+ * any network id works (e.g. testnet2 = 4) with no enum entry. Typed `unknown` to
239
+ * keep SDK types off the public surface (the factory parses it internally).
240
+ */
241
+ readonly trustBaseJson: unknown;
242
+ /** Inclusion-proof poll cadence in ms (engine owns the await policy; Spike S1). */
243
+ readonly proofPollIntervalMs?: number;
244
+ /** Inclusion-proof overall timeout in ms (0/undefined = no engine-side cap). */
245
+ readonly proofTimeoutMs?: number;
246
+ }
247
+ /** Factory signature for the real adapter (implemented in Track A). */
248
+ type CreateTokenEngine = (config: EngineConfig) => Promise<ITokenEngine>;
249
+
250
+ /**
251
+ * Derive the legacy `DIRECT://` identity address for a compressed (33-byte)
252
+ * secp256k1 public key. Deterministic; byte-identical to the v1 path (Path A).
253
+ */
254
+ declare function deriveDirectAddress(publicKey: Uint8Array): Promise<string>;
255
+
256
+ /**
257
+ * token-engine/factory.ts — the real engine constructor (A4).
258
+ *
259
+ * `createSphereTokenEngine` is the public way to obtain an ITokenEngine. It maps
260
+ * the sphere-domain EngineConfig to the SDK objects the engine needs: the
261
+ * aggregator client (from `aggregatorUrl`), the trust base (parsed from
262
+ * `trustBaseJson`), the wallet signing key (from `privateKey`), the network id,
263
+ * and a mint-justification verifier with the split verifier registered (so
264
+ * split-output tokens verify).
265
+ *
266
+ * Loading the trust base per environment (browser fetch / node file) stays with
267
+ * the caller (impl/<env>/oracle, reusing the existing trust-base loaders); it
268
+ * passes the parsed JSON in via `trustBaseJson`, keeping this factory env-agnostic.
269
+ */
270
+
271
+ declare function createSphereTokenEngine(config: EngineConfig): Promise<ITokenEngine>;
272
+
273
+ /**
274
+ * token-engine/unicity-id.ts — self-issued v2 UnicityIdToken mint (the v2 analog
275
+ * of the v1 nametag-token mint).
276
+ *
277
+ * User decision 2026-06-10: the on-chain Unicity ID claim is minted AND STORED
278
+ * again at nametag registration (it was retired with v1 in Track B / D5), but it
279
+ * is NOT used at runtime — name resolution stays Nostr-binding-only, receive
280
+ * stays SignaturePredicate(chainPubkey), and no PROXY semantics return. The
281
+ * token is kept for the future (e.g. an issuer/verification model).
282
+ *
283
+ * Trust model: SELF-ISSUED. The wallet's own key is the issuer lock script, the
284
+ * recipient AND the target predicate (the v2 local-mint path — the issuer pin is
285
+ * only meaningful when verifying third-party tokens). Because the issuer lock
286
+ * script is part of the StateId, on-chain uniqueness is per-issuer only; GLOBAL
287
+ * name uniqueness remains the Nostr first-seen-wins binding's job (unchanged).
288
+ *
289
+ * Determinism / idempotency: tokenId = SHA256(CBOR["NAMETAG_", null, name]),
290
+ * tokenType is pinned (UNICITY_TOKEN_TYPE_HEX), and all predicates derive from
291
+ * the wallet key — the whole mint transaction is reproducible byte-for-byte, so
292
+ * a re-mint (e.g. after the token was lost from local storage) re-certifies the
293
+ * same state and yields the identical token (the v1 REQUEST_ID_EXISTS recovery
294
+ * analog).
295
+ */
296
+
297
+ interface UnicityIdMintResult {
298
+ /** UnicityIdToken CBOR, hex-encoded — the storable form (UnicityIdToken.fromCBOR round-trips it). */
299
+ readonly tokenCborHex: string;
300
+ /** 64-char hex token id, derived from the name (stable across re-mints). */
301
+ readonly tokenId: string;
302
+ }
303
+ /** Self-issued Unicity ID (nametag) token minter. */
304
+ interface IUnicityIdMinter {
305
+ /**
306
+ * Mint (or idempotently re-certify) the UnicityIdToken for `name`,
307
+ * self-issued by this wallet's key. Network-bound: submits the certification
308
+ * request and waits for the inclusion proof.
309
+ */
310
+ mintUnicityIdToken(name: string, options?: EngineOpOptions): Promise<UnicityIdMintResult>;
311
+ }
312
+ /**
313
+ * Build the self-issued Unicity ID minter from the same config the token engine
314
+ * uses (trust base JSON + gateway URL + API key + wallet key).
315
+ */
316
+ declare function createUnicityIdMinter(config: EngineConfig): IUnicityIdMinter;
317
+
318
+ /**
319
+ * token-engine/SpherePaymentData.ts — the sphere value model.
320
+ *
321
+ * v2 `Token` carries no coins; value is app-defined and stored in
322
+ * `MintTransaction.data`. `SpherePaymentData` is that payload: it implements the
323
+ * SDK's `IPaymentData` (so `TokenSplit` can read it for value conservation) and
324
+ * encodes a `PaymentAssetCollection` inside a versioned, tagged CBOR envelope.
325
+ *
326
+ * The SDK never inspects our raw bytes — it only calls `decodePaymentData(data)`
327
+ * and reads `.assets` — so the envelope (tag + version) is ours, chosen for
328
+ * forward-compatible storage. `fromValue`/`toValue` bridge sphere-domain values
329
+ * (hex coin id + bigint amount) to/from the SDK asset collection.
330
+ */
331
+
332
+ /** Validate a sphere-domain asset and build the SDK Asset — the single validation point. */
333
+ declare function sphereAssetToSdk(coinId: CoinId, amount: bigint): Asset;
334
+ declare class SpherePaymentData implements IPaymentData {
335
+ readonly assets: PaymentAssetCollection;
336
+ private readonly _memo;
337
+ /** Sphere-private CBOR tag (verified free in the v2 SDK tag space). */
338
+ static readonly CBOR_TAG = 39050n;
339
+ /** Envelope version; bump when the structure changes. */
340
+ static readonly VERSION = 1n;
341
+ private constructor();
342
+ /** Opaque, app-defined memo carried alongside the value (e.g. invoice attribution). */
343
+ get memo(): Uint8Array | null;
344
+ /** Wrap an existing SDK asset collection (+ optional opaque memo). */
345
+ static create(assets: PaymentAssetCollection, memo?: Uint8Array | null): SpherePaymentData;
346
+ /** Build from a sphere-domain value (hex coin id → bigint amount) + optional opaque memo. */
347
+ static fromValue(value: SphereValue, memo?: Uint8Array | null): SpherePaymentData;
348
+ /** Decode from the CBOR envelope produced by {@link encode}. */
349
+ static fromCBOR(bytes: Uint8Array): SpherePaymentData;
350
+ /** Deterministic, versioned, tagged CBOR: `tag(39050)[ version, assets, memo? ]`. */
351
+ encode(): Promise<Uint8Array>;
352
+ /** Project to a sphere-domain value (hex coin id + bigint amount), preserving order. */
353
+ toValue(): SphereValue;
354
+ /** Balance of a single coin within this payload (0n when absent). */
355
+ balanceOf(coinId: CoinId): bigint;
356
+ }
357
+ /**
358
+ * Async payment-data decoder matching the SDK's `decodePaymentData` signature.
359
+ * Used by `TokenSplit.split` (value conservation) and `SplitMintJustificationVerifier`.
360
+ */
361
+ declare function decodeSpherePaymentData(bytes: Uint8Array): Promise<IPaymentData>;
362
+
363
+ export { type CoinId, type CreateTokenEngine, type EngineConfig, type EngineIdentity, type EngineOpOptions, type EngineVerifyResult, type ITokenEngine, type IUnicityIdMinter, type MintDataTokenParams, type MintParams, type SphereAsset, type SphereNetwork, SpherePaymentData, type SphereToken, type SphereValue, type SplitOutput, type SplitParams, type SplitResult, type TokenBlob, type TransferParams, type UnicityIdMintResult, createSphereTokenEngine, createUnicityIdMinter, decodeSpherePaymentData, deriveDirectAddress, sphereAssetToSdk };