@toon-protocol/sdk 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-Z6S56VPV.js → chunk-7LBYFU4L.js} +658 -204
- package/dist/chunk-7LBYFU4L.js.map +1 -0
- package/dist/index.d.ts +29 -6
- package/dist/index.js +361 -1
- package/dist/index.js.map +1 -1
- package/dist/swap-Oub-0vqU.d.ts +911 -0
- package/dist/swap.d.ts +1 -1
- package/dist/swap.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-Z6S56VPV.js.map +0 -1
- package/dist/swap-DF4ghKio.d.ts +0 -484
package/dist/swap.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { A as AccumulatedClaim,
|
|
1
|
+
export { A as AccumulatedClaim, d as PacketProgress, R as RateMonitorCallback, B as StreamSwapClient, f as StreamSwapController, g as StreamSwapParams, h as StreamSwapResult, q as decryptFulfillClaim, t as streamSwap, u as streamSwapControlled, z as wrapSwapPacketToToon } from './swap-Oub-0vqU.js';
|
|
2
2
|
import 'nostr-tools/pure';
|
|
3
3
|
import '@toon-protocol/core';
|
package/dist/swap.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toon-protocol/sdk",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "TOON SDK for building ILP-gated Nostr services",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"@scure/bip39": "^2.0.0",
|
|
47
47
|
"@ardrive/turbo-sdk": "^1.19.0",
|
|
48
48
|
"nostr-tools": "^2.20.0",
|
|
49
|
-
"@toon-protocol/core": "2.0
|
|
49
|
+
"@toon-protocol/core": "2.1.0"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"@toon-protocol/connector": ">=3.3.3",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/identity.ts","../src/gift-wrap.ts","../src/swap-handler.ts","../src/stream-swap.ts"],"sourcesContent":["/**\n * SDK-specific error classes for @toon-protocol/sdk.\n * All errors extend ToonError from @toon-protocol/core for a consistent error hierarchy.\n */\n\nimport { ToonError } from '@toon-protocol/core';\n\n/**\n * Error thrown when identity operations fail.\n * Used for invalid mnemonics, invalid secret keys, and key derivation failures.\n */\nexport class IdentityError extends ToonError {\n constructor(message: string, cause?: Error) {\n super(message, 'IDENTITY_ERROR', cause);\n this.name = 'IdentityError';\n }\n}\n\n/**\n * Error thrown when node lifecycle operations fail.\n * Used for start/stop failures, configuration errors, etc.\n */\nexport class NodeError extends ToonError {\n constructor(message: string, cause?: Error) {\n super(message, 'NODE_ERROR', cause);\n this.name = 'NodeError';\n }\n}\n\n/**\n * Error thrown when handler dispatch operations fail.\n * Used for handler registration conflicts, missing handlers, and dispatch errors.\n */\nexport class HandlerError extends ToonError {\n constructor(message: string, cause?: Error) {\n super(message, 'HANDLER_ERROR', cause);\n this.name = 'HandlerError';\n }\n}\n\n/**\n * Error thrown when Schnorr signature verification fails.\n * Used for invalid signatures, malformed events, and verification pipeline errors.\n */\nexport class VerificationError extends ToonError {\n constructor(message: string, cause?: Error) {\n super(message, 'VERIFICATION_ERROR', cause);\n this.name = 'VerificationError';\n }\n}\n\n/**\n * Error thrown when payment validation fails.\n * Used for pricing calculation errors, insufficient payment, and pricing policy violations.\n */\nexport class PricingError extends ToonError {\n constructor(message: string, cause?: Error) {\n super(message, 'PRICING_ERROR', cause);\n this.name = 'PricingError';\n }\n}\n\n/**\n * Error thrown when NIP-59 gift wrap or NIP-44 FULFILL encryption operations fail.\n * Used for wrap/unwrap failures, decryption errors, and malformed gift wrap events.\n */\nexport class GiftWrapError extends ToonError {\n constructor(message: string, cause?: Error) {\n super(message, 'GIFT_WRAP_ERROR', cause);\n this.name = 'GiftWrapError';\n }\n}\n\n/**\n * Error thrown when swap handler orchestration fails.\n * Used for rate-conversion errors (invalid format, zero, overflow guards),\n * unsupported pair lookups, and issuer-boundary failures that are NOT\n * gift-wrap-specific. Gift-wrap failures continue to surface as `GiftWrapError`.\n */\nexport class SwapHandlerError extends ToonError {\n constructor(message: string, cause?: Error) {\n super(message, 'SWAP_HANDLER_ERROR', cause);\n this.name = 'SwapHandlerError';\n }\n}\n\n/**\n * Error thrown by the sender-side `streamSwap()` API (Story 12.5).\n *\n * All failures are categorized by a narrow `code` so callers can branch on\n * cause. `INVALID_*` codes are construction-time validation failures (thrown\n * synchronously before any packet fires). `FULFILL_DECODE_FAILED` surfaces\n * when the Swap returns `accepted: true` but the FULFILL data cannot be\n * decoded — this is a non-fatal per-packet error and is captured in\n * `StreamSwapResult.errors[]`.\n */\nexport class StreamSwapError extends Error {\n readonly code:\n | 'INVALID_AMOUNT'\n | 'INVALID_CHUNKING'\n | 'INVALID_PAIR'\n | 'INVALID_STATE'\n | 'INVALID_CHAIN_RECIPIENT'\n | 'FULFILL_DECODE_FAILED';\n // Not declared on Error in lib.es5; ES2022 adds it, but some tsconfigs\n // still target older libs. Declare explicitly for cross-version safety.\n declare readonly cause?: unknown;\n\n constructor(\n code: StreamSwapError['code'],\n message: string,\n options?: { cause?: unknown }\n ) {\n super(message);\n this.name = 'StreamSwapError';\n this.code = code;\n if (options && 'cause' in options) {\n Object.defineProperty(this, 'cause', {\n value: options.cause,\n enumerable: false,\n writable: true,\n configurable: true,\n });\n }\n }\n}\n\n/**\n * Error thrown by the sender-side `buildSettlementTx()` helper (Story 12.6).\n *\n * Settlement is a post-swap one-shot computation, so this error class is\n * THROWN synchronously (unlike `streamSwap` which routes per-packet failures\n * through `StreamSwapResult`). Callers are expected to wrap the call in\n * `try/catch`.\n *\n * Narrow `code` union lets callers branch on cause — see\n * `_bmad-output/implementation-artifacts/12-6-build-settlement-tx.md` AC-11\n * for the per-code semantics.\n *\n * @since 12.6\n * @stable — Epic 13 Chain Bridge DVM depends on this error shape.\n */\nexport class SettlementTxError extends Error {\n readonly code:\n | 'INVALID_INPUT'\n | 'MISSING_SETTLEMENT_METADATA'\n | 'UNSUPPORTED_CHAIN'\n | 'MISSING_RECIPIENT'\n | 'RECIPIENT_MISMATCH'\n | 'SWAP_SIGNER_MISMATCH'\n | 'DUPLICATE_NONCE'\n | 'NON_MONOTONIC_CUMULATIVE'\n | 'INVALID_SIGNATURE_LENGTH'\n | 'INVALID_SIGNATURE_V'\n | 'ENCODING_FAILED';\n declare readonly cause?: unknown;\n\n constructor(\n code: SettlementTxError['code'],\n message: string,\n options?: { cause?: unknown }\n ) {\n super(message);\n this.name = 'SettlementTxError';\n this.code = code;\n if (options && 'cause' in options) {\n Object.defineProperty(this, 'cause', {\n value: options.cause,\n enumerable: false,\n writable: true,\n configurable: true,\n });\n }\n }\n}\n","/**\n * Unified identity module for @toon-protocol/sdk.\n *\n * Derives both a Nostr pubkey (x-only Schnorr, BIP-340) and an EVM address\n * (Keccak-256) from a single secp256k1 private key, following the NIP-06\n * derivation standard.\n *\n * Both Nostr and EVM use the secp256k1 elliptic curve, so a single 12-word\n * seed phrase can recover the complete identity across both layers.\n */\n\nimport {\n generateMnemonic as _generateMnemonic,\n validateMnemonic,\n mnemonicToSeedSync,\n} from '@scure/bip39';\nimport { wordlist } from '@scure/bip39/wordlists/english.js';\nimport { HDKey } from '@scure/bip32';\nimport { getPublicKey } from 'nostr-tools/pure';\nimport { secp256k1 } from '@noble/curves/secp256k1.js';\nimport { ed25519 } from '@noble/curves/ed25519.js';\nimport { keccak_256 } from '@noble/hashes/sha3.js';\nimport { hmac } from '@noble/hashes/hmac.js';\nimport { sha512 } from '@noble/hashes/sha2.js';\nimport { bytesToHex } from '@noble/hashes/utils.js';\nimport { hexToMinaBase58PrivateKey } from '@toon-protocol/core';\nimport { IdentityError } from './errors.js';\n\n/**\n * Represents a node's complete identity: secret key, Nostr pubkey, and EVM address.\n */\nexport interface NodeIdentity {\n /** The 32-byte secp256k1 secret key. */\n secretKey: Uint8Array;\n /** The x-only Schnorr public key (32 bytes, 64 lowercase hex characters). */\n pubkey: string;\n /** The EIP-55 checksummed EVM address (0x-prefixed, 42 characters). */\n evmAddress: string;\n}\n\n/**\n * Solana Ed25519 identity derived via SLIP-0010 from a BIP-39 mnemonic.\n */\nexport interface SolanaIdentity {\n /** 64-byte Ed25519 keypair (32-byte private key + 32-byte public key). */\n secretKey: Uint8Array;\n /** Base58-encoded Ed25519 public key (Solana address). */\n publicKey: string;\n}\n\n/**\n * Mina Pallas identity derived from a BIP-39 mnemonic via mina-signer.\n */\nexport interface MinaIdentity {\n /** Hex-encoded Pallas private key. */\n privateKey: string;\n /** Base58 Mina public key (B62 prefix). */\n publicKey: string;\n}\n\n/**\n * Full multi-chain identity derived from a single BIP-39 mnemonic.\n * Extends NodeIdentity (Nostr + EVM) with Solana and optionally Mina.\n */\nexport interface ToonIdentity extends NodeIdentity {\n /** Solana Ed25519 identity (always populated from mnemonic derivation). */\n solana: SolanaIdentity;\n /** Mina Pallas identity (undefined when mina-signer is not installed). */\n mina?: MinaIdentity;\n}\n\n/**\n * Options for mnemonic-based key derivation.\n */\nexport interface FromMnemonicOptions {\n /** Key index in the NIP-06 derivation path. Defaults to 0. */\n accountIndex?: number;\n}\n\n/**\n * Generates a valid 12-word BIP-39 mnemonic using 128-bit entropy.\n *\n * @returns A space-separated string of 12 BIP-39 English words.\n */\nexport function generateMnemonic(): string {\n return _generateMnemonic(wordlist, 128);\n}\n\n/**\n * Derives a complete NodeIdentity from a BIP-39 mnemonic phrase.\n *\n * Uses the NIP-06 derivation path: m/44'/1237'/0'/0/{accountIndex}\n *\n * @param mnemonic - A valid BIP-39 mnemonic (12 or 24 words).\n * @param options - Optional derivation options (accountIndex defaults to 0).\n * @returns The derived NodeIdentity with secretKey, pubkey, and evmAddress.\n * @throws {IdentityError} If the mnemonic is invalid.\n */\nexport function fromMnemonic(\n mnemonic: string,\n options?: FromMnemonicOptions\n): ToonIdentity {\n if (!validateMnemonic(mnemonic, wordlist)) {\n throw new IdentityError(\n `Invalid BIP-39 mnemonic: the provided words do not form a valid mnemonic phrase`\n );\n }\n\n const accountIndex = options?.accountIndex ?? 0;\n\n if (\n !Number.isInteger(accountIndex) ||\n accountIndex < 0 ||\n accountIndex > MAX_BIP32_INDEX\n ) {\n throw new IdentityError(\n `Invalid accountIndex: expected a non-negative integer (0 to ${MAX_BIP32_INDEX}), got ${String(accountIndex)}`\n );\n }\n\n const path = `m/44'/1237'/0'/0/${accountIndex}`;\n\n let seed: Uint8Array | undefined;\n try {\n seed = mnemonicToSeedSync(mnemonic);\n const hdKey = HDKey.fromMasterSeed(seed).derive(path);\n\n if (!hdKey.privateKey) {\n throw new IdentityError(`Failed to derive private key at path ${path}`);\n }\n\n const secretKey = hdKey.privateKey;\n const base = deriveIdentity(secretKey);\n\n // Derive Solana Ed25519 identity from the same seed via SLIP-0010,\n // varying by accountIndex so distinct indices yield distinct Solana keys.\n const solana = deriveSolanaIdentity(seed, accountIndex);\n\n return { ...base, solana };\n } catch (error: unknown) {\n if (error instanceof IdentityError) {\n throw error;\n }\n throw new IdentityError(\n `Key derivation failed at path ${path}: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n } finally {\n // Best-effort zeroing of the intermediate seed to reduce the window\n // during which sensitive material remains in memory. This is not a\n // guarantee (JS has no secure-erase primitive), but it limits exposure.\n if (seed) {\n seed.fill(0);\n }\n }\n}\n\n/**\n * Derives a complete NodeIdentity from an existing 32-byte secret key.\n *\n * @param secretKey - A 32-byte secp256k1 secret key.\n * @returns The derived NodeIdentity with secretKey, pubkey, and evmAddress.\n * @throws {IdentityError} If the secret key is not exactly 32 bytes.\n */\nexport function fromSecretKey(secretKey: Uint8Array): NodeIdentity {\n if (!(secretKey instanceof Uint8Array)) {\n throw new IdentityError(\n `Invalid secret key: expected Uint8Array, got ${secretKey === null ? 'null' : typeof secretKey}`\n );\n }\n\n if (secretKey.length !== 32) {\n throw new IdentityError(\n `Invalid secret key: expected 32 bytes, got ${secretKey.length} bytes`\n );\n }\n\n try {\n return deriveIdentity(secretKey);\n } catch (error: unknown) {\n if (error instanceof IdentityError) {\n throw error;\n }\n throw new IdentityError(\n `Invalid secret key: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n}\n\n/**\n * Maximum valid BIP-32 non-hardened child index (2^31 - 1).\n * Values at or above 2^31 are reserved for hardened derivation.\n */\nconst MAX_BIP32_INDEX = 0x7fffffff;\n\n/**\n * Shared helper that computes pubkey and evmAddress from a 32-byte secret key.\n * Returns a defensive copy of the secret key to prevent external mutation.\n */\nfunction deriveIdentity(secretKey: Uint8Array): NodeIdentity {\n // Compute x-only Schnorr pubkey (32 bytes hex) via nostr-tools\n const pubkey = getPublicKey(secretKey);\n\n // Compute EVM address from the uncompressed secp256k1 public key\n const evmAddress = computeEvmAddress(secretKey);\n\n // Return a defensive copy of the secret key to prevent external mutation\n // from affecting the identity or vice versa\n return { secretKey: new Uint8Array(secretKey), pubkey, evmAddress };\n}\n\n/**\n * Computes an EIP-55 checksummed EVM address from a secp256k1 private key.\n *\n * Steps:\n * 1. Compute the full uncompressed public key (65 bytes: 0x04 + 64 bytes X,Y)\n * 2. Strip the 0x04 prefix to get 64 bytes\n * 3. Hash with Keccak-256\n * 4. Take the last 20 bytes\n * 5. Format as 0x-prefixed hex with EIP-55 checksum\n */\nfunction computeEvmAddress(secretKey: Uint8Array): string {\n // Get the uncompressed public key (65 bytes: 0x04 prefix + 64 bytes X,Y)\n const uncompressedPubkey = secp256k1.getPublicKey(secretKey, false);\n\n // Strip the 0x04 prefix (first byte), leaving 64 bytes of X,Y coordinates\n const pubkeyWithoutPrefix = uncompressedPubkey.slice(1);\n\n // Hash with Keccak-256\n const hash = keccak_256(pubkeyWithoutPrefix);\n\n // Take last 20 bytes\n const addressBytes = hash.slice(-20);\n const addressHex = bytesToHex(addressBytes);\n\n // Apply EIP-55 mixed-case checksum\n return toChecksumAddress(addressHex);\n}\n\n/**\n * Applies EIP-55 mixed-case checksum encoding to an Ethereum address.\n *\n * EIP-55 rules:\n * - Hash the lowercase hex address (without 0x) with Keccak-256\n * - For each hex character in the address:\n * - If the corresponding nibble in the hash is >= 8, uppercase it\n * - Otherwise, lowercase it\n *\n * @param addressHex - The 40-character lowercase hex address (without 0x prefix).\n * @returns The checksummed address with 0x prefix.\n */\nfunction toChecksumAddress(addressHex: string): string {\n const lower = addressHex.toLowerCase();\n const hash = bytesToHex(keccak_256(new TextEncoder().encode(lower)));\n\n let checksummed = '0x';\n for (let i = 0; i < 40; i++) {\n const char = lower[i];\n const hashChar = hash[i];\n if (char === undefined || hashChar === undefined) {\n throw new IdentityError(\n `Unexpected undefined at index ${i} during checksum computation`\n );\n }\n const hashNibble = parseInt(hashChar, 16);\n checksummed += hashNibble >= 8 ? char.toUpperCase() : char;\n }\n\n return checksummed;\n}\n\n// ---------------------------------------------------------------------------\n// SLIP-0010 Ed25519 HD Key Derivation\n// ---------------------------------------------------------------------------\n\n/**\n * SLIP-0010 master key and child key derivation for Ed25519.\n *\n * Ed25519 SLIP-0010 only supports hardened derivation. The path\n * m/44'/501'/0'/0' is standard for Solana wallets.\n *\n * @param seed - The BIP-39 seed (64 bytes).\n * @param path - Array of hardened indices (must have 0x80000000 bit set).\n * @returns The derived 32-byte Ed25519 private key.\n */\nfunction slip0010Derive(seed: Uint8Array, path: number[]): Uint8Array {\n const encoder = new TextEncoder();\n\n // Master key: HMAC-SHA512(\"ed25519 seed\", seed)\n let I = hmac(sha512, encoder.encode('ed25519 seed'), seed);\n let key = I.slice(0, 32);\n let chainCode = I.slice(32);\n\n // Child derivation (hardened only)\n for (const index of path) {\n const data = new Uint8Array(37);\n data[0] = 0x00;\n data.set(key, 1);\n // Write index as big-endian uint32\n data[33] = (index >>> 24) & 0xff;\n data[34] = (index >>> 16) & 0xff;\n data[35] = (index >>> 8) & 0xff;\n data[36] = index & 0xff;\n\n I = hmac(sha512, chainCode, data);\n key = I.slice(0, 32);\n chainCode = I.slice(32);\n }\n\n return key;\n}\n\n/**\n * SLIP-0010 path for Solana, varying the hardened account component by\n * `accountIndex`: m/44'/501'/{accountIndex}'/0' (all hardened). At\n * `accountIndex` 0 this is the canonical m/44'/501'/0'/0', so existing\n * (index-0) keys are unchanged.\n */\nfunction solanaPath(accountIndex: number): number[] {\n return [\n 0x8000002c, // 44'\n 0x800001f5, // 501'\n (0x80000000 + accountIndex) >>> 0, // {accountIndex}'\n 0x80000000, // 0'\n ];\n}\n\n/**\n * Derives a Solana Ed25519 identity from a BIP-39 seed using SLIP-0010.\n *\n * @param accountIndex - Hardened account index (default 0). Distinct indices\n * yield distinct keypairs from the same seed.\n */\nfunction deriveSolanaIdentity(\n seed: Uint8Array,\n accountIndex = 0\n): SolanaIdentity {\n const privateKey = slip0010Derive(seed, solanaPath(accountIndex));\n const publicKeyBytes = ed25519.getPublicKey(privateKey);\n\n // Solana keypair = 32-byte private key + 32-byte public key = 64 bytes\n const keypair = new Uint8Array(64);\n keypair.set(privateKey, 0);\n keypair.set(publicKeyBytes, 32);\n\n return { secretKey: keypair, publicKey: base58Encode(publicKeyBytes) };\n}\n\n// ---------------------------------------------------------------------------\n// Mina Pallas Key Derivation (optional, requires mina-signer)\n// ---------------------------------------------------------------------------\n\n/**\n * Derives a Mina Pallas identity from a BIP-39 seed.\n *\n * Uses BIP-32 secp256k1 derivation at path m/44'/12586'/{accountIndex}'/0/0 to\n * produce a 32-byte scalar, then converts to Mina key format via mina-signer.\n * At `accountIndex` 0 this is the canonical m/44'/12586'/0'/0/0, so existing\n * (index-0) keys are unchanged.\n *\n * @param seed - The BIP-39 seed (64 bytes).\n * @param accountIndex - Hardened account index (default 0). Distinct indices\n * yield distinct keys from the same seed.\n * @returns The Mina identity, or undefined if mina-signer is not installed.\n */\nasync function deriveMinaIdentity(\n seed: Uint8Array,\n accountIndex = 0\n): Promise<MinaIdentity | undefined> {\n const path = `m/44'/12586'/${accountIndex}'/0/0`;\n const hdKey = HDKey.fromMasterSeed(seed).derive(path);\n\n if (!hdKey.privateKey) {\n throw new IdentityError(\n `Failed to derive Mina private key at path ${path}`\n );\n }\n\n const keyBytes = new Uint8Array(hdKey.privateKey);\n // Clamp the top 2 bits so the big-endian scalar is within the Pallas\n // base-field order. A raw BIP-32 child scalar can exceed it (~75% of 256-bit\n // values do), and mina-signer then rejects it with\n // \"Scalar: inputs larger than … are not allowed\". Matches the client's\n // deriveMinaKey and swap's deriveSwapKeys, so all three produce the SAME Mina\n // key for a given mnemonic + accountIndex.\n keyBytes[0] = (keyBytes[0] ?? 0) & 0x3f;\n const hexKey = bytesToHex(keyBytes);\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const MinaSignerLib: any = await import('mina-signer');\n const Client =\n 'default' in MinaSignerLib ? MinaSignerLib.default : MinaSignerLib;\n const client = new Client({ network: 'mainnet' });\n\n // mina-signer's derivePublicKey expects a Mina base58check (\"EK…\") private\n // key, NOT a raw hex scalar. Convert first — passing hex makes mina-signer\n // throw `fromBase58: invalid character`, which the old broad catch silently\n // swallowed (so Mina derivation always returned undefined). Keep the\n // exposed privateKey as hex for consumer compatibility (e.g. the client's\n // MinaSigner accepts hex and converts internally).\n const minaBase58PrivateKey = hexToMinaBase58PrivateKey(hexKey);\n const publicKey: string = client.derivePublicKey(minaBase58PrivateKey);\n return { privateKey: hexKey, publicKey };\n } catch (err: unknown) {\n // Degrade gracefully ONLY when mina-signer is genuinely absent (optional\n // dependency). Surface any other failure instead of masking it — the broad\n // catch previously hid the hex-vs-base58 key-format bug above.\n const code = (err as { code?: string } | undefined)?.code;\n if (code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND') {\n return undefined;\n }\n throw new IdentityError(\n `Mina identity derivation failed at path ${path}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n err instanceof Error ? err : undefined\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public Multi-Chain Functions\n// ---------------------------------------------------------------------------\n\n/**\n * Derives a full multi-chain ToonIdentity from a BIP-39 mnemonic,\n * including async Mina derivation (requires mina-signer).\n *\n * Chains derived:\n * - Nostr (secp256k1): m/44'/1237'/0'/0/{accountIndex}\n * - EVM (secp256k1): same key as Nostr, Keccak-256 for address\n * - Solana (Ed25519): m/44'/501'/{accountIndex}'/0' (SLIP-0010)\n * - Mina (Pallas): m/44'/12586'/{accountIndex}'/0/0 (optional, requires mina-signer)\n *\n * All four chains vary by `accountIndex`, so distinct indices yield fully\n * distinct multi-chain identities from one seed. Index 0 is unchanged from the\n * historical fixed paths.\n *\n * @param mnemonic - A valid BIP-39 mnemonic (12 or 24 words).\n * @param options - Optional derivation options (accountIndex defaults to 0).\n * @returns The derived ToonIdentity with all chain identities populated.\n * @throws {IdentityError} If the mnemonic is invalid.\n */\nexport async function fromMnemonicFull(\n mnemonic: string,\n options?: FromMnemonicOptions\n): Promise<ToonIdentity> {\n // Derive Nostr + EVM + Solana synchronously\n const identity = fromMnemonic(mnemonic, options);\n const accountIndex = options?.accountIndex ?? 0;\n\n // Attempt async Mina derivation (varies by accountIndex, like the others)\n let seed: Uint8Array | undefined;\n try {\n seed = mnemonicToSeedSync(mnemonic);\n const mina = await deriveMinaIdentity(seed, accountIndex);\n if (mina) {\n return { ...identity, mina };\n }\n } finally {\n if (seed) {\n seed.fill(0);\n }\n }\n\n return identity;\n}\n\n/**\n * Generates a random Solana Ed25519 keypair (non-deterministic).\n *\n * For deterministic derivation from a mnemonic, use fromMnemonic() instead.\n *\n * @returns A SolanaIdentity with random keypair.\n */\nexport function generateSolanaKeypair(): SolanaIdentity {\n const privateKey = ed25519.utils.randomSecretKey();\n const publicKeyBytes = ed25519.getPublicKey(privateKey);\n\n const keypair = new Uint8Array(64);\n keypair.set(privateKey, 0);\n keypair.set(publicKeyBytes, 32);\n\n return { secretKey: keypair, publicKey: base58Encode(publicKeyBytes) };\n}\n\n// ---------------------------------------------------------------------------\n// Base58 Encoding/Decoding\n// ---------------------------------------------------------------------------\n\nconst BASE58_ALPHABET =\n '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';\n\n/**\n * Encodes a byte array to a Base58 string (Bitcoin/Solana alphabet).\n */\nexport function base58Encode(bytes: Uint8Array): string {\n // Count leading zeros\n let zeros = 0;\n for (let i = 0; i < bytes.length && bytes[i] === 0; i++) zeros++;\n\n let value = 0n;\n for (const byte of bytes) {\n value = value * 256n + BigInt(byte);\n }\n\n let result = '';\n while (value > 0n) {\n result = BASE58_ALPHABET[Number(value % 58n)] + result;\n value = value / 58n;\n }\n\n // Add leading '1's for leading zero bytes\n for (let i = 0; i < zeros; i++) {\n result = '1' + result;\n }\n\n return result || '1';\n}\n\n/**\n * Decodes a Base58 string to a byte array (Bitcoin/Solana alphabet).\n */\nexport function base58Decode(str: string): Uint8Array {\n // Count leading '1's (zero bytes)\n let zeros = 0;\n for (let i = 0; i < str.length && str[i] === '1'; i++) zeros++;\n\n let value = 0n;\n for (const ch of str) {\n const idx = BASE58_ALPHABET.indexOf(ch);\n if (idx === -1) throw new IdentityError(`Invalid base58 character: ${ch}`);\n value = value * 58n + BigInt(idx);\n }\n\n // Convert bigint to bytes\n const hex = value === 0n ? '' : value.toString(16);\n const hexPadded = hex.length % 2 ? '0' + hex : hex;\n const rawBytes: number[] = [];\n for (let i = 0; i < hexPadded.length; i += 2) {\n rawBytes.push(parseInt(hexPadded.slice(i, i + 2), 16));\n }\n\n const result = new Uint8Array(zeros + rawBytes.length);\n result.set(rawBytes, zeros);\n return result;\n}\n","/**\n * NIP-59 Gift Wrap Integration for ILP Packets (Story 12.2)\n *\n * Provides privacy-preserving encoding/decoding of swap packets using the\n * NIP-59 three-layer gift wrap construction (rumor -> seal -> gift wrap).\n * Intermediary peers routing ILP packets see only opaque TOON-encoded binary\n * in the data field -- they cannot determine the event kind, sender identity,\n * or swap intent. Only the destination Swap can unwrap and process.\n *\n * Also provides NIP-44 encryption/decryption for FULFILL claim return path\n * (D12-008), using ephemeral keys so intermediaries on the return path see\n * only opaque ciphertext.\n *\n * @module\n */\n\nimport { generateSecretKey, getPublicKey } from 'nostr-tools/pure';\nimport type { UnsignedEvent, NostrEvent } from 'nostr-tools/pure';\nimport { createRumor, createSeal, createWrap } from 'nostr-tools/nip59';\nimport {\n encrypt as nip44Encrypt,\n decrypt as nip44Decrypt,\n getConversationKey,\n} from 'nostr-tools/nip44';\nimport {\n encodeEventToToon,\n decodeEventFromToon,\n} from '@toon-protocol/core/toon';\nimport { buildIlpPrepare } from '@toon-protocol/core';\nimport type { IlpPreparePacket } from '@toon-protocol/core';\n\nimport { GiftWrapError } from './errors.js';\n\n// ---------------------------------------------------------------------------\n// Type definitions\n// ---------------------------------------------------------------------------\n\n/** Parameters for {@link wrapSwapPacket}. */\nexport interface WrapSwapPacketParams {\n /** Unsigned inner event containing swap metadata. */\n rumor: UnsignedEvent;\n /** Sender's secp256k1 secret key. */\n senderSecretKey: Uint8Array;\n /** Swap's compressed hex pubkey (64 chars). */\n recipientPubkey: string;\n}\n\n/** Result of {@link wrapSwapPacket}. */\nexport interface WrapSwapPacketResult {\n /** Fully-formed kind:1059 gift wrap event signed by a fresh ephemeral key. */\n giftWrap: NostrEvent;\n /** The ephemeral pubkey used for the outer gift wrap layer. */\n ephemeralPubkey: string;\n}\n\n/** Parameters for {@link unwrapSwapPacket}. */\nexport interface UnwrapSwapPacketParams {\n /** A kind:1059 gift wrap event. */\n giftWrap: NostrEvent;\n /** Recipient's (Swap's) secret key. */\n recipientSecretKey: Uint8Array;\n}\n\n/** Result of {@link unwrapSwapPacket}. */\nexport interface UnwrapSwapPacketResult {\n /** The decrypted inner rumor (unsigned). */\n rumor: UnsignedEvent;\n /** The sender's real pubkey (extracted from the seal layer). */\n senderPubkey: string;\n}\n\n/** Parameters for {@link wrapSwapPacketToToon}. */\nexport interface WrapSwapPacketToToonParams {\n /** Unsigned inner event containing swap metadata. */\n rumor: UnsignedEvent;\n /** Sender's secp256k1 secret key. */\n senderSecretKey: Uint8Array;\n /** Swap's compressed hex pubkey (64 chars). */\n recipientPubkey: string;\n /** ILP destination address. */\n destination: string;\n /** Payment amount in ILP units (bigint). */\n amount: bigint;\n /** Packet expiry. Default: 30s from now (inside buildIlpPrepare). */\n expiresAt?: Date;\n}\n\n/** Result of {@link wrapSwapPacketToToon}. */\nexport interface WrapSwapPacketToToonResult {\n /** Ready-to-send ILP PREPARE packet with TOON-encoded gift wrap as data. */\n ilpPrepare: IlpPreparePacket;\n /** The ephemeral pubkey used for the outer gift wrap layer. */\n ephemeralPubkey: string;\n}\n\n/** Parameters for {@link unwrapSwapPacketFromToon}. */\nexport interface UnwrapSwapPacketFromToonParams {\n /** The data field from an incoming ILP PREPARE (TOON-encoded gift wrap). */\n toonData: Uint8Array;\n /** Recipient's (Swap's) secret key. */\n recipientSecretKey: Uint8Array;\n}\n\n/** Parameters for {@link encryptFulfillClaim}. */\nexport interface EncryptFulfillClaimParams {\n /** The signed claim bytes to encrypt. */\n claimData: Uint8Array;\n /** The original sender's pubkey (recovered from unwrap). */\n senderPubkey: string;\n}\n\n/** Result of {@link encryptFulfillClaim}. */\nexport interface EncryptFulfillClaimResult {\n /** NIP-44 encrypted claim bytes. */\n ciphertext: Uint8Array;\n /** The Swap's ephemeral pubkey (included in FULFILL so sender can decrypt). */\n ephemeralPubkey: string;\n}\n\n/** Parameters for {@link decryptFulfillClaim}. */\nexport interface DecryptFulfillClaimParams {\n /** The encrypted claim bytes from the FULFILL data field. */\n ciphertext: Uint8Array;\n /** The Swap's ephemeral pubkey from the FULFILL data field. */\n ephemeralPubkey: string;\n /** The sender's (recipient of FULFILL) secret key. */\n recipientSecretKey: Uint8Array;\n}\n\n// ---------------------------------------------------------------------------\n// Input validation helpers\n// ---------------------------------------------------------------------------\n\n/** Validate a 32-byte secret key. */\nfunction validateSecretKey(key: Uint8Array, paramName: string): void {\n if (!(key instanceof Uint8Array) || key.length !== 32) {\n throw new GiftWrapError(\n `${paramName} must be a 32-byte Uint8Array, got ${key instanceof Uint8Array ? `${key.length} bytes` : typeof key}`\n );\n }\n}\n\n/** Validate a 64-char lowercase hex pubkey. */\nfunction validatePubkey(pubkey: string, paramName: string): void {\n if (typeof pubkey !== 'string' || !/^[0-9a-f]{64}$/.test(pubkey)) {\n throw new GiftWrapError(\n `${paramName} must be a 64-character lowercase hex string`\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Core wrap/unwrap functions (AC-1, AC-2)\n// ---------------------------------------------------------------------------\n\n/**\n * NIP-59 three-layer gift wrap a swap packet.\n *\n * Constructs a kind:1059 gift wrap event using a fresh ephemeral keypair.\n * The rumor is sealed with the sender's real key, then wrapped with the\n * ephemeral key. Intermediaries see only the ephemeral pubkey.\n *\n * Each invocation generates a fresh ephemeral keypair for forward secrecy\n * and message unlinkability (risk R-006).\n *\n * @throws {GiftWrapError} If the wrap operation fails (invalid keys, crypto failure).\n */\nexport function wrapSwapPacket(\n params: WrapSwapPacketParams\n): WrapSwapPacketResult {\n const { rumor, senderSecretKey, recipientPubkey } = params;\n\n validateSecretKey(senderSecretKey, 'senderSecretKey');\n validatePubkey(recipientPubkey, 'recipientPubkey');\n\n try {\n // Use nostr-tools nip59 building blocks so we can capture the ephemeral pubkey.\n // Step 1: Create rumor (adds id + pubkey derived from senderSecretKey)\n const rumorEvent = createRumor(rumor, senderSecretKey);\n\n // Step 2: Create seal (kind:13, NIP-44 encrypted rumor, randomized timestamp)\n const seal = createSeal(rumorEvent, senderSecretKey, recipientPubkey);\n\n // Step 3: Create gift wrap (kind:1059, fresh ephemeral key, NIP-44 encrypted seal)\n const giftWrap = createWrap(seal, recipientPubkey);\n\n // The ephemeral pubkey is the pubkey on the gift wrap event\n const ephemeralPubkey = giftWrap.pubkey;\n\n return { giftWrap, ephemeralPubkey };\n } catch (error) {\n throw new GiftWrapError(\n `Failed to wrap swap packet: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n}\n\n/**\n * Unwrap a NIP-59 gift-wrapped swap packet, recovering the inner rumor\n * and the sender's real pubkey.\n *\n * Performs two-layer decryption: gift wrap -> seal -> rumor.\n * The sender pubkey is extracted from the seal's pubkey field.\n *\n * @throws {GiftWrapError} If kind is not 1059, decryption fails, or structure is malformed.\n */\nexport function unwrapSwapPacket(\n params: UnwrapSwapPacketParams\n): UnwrapSwapPacketResult {\n const { giftWrap, recipientSecretKey } = params;\n\n validateSecretKey(recipientSecretKey, 'recipientSecretKey');\n\n // Guard against null/undefined/non-object giftWrap before property access\n if (!giftWrap || typeof giftWrap !== 'object') {\n throw new GiftWrapError('giftWrap must be a non-null object');\n }\n\n // Validate kind before attempting decryption\n if (giftWrap.kind !== 1059) {\n throw new GiftWrapError('Expected kind:1059 gift wrap');\n }\n\n let conversationKey1: Uint8Array | null = null;\n let conversationKey2: Uint8Array | null = null;\n\n try {\n // Layer 1: Decrypt the gift wrap to recover the seal.\n // The gift wrap was encrypted with ECDH(ephemeralPrivkey, recipientPubkey),\n // so we decrypt with ECDH(recipientSecretKey, giftWrap.pubkey).\n conversationKey1 = getConversationKey(recipientSecretKey, giftWrap.pubkey);\n const sealJson = nip44Decrypt(giftWrap.content, conversationKey1);\n const seal = JSON.parse(sealJson) as NostrEvent;\n\n // Validate seal kind (must be kind:13 per NIP-59)\n if (seal.kind !== 13) {\n throw new GiftWrapError(\n `Expected kind:13 seal inside gift wrap, got kind:${seal.kind}`\n );\n }\n\n // Extract and validate the sender's real pubkey from the seal\n const senderPubkey = seal.pubkey;\n validatePubkey(senderPubkey, 'seal.pubkey (sender identity)');\n\n // Layer 2: Decrypt the seal to recover the rumor.\n // The seal was encrypted with ECDH(senderSecretKey, recipientPubkey),\n // so we decrypt with ECDH(recipientSecretKey, senderPubkey).\n conversationKey2 = getConversationKey(recipientSecretKey, senderPubkey);\n const rumorJson = nip44Decrypt(seal.content, conversationKey2);\n const rumor = JSON.parse(rumorJson) as UnsignedEvent & {\n id?: string;\n sig?: string;\n };\n\n // Strip sig if present (rumor should be unsigned)\n delete rumor.sig;\n\n return { rumor, senderPubkey };\n } catch (error) {\n if (error instanceof GiftWrapError) {\n throw error;\n }\n throw new GiftWrapError(\n `Failed to unwrap swap packet: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n } finally {\n // Zero ECDH-derived conversation keys (defense-in-depth)\n if (conversationKey1) {\n conversationKey1.fill(0);\n conversationKey1 = null;\n }\n if (conversationKey2) {\n conversationKey2.fill(0);\n conversationKey2 = null;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Convenience wrappers for TOON + ILP integration (AC-3, AC-4)\n// ---------------------------------------------------------------------------\n\n/**\n * Convenience: gift wrap a swap packet, encode to TOON binary, and build\n * an ILP PREPARE packet.\n *\n * This is the path that `streamSwap()` (Story 12.5) will use in its\n * per-packet loop.\n */\nexport function wrapSwapPacketToToon(\n params: WrapSwapPacketToToonParams\n): WrapSwapPacketToToonResult {\n const {\n rumor,\n senderSecretKey,\n recipientPubkey,\n destination,\n amount,\n expiresAt,\n } = params;\n\n // Step 1: Gift wrap\n const { giftWrap, ephemeralPubkey } = wrapSwapPacket({\n rumor,\n senderSecretKey,\n recipientPubkey,\n });\n\n // Step 2: Encode to TOON binary\n const toonBinary = encodeEventToToon(giftWrap);\n\n // Step 3: Build ILP PREPARE packet\n const ilpPrepare = buildIlpPrepare({\n destination,\n amount,\n data: toonBinary,\n expiresAt,\n });\n\n return { ilpPrepare, ephemeralPubkey };\n}\n\n/**\n * Convenience: decode TOON binary from an incoming ILP PREPARE data field\n * and unwrap the gift-wrapped swap packet.\n *\n * This is the path that the Swap handler (Story 12.3) will use to process\n * incoming swap packets.\n */\nexport function unwrapSwapPacketFromToon(\n params: UnwrapSwapPacketFromToonParams\n): UnwrapSwapPacketResult {\n const { toonData, recipientSecretKey } = params;\n\n if (!(toonData instanceof Uint8Array) || toonData.length === 0) {\n throw new GiftWrapError('toonData must be a non-empty Uint8Array');\n }\n\n try {\n // Step 1: Decode TOON binary to NostrEvent\n const giftWrap = decodeEventFromToon(toonData);\n\n // Step 2: Unwrap\n return unwrapSwapPacket({ giftWrap, recipientSecretKey });\n } catch (error) {\n if (error instanceof GiftWrapError) {\n throw error;\n }\n throw new GiftWrapError(\n `Failed to unwrap swap packet from TOON: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// FULFILL encryption/decryption (AC-5, AC-6)\n// ---------------------------------------------------------------------------\n\n/**\n * Encrypt a FULFILL claim for the return path (D12-008).\n *\n * Generates a fresh ephemeral keypair and NIP-44 encrypts the claim data.\n * The ephemeral pubkey is included alongside the ciphertext so the sender\n * can decrypt. The ephemeral privkey is discarded after encryption.\n *\n * @throws {GiftWrapError} If claimData is empty or encryption fails.\n */\nexport function encryptFulfillClaim(\n params: EncryptFulfillClaimParams\n): EncryptFulfillClaimResult {\n const { claimData, senderPubkey } = params;\n\n validatePubkey(senderPubkey, 'senderPubkey');\n\n if (!(claimData instanceof Uint8Array)) {\n throw new GiftWrapError(\n `claimData must be a Uint8Array, got ${typeof claimData}`\n );\n }\n\n if (claimData.length === 0) {\n throw new GiftWrapError('claimData must not be empty');\n }\n\n // Generate fresh ephemeral keypair (let-scoped for GC after return)\n let ephemeralSecretKey: Uint8Array | null = generateSecretKey();\n const ephemeralPubkey = getPublicKey(ephemeralSecretKey);\n let conversationKey: Uint8Array | null = null;\n\n try {\n // NIP-44 encrypt: ECDH(ephemeralPrivkey, senderPubkey)\n conversationKey = getConversationKey(ephemeralSecretKey, senderPubkey);\n\n // NIP-44 encrypts strings, so base64-encode the claim bytes\n const claimString = Buffer.from(claimData).toString('base64');\n const ciphertextString = nip44Encrypt(claimString, conversationKey);\n\n // Convert ciphertext string to Uint8Array for transport\n const ciphertext = new TextEncoder().encode(ciphertextString);\n\n return { ciphertext, ephemeralPubkey };\n } catch (error) {\n if (error instanceof GiftWrapError) {\n throw error;\n }\n throw new GiftWrapError(\n `Failed to encrypt FULFILL claim: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n } finally {\n // Zero sensitive key material before releasing for GC (defense-in-depth)\n if (ephemeralSecretKey) {\n ephemeralSecretKey.fill(0);\n ephemeralSecretKey = null;\n }\n if (conversationKey) {\n conversationKey.fill(0);\n conversationKey = null;\n }\n }\n}\n\n/**\n * Decrypt a FULFILL claim from the return path.\n *\n * Uses the sender's secret key and the Swap's ephemeral pubkey (from the\n * FULFILL data field) to NIP-44 decrypt the claim bytes.\n *\n * @throws {GiftWrapError} If decryption fails (wrong key, malformed ciphertext).\n */\nexport function decryptFulfillClaim(\n params: DecryptFulfillClaimParams\n): Uint8Array {\n const { ciphertext, ephemeralPubkey, recipientSecretKey } = params;\n\n validateSecretKey(recipientSecretKey, 'recipientSecretKey');\n validatePubkey(ephemeralPubkey, 'ephemeralPubkey');\n\n if (!(ciphertext instanceof Uint8Array) || ciphertext.length === 0) {\n throw new GiftWrapError('ciphertext must be a non-empty Uint8Array');\n }\n\n let conversationKey: Uint8Array | null = null;\n\n try {\n // NIP-44 decrypt: ECDH(recipientSecretKey, ephemeralPubkey)\n conversationKey = getConversationKey(recipientSecretKey, ephemeralPubkey);\n\n // Convert ciphertext Uint8Array back to string\n const ciphertextString = new TextDecoder().decode(ciphertext);\n\n // NIP-44 decrypt returns a string (base64-encoded claim bytes)\n const claimString = nip44Decrypt(ciphertextString, conversationKey);\n\n // Decode base64 back to Uint8Array\n return new Uint8Array(Buffer.from(claimString, 'base64'));\n } catch (error) {\n if (error instanceof GiftWrapError) {\n throw error;\n }\n throw new GiftWrapError(\n `Failed to decrypt FULFILL claim: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n } finally {\n // Zero ECDH-derived conversation key (defense-in-depth)\n if (conversationKey) {\n conversationKey.fill(0);\n conversationKey = null;\n }\n }\n}\n","/**\n * Swap Handler (Story 12.3)\n *\n * `createSwapHandler()` factory produces a kind:1059 `Handler` that:\n * 1. Unwraps an incoming NIP-59 gift-wrapped ILP swap packet (via Story 12.2).\n * 2. Identifies the requested `SwapPair` from inner-rumor `swap-from` / `swap-to` tags.\n * 3. Applies a per-packet exchange rate (pair.rate or live rateProvider hook).\n * 4. Delegates signed claim issuance to a pluggable `ClaimIssuer` (Story 12.4).\n * 5. NIP-44 encrypts the claim with an ephemeral key (Story 12.2) for return.\n *\n * The handler is a pure application-layer composition — no connector, routing,\n * or wallet code lives here. See `_bmad-output/epics/epic-12-token-swap-primitive.md`\n * for D12-001/D12-008/D12-009/D12-010 and the scope fence.\n *\n * Transport encoding: the `accept()` metadata emits `claim` as a base64-encoded\n * NIP-44 ciphertext, `ephemeralPubkey` as 64-char lowercase hex, and optional\n * `claimId`. The sender-side `streamSwap()` (Story 12.5) base64-decodes `claim`\n * before calling `decryptFulfillClaim`.\n *\n * @module\n */\n\nimport { createHash } from 'node:crypto';\nimport type { UnsignedEvent } from 'nostr-tools/pure';\nimport type { SwapPair } from '@toon-protocol/core';\n\nimport { GiftWrapError, SwapHandlerError } from './errors.js';\nimport { unwrapSwapPacketFromToon, encryptFulfillClaim } from './gift-wrap.js';\nimport type { Handler } from './handler-registry.js';\nimport { base58Decode } from './identity.js';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Parameters passed to a {@link ClaimIssuer.issueClaim} call. */\nexport interface IssueClaimParams {\n /** Source-asset amount received by the Swap (ILP packet amount, source micro-units). */\n sourceAmount: bigint;\n /** Target-asset amount owed to the sender (post-rate-conversion, target micro-units). */\n targetAmount: bigint;\n /** The `SwapPair` this packet is being priced against. */\n pair: SwapPair;\n /**\n * The sender's real Nostr pubkey (extracted from the unwrapped seal).\n *\n * Identity-layer key only: used by the Swap for inventory ledger keying and\n * the sender→channel sticky binding (`channelState.reserve()` /\n * `channelState.release()`). The Swap MUST NOT pass this to chain-layer\n * signers as the balance-proof `recipient` — use {@link chainRecipient}\n * for that (Story 12.9 D12-011).\n */\n senderPubkey: string;\n /**\n * The sender's chain-specific payout address for `pair.to.chain`\n * (Story 12.9 AC-10). Extracted and format-validated from the rumor's\n * `chain-recipient` tag by the swap handler. REQUIRED. This is the\n * address the Swap's `PaymentChannelSigner` MUST use as the balance-proof\n * `recipient` (e.g., 20-byte EVM address, 32-byte Solana Ed25519 pubkey).\n */\n chainRecipient: string;\n /** The inner rumor (for optional Swap-side context; may be ignored by the issuer). */\n rumor: UnsignedEvent;\n}\n\n/**\n * Result returned from {@link ClaimIssuer.issueClaim}.\n *\n * Story 12.6 extension: additive settlement-context fields let the sender\n * reconstruct the balance-proof message hash for signature verification and\n * on-chain settlement (see `buildSettlementTx()`).\n */\nexport interface IssueClaimResult {\n /** Signed claim bytes ready for NIP-44 encryption (chain-specific format). */\n claim: Uint8Array;\n /** Optional Swap-side claim ID for logging/tracing. */\n claimId?: string;\n // --- Story 12.6 settlement-context fields (additive, all optional for\n // one-story-cycle backward compat; the Swap SHOULD emit all of them) ---\n /** Channel identifier on the target chain. */\n channelId?: string;\n /** Balance-proof nonce (monotonically increasing per channel). */\n nonce?: bigint;\n /** Cumulative transferred amount on the channel (target micro-units). */\n cumulativeAmount?: bigint;\n /** Recipient address (the sender's target-asset address). */\n recipient?: string;\n /** Swap's on-chain signer address. */\n swapSignerAddress?: string;\n}\n\n/**\n * Pluggable signed-claim issuer. Story 12.3 defines only the contract — the\n * concrete multi-chain implementation ships in Story 12.4.\n *\n * The issuer owns inventory accounting and signing-key material. The handler\n * relies on `issueClaim()` being atomic with inventory debit: if the call\n * resolves, the target-asset amount MUST be considered committed from the\n * Swap's reserves. If the call throws, no inventory change SHOULD have occurred.\n */\nexport interface ClaimIssuer {\n /**\n * Produce a signed off-chain payment-channel claim in the target asset.\n *\n * @throws Error (or subclass) on insufficient reserves, unsupported pair,\n * or signing failure. Errors with `code === 'INSUFFICIENT_INVENTORY'` or\n * messages matching `/insufficient/i` are surfaced as ILP T04; all other\n * errors as T00.\n */\n issueClaim(params: IssueClaimParams): Promise<IssueClaimResult>;\n}\n\n/** Parameters for {@link applyRate}. */\nexport interface ApplyRateParams {\n /** Source amount in source micro-units. */\n sourceAmount: bigint;\n /** `SwapPair.from.assetScale` (number of decimals on source side). */\n fromScale: number;\n /** `SwapPair.to.assetScale` (number of decimals on target side). */\n toScale: number;\n /** Decimal-string rate (target whole-units per source whole-unit). */\n rate: string;\n}\n\n/** Minimal pino-compatible logger interface. */\nexport interface SwapHandlerLogger {\n debug: (...args: unknown[]) => void;\n info: (...args: unknown[]) => void;\n warn: (...args: unknown[]) => void;\n error: (...args: unknown[]) => void;\n}\n\n/**\n * Minimal `Set`-like contract the handler requires from\n * `seenPacketIds`. Both the native `Set<string>` and\n * {@link BoundedSeenPacketIds} satisfy this; operators can swap in a\n * persistent/remote-backed replacement too.\n */\nexport interface SeenPacketIdsLike {\n has(value: string): boolean;\n add(value: string): unknown;\n delete(value: string): boolean;\n}\n\n/** Configuration for {@link createSwapHandler}. */\nexport interface CreateSwapHandlerConfig {\n /** Swap's secp256k1 secret key for unwrapping gift-wrapped packets (32 bytes). */\n recipientSecretKey: Uint8Array;\n /** Swap pairs this Swap currently supports. */\n swapPairs: SwapPair[];\n /** Claim issuer delegate (Story 12.4 plugs in the multi-chain implementation). */\n claimIssuer: ClaimIssuer;\n /**\n * Optional live-rate override hook. When provided, the handler calls this per\n * packet instead of reading `pair.rate`. MUST return a decimal string matching\n * `SwapPair.rate` format: /^(0|[1-9]\\d*)(\\.\\d+)?$/.\n */\n rateProvider?: (pair: SwapPair) => string | Promise<string>;\n /**\n * Optional replay-protection set. When provided, the handler uses it\n * VERBATIM (operator-owned — bounding/persistence is the operator's\n * responsibility). When OMITTED (Story 12.8 AC-14), the handler\n * defaults to a {@link BoundedSeenPacketIds} with cap\n * {@link DEFAULT_SEEN_PACKET_IDS_CAP}. Any `Set`-shaped object with\n * `has`/`add`/`delete` (and a `size` getter for test introspection)\n * satisfies the contract.\n */\n seenPacketIds?: SeenPacketIdsLike;\n /** Optional pino-compatible logger. Defaults to a no-op logger. */\n logger?: SwapHandlerLogger;\n}\n\n// ---------------------------------------------------------------------------\n// Story 12.8 AC-14 — bounded seenPacketIds default\n// ---------------------------------------------------------------------------\n\n/**\n * Default ceiling for the `seenPacketIds` replay-protection set when the\n * operator does not supply a custom `Set`. Rationale: 10_000 packet-ids at\n * ~64 bytes each yields a ~640KB memory ceiling — high enough to absorb\n * legitimate bursts, low enough to bound DoS via distinct-id flooding.\n *\n * When the default bounded set is in use, eviction is LAST-ACCESS order\n * (LRU), NOT insertion order: a replay attacker who retries the same\n * packet-id forever would, under insertion-order LRU, have their entry\n * evicted after 10_000 new ids pass through — re-opening the replay\n * window. Access-order keeps frequently-replayed ids pinned, so the\n * window stays closed.\n *\n * Operators may override by supplying a custom `Set<string>` (e.g. a\n * persistent backing store). `createSwapHandler` uses the supplied set\n * verbatim without size-capping.\n */\nexport const DEFAULT_SEEN_PACKET_IDS_CAP = 10_000;\n\n/**\n * ILP REJECT codes emitted by `createSwapHandler()`.\n *\n * Exported as named constants so tests (Story 12.8 AC-3 forbids hardcoded\n * error-code strings) and downstream integrators can assert against the\n * same symbolic source the handler uses. Values follow RFC 27 / ILPv4\n * reject-code conventions (F01 = malformed, F02 = unreachable, F04 =\n * duplicate, F06 = unsupported pair, T00 = transient internal, T04 =\n * insufficient liquidity).\n */\nexport const SWAP_HANDLER_REJECT_CODES = {\n /** Malformed / invalid PREPARE content — gift-wrap shape or amount invalid. */\n INVALID_GIFT_WRAP: 'F01',\n /** No route — the handler did not match a registered destination. */\n UNREACHABLE: 'F02',\n /** Duplicate packet — `seenPacketIds` replay hit. */\n DUPLICATE_PACKET: 'F04',\n /** Requested swap pair is not advertised by this Swap. */\n UNSUPPORTED_PAIR: 'F06',\n /** Transient internal failure — signing, rate provider, or unexpected. */\n INTERNAL: 'T00',\n /** Insufficient Swap inventory for the requested amount. */\n INSUFFICIENT_LIQUIDITY: 'T04',\n} as const;\n\n/**\n * Human-readable reject messages emitted by `createSwapHandler()`.\n *\n * Tests may assert against these verbatim rather than matching regexes\n * (Story 12.8 AC-3 guidance).\n */\nexport const SWAP_HANDLER_REJECT_MESSAGES = {\n INVALID_GIFT_WRAP: 'Invalid gift wrap',\n INVALID_AMOUNT: 'Invalid amount',\n UNREACHABLE: 'Unreachable',\n DUPLICATE_PACKET: 'Duplicate packet',\n UNSUPPORTED_PAIR: 'Unsupported swap pair',\n INTERNAL: 'Internal error',\n INSUFFICIENT_LIQUIDITY: 'Insufficient liquidity',\n RATE_PROVIDER: 'Rate provider error',\n RATE_CONVERSION: 'Rate conversion error',\n} as const;\n\n/**\n * LRU-ish `Set<string>`: re-adding an existing element promotes it to\n * \"most recently accessed\"; `has()` also promotes. Eviction occurs on\n * `add()` when size exceeds the cap — the least-recently-accessed entry\n * (Map insertion-order head) is removed.\n *\n * Exported as `@internal` for Story 12.8 AC-10 test introspection.\n *\n * @internal\n */\nexport class BoundedSeenPacketIds {\n readonly #map = new Map<string, true>();\n readonly #cap: number;\n readonly [Symbol.toStringTag] = 'Set';\n\n constructor(cap: number = DEFAULT_SEEN_PACKET_IDS_CAP) {\n if (!Number.isInteger(cap) || cap <= 0) {\n throw new Error(\n `BoundedSeenPacketIds cap must be a positive integer, got ${cap}`\n );\n }\n this.#cap = cap;\n }\n\n get size(): number {\n return this.#map.size;\n }\n\n /** Exposed for AC-10 test introspection. */\n get cap(): number {\n return this.#cap;\n }\n\n has(value: string): boolean {\n if (!this.#map.has(value)) return false;\n // Access-order promotion: re-insert so iteration order puts this at tail.\n this.#map.delete(value);\n this.#map.set(value, true);\n return true;\n }\n\n add(value: string): this {\n if (this.#map.has(value)) {\n // Promote to most-recently-accessed.\n this.#map.delete(value);\n this.#map.set(value, true);\n return this;\n }\n this.#map.set(value, true);\n while (this.#map.size > this.#cap) {\n // Evict least-recently-accessed (Map iteration is insertion order;\n // first key is the oldest that hasn't been re-added).\n const first = this.#map.keys().next();\n if (first.done) break;\n this.#map.delete(first.value);\n }\n return this;\n }\n\n delete(value: string): boolean {\n return this.#map.delete(value);\n }\n\n clear(): void {\n this.#map.clear();\n }\n\n forEach(\n callback: (value: string, value2: string, set: Set<string>) => void,\n thisArg?: unknown\n ): void {\n for (const k of this.#map.keys()) {\n callback.call(thisArg, k, k, this);\n }\n }\n\n *[Symbol.iterator](): IterableIterator<string> {\n yield* this.#map.keys();\n }\n\n *keys(): IterableIterator<string> {\n yield* this.#map.keys();\n }\n\n *values(): IterableIterator<string> {\n yield* this.#map.keys();\n }\n\n *entries(): IterableIterator<[string, string]> {\n for (const k of this.#map.keys()) yield [k, k];\n }\n}\n\n// ---------------------------------------------------------------------------\n// applyRate helper (AC-8)\n// ---------------------------------------------------------------------------\n\nconst RATE_REGEX = /^(0|[1-9]\\d*)(\\.\\d+)?$/;\n\n/**\n * Apply a decimal-string exchange rate to a source amount across asset scales.\n * Uses BigInt arithmetic throughout — never coerces to `Number` — to preserve\n * 18-decimal EVM precision (Epic 11 retro MAX_SAFE_INTEGER guard).\n *\n * Rounds toward zero (integer division), which economically favors the Swap\n * (standard market-maker convention).\n *\n * @throws {SwapHandlerError} If rate format is invalid, rate is zero, or\n * sourceAmount is not positive.\n */\nexport function applyRate(params: ApplyRateParams): bigint {\n const { sourceAmount, fromScale, toScale, rate } = params;\n\n if (!RATE_REGEX.test(rate)) {\n throw new SwapHandlerError(`Invalid rate format: ${rate}`);\n }\n // Reject any zero-valued rate regardless of decimal presentation.\n // RATE_REGEX matches `'0'`, `'0.0'`, `'0.00'`, etc. — all semantically\n // \"not quoting\". Previous check only caught the bare `'0'` form, letting\n // `'0.0'` slip through and produce a zero-valued targetAmount that the\n // sender's rate-deviation guard could not catch (expectedTargetAmount=0n\n // skips the deviation math). (Story 12.5 code-review pass #3.)\n if (/^0(\\.0+)?$/.test(rate)) {\n throw new SwapHandlerError('Rate is zero (pair not quoting)');\n }\n if (sourceAmount <= 0n) {\n throw new SwapHandlerError(\n `sourceAmount must be positive, got ${sourceAmount}`\n );\n }\n\n const dotIdx = rate.indexOf('.');\n const integerPart = dotIdx === -1 ? rate : rate.slice(0, dotIdx);\n const fractionalPart = dotIdx === -1 ? '' : rate.slice(dotIdx + 1);\n\n const rateNumerator = BigInt(integerPart + fractionalPart);\n const rateDenominator = 10n ** BigInt(fractionalPart.length);\n\n const scaleUp = 10n ** BigInt(toScale);\n const scaleDown = 10n ** BigInt(fromScale);\n\n return (\n (sourceAmount * rateNumerator * scaleUp) / (rateDenominator * scaleDown)\n );\n}\n\n// ---------------------------------------------------------------------------\n// findSwapPair helper (AC-7)\n// ---------------------------------------------------------------------------\n\n/**\n * Find the `SwapPair` identified by the rumor's `swap-from` / `swap-to` tags.\n *\n * Each tag value is parsed as `<assetCode>:<chain>`, split on the FIRST `:`\n * so multi-segment chain IDs like `evm:base:8453` remain intact as the chain\n * portion. Returns `null` for any malformed/missing tag — the handler\n * interprets `null` as \"unsupported pair\" and rejects via ILP F06.\n */\nexport function findSwapPair(\n rumor: UnsignedEvent,\n pairs: SwapPair[]\n): SwapPair | null {\n const fromTag = findTagValue(rumor, 'swap-from');\n const toTag = findTagValue(rumor, 'swap-to');\n\n if (!fromTag || !toTag) return null;\n\n const fromParts = splitAssetChain(fromTag);\n const toParts = splitAssetChain(toTag);\n if (!fromParts || !toParts) return null;\n\n for (const pair of pairs) {\n if (\n pair.from.assetCode === fromParts.assetCode &&\n pair.from.chain === fromParts.chain &&\n pair.to.assetCode === toParts.assetCode &&\n pair.to.chain === toParts.chain\n ) {\n return pair;\n }\n }\n return null;\n}\n\nfunction findTagValue(\n rumor: UnsignedEvent,\n tagName: string\n): string | undefined {\n if (!Array.isArray(rumor.tags)) return undefined;\n for (const t of rumor.tags) {\n if (Array.isArray(t) && t[0] === tagName && typeof t[1] === 'string') {\n return t[1];\n }\n }\n return undefined;\n}\n\nfunction splitAssetChain(\n raw: string\n): { assetCode: string; chain: string } | null {\n const idx = raw.indexOf(':');\n if (idx <= 0 || idx === raw.length - 1) return null;\n const assetCode = raw.slice(0, idx);\n const chain = raw.slice(idx + 1);\n if (!assetCode || !chain) return null;\n return { assetCode, chain };\n}\n\n// ---------------------------------------------------------------------------\n// Story 12.9 AC-2 / AC-8 — chain-recipient format validation\n// ---------------------------------------------------------------------------\n//\n// Duplicated (intentionally small) from `stream-swap.ts` to avoid a circular\n// module cycle (`stream-swap` imports `applyRate` from this file). Guardrail\n// 8.5 sanctions local duplication rather than introducing a shared helper\n// package. Rules MUST match the sender-side validator byte-for-byte.\n\nconst SWAP_HANDLER_EVM_ADDRESS_REGEX = /^0x[0-9a-f]{40}$/;\nconst SWAP_HANDLER_BASE58_REGEX = /^[1-9A-HJ-NP-Za-km-z]+$/;\n\n/**\n * Validate a chain-recipient address against `chain`. MUST remain in sync\n * with `validateChainAddress(value, chain, 'address')` in `stream-swap.ts`.\n */\nexport function validateChainRecipient(value: string, chain: string): boolean {\n if (typeof value !== 'string' || value.length === 0) return false;\n if (chain.startsWith('evm:')) {\n return SWAP_HANDLER_EVM_ADDRESS_REGEX.test(value);\n }\n if (chain.startsWith('solana:')) {\n if (!SWAP_HANDLER_BASE58_REGEX.test(value)) return false;\n if (value.length < 32 || value.length > 44) return false;\n try {\n return base58Decode(value).length === 32;\n } catch {\n return false;\n }\n }\n if (chain.startsWith('mina:')) {\n return SWAP_HANDLER_BASE58_REGEX.test(value) && value.length >= 32;\n }\n return value.length > 0;\n}\n\n/**\n * Extract the sender-supplied chain-recipient address from the rumor's\n * `chain-recipient` tag (Story 12.9 AC-8). Returns `null` if the tag is\n * missing or malformed for `chain`.\n */\nexport function findChainRecipient(\n rumor: UnsignedEvent,\n chain: string\n): string | null {\n const raw = findTagValue(rumor, 'chain-recipient');\n if (!raw) return null;\n if (!validateChainRecipient(raw, chain)) return null;\n return raw;\n}\n\n// ---------------------------------------------------------------------------\n// No-op logger\n// ---------------------------------------------------------------------------\n\nconst noop = (): void => undefined;\nconst NOOP_LOGGER: SwapHandlerLogger = {\n debug: noop,\n info: noop,\n warn: noop,\n error: noop,\n};\n\n// ---------------------------------------------------------------------------\n// createSwapHandler factory (AC-3..AC-12)\n// ---------------------------------------------------------------------------\n\n/**\n * Construct a kind:1059 Swap inbound-swap handler.\n *\n * The returned `Handler` is a pure closure over `config`; two calls with the\n * same config yield two independent-but-equivalent handlers. Register via\n * `node.handlers.on(1059, handler)` (Story 12.7).\n *\n * @throws {SwapHandlerError} At construction time if config is malformed.\n */\nexport function createSwapHandler(config: CreateSwapHandlerConfig): Handler {\n // Construction-time validation (pre-empt Story 12.2 retro finding #1).\n if (\n !(config.recipientSecretKey instanceof Uint8Array) ||\n config.recipientSecretKey.length !== 32\n ) {\n throw new SwapHandlerError(\n 'recipientSecretKey must be a 32-byte Uint8Array'\n );\n }\n if (!Array.isArray(config.swapPairs)) {\n throw new SwapHandlerError('swapPairs must be an array');\n }\n if (\n !config.claimIssuer ||\n typeof config.claimIssuer.issueClaim !== 'function'\n ) {\n throw new SwapHandlerError(\n 'claimIssuer must implement issueClaim(params): Promise<IssueClaimResult>'\n );\n }\n\n const logger = config.logger ?? NOOP_LOGGER;\n\n // Story 12.8 AC-14: when the operator does not supply a custom set,\n // default to a bounded access-order-LRU set. Operator-supplied sets are\n // used verbatim (we do not second-guess the operator's choice of\n // persistence / bounding).\n const seenPacketIds: SeenPacketIdsLike =\n config.seenPacketIds ?? new BoundedSeenPacketIds();\n\n return async (ctx) => {\n // AC-4: defensive kind guard. HandlerRegistry.dispatch already routes by\n // kind, but a mis-registered handler should fail loudly rather than\n // silently mutate unrelated traffic.\n if (ctx.kind !== 1059) {\n // Generic reject message -- do not leak handler role to the caller.\n // A swap handler registered for non-1059 traffic is a mis-configuration;\n // the caller doesn't need to know which handler fielded the packet.\n return ctx.reject('F02', 'Unreachable');\n }\n\n // Defense-in-depth: reject non-positive amounts eagerly with a dedicated\n // code so the sender gets an unambiguous error (otherwise applyRate would\n // throw and surface as the generic T00 \"Rate conversion error\"). ILP\n // connectors already enforce amount > 0, but we double-check at the\n // protocol boundary.\n if (typeof ctx.amount !== 'bigint' || ctx.amount <= 0n) {\n logger.warn({\n event: 'swap_handler.invalid_amount',\n destination: ctx.destination,\n });\n return ctx.reject('F01', 'Invalid amount');\n }\n\n // AC-4 / AC-5 / AC-6: decode and unwrap. `ctx.toon` is the base64 string\n // lifted verbatim from `ilpPrepare.data` (which `buildIlpPrepare` produces\n // by base64-encoding the raw TOON binary). Single decode -> TOON bytes.\n //\n // NOTE: `ctx.pubkey` is the OUTER ephemeral gift-wrap pubkey, NOT the real\n // sender. The real sender comes from the seal inside\n // unwrapSwapPacketFromToon. Do not use `ctx.pubkey` for sender identity.\n if (typeof ctx.toon !== 'string' || ctx.toon.length === 0) {\n logger.warn({\n event: 'swap_handler.invalid_toon',\n destination: ctx.destination,\n });\n return ctx.reject('F01', 'Invalid gift wrap');\n }\n let rumor: UnsignedEvent;\n let senderPubkey: string;\n try {\n const toonData = new Uint8Array(Buffer.from(ctx.toon, 'base64'));\n ({ rumor, senderPubkey } = unwrapSwapPacketFromToon({\n toonData,\n recipientSecretKey: config.recipientSecretKey,\n }));\n } catch (err) {\n if (err instanceof GiftWrapError) {\n logger.warn({\n event: 'swap_handler.unwrap_failed',\n destination: ctx.destination,\n error: err.message,\n });\n return ctx.reject('F01', 'Invalid gift wrap');\n }\n logger.error({\n event: 'swap_handler.unwrap_unexpected_error',\n destination: ctx.destination,\n error: err instanceof Error ? err.message : String(err),\n });\n return ctx.reject('F01', 'Invalid gift wrap');\n }\n\n // AC-7: pair lookup\n const pair = findSwapPair(rumor, config.swapPairs);\n if (!pair) {\n logger.debug({\n event: 'swap_handler.unsupported_pair',\n destination: ctx.destination,\n });\n return ctx.reject('F06', 'Unsupported swap pair');\n }\n\n // Story 12.9 AC-1 / AC-8: extract and validate the `chain-recipient`\n // tag from the inner rumor. Missing or malformed values are treated as\n // a malformed rumor (T00, consistent with other opaque-internal-error\n // paths) — the sender may retry with a corrected address.\n const chainRecipient = findChainRecipient(rumor, pair.to.chain);\n if (!chainRecipient) {\n logger.debug({\n event: 'swap_handler.malformed_rumor',\n destination: ctx.destination,\n reason: 'missing_or_malformed_chain_recipient',\n chain: pair.to.chain,\n });\n return ctx.reject('T00', 'Internal error');\n }\n\n // AC-11: replay protection check. We RESERVE the packetId synchronously\n // here (before the first `await`) so that two concurrent invocations with\n // an identical packet ID cannot both pass the `has()` gate. Because the\n // JS event loop is cooperative, the check-and-add pair is atomic relative\n // to other microtasks as long as it straddles no `await`. If issuance or\n // encryption later fails, we release the reservation so the sender can\n // legitimately retry (AC-11 requires retries of rejected packets).\n // Story 12.8 AC-14: replay check always runs (against the bounded default\n // when the operator did not supply a custom set).\n const packetId: string = computePacketId(senderPubkey, ctx.amount, rumor);\n if (seenPacketIds.has(packetId)) {\n logger.debug({\n event: 'swap_handler.duplicate_packet',\n packetId,\n });\n return ctx.reject('F04', 'Duplicate packet');\n }\n // Reserve eagerly to close the concurrent check-then-add race.\n seenPacketIds.add(packetId);\n\n // Helper: release the replay reservation on failure so the sender can retry.\n const releaseReservation = (): void => {\n seenPacketIds.delete(packetId);\n };\n\n // AC-9 rate resolution (optional live hook per D12-006).\n let rate: string;\n try {\n rate = config.rateProvider ? await config.rateProvider(pair) : pair.rate;\n } catch (err) {\n logger.error({\n event: 'swap_handler.rate_provider_failed',\n error: err instanceof Error ? err.message : String(err),\n });\n releaseReservation();\n return ctx.reject('T00', 'Rate provider error');\n }\n\n // AC-8: apply rate (BigInt throughout).\n let targetAmount: bigint;\n try {\n targetAmount = applyRate({\n sourceAmount: ctx.amount,\n fromScale: pair.from.assetScale,\n toScale: pair.to.assetScale,\n rate,\n });\n logger.debug({\n event: 'swap_handler.rate_applied',\n sourceAmount: ctx.amount.toString(),\n targetAmount: targetAmount.toString(),\n rate,\n });\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n logger.warn({\n event: 'swap_handler.rate_conversion_failed',\n error: msg,\n });\n // Do NOT surface the internal rate string / validation detail to the\n // sender -- return a generic privacy-preserving message.\n releaseReservation();\n return ctx.reject('T00', 'Rate conversion error');\n }\n\n // AC-9: delegate to claim issuer\n let claim: Uint8Array;\n let claimId: string | undefined;\n let settlementChannelId: string | undefined;\n let settlementNonce: bigint | undefined;\n let settlementCumulative: bigint | undefined;\n let settlementRecipient: string | undefined;\n let settlementSwapSigner: string | undefined;\n try {\n const result = await config.claimIssuer.issueClaim({\n sourceAmount: ctx.amount,\n targetAmount,\n pair,\n senderPubkey,\n chainRecipient,\n rumor,\n });\n claim = result.claim;\n claimId = result.claimId;\n settlementChannelId = result.channelId;\n settlementNonce = result.nonce;\n settlementCumulative = result.cumulativeAmount;\n settlementRecipient = result.recipient;\n settlementSwapSigner = result.swapSignerAddress;\n } catch (err) {\n const code = (err as { code?: unknown })?.code;\n const message = err instanceof Error ? err.message : String(err);\n if (code === 'INSUFFICIENT_INVENTORY' || /insufficient/i.test(message)) {\n logger.warn({\n event: 'swap_handler.insufficient_inventory',\n error: message,\n });\n releaseReservation();\n return ctx.reject('T04', 'Insufficient liquidity');\n }\n logger.error({\n event: 'swap_handler.issuer_failed',\n error: message,\n });\n releaseReservation();\n return ctx.reject('T00', 'Internal error');\n }\n\n // AC-10: NIP-44 encrypt the claim (Story 12.2 handles ephemeral-key zeroing).\n let ciphertext: Uint8Array;\n let ephemeralPubkey: string;\n try {\n const enc = encryptFulfillClaim({ claimData: claim, senderPubkey });\n ciphertext = enc.ciphertext;\n ephemeralPubkey = enc.ephemeralPubkey;\n } catch (err) {\n logger.error({\n event: 'swap_handler.encrypt_failed',\n error: err instanceof Error ? err.message : String(err),\n });\n releaseReservation();\n return ctx.reject('T00', 'Internal error');\n }\n\n // AC-11: packetId was reserved pre-issuance to close the concurrent\n // check-then-add race; it remains committed on this success path.\n\n const claimBase64 = Buffer.from(ciphertext).toString('base64');\n logger.info({\n event: 'swap_handler.claim_issued',\n claimId,\n ephemeralPubkey,\n });\n\n // Story 12.5: emit the Swap-computed `targetAmount` (decimal string) so\n // the sender can run an end-to-end rate-deviation check without having to\n // parse the opaque chain-specific `claimBytes`. This is additive and\n // backward compatible — legacy senders that ignore the field get the\n // same {claim, ephemeralPubkey, claimId?} shape they always did.\n const metadata: Record<string, unknown> = {\n claim: claimBase64,\n ephemeralPubkey,\n targetAmount: targetAmount.toString(),\n };\n if (claimId !== undefined) metadata['claimId'] = claimId;\n // Story 12.6: thread settlement-context fields through when the claim\n // issuer emits them. All-or-nothing: a Swap that supplies settlement\n // fields MUST supply all five, since the sender's FULFILL decoder\n // enforces all-present-or-all-absent.\n if (\n settlementChannelId !== undefined &&\n settlementNonce !== undefined &&\n settlementCumulative !== undefined &&\n settlementRecipient !== undefined &&\n settlementSwapSigner !== undefined\n ) {\n metadata['channelId'] = settlementChannelId;\n metadata['nonce'] = settlementNonce.toString();\n metadata['cumulativeAmount'] = settlementCumulative.toString();\n metadata['recipient'] = settlementRecipient;\n metadata['swapSignerAddress'] = settlementSwapSigner;\n }\n\n return ctx.accept(metadata);\n };\n}\n\n// ---------------------------------------------------------------------------\n// Replay packet ID hash\n// ---------------------------------------------------------------------------\n\n/**\n * Compute a deterministic packet ID for replay protection.\n *\n * Uses explicit length-prefix delimiters between the three inputs so that\n * `('ab','c12','3')` and `('abc','12','3')` produce distinct digests. Without\n * delimiters, variable-width string concatenation is ambiguous under hashing.\n */\nfunction computePacketId(\n senderPubkey: string,\n sourceAmount: bigint,\n rumor: UnsignedEvent\n): string {\n const rumorId = (rumor as UnsignedEvent & { id?: string }).id ?? '';\n const hash = createHash('sha256');\n const parts = [senderPubkey, sourceAmount.toString(), rumorId];\n for (const p of parts) {\n // 4-byte big-endian length prefix followed by UTF-8 bytes.\n const buf = Buffer.from(p, 'utf8');\n const lenBuf = Buffer.alloc(4);\n lenBuf.writeUInt32BE(buf.length, 0);\n hash.update(lenBuf);\n hash.update(buf);\n }\n return hash.digest('hex');\n}\n","/**\n * Sender-side `streamSwap()` API (Story 12.5).\n *\n * Drives the sender side of the Token Swap Primitive: chunks a total source\n * amount into N sender-chosen packets, wraps each with `wrapSwapPacketToToon()`\n * using a fresh ephemeral gift-wrap key per packet (D12-003 / risk R-006),\n * sends each via `ToonClient.sendSwapPacket(...)`, decrypts each FULFILL's\n * NIP-44-encrypted claim with `decryptFulfillClaim()`, accumulates the\n * decrypted claims into an ordered array, invokes a per-packet rate monitoring\n * callback, and supports pause / resume / stop / AbortSignal so the sender\n * can abort when rate drifts past tolerance.\n *\n * Composition story: consumes Stories 12.1 (SwapPair type), 12.2 (gift wrap\n * primitives), 12.3 (handler wire contract). Produces `AccumulatedClaim[]`\n * consumed by Story 12.6 (`buildSettlementTx()`).\n *\n * Design decisions:\n * - `streamSwap()` does NOT throw on mid-stream failure. Caller gets\n * `StreamSwapResult` with `state` and `abortReason`. Only construction-time\n * validation throws synchronously.\n * - `streamSwap()` does NOT retry individual packets. BTP-level connection\n * retries are handled inside `BtpRuntimeClient`; application-layer retry\n * (e.g., T04 insufficient inventory) is a future-story concern.\n * - Rate deviation math stays BigInt throughout. The `effectiveRate` on\n * `PacketProgress` is a display-only `number` (Epic 11 retro MAX_SAFE_INTEGER\n * guard).\n *\n * @module\n */\n\nimport { getPublicKey } from 'nostr-tools/pure';\nimport type { UnsignedEvent } from 'nostr-tools/pure';\nimport type { SwapPair } from '@toon-protocol/core';\n\nimport { StreamSwapError } from './errors.js';\nimport { wrapSwapPacketToToon, decryptFulfillClaim } from './gift-wrap.js';\nimport { base58Decode } from './identity.js';\nimport { applyRate } from './swap-handler.js';\n\n// ---------------------------------------------------------------------------\n// Minimal structural shapes (avoid cross-package type imports)\n// ---------------------------------------------------------------------------\n\n/** Minimal `IlpSendResult` shape — mirrors `packages/client/src/types.ts`. */\ninterface IlpSendResultLike {\n accepted: boolean;\n data?: string;\n code?: string;\n message?: string;\n}\n\n/**\n * Minimal `SignedBalanceProof` structural type.\n *\n * We avoid importing from `@toon-protocol/client` so the SDK package does not\n * gain a runtime dep on the client package. The sender layer only forwards\n * this opaquely to `ToonClient.sendSwapPacket`.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any -- opaque forwarded type\ntype SignedBalanceProofLike = any;\n\n/**\n * The subset of `ToonClient` that `streamSwap()` requires. This narrow shape\n * exists so that consumers can pass a real `ToonClient` OR a mock without\n * importing the whole class.\n */\nexport interface StreamSwapClient {\n sendSwapPacket(params: {\n destination: string;\n amount: bigint;\n toonData: Uint8Array;\n timeout?: number;\n claim?: SignedBalanceProofLike;\n }): Promise<IlpSendResultLike>;\n getPublicKey(): string;\n}\n\n// ---------------------------------------------------------------------------\n// Public interfaces (AC-2, AC-7, AC-8, AC-9, AC-10)\n// ---------------------------------------------------------------------------\n\n/**\n * Parameters for {@link streamSwap} / {@link streamSwapControlled}.\n *\n * @stable — downstream Stories 12.6 and 12.8 depend on this shape.\n */\nexport interface StreamSwapParams {\n /** SDK client with BTP wiring (see {@link StreamSwapClient}). */\n client: StreamSwapClient;\n /** Swap's 64-char lowercase hex pubkey (recipient of gift wrap). */\n swapPubkey: string;\n /** Swap's ILP destination address (e.g., 'g.toon.swap1'). */\n swapIlpAddress: string;\n /** The `SwapPair` being executed (from kind:10032 discovery, Story 12.1). */\n pair: SwapPair;\n /** Sender's 32-byte secp256k1 secret key. Used for seal signing AND FULFILL decryption. */\n senderSecretKey: Uint8Array;\n /**\n * Sender's chain-specific payout address for `pair.to.chain` (Story 12.9 AC-4).\n *\n * REQUIRED. The Nostr `senderPubkey` (identity layer) cannot be used as the\n * on-chain `recipient` in a balance-proof because they are cryptographically\n * independent keys (D12-011). For `evm:*` chains this must be a 20-byte\n * lowercased `0x`-prefixed hex address; for `solana:*` a base58 pubkey that\n * decodes to 32 bytes; for `mina:*` a base58 public-key string. The value\n * is validated at `streamSwapControlled()` entry via `validateChainAddress`\n * and echoed on every rumor as the `chain-recipient` tag (AC-6).\n */\n chainRecipient: string;\n /** Total source-asset amount to swap (source micro-units). */\n totalAmount: bigint;\n /** Even-split packet count. EXACTLY ONE of this or `packetAmounts` is required. */\n packetCount?: number;\n /** Explicit per-packet amounts. EXACTLY ONE of this or `packetCount` is required. */\n packetAmounts?: readonly bigint[];\n /** Source-asset balance proof claim. Required unless ChannelManager is wired. */\n claim?: SignedBalanceProofLike;\n /** Rate monitoring callback (fires after each accepted FULFILL). */\n onPacket?: RateMonitorCallback;\n /** Rate deviation threshold (decimal, e.g., 0.02 = 2%). */\n rateDeviationThreshold?: number;\n /** Per-packet timeout in ms. Default 30000. */\n packetTimeoutMs?: number;\n /** Optional AbortSignal. */\n signal?: AbortSignal;\n /**\n * Optional pino-compatible logger. Defaults to a no-op.\n *\n * Note: `streamSwap` calls each method with a single structured-event object\n * (e.g., `logger.warn({ event: 'stream_swap.packet_rejected', code, ... })`).\n * Pino accepts this form (the object is logged as top-level fields and the\n * message is left empty); other loggers that expect `(msg, meta)` should\n * wrap accordingly.\n */\n logger?: {\n debug: (...a: unknown[]) => void;\n info: (...a: unknown[]) => void;\n warn: (...a: unknown[]) => void;\n error: (...a: unknown[]) => void;\n };\n}\n\n/**\n * Rate monitoring callback. Fires after each successful FULFILL decryption,\n * before the next packet is sent. If the callback throws or rejects, the\n * stream is treated as stopped.\n */\nexport type RateMonitorCallback = (\n progress: PacketProgress\n) => void | Promise<void>;\n\n/**\n * Progress payload delivered to {@link RateMonitorCallback}. Frozen to\n * prevent callback mutation.\n */\nexport interface PacketProgress {\n /** 0-indexed packet number within this streamSwap invocation. */\n index: number;\n /** Total number of packets scheduled. */\n total: number;\n /** Source-asset amount sent for this packet (micro-units). */\n sourceAmount: bigint;\n /** Target-asset claim amount derived for this packet (micro-units). */\n targetAmount: bigint;\n /** Rate advertised on the `SwapPair` at swap start (decimal string). */\n advertisedRate: string;\n /** Effective rate for this packet as JS Number (target / source in whole units). Display-only. */\n effectiveRate: number;\n /** Absolute deviation from advertisedRate as a decimal (e.g., 0.0125 = 1.25%). */\n rateDeviation: number;\n /** Cumulative source sent across accepted packets so far (including this one). */\n cumulativeSource: bigint;\n /** Cumulative target received so far (including this one). */\n cumulativeTarget: bigint;\n /** Controller state at callback time. */\n state: 'running' | 'paused' | 'stopped';\n}\n\n/**\n * An accumulated claim successfully harvested from a single packet.\n *\n * @stable — Story 12.6 (`buildSettlementTx()`) depends on this shape.\n * Breaking changes require a coordinated migration.\n *\n * Story 12.6 ADDITIVE extension: the settlement-context fields\n * `channelId`, `nonce`, `cumulativeAmount`, `recipient`, and\n * `swapSignerAddress` are marked optional (`?:`) for one story-cycle of\n * backward compat but are REQUIRED in practice: Story 12.6's\n * `buildSettlementTx()` throws `MISSING_SETTLEMENT_METADATA` when any of\n * these are absent.\n */\nexport interface AccumulatedClaim {\n /** 0-indexed position in the swap's packet stream. */\n packetIndex: number;\n /** Source-asset amount sent for this packet (micro-units). */\n sourceAmount: bigint;\n /**\n * Target-asset amount claimed (micro-units).\n *\n * **Source of truth caveat:** This is the expected target amount computed\n * by `applyRate(pair.rate)`. The actual signed-claim amount lives inside\n * `claimBytes`; Story 12.6 is responsible for parsing `claimBytes` per\n * chain and verifying the on-wire signed amount equals this expected amount.\n */\n targetAmount: bigint;\n /** Decrypted signed claim bytes. Chain-specific encoding per Story 12.4. */\n claimBytes: Uint8Array;\n /** Swap's ephemeral pubkey from the FULFILL (64-char lowercase hex). */\n swapEphemeralPubkey: string;\n /** Optional Swap-side claim ID (passed through from handler metadata). */\n claimId?: string;\n /** Swap pair this claim was priced against (copy of `pair` for settlement-time routing). */\n pair: SwapPair;\n /** Unix ms timestamp when this claim was accepted. */\n receivedAt: number;\n // --- Story 12.6 settlement-context fields (additive) ---\n /** Channel identifier on the target chain (lowercase hex with 0x prefix for EVM; base58 for Solana). */\n channelId?: string;\n /** Balance-proof nonce (decimal string). Monotonically increasing within a channel. */\n nonce?: string;\n /** Cumulative transferred amount on the channel (target micro-units, decimal string). */\n cumulativeAmount?: string;\n /** Recipient address (the sender's target-asset address). */\n recipient?: string;\n /** Swap's on-chain signer address. */\n swapSignerAddress?: string;\n}\n\n/**\n * Aggregate result of a `streamSwap()` invocation.\n *\n * @stable — Stories 12.6 and 12.8 depend on this shape.\n */\nexport interface StreamSwapResult {\n state: 'completed' | 'failed' | 'stopped';\n claims: AccumulatedClaim[];\n rejections: {\n packetIndex: number;\n sourceAmount: bigint;\n code: string;\n message: string;\n }[];\n errors: { packetIndex: number; cause: Error }[];\n abortReason:\n | 'complete'\n | 'aborted'\n | 'stopped'\n | 'callback-stop'\n | 'callback-throw'\n | 'rate-deviation'\n | 'all-rejected';\n cumulativeSource: bigint;\n cumulativeTarget: bigint;\n packetsSent: number;\n packetsScheduled: number;\n}\n\n/** External controller returned by {@link streamSwapControlled}. */\nexport interface StreamSwapController {\n pause(): void;\n resume(): void;\n stop(): void;\n readonly state: 'running' | 'paused' | 'stopped' | 'completed' | 'failed';\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nconst RATE_REGEX = /^(0|[1-9]\\d*)(\\.\\d+)?$/;\nconst HEX64_REGEX = /^[0-9a-f]{64}$/;\n// Strict base64: only `=` padding at the end, 0/1/2 pad chars. The character\n// class is intentionally restrictive — previous form `/^[A-Za-z0-9+/=]+$/`\n// accepted `=` anywhere and non-multiple-of-4 lengths, which could funnel\n// malformed payloads into `Buffer.from`/`JSON.parse` and surface confusing\n// errors. (Story 12.5 code-review pass #3.)\nconst BASE64_REGEX = /^[A-Za-z0-9+/]+={0,2}$/;\n\n/** True iff `s` is a non-empty, base64-shaped string of length multiple of 4. */\nfunction isBase64(s: string): boolean {\n if (s.length === 0 || s.length % 4 !== 0) return false;\n return BASE64_REGEX.test(s);\n}\n\n/** Length-split `total` into `count` bigints. Remainder absorbed on last element. */\nfunction chunkAmount(total: bigint, count: number): bigint[] {\n if (\n !Number.isInteger(count) ||\n count <= 0 ||\n count > Number.MAX_SAFE_INTEGER\n ) {\n throw new StreamSwapError(\n 'INVALID_CHUNKING',\n `packetCount must be a positive integer, got ${count}`\n );\n }\n if (total < BigInt(count)) {\n throw new StreamSwapError(\n 'INVALID_CHUNKING',\n `totalAmount (${total}) must be >= packetCount (${count}) so per-packet amount >= 1`\n );\n }\n const base = total / BigInt(count);\n const remainder = total - base * BigInt(count);\n const out: bigint[] = new Array(count);\n for (let i = 0; i < count; i++) out[i] = base;\n const last = out[count - 1] ?? 0n;\n out[count - 1] = last + remainder;\n return out;\n}\n\n/** Build the kind:20032 unsigned \"swap rumor\" event per AC-4.\n *\n * Story 12.9 AC-1/AC-6: the returned rumor MUST include a `chain-recipient`\n * tag carrying the sender-supplied chain-format payout address for\n * `pair.to.chain`. The value is echoed verbatim per packet — no case-folding\n * or transformation beyond what the sender-side `validateChainAddress`\n * accepts.\n */\nfunction buildSwapRumor(input: {\n senderPubkey: string;\n pair: SwapPair;\n sourceAmount: bigint;\n packetIndex: number;\n totalPackets: number;\n nonce: Uint8Array;\n createdAt: number;\n chainRecipient: string;\n}): UnsignedEvent {\n const {\n senderPubkey,\n pair,\n sourceAmount,\n packetIndex,\n totalPackets,\n nonce,\n createdAt,\n chainRecipient,\n } = input;\n return {\n kind: 20032,\n pubkey: senderPubkey,\n content: '',\n created_at: createdAt,\n tags: [\n ['swap-from', `${pair.from.assetCode}:${pair.from.chain}`],\n ['swap-to', `${pair.to.assetCode}:${pair.to.chain}`],\n ['amount', sourceAmount.toString()],\n ['seq', String(packetIndex), String(totalPackets)],\n ['nonce', Buffer.from(nonce).toString('hex')],\n ['chain-recipient', chainRecipient],\n ],\n };\n}\n\nconst EVM_CHANNEL_ID_REGEX = /^0x[0-9a-f]{64}$/;\nconst EVM_ADDRESS_REGEX = /^0x[0-9a-f]{40}$/;\nconst DECIMAL_UINT_REGEX = /^(0|[1-9]\\d*)$/;\n// Permissive base58 charset (Bitcoin alphabet — no 0, O, I, l).\nconst BASE58_REGEX = /^[1-9A-HJ-NP-Za-km-z]+$/;\n\n/**\n * Validate a chain-specific address or channelId string. `chain` is the\n * `pair.to.chain` string (e.g., `'evm:base:8453'`, `'solana:mainnet'`).\n *\n * Returns true for valid formats; false otherwise.\n */\nexport function validateChainAddress(\n value: string,\n chain: string,\n kind: 'channelId' | 'address'\n): boolean {\n if (chain.startsWith('evm:')) {\n // #153: viem / EIP-55 emits checksummed (mixed-case) addresses, and\n // callers commonly pass a checksummed `chainRecipient`. Lowercase-normalize\n // before the strict-lowercase-hex regex so a valid checksummed address (or\n // channelId) is accepted instead of being spuriously rejected with\n // INVALID_CHAIN_RECIPIENT / FULFILL_DECODE_FAILED.\n const normalized = value.toLowerCase();\n if (kind === 'channelId') return EVM_CHANNEL_ID_REGEX.test(normalized);\n return EVM_ADDRESS_REGEX.test(normalized);\n }\n if (chain.startsWith('solana:')) {\n // AC-3: base58 decode MUST succeed AND length MUST be 32 bytes. A pure\n // regex + char-length sanity check is too loose — a malformed \"32-char\"\n // base58 string may decode to 22–24 bytes and slip through.\n if (!BASE58_REGEX.test(value)) return false;\n if (value.length < 32 || value.length > 44) return false;\n try {\n return base58Decode(value).length === 32;\n } catch {\n return false;\n }\n }\n if (chain.startsWith('mina:')) {\n return BASE58_REGEX.test(value) && value.length >= 32;\n }\n // Unknown chain — permit; settlement layer will surface UNSUPPORTED_CHAIN.\n return value.length > 0;\n}\n\n/**\n * Decode the FULFILL response `data` (base64-encoded JSON metadata) into\n * the `{ claim, ephemeralPubkey, claimId?, targetAmount?, ...settlement }`\n * shape.\n *\n * Story 12.6 extension: also parses the settlement-context fields\n * (`channelId`, `nonce`, `cumulativeAmount`, `recipient`, `swapSignerAddress`).\n * These are OPTIONAL and best-effort (#153): each is threaded through only\n * when it is a well-formed string for the target chain; an absent or malformed\n * settlement field is silently dropped rather than failing the whole decode,\n * so a fulfilled swap still surfaces its signed `claim` + `ephemeralPubkey`.\n * Only `claim` and `ephemeralPubkey` are strictly required.\n *\n * @param chain Optional `pair.to.chain` string for per-chain format validation\n * of channelId / recipient / swapSignerAddress. When omitted, format checks\n * fall back to a length-only sanity check.\n */\nfunction decodeFulfillMetadata(\n data: string | undefined,\n chain?: string\n): {\n claim: string;\n ephemeralPubkey: string;\n claimId?: string;\n /** Optional Swap-reported actual target amount (decimal string). Used for rate deviation when present. */\n targetAmount?: string;\n channelId?: string;\n nonce?: string;\n cumulativeAmount?: string;\n recipient?: string;\n swapSignerAddress?: string;\n} {\n if (data === undefined || data === null || data === '') {\n throw new StreamSwapError('FULFILL_DECODE_FAILED', 'FULFILL data missing');\n }\n // Quick-fail for obvious non-base64 input. Rejects '@' etc. and strings\n // that aren't a multiple-of-4 length.\n if (!isBase64(data)) {\n throw new StreamSwapError(\n 'FULFILL_DECODE_FAILED',\n 'FULFILL data is not valid base64'\n );\n }\n let jsonBytes: Buffer;\n try {\n jsonBytes = Buffer.from(data, 'base64');\n } catch (err) {\n throw new StreamSwapError(\n 'FULFILL_DECODE_FAILED',\n `FULFILL data base64 decode failed: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err }\n );\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(jsonBytes.toString('utf8'));\n } catch (err) {\n throw new StreamSwapError(\n 'FULFILL_DECODE_FAILED',\n `FULFILL data JSON parse failed: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err }\n );\n }\n if (!parsed || typeof parsed !== 'object') {\n throw new StreamSwapError(\n 'FULFILL_DECODE_FAILED',\n 'FULFILL metadata is not an object'\n );\n }\n const obj = parsed as Record<string, unknown>;\n const claim = obj['claim'];\n const ephemeralPubkey = obj['ephemeralPubkey'];\n if (typeof claim !== 'string' || !isBase64(claim)) {\n throw new StreamSwapError(\n 'FULFILL_DECODE_FAILED',\n 'FULFILL metadata.claim is missing or not base64 string'\n );\n }\n if (\n typeof ephemeralPubkey !== 'string' ||\n !HEX64_REGEX.test(ephemeralPubkey)\n ) {\n throw new StreamSwapError(\n 'FULFILL_DECODE_FAILED',\n 'FULFILL metadata.ephemeralPubkey is missing or not 64-char hex'\n );\n }\n const result: {\n claim: string;\n ephemeralPubkey: string;\n claimId?: string;\n targetAmount?: string;\n channelId?: string;\n nonce?: string;\n cumulativeAmount?: string;\n recipient?: string;\n swapSignerAddress?: string;\n } = {\n claim,\n ephemeralPubkey,\n };\n if (typeof obj['claimId'] === 'string') {\n result.claimId = obj['claimId'] as string;\n }\n // Swap-reported target amount (optional). MUST be a non-negative integer\n // decimal string — reject signed / fractional / non-numeric values so a\n // malicious or buggy Swap cannot poison `cumulativeTarget`, the deviation\n // calc, or the `AccumulatedClaim.targetAmount` surface that Story 12.6\n // settles against. Presence of the field with a malformed value is a\n // FULFILL_DECODE_FAILED — the sender cannot safely consume the metadata.\n if (obj['targetAmount'] !== undefined) {\n const ta = obj['targetAmount'];\n if (typeof ta !== 'string' || !/^(0|[1-9]\\d*)$/.test(ta)) {\n throw new StreamSwapError(\n 'FULFILL_DECODE_FAILED',\n 'FULFILL metadata.targetAmount must be a non-negative integer decimal string'\n );\n }\n result.targetAmount = ta;\n }\n // Story 12.6 settlement-context fields — OPTIONAL, best-effort (#153).\n //\n // These five fields are only consumed downstream by `buildSettlementTx()`\n // (Story 12.6), which performs on-chain target redemption — a flow that is\n // itself #82-bounded (synthetic devnet channels) and independently validates\n // every field it consumes before signing. They are NOT required to surface\n // the swap's signed claim to the caller.\n //\n // The previous contract was all-or-nothing AND hard-failed the entire FULFILL\n // decode (`FULFILL_DECODE_FAILED`) on any partial/malformed settlement field.\n // That rejected otherwise-valid swap FULFILLs whenever the swap's real\n // envelope carried, e.g., a cross-chain channelId (an EVM-style hex channelId\n // echoed on a `solana:`/`mina:` target) or a checksummed address — so a\n // fulfilled swap reported `state: failed` with an empty `claims[]`.\n //\n // New contract: thread each settlement field through ONLY when it is a\n // well-formed string for the target chain; silently drop any field that is\n // absent or malformed. A swap whose swap omits/garbles settlement metadata\n // still completes with the signed claim; `buildSettlementTx()` will then\n // surface `MISSING_SETTLEMENT_METADATA` at settlement time if the caller\n // actually attempts on-chain redemption with an incomplete bundle.\n //\n // `recipient` is special-cased: it is the anti-substitution security check in\n // `runLoop` (the swap must echo the sender-supplied `chainRecipient`). We thread\n // it through whenever it is a non-empty string — even if it fails the chain\n // format check — so the runLoop equality check still fires. The equality\n // comparison there is the real boundary; a format-only mismatch must not be\n // silently dropped (which would skip the check entirely).\n const channelId = obj['channelId'];\n if (\n typeof channelId === 'string' &&\n (!chain || validateChainAddress(channelId, chain, 'channelId'))\n ) {\n result.channelId = channelId;\n }\n const nonce = obj['nonce'];\n if (typeof nonce === 'string' && DECIMAL_UINT_REGEX.test(nonce)) {\n result.nonce = nonce;\n }\n const cumulativeAmount = obj['cumulativeAmount'];\n if (\n typeof cumulativeAmount === 'string' &&\n DECIMAL_UINT_REGEX.test(cumulativeAmount)\n ) {\n result.cumulativeAmount = cumulativeAmount;\n }\n const recipient = obj['recipient'];\n if (typeof recipient === 'string' && recipient.length > 0) {\n // Always thread a present recipient so the runLoop anti-substitution\n // equality check (`metadata.recipient === params.chainRecipient`) runs.\n result.recipient = recipient;\n }\n const swapSignerAddress = obj['swapSignerAddress'];\n if (\n typeof swapSignerAddress === 'string' &&\n (!chain || validateChainAddress(swapSignerAddress, chain, 'address'))\n ) {\n result.swapSignerAddress = swapSignerAddress;\n }\n return result;\n}\n\n/** Simple Deferred for pause/resume gating. */\nclass Deferred<T> {\n readonly promise: Promise<T>;\n resolve!: (v: T) => void;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- mirror Promise constructor\n reject!: (e: any) => void;\n constructor() {\n this.promise = new Promise<T>((res, rej) => {\n this.resolve = res;\n this.reject = rej;\n });\n }\n}\n\nconst noop = (): void => undefined;\nconst NOOP_LOGGER: NonNullable<StreamSwapParams['logger']> = {\n debug: noop,\n info: noop,\n warn: noop,\n error: noop,\n};\n\n// ---------------------------------------------------------------------------\n// Validation (AC-2) — synchronous, fires before any packet\n// ---------------------------------------------------------------------------\n\nfunction validateParams(params: StreamSwapParams): void {\n if (typeof params.totalAmount !== 'bigint' || params.totalAmount <= 0n) {\n throw new StreamSwapError(\n 'INVALID_AMOUNT',\n `totalAmount must be a positive bigint, got ${String(params.totalAmount)}`\n );\n }\n\n const hasCount = params.packetCount !== undefined;\n const hasAmounts = params.packetAmounts !== undefined;\n if (hasCount === hasAmounts) {\n throw new StreamSwapError(\n 'INVALID_CHUNKING',\n 'Exactly one of packetCount or packetAmounts must be provided'\n );\n }\n\n if (hasCount) {\n const c = params.packetCount as number;\n if (!Number.isInteger(c) || c <= 0) {\n throw new StreamSwapError(\n 'INVALID_CHUNKING',\n `packetCount must be a positive integer, got ${c}`\n );\n }\n if (BigInt(c) > params.totalAmount) {\n throw new StreamSwapError(\n 'INVALID_CHUNKING',\n `packetCount (${c}) exceeds totalAmount (${params.totalAmount}); per-packet amount would be < 1 micro-unit`\n );\n }\n } else {\n const arr = params.packetAmounts as readonly bigint[];\n if (!Array.isArray(arr) || arr.length === 0) {\n throw new StreamSwapError(\n 'INVALID_CHUNKING',\n 'packetAmounts must be a non-empty array'\n );\n }\n let sum = 0n;\n for (const a of arr) {\n if (typeof a !== 'bigint' || a <= 0n) {\n throw new StreamSwapError(\n 'INVALID_CHUNKING',\n `packetAmounts entries must be positive bigint, got ${String(a)}`\n );\n }\n sum += a;\n }\n if (sum !== params.totalAmount) {\n throw new StreamSwapError(\n 'INVALID_CHUNKING',\n `sum(packetAmounts) (${sum}) !== totalAmount (${params.totalAmount})`\n );\n }\n }\n\n if (\n !(params.senderSecretKey instanceof Uint8Array) ||\n params.senderSecretKey.length !== 32\n ) {\n throw new StreamSwapError(\n 'INVALID_STATE',\n 'senderSecretKey must be a 32-byte Uint8Array'\n );\n }\n\n if (\n typeof params.swapPubkey !== 'string' ||\n !HEX64_REGEX.test(params.swapPubkey)\n ) {\n throw new StreamSwapError(\n 'INVALID_STATE',\n 'swapPubkey must be a 64-char lowercase hex string'\n );\n }\n\n if (!params.pair || typeof params.pair !== 'object') {\n throw new StreamSwapError('INVALID_PAIR', 'pair is required');\n }\n // Guard the nested `from` / `to` shape before deep-reading `.assetScale` etc.\n // Otherwise malformed input produces a bare TypeError at `applyRate()` or\n // `buildSwapRumor()` instead of a categorized StreamSwapError.\n if (\n !params.pair.from ||\n typeof params.pair.from !== 'object' ||\n typeof params.pair.from.assetCode !== 'string' ||\n typeof params.pair.from.assetScale !== 'number' ||\n typeof params.pair.from.chain !== 'string'\n ) {\n throw new StreamSwapError(\n 'INVALID_PAIR',\n 'pair.from must have { assetCode: string, assetScale: number, chain: string }'\n );\n }\n if (\n !params.pair.to ||\n typeof params.pair.to !== 'object' ||\n typeof params.pair.to.assetCode !== 'string' ||\n typeof params.pair.to.assetScale !== 'number' ||\n typeof params.pair.to.chain !== 'string'\n ) {\n throw new StreamSwapError(\n 'INVALID_PAIR',\n 'pair.to must have { assetCode: string, assetScale: number, chain: string }'\n );\n }\n if (\n typeof params.pair.rate !== 'string' ||\n !RATE_REGEX.test(params.pair.rate)\n ) {\n throw new StreamSwapError(\n 'INVALID_PAIR',\n `pair.rate must match ${RATE_REGEX}, got ${params.pair.rate}`\n );\n }\n try {\n applyRate({\n sourceAmount: 1n,\n fromScale: params.pair.from.assetScale,\n toScale: params.pair.to.assetScale,\n rate: params.pair.rate,\n });\n } catch (err) {\n throw new StreamSwapError(\n 'INVALID_PAIR',\n `pair failed applyRate sanity check: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err }\n );\n }\n\n if (\n params.rateDeviationThreshold !== undefined &&\n (typeof params.rateDeviationThreshold !== 'number' ||\n !Number.isFinite(params.rateDeviationThreshold) ||\n params.rateDeviationThreshold < 0)\n ) {\n throw new StreamSwapError(\n 'INVALID_STATE',\n `rateDeviationThreshold must be a non-negative finite number, got ${params.rateDeviationThreshold}`\n );\n }\n\n // Story 12.9 AC-4 / AC-5: `chainRecipient` is REQUIRED and MUST validate\n // against `pair.to.chain`. Defense-in-depth for JS callers who bypass the\n // TS interface (the field is declared non-optional on StreamSwapParams).\n if (\n typeof params.chainRecipient !== 'string' ||\n params.chainRecipient.length === 0\n ) {\n throw new StreamSwapError(\n 'INVALID_STATE',\n 'chainRecipient must be a non-empty string (sender payout address for pair.to.chain)'\n );\n }\n if (\n !validateChainAddress(\n params.chainRecipient,\n params.pair.to.chain,\n 'address'\n )\n ) {\n throw new StreamSwapError(\n 'INVALID_CHAIN_RECIPIENT',\n `chainRecipient ${params.chainRecipient} is malformed for chain ${params.pair.to.chain}`\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Core: streamSwapControlled (AC-6, AC-7, AC-10)\n// ---------------------------------------------------------------------------\n\n/**\n * Drive a multi-packet swap against a Swap and return a `StreamSwapResult`.\n *\n * `streamSwap()` does NOT throw on mid-stream failure — inspect the result's\n * `state`, `abortReason`, `rejections[]`, and `errors[]` to diagnose.\n *\n * @example\n * ```ts\n * // Discover the SwapPair from the Swap's kind:10032 peer-info event (Story 12.1).\n * const result = await streamSwap({\n * client: toonClient,\n * swapPubkey: swap.pubkey,\n * swapIlpAddress: 'g.toon.swap1',\n * pair,\n * senderSecretKey,\n * totalAmount: 1_000_000n,\n * packetCount: 10,\n * onPacket: (p) => console.log('packet', p.index, 'rate', p.effectiveRate),\n * rateDeviationThreshold: 0.02,\n * });\n * // Feed result.claims into buildSettlementTx() from Story 12.6.\n * ```\n *\n * @throws {StreamSwapError} Only for construction-time validation failures\n * (INVALID_AMOUNT, INVALID_CHUNKING, INVALID_PAIR, INVALID_STATE).\n */\nexport async function streamSwap(\n params: StreamSwapParams\n): Promise<StreamSwapResult> {\n // Note: validation errors are thrown synchronously inside\n // `streamSwapControlled`. We wrap the call so construction-time throws\n // become Promise rejections for ergonomic `await` / `.rejects` handling.\n return streamSwapControlled(params).result;\n}\n\n/**\n * Two-form variant of {@link streamSwap} that additionally returns a\n * {@link StreamSwapController} with `pause()` / `resume()` / `stop()`.\n *\n * @throws {StreamSwapError} Synchronously for construction-time validation.\n */\nexport function streamSwapControlled(params: StreamSwapParams): {\n result: Promise<StreamSwapResult>;\n controller: StreamSwapController;\n} {\n // Construction-time validation — synchronous throw per AC-2 & AC-9.\n validateParams(params);\n\n const logger = params.logger ?? NOOP_LOGGER;\n\n // Derive schedule\n const schedule: bigint[] = params.packetAmounts\n ? [...params.packetAmounts]\n : chunkAmount(params.totalAmount, params.packetCount as number);\n\n // Freeze a defensive copy of `pair` so callers can't mutate the stored\n // reference on every AccumulatedClaim post-call. (Story 12.5 code-review\n // pass #3.) Shape matches SwapPair (Story 12.1 stable type).\n const frozenPair: SwapPair = Object.freeze({\n from: Object.freeze({ ...params.pair.from }),\n to: Object.freeze({ ...params.pair.to }),\n rate: params.pair.rate,\n }) as SwapPair;\n\n const senderPubkey = getPublicKey(params.senderSecretKey);\n\n // Controller state machine\n type State = 'running' | 'paused' | 'stopped' | 'completed' | 'failed';\n let streamState: State = 'running';\n let resumeDeferred: Deferred<'resume' | 'stop'> | null = null;\n\n const controller: StreamSwapController = {\n pause(): void {\n if (streamState === 'running') {\n streamState = 'paused';\n // resumeDeferred will be created on first await in loop\n }\n },\n resume(): void {\n if (streamState === 'paused') {\n streamState = 'running';\n if (resumeDeferred) {\n resumeDeferred.resolve('resume');\n resumeDeferred = null;\n }\n } else if (streamState === 'running') {\n // no-op\n } else {\n throw new StreamSwapError(\n 'INVALID_STATE',\n `Cannot resume from state \"${streamState}\"`\n );\n }\n },\n stop(): void {\n if (streamState === 'completed' || streamState === 'failed') return;\n const prev = streamState;\n streamState = 'stopped';\n if (prev === 'paused' && resumeDeferred) {\n resumeDeferred.resolve('stop');\n resumeDeferred = null;\n }\n },\n get state(): State {\n return streamState;\n },\n };\n\n const result = runLoop(\n params,\n frozenPair,\n schedule,\n senderPubkey,\n logger,\n () => streamState,\n (v: State) => {\n streamState = v;\n },\n () => {\n if (streamState !== 'paused') return Promise.resolve('resume');\n if (!resumeDeferred) resumeDeferred = new Deferred<'resume' | 'stop'>();\n return resumeDeferred.promise;\n }\n );\n\n return { result, controller };\n}\n\n// ---------------------------------------------------------------------------\n// Core loop\n// ---------------------------------------------------------------------------\n\nasync function runLoop(\n params: StreamSwapParams,\n pair: SwapPair,\n schedule: bigint[],\n senderPubkey: string,\n logger: NonNullable<StreamSwapParams['logger']>,\n getState: () => 'running' | 'paused' | 'stopped' | 'completed' | 'failed',\n setState: (\n v: 'running' | 'paused' | 'stopped' | 'completed' | 'failed'\n ) => void,\n waitForResumeOrStop: () => Promise<'resume' | 'stop'>\n): Promise<StreamSwapResult> {\n const claims: AccumulatedClaim[] = [];\n const rejections: StreamSwapResult['rejections'] = [];\n const errors: StreamSwapResult['errors'] = [];\n let cumulativeSource = 0n;\n let cumulativeTarget = 0n;\n let packetsSent = 0;\n let abortReason: StreamSwapResult['abortReason'] = 'complete';\n\n const totalPackets = schedule.length;\n\n // Snapshot the signal abort state at each check — we don't listen via\n // addEventListener because we check at loop boundaries (between packets).\n const isAborted = (): boolean => params.signal?.aborted === true;\n\n packetLoop: for (\n let packetIndex = 0;\n packetIndex < totalPackets;\n packetIndex++\n ) {\n // --- Abort/stop/pause checks at loop boundary ---\n if (isAborted()) {\n abortReason = 'aborted';\n break;\n }\n if (getState() === 'stopped') {\n abortReason = 'stopped';\n break;\n }\n if (getState() === 'paused') {\n const resumedBy = await waitForResumeOrStop();\n if (resumedBy === 'stop' || getState() === 'stopped') {\n abortReason = 'stopped';\n break;\n }\n // After resume, re-check abort signal\n if (isAborted()) {\n abortReason = 'aborted';\n break;\n }\n }\n\n // Bounds-checked at loop init (`packetIndex < totalPackets === schedule.length`).\n // Narrow out the `undefined` TS widening without silently masking with 0n,\n // which would hide a real bug if the schedule were ever mutated mid-loop.\n // (Story 12.5 code-review pass #3.)\n const sourceAmount = schedule[packetIndex];\n if (sourceAmount === undefined) {\n // Defensive: should be impossible given the bound check. Surface, don't mask.\n throw new StreamSwapError(\n 'INVALID_STATE',\n `schedule[${packetIndex}] is undefined; schedule was mutated mid-stream`\n );\n }\n\n // --- Build + wrap packet ---\n const nonce = new Uint8Array(16);\n getRandomValues(nonce);\n const rumor = buildSwapRumor({\n senderPubkey,\n pair,\n sourceAmount,\n packetIndex: packetIndex + 1,\n totalPackets,\n nonce,\n createdAt: Math.floor(Date.now() / 1000),\n chainRecipient: params.chainRecipient,\n });\n\n let toonData: Uint8Array;\n try {\n const wrapped = wrapSwapPacketToToon({\n rumor,\n senderSecretKey: params.senderSecretKey,\n recipientPubkey: params.swapPubkey,\n destination: params.swapIlpAddress,\n amount: sourceAmount,\n });\n // `wrapped.ilpPrepare.data` is base64 per buildIlpPrepare. Decode back\n // to raw bytes for the sender API (Uint8Array contract in AC-3).\n toonData = new Uint8Array(Buffer.from(wrapped.ilpPrepare.data, 'base64'));\n } catch (err) {\n logger.error({\n event: 'stream_swap.wrap_failed',\n packetIndex,\n error: err instanceof Error ? err.message : String(err),\n });\n errors.push({\n packetIndex,\n cause: err instanceof Error ? err : new Error(String(err)),\n });\n continue;\n }\n\n // --- Send packet via client ---\n let sendResult: IlpSendResultLike;\n try {\n sendResult = await params.client.sendSwapPacket({\n destination: params.swapIlpAddress,\n amount: sourceAmount,\n toonData,\n timeout: params.packetTimeoutMs ?? 30000,\n claim: params.claim,\n });\n packetsSent += 1;\n } catch (err) {\n logger.error({\n event: 'stream_swap.send_failed',\n packetIndex,\n error: err instanceof Error ? err.message : String(err),\n });\n errors.push({\n packetIndex,\n cause: err instanceof Error ? err : new Error(String(err)),\n });\n continue;\n }\n\n // --- Rejection path ---\n if (!sendResult.accepted) {\n const code = sendResult.code ?? 'F00';\n const message = sendResult.message ?? 'rejected';\n logger.warn({\n event: 'stream_swap.packet_rejected',\n packetIndex,\n code,\n message,\n });\n rejections.push({ packetIndex, sourceAmount, code, message });\n continue;\n }\n\n // --- Decode FULFILL metadata ---\n let metadata: {\n claim: string;\n ephemeralPubkey: string;\n claimId?: string;\n targetAmount?: string;\n channelId?: string;\n nonce?: string;\n cumulativeAmount?: string;\n recipient?: string;\n swapSignerAddress?: string;\n };\n try {\n metadata = decodeFulfillMetadata(sendResult.data, pair.to.chain);\n } catch (err) {\n logger.error({\n event: 'stream_swap.fulfill_decode_failed',\n packetIndex,\n error: err instanceof Error ? err.message : String(err),\n });\n errors.push({\n packetIndex,\n cause: err instanceof Error ? err : new Error(String(err)),\n });\n continue;\n }\n\n // Story 12.9 AC-7: when the Swap echoes a `recipient` in FULFILL metadata\n // (Story 12.6 settlement context), it MUST equal the sender-supplied\n // `chainRecipient`. A mismatch indicates the Swap is substituting its own\n // address — refuse to accumulate the claim and surface a per-packet\n // rejection with a clear reason code. Missing recipient = legacy\n // (pre-12.6) metadata, permitted.\n //\n // #153: EVM addresses are case-insensitive (EIP-55 checksum casing is\n // purely a typo-detection hint). The swap lowercases its echoed recipient\n // while the sender may pass a checksummed `chainRecipient` (or vice versa);\n // compare case-insensitively on EVM targets so a casing-only difference is\n // NOT flagged as a substitution attack. Non-EVM chains keep the exact\n // (base58 case-sensitive) comparison.\n const isEvmTarget = pair.to.chain.startsWith('evm:');\n const recipientMatches =\n metadata.recipient === undefined ||\n (isEvmTarget\n ? metadata.recipient.toLowerCase() ===\n params.chainRecipient.toLowerCase()\n : metadata.recipient === params.chainRecipient);\n if (!recipientMatches) {\n logger.warn({\n event: 'stream_swap.recipient_mismatch',\n packetIndex,\n expected: params.chainRecipient,\n actual: metadata.recipient,\n });\n rejections.push({\n packetIndex,\n sourceAmount,\n code: 'SWAP_RECIPIENT_MISMATCH',\n message: `Swap echoed recipient ${metadata.recipient} but sender expected ${params.chainRecipient}`,\n });\n continue;\n }\n\n // --- Decrypt claim ---\n let claimBytes: Uint8Array;\n try {\n const ciphertext = new Uint8Array(Buffer.from(metadata.claim, 'base64'));\n claimBytes = decryptFulfillClaim({\n ciphertext,\n ephemeralPubkey: metadata.ephemeralPubkey,\n recipientSecretKey: params.senderSecretKey,\n });\n } catch (err) {\n logger.error({\n event: 'stream_swap.decrypt_failed',\n packetIndex,\n error: err instanceof Error ? err.message : String(err),\n });\n errors.push({\n packetIndex,\n cause: err instanceof Error ? err : new Error(String(err)),\n });\n continue;\n }\n\n if (claimBytes.length === 0) {\n logger.warn({\n event: 'stream_swap.empty_claim_bytes',\n packetIndex,\n });\n }\n\n // --- Compute expected + actual target + deviation ---\n const expectedTargetAmount = applyRate({\n sourceAmount,\n fromScale: pair.from.assetScale,\n toScale: pair.to.assetScale,\n rate: pair.rate,\n });\n\n // If the Swap includes a `targetAmount` in the FULFILL metadata, use it\n // (chain-specific parsing of `claimBytes` is Story 12.6's job). Otherwise\n // fall back to the advertised-rate expected amount so settlement-time\n // verification still has a baseline.\n // `metadata.targetAmount`, when present, is already validated as a\n // non-negative integer decimal string by `decodeFulfillMetadata`, so\n // `BigInt()` is safe here (no try/catch needed).\n const targetAmount: bigint =\n metadata.targetAmount !== undefined\n ? BigInt(metadata.targetAmount)\n : expectedTargetAmount;\n\n // BigInt-safe deviation: scale up by 1e6 before dividing so we don't lose\n // precision on 18-decimal assets. (Epic 11 retro MAX_SAFE_INTEGER guard.)\n let rateDeviation = 0;\n if (expectedTargetAmount > 0n) {\n const diff =\n targetAmount >= expectedTargetAmount\n ? targetAmount - expectedTargetAmount\n : expectedTargetAmount - targetAmount;\n const scaled = (diff * 1_000_000n) / expectedTargetAmount;\n rateDeviation = Number(scaled) / 1_000_000;\n }\n\n // Display-only effectiveRate for the callback payload. Guard against\n // non-finite results (e.g., advertisedRate=0 + any deviation -> 0;\n // parseFloat surprises) so callback consumers never observe NaN/Infinity.\n const advertisedRate = parseFloat(pair.rate);\n let effectiveRate: number;\n if (targetAmount === expectedTargetAmount) {\n effectiveRate = advertisedRate;\n } else {\n const signedDeviation =\n targetAmount >= expectedTargetAmount ? rateDeviation : -rateDeviation;\n effectiveRate = advertisedRate * (1 + signedDeviation);\n }\n if (!Number.isFinite(effectiveRate)) {\n effectiveRate = advertisedRate;\n }\n\n cumulativeSource += sourceAmount;\n cumulativeTarget += targetAmount;\n\n const accumulated: AccumulatedClaim = {\n packetIndex,\n sourceAmount,\n targetAmount,\n claimBytes,\n swapEphemeralPubkey: metadata.ephemeralPubkey,\n pair,\n receivedAt: Date.now(),\n };\n if (metadata.claimId !== undefined) accumulated.claimId = metadata.claimId;\n // Story 12.6: thread settlement-context fields through when present.\n if (metadata.channelId !== undefined)\n accumulated.channelId = metadata.channelId;\n if (metadata.nonce !== undefined) accumulated.nonce = metadata.nonce;\n if (metadata.cumulativeAmount !== undefined)\n accumulated.cumulativeAmount = metadata.cumulativeAmount;\n if (metadata.recipient !== undefined)\n accumulated.recipient = metadata.recipient;\n if (metadata.swapSignerAddress !== undefined)\n accumulated.swapSignerAddress = metadata.swapSignerAddress;\n claims.push(accumulated);\n\n logger.debug({\n event: 'stream_swap.packet_accepted',\n packetIndex,\n sourceAmount: sourceAmount.toString(),\n targetAmount: targetAmount.toString(),\n });\n\n // --- onPacket callback ---\n if (params.onPacket) {\n const progress: PacketProgress = Object.freeze({\n index: packetIndex,\n total: totalPackets,\n sourceAmount,\n targetAmount,\n advertisedRate: pair.rate,\n effectiveRate,\n rateDeviation,\n cumulativeSource,\n cumulativeTarget,\n state:\n getState() === 'paused'\n ? 'paused'\n : getState() === 'stopped'\n ? 'stopped'\n : 'running',\n });\n\n try {\n const maybePromise = params.onPacket(progress);\n if (\n maybePromise &&\n typeof (maybePromise as Promise<void>).then === 'function'\n ) {\n await maybePromise;\n }\n } catch (err) {\n logger.warn({\n event: 'stream_swap.callback_threw',\n packetIndex,\n error: err instanceof Error ? err.message : String(err),\n });\n errors.push({\n packetIndex,\n cause: err instanceof Error ? err : new Error(String(err)),\n });\n abortReason = 'callback-throw';\n break packetLoop;\n }\n }\n\n // --- Abort signal / stop check AFTER callback (so tests can abort\n // inside onPacket and we exit the loop on the next iteration's\n // boundary check — but honor stopped/aborted without running\n // additional packets). ---\n if (isAborted()) {\n abortReason = 'aborted';\n break;\n }\n if (getState() === 'stopped') {\n abortReason = 'stopped';\n break;\n }\n\n // --- Rate deviation check (after callback, after accumulation) ---\n if (\n params.rateDeviationThreshold !== undefined &&\n rateDeviation > params.rateDeviationThreshold\n ) {\n abortReason = 'rate-deviation';\n break;\n }\n }\n\n // --- Terminal state ---\n let finalState: 'completed' | 'failed' | 'stopped';\n if (abortReason === 'aborted' || abortReason === 'stopped') {\n finalState = 'stopped';\n } else if (\n claims.length === 0 &&\n (rejections.length > 0 || errors.length > 0)\n ) {\n finalState = 'failed';\n // If we drained the entire schedule without accepting any claim AND the\n // only failures were Swap rejections, surface that explicitly so callers\n // can distinguish \"all rejected\" from \"loop aborted early with no claims\".\n if (\n abortReason === 'complete' &&\n rejections.length > 0 &&\n errors.length === 0\n ) {\n abortReason = 'all-rejected';\n }\n } else {\n finalState = 'completed';\n }\n\n setState(finalState);\n\n return {\n state: finalState,\n claims,\n rejections,\n errors,\n abortReason,\n cumulativeSource,\n cumulativeTarget,\n packetsSent,\n packetsScheduled: totalPackets,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Crypto RNG — prefer globalThis.crypto; fall back to node:crypto.webcrypto.\n// ---------------------------------------------------------------------------\n\nfunction getRandomValues(buf: Uint8Array): Uint8Array {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- defensive env probe\n const g: any = globalThis as any;\n if (g.crypto && typeof g.crypto.getRandomValues === 'function') {\n g.crypto.getRandomValues(buf);\n return buf;\n }\n // Node 18/20 fallback\n // eslint-disable-next-line @typescript-eslint/no-require-imports -- fallback only\n const nodeCrypto = require('node:crypto') as {\n webcrypto?: { getRandomValues: (b: Uint8Array) => Uint8Array };\n randomFillSync?: (b: Uint8Array) => Uint8Array;\n };\n if (nodeCrypto.webcrypto?.getRandomValues) {\n nodeCrypto.webcrypto.getRandomValues(buf);\n return buf;\n }\n if (nodeCrypto.randomFillSync) {\n nodeCrypto.randomFillSync(buf);\n return buf;\n }\n throw new StreamSwapError(\n 'INVALID_STATE',\n 'No crypto.getRandomValues available in this environment'\n );\n}\n\n// Re-export `chunkAmount` / `decodeFulfillMetadata` / `buildSwapRumor` via a\n// single-purpose testing surface so unit tests can exercise helpers directly.\n// This surface is NOT part of the public SDK; it is intentionally excluded\n// from `packages/sdk/src/index.ts` per AC-1.\nexport const __testing = {\n chunkAmount,\n decodeFulfillMetadata,\n buildSwapRumor,\n};\n"],"mappings":"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","names":["getPublicKey","getPublicKey","getPublicKey","RATE_REGEX","noop","NOOP_LOGGER","getPublicKey"]}
|