@toon-protocol/sdk 2.0.0 → 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/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/handler-context.ts","../src/handler-registry.ts","../src/pricing-validator.ts","../src/verification-pipeline.ts","../src/payment-handler-bridge.ts","../src/event-storage-handler.ts","../src/create-node.ts","../src/skill-descriptor.ts","../src/workflow-orchestrator.ts","../src/swarm-coordinator.ts","../src/prefix-claim-handler.ts","../src/arweave/arweave-dvm-handler.ts","../src/arweave/turbo-adapter.ts","../src/arweave/chunk-manager.ts","../src/arweave/chunked-upload.ts","../src/settlement/evm.ts","../src/settlement/hashes.ts","../src/settlement/mina.ts","../src/settlement/solana.ts","../src/settlement/build-settlement-tx.ts"],"sourcesContent":["/**\n * Handler context for @toon-protocol/sdk.\n *\n * Provides a context object passed to kind-based handlers with methods\n * for accessing TOON data, shallow-parsed metadata, and accept/reject actions.\n */\n\nimport type { NostrEvent } from 'nostr-tools/pure';\nimport type { ToonRoutingMeta } from '@toon-protocol/core/toon';\nimport type {\n HandlePacketAcceptResponse,\n HandlePacketRejectResponse,\n} from '@toon-protocol/core';\n\n// Re-export core response types so SDK consumers don't need to import from core\nexport type { HandlePacketAcceptResponse, HandlePacketRejectResponse };\n\n/**\n * The handler context passed to each kind-based handler.\n */\nexport interface HandlerContext {\n /** Raw TOON string (base64-encoded). */\n readonly toon: string;\n /** Event kind from shallow parse. */\n readonly kind: number;\n /** Event pubkey from shallow parse. */\n readonly pubkey: string;\n /** Payment amount in the ILP packet. */\n readonly amount: bigint;\n /** ILP destination address. */\n readonly destination: string;\n /** Lazy-decode the TOON payload into a full NostrEvent. */\n decode(): NostrEvent;\n /** Accept the packet with optional metadata. */\n accept(metadata?: Record<string, unknown>): HandlePacketAcceptResponse;\n /** Reject the packet with an ILP error code and message. */\n reject(code: string, message: string): HandlePacketRejectResponse;\n}\n\nexport interface CreateHandlerContextOptions {\n toon: string;\n meta: ToonRoutingMeta;\n amount: bigint;\n destination: string;\n toonDecoder: (toon: string) => NostrEvent;\n}\n\n/**\n * Creates a HandlerContext from the given options.\n */\nexport function createHandlerContext(\n options: CreateHandlerContextOptions\n): HandlerContext {\n let cachedEvent: NostrEvent | undefined;\n\n return {\n get toon() {\n return options.toon;\n },\n get kind() {\n return options.meta.kind;\n },\n get pubkey() {\n return options.meta.pubkey;\n },\n get amount() {\n return options.amount;\n },\n get destination() {\n return options.destination;\n },\n decode(): NostrEvent {\n if (!cachedEvent) {\n cachedEvent = options.toonDecoder(options.toon);\n }\n return cachedEvent;\n },\n accept(metadata?: Record<string, unknown>): HandlePacketAcceptResponse {\n return {\n accept: true,\n ...(metadata ? { metadata } : {}),\n };\n },\n reject(code: string, message: string): HandlePacketRejectResponse {\n return {\n accept: false,\n code,\n message,\n };\n },\n };\n}\n","/**\n * Handler registry for @toon-protocol/sdk.\n *\n * Maps event kinds to handler functions for dispatching incoming ILP packets.\n */\n\nimport { JOB_REQUEST_KIND_BASE } from '@toon-protocol/core';\nimport type {\n HandlerContext,\n HandlePacketAcceptResponse,\n HandlePacketRejectResponse,\n} from './handler-context.js';\n\nexport type HandlerResponse =\n | HandlePacketAcceptResponse\n | HandlePacketRejectResponse;\n\nexport type Handler = (ctx: HandlerContext) => Promise<HandlerResponse>;\n\n/**\n * Registry that maps Nostr event kinds to handler functions.\n */\nexport class HandlerRegistry {\n private handlers = new Map<number, Handler>();\n private defaultHandler: Handler | undefined;\n\n /**\n * Register a handler for a specific event kind.\n * Replaces any existing handler for that kind.\n */\n on(kind: number, handler: Handler): this {\n this.handlers.set(kind, handler);\n return this;\n }\n\n /**\n * Register a default handler for unrecognized kinds.\n */\n onDefault(handler: Handler): this {\n this.defaultHandler = handler;\n return this;\n }\n\n /**\n * Returns all registered kind numbers, sorted ascending.\n */\n getRegisteredKinds(): number[] {\n return [...this.handlers.keys()].sort((a, b) => a - b);\n }\n\n /**\n * Returns the handler registered for `kind`, or `undefined` if none.\n * Added for Story 12.7 AC-10 (handler-registration verification).\n */\n get(kind: number): Handler | undefined {\n return this.handlers.get(kind);\n }\n\n /**\n * Returns registered kinds in the DVM request range (5000-5999), sorted ascending.\n * Uses JOB_REQUEST_KIND_BASE (5000) as the range start.\n */\n getDvmKinds(): number[] {\n const dvmRangeStart = JOB_REQUEST_KIND_BASE;\n const dvmRangeEnd = JOB_REQUEST_KIND_BASE + 999;\n return this.getRegisteredKinds().filter(\n (k) => k >= dvmRangeStart && k <= dvmRangeEnd\n );\n }\n\n /**\n * Dispatch a context to the appropriate handler based on kind.\n */\n async dispatch(ctx: HandlerContext): Promise<HandlerResponse> {\n const handler = this.handlers.get(ctx.kind);\n if (handler) {\n return handler(ctx);\n }\n if (this.defaultHandler) {\n return this.defaultHandler(ctx);\n }\n return {\n accept: false,\n code: 'F00',\n message: `No handler registered for kind ${ctx.kind}`,\n };\n }\n}\n","/**\n * Pricing validator for @toon-protocol/sdk.\n *\n * Validates that incoming ILP payments meet the required price based on\n * TOON payload size and configured price per byte.\n */\n\nimport type { ToonRoutingMeta } from '@toon-protocol/core/toon';\n\nexport interface PricingValidatorConfig {\n basePricePerByte?: bigint;\n ownPubkey: string;\n kindPricing?: Record<number, bigint>;\n}\n\nexport interface PricingValidationResult {\n accepted: boolean;\n rejection?: {\n accept: false;\n code: string;\n message: string;\n metadata?: Record<string, string>;\n };\n}\n\n/**\n * Creates a pricing validator that checks payment amounts against TOON size.\n */\nexport function createPricingValidator(config: PricingValidatorConfig) {\n const basePricePerByte = config.basePricePerByte ?? 10n;\n\n return {\n validate(meta: ToonRoutingMeta, amount: bigint): PricingValidationResult {\n // Self-write bypass\n if (meta.pubkey === config.ownPubkey) {\n return { accepted: true };\n }\n\n // Kind-specific pricing override (use Object.hasOwn for prototype-safe lookup)\n const kindOverride =\n config.kindPricing && Object.hasOwn(config.kindPricing, meta.kind)\n ? config.kindPricing[meta.kind]\n : undefined;\n const pricePerByte = kindOverride ?? basePricePerByte;\n\n const requiredAmount = BigInt(meta.rawBytes.length) * pricePerByte;\n\n if (amount < requiredAmount) {\n return {\n accepted: false,\n rejection: {\n accept: false,\n code: 'F04',\n message: 'Insufficient payment',\n metadata: {\n required: requiredAmount.toString(),\n received: amount.toString(),\n },\n },\n };\n }\n\n return { accepted: true };\n },\n };\n}\n","/**\n * Verification pipeline for @toon-protocol/sdk.\n *\n * Verifies Schnorr signatures on incoming TOON payloads before dispatching\n * to handlers. In dev mode, verification is skipped.\n */\n\nimport type { ToonRoutingMeta } from '@toon-protocol/core/toon';\nimport { schnorr } from '@noble/curves/secp256k1.js';\nimport { hexToBytes } from '@noble/hashes/utils.js';\n\nexport interface VerificationResult {\n verified: boolean;\n rejection?: {\n accept: false;\n code: string;\n message: string;\n };\n}\n\nexport interface VerificationPipelineConfig {\n devMode: boolean;\n}\n\n/**\n * Creates a verification pipeline that checks Schnorr signatures.\n */\nexport function createVerificationPipeline(config: VerificationPipelineConfig) {\n return {\n async verify(\n meta: ToonRoutingMeta,\n _toonData: string\n ): Promise<VerificationResult> {\n if (config.devMode) {\n return { verified: true };\n }\n\n try {\n const sigBytes = hexToBytes(meta.sig);\n const msgBytes = hexToBytes(meta.id);\n const pubkeyBytes = hexToBytes(meta.pubkey);\n const valid = schnorr.verify(sigBytes, msgBytes, pubkeyBytes);\n if (!valid) {\n return {\n verified: false,\n rejection: {\n accept: false,\n code: 'F06',\n message: 'Invalid Schnorr signature',\n },\n };\n }\n return { verified: true };\n } catch {\n return {\n verified: false,\n rejection: {\n accept: false,\n code: 'F06',\n message: 'Signature verification failed',\n },\n };\n }\n },\n };\n}\n","/**\n * Payment handler bridge for @toon-protocol/sdk.\n *\n * Bridges between ILP packet handling and the handler registry,\n * using isTransit to distinguish fire-and-forget from await semantics.\n */\n\nimport type { HandlerRegistry } from './handler-registry.js';\nimport { createHandlerContext } from './handler-context.js';\nimport type { ToonRoutingMeta } from '@toon-protocol/core/toon';\n\nexport interface PaymentHandlerBridgeConfig {\n registry: HandlerRegistry;\n devMode: boolean;\n ownPubkey: string;\n basePricePerByte: bigint;\n}\n\nexport interface PaymentRequest {\n paymentId: string;\n destination: string;\n amount: string;\n data: string;\n isTransit: boolean;\n}\n\nexport interface PaymentResponse {\n accept: boolean;\n code?: string;\n message?: string;\n data?: string;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Creates a payment handler bridge that connects ILP packets to the handler registry.\n */\nexport function createPaymentHandlerBridge(config: PaymentHandlerBridgeConfig) {\n const { registry } = config;\n\n return {\n async handlePayment(request: PaymentRequest): Promise<PaymentResponse> {\n let amount: bigint;\n try {\n amount = BigInt(request.amount);\n } catch {\n return {\n accept: false,\n code: 'T00',\n message: 'Invalid payment amount',\n };\n }\n\n const ctx = createHandlerContext({\n toon: request.data,\n meta: {\n kind: 0,\n pubkey: '',\n id: '',\n sig: '',\n rawBytes: new Uint8Array(0),\n } as ToonRoutingMeta,\n amount,\n destination: request.destination,\n toonDecoder: () => {\n throw new Error('decode not available in bridge');\n },\n });\n\n if (request.isTransit) {\n void registry.dispatch(ctx).catch((err: unknown) => {\n // Log only the error message, not the full error object (which may contain payload data)\n const errMsg = err instanceof Error ? err.message : 'Unknown error';\n console.error('Transit handler error:', errMsg);\n });\n return { accept: true };\n }\n\n try {\n return await registry.dispatch(ctx);\n } catch (err: unknown) {\n // Log only the error message, not the full error object (which may contain payload data)\n const errMsg = err instanceof Error ? err.message : 'Unknown error';\n console.error('Handler error:', errMsg);\n return {\n accept: false,\n code: 'T00',\n message: 'Internal error',\n };\n }\n },\n };\n}\n","/**\n * Event storage handler stub for @toon-protocol/sdk.\n *\n * This is a stub that throws. The real implementation lives in\n * `@toon-protocol/relay` -- see `createEventStorageHandler` from that package.\n *\n * The SDK is the framework; Relay is the relay implementation. SDK consumers\n * building relay functionality should use `@toon-protocol/relay` directly.\n */\n\n/**\n * Creates an event storage handler.\n *\n * **Stub** -- throws \"not yet implemented\". See `@toon-protocol/relay` for the\n * real relay implementation of this handler.\n *\n * @see {@link https://github.com/toon-protocol/relay | @toon-protocol/relay}\n */\nexport function createEventStorageHandler(_config: unknown): unknown {\n throw new Error(\n 'createEventStorageHandler is not yet implemented in @toon-protocol/sdk. ' +\n 'Use @toon-protocol/relay for the relay implementation.'\n );\n}\n","/**\n * createNode() composition for @toon-protocol/sdk.\n *\n * Wires the full ILP packet processing pipeline:\n * shallow TOON parse -> Schnorr signature verification -> pricing validation -> handler dispatch\n *\n * Provides start() / stop() lifecycle management by delegating to\n * the core ToonNode composition (embedded mode) or manual HTTP\n * composition (standalone mode).\n *\n * ## Deployment Modes\n *\n * - **Embedded** (`connector`): Pass an EmbeddableConnectorLike directly.\n * Zero-latency packet delivery via direct function calls.\n * - **Standalone** (`connectorUrl` + `handlerPort`): Connect to an external\n * connector via HTTP. The SDK starts an HTTP server to receive packets.\n */\n\nimport { createServer, type Server as HttpServer } from 'node:http';\nimport type { NostrEvent } from 'nostr-tools/pure';\nimport type {\n EmbeddableConnectorLike,\n HandlePacketRequest,\n HandlePacketResponse,\n ConnectorChannelClient,\n} from '@toon-protocol/core';\nimport type { TransportConfig } from '@toon-protocol/connector';\nimport type {\n KnownPeer,\n BootstrapResult,\n BootstrapEventListener,\n} from '@toon-protocol/core';\nimport type { SettlementConfig } from '@toon-protocol/core';\nimport {\n createToonNode,\n BootstrapService,\n createDiscoveryTracker,\n createHttpIlpClient,\n createHttpConnectorAdmin,\n createHttpChannelClient,\n resolveChainConfig,\n buildIlpPrepare,\n buildJobFeedbackEvent,\n buildJobResultEvent,\n parseJobResult,\n deriveChildAddress,\n checkAddressCollision,\n AddressRegistry,\n ILP_ROOT_PREFIX,\n calculateRouteAmount,\n resolveRouteFees,\n buildPrefixClaimEvent,\n validatePrefix,\n ilpCodeToSemantic,\n} from '@toon-protocol/core';\nimport type { ChainProviderConfigEntry } from '@toon-protocol/core';\nimport type { DvmJobStatus, IlpSendResult } from '@toon-protocol/core';\nimport type {\n IlpClient,\n ConnectorAdminClient,\n DiscoveryTracker,\n} from '@toon-protocol/core';\nimport {\n shallowParseToon,\n decodeEventFromToon,\n encodeEventToToon,\n} from '@toon-protocol/core/toon';\n\nimport type { SkillDescriptor } from '@toon-protocol/core';\nimport { fromSecretKey } from './identity.js';\nimport { HandlerRegistry, type Handler } from './handler-registry.js';\nimport { createHandlerContext } from './handler-context.js';\nimport { createVerificationPipeline } from './verification-pipeline.js';\nimport { createPricingValidator } from './pricing-validator.js';\nimport {\n buildSkillDescriptor,\n type BuildSkillDescriptorConfig,\n} from './skill-descriptor.js';\nimport { NodeError } from './errors.js';\n\n/**\n * Maximum base64-encoded payload size (in characters) accepted by the pipeline.\n * Defense-in-depth against DoS via oversized payloads. 1MB of base64 decodes\n * to ~750KB of raw TOON data, which is far beyond any legitimate Nostr event.\n * The pay-per-byte pricing model provides an additional economic disincentive.\n */\nconst MAX_PAYLOAD_BASE64_LENGTH = 1_048_576;\n\n/**\n * Configuration for creating a ServiceNode via createNode().\n *\n * Supports three deployment modes:\n * - **Default** (no connector args): Auto-creates an embedded ConnectorNode via\n * dynamic import. Requires `@toon-protocol/connector` as peer dependency.\n * - **Embedded** (`connector`): Pass a pre-configured EmbeddableConnectorLike.\n * - **Standalone** (`connectorUrl` + `handlerPort`): Connect to an external\n * connector via HTTP. The SDK starts an HTTP server to receive packets.\n *\n * Provide `connector` OR (`connectorUrl` + `handlerPort`), or neither (auto-create).\n */\nexport interface NodeConfig {\n /** 32-byte secp256k1 secret key */\n secretKey: Uint8Array;\n\n /** Chain preset name (default: 'anvil'). See resolveChainConfig(). */\n chain?: string;\n\n // --- Connector (optional — defaults to auto-created embedded connector) ---\n\n /**\n * Embedded connector instance for zero-latency mode.\n * If neither `connector` nor `connectorUrl` is provided, an embedded\n * ConnectorNode is auto-created (requires @toon-protocol/connector peer dep).\n */\n connector?: EmbeddableConnectorLike;\n /**\n * External connector admin URL for standalone mode (e.g., \"http://localhost:8081\").\n * Must be provided with `handlerPort`.\n */\n connectorUrl?: string;\n /**\n * Port for the HTTP server that receives ILP packets from the external connector.\n * Must be provided with `connectorUrl`.\n */\n handlerPort?: number;\n\n /**\n * BTP server port for the auto-created embedded connector (default: 3000).\n * Only used when neither `connector` nor `connectorUrl` is provided.\n */\n btpServerPort?: number;\n\n /**\n * EVM private key for settlement infrastructure.\n * Only used when auto-creating an embedded connector.\n * If not set, the identity's secp256k1 key is used.\n */\n settlementPrivateKey?: string;\n\n // --- Network ---\n\n /**\n * Upstream peer's ILP address prefix for address derivation.\n * When set, the node derives its ILP address as\n * `deriveChildAddress(upstreamPrefix, pubkey)` and ignores `ilpAddress`.\n */\n upstreamPrefix?: string;\n /**\n * Multiple upstream peer prefixes for multi-peered nodes (Story 7.3).\n * When set, derives one ILP address per upstream prefix. Takes priority\n * over `upstreamPrefix` (singular) when both are set.\n */\n upstreamPrefixes?: string[];\n /** ILP address (default: derived from pubkey under ILP_ROOT_PREFIX) */\n ilpAddress?: string;\n /** BTP endpoint URL advertised in kind:10032 announcements */\n btpEndpoint?: string;\n /**\n * ILP-over-HTTP endpoint URL (RFC-0035) advertised in kind:10032, for\n * stateless one-shot writes (`POST /ilp`). With the shared transport server,\n * this is typically the same host/port as `btpEndpoint` (e.g.\n * `http://host:3000/ilp`).\n */\n httpEndpoint?: string;\n /** Whether `httpEndpoint` accepts an HTTP Upgrade to BTP (default: false). */\n supportsUpgrade?: boolean;\n /** Asset code (default: 'USD') */\n assetCode?: string;\n /** Asset scale (default: 6) */\n assetScale?: number;\n /**\n * Base price per byte for pricing validation (default: 10n).\n *\n * Amounts are in USDC micro-units (6 decimals) for production.\n * Default 10n = 10 micro-USDC per byte = $0.00001/byte.\n * A 1KB event costs 10,240 micro-USDC = ~$0.01.\n */\n basePricePerByte?: bigint;\n /**\n * Routing fee per byte charged by this node as an intermediary (default: 0n = free routing).\n * Advertised in kind:10032 peer info events as a non-negative integer string.\n */\n feePerByte?: bigint;\n /** Dev mode skips signature verification (default: false) */\n devMode?: boolean;\n /** TOON encoder function */\n toonEncoder?: (event: NostrEvent) => Uint8Array;\n /** TOON decoder function */\n toonDecoder?: (bytes: Uint8Array) => NostrEvent;\n /** Initial known peers for bootstrap */\n knownPeers?: KnownPeer[];\n /** Relay WebSocket URL */\n relayUrl?: string;\n /** Settlement info for peer registration */\n settlementInfo?: SettlementConfig;\n /** Enable ArDrive peer lookup */\n ardriveEnabled?: boolean;\n /** Per-kind pricing overrides */\n kindPricing?: Record<number, bigint>;\n /**\n * Multi-chain provider configuration for the embedded connector.\n * When provided, passed directly to ConnectorNode as `chainProviders`.\n * Takes priority over the legacy `settlementInfra` auto-configuration.\n * Only used when auto-creating an embedded connector.\n */\n chainProviders?: ChainProviderConfigEntry[];\n\n /**\n * Transport configuration for the embedded connector (ator/SOCKS5 privacy overlay).\n * When provided, passed directly to ConnectorNode as `transport`.\n * Only used when auto-creating an embedded connector.\n */\n transport?: TransportConfig;\n\n /**\n * NIP-59 transport privacy configuration for the embedded connector.\n * When enabled, per-packet claims are wrapped in three-layer encryption.\n * Only used when auto-creating an embedded connector.\n */\n nip59?: { enabled: boolean };\n /**\n * Per-peer NIP-59 public keys for claim encryption.\n * Map of peer ID to compressed secp256k1 public key (hex, 66 chars).\n * Applied to peer configs when registering peers with the connector.\n * Only used when auto-creating an embedded connector.\n */\n peerNip59PublicKeys?: Record<string, string>;\n /** Config-based handler registration (alternative to post-creation .on()) */\n handlers?: Record<number, Handler>;\n /** Config-based default handler (alternative to post-creation .onDefault()) */\n defaultHandler?: Handler;\n /**\n * Optional skill descriptor configuration overrides.\n * When DVM handlers are registered, these values override the auto-derived\n * defaults in the skill descriptor. See buildSkillDescriptor().\n */\n skillConfig?: Omit<\n BuildSkillDescriptorConfig,\n 'basePricePerByte' | 'kindPricing'\n >;\n}\n\n/**\n * Result returned by ServiceNode.start().\n */\nexport interface StartResult {\n /** Number of peers successfully bootstrapped */\n peerCount: number;\n /** Number of payment channels opened */\n channelCount: number;\n /** Detailed results from the bootstrap phase */\n bootstrapResults: BootstrapResult[];\n}\n\n/**\n * Result returned by ServiceNode.publishEvent().\n */\nexport interface PublishEventResult {\n success: boolean;\n eventId: string;\n /** Response data from the ILP FULFILL packet (e.g., Arweave tx ID for kind:5094). */\n data?: string;\n code?: string;\n message?: string;\n}\n\n/**\n * A fully wired TOON node with lifecycle management.\n */\nexport interface ServiceNode {\n /** Nostr x-only public key (32 bytes, 64 hex chars) */\n readonly pubkey: string;\n /** EVM address derived from the same secp256k1 key */\n readonly evmAddress: string;\n /** Pass-through to the underlying connector (null in standalone mode) */\n readonly connector: EmbeddableConnectorLike | null;\n /** Channel client (null if connector lacks channel support or in standalone mode) */\n readonly channelClient: ConnectorChannelClient | null;\n /** Register a handler for a specific event kind (builder pattern) */\n on(kind: number, handler: Handler): ServiceNode;\n /** Register a lifecycle event listener */\n on(event: 'bootstrap', listener: BootstrapEventListener): ServiceNode;\n /** Register a default handler for unrecognized kinds (builder pattern) */\n onDefault(handler: Handler): ServiceNode;\n /** Start the node: wire packet handler, run bootstrap, start discovery */\n start(): Promise<StartResult>;\n /** Stop the node: clean up lifecycle state */\n stop(): Promise<void>;\n /** Initiate peering with a discovered peer (register + settlement) */\n peerWith(pubkey: string): Promise<void>;\n /**\n * Publish a Nostr event to a remote peer via the embedded connector.\n *\n * TOON-encodes the event, computes payment amount, and sends as an\n * ILP PREPARE packet via the runtime client.\n *\n * @param event - The Nostr event to publish\n * @param options - Must include destination ILP address. Optional `amount`\n * overrides the default basePricePerByte * toonData.length calculation\n * (prepaid model: send exact destination amount). Optional `bid` is a\n * client-side safety cap: if the destination amount exceeds `bid`, the\n * SDK throws before sending any ILP packet.\n * @returns Result with success/failure info and event ID\n */\n publishEvent(\n event: NostrEvent,\n options?: { destination: string; amount?: bigint; bid?: bigint }\n ): Promise<PublishEventResult>;\n\n /**\n * Publish a Kind 7000 DVM job feedback event via ILP PREPARE.\n *\n * Builds a signed Kind 7000 feedback event with NIP-90 tags (e, p, status)\n * and delegates to publishEvent() for TOON encoding and ILP delivery.\n * Standard relay write fee applies: basePricePerByte * toonData.length.\n *\n * @param requestEventId - 64-char hex event ID of the original Kind 5xxx request\n * @param customerPubkey - 64-char hex pubkey of the customer who posted the request\n * @param status - Job status value ('processing', 'error', 'success', 'partial')\n * @param content - Optional status details or error message\n * @param options - Must include destination ILP address\n * @returns Result with success/failure info and event ID\n */\n publishFeedback(\n requestEventId: string,\n customerPubkey: string,\n status: DvmJobStatus,\n content?: string,\n options?: { destination: string }\n ): Promise<PublishEventResult>;\n\n /**\n * Publish a Kind 6xxx DVM job result event via ILP PREPARE.\n *\n * Builds a signed Kind 6xxx result event with NIP-90 tags (e, p, amount)\n * and delegates to publishEvent() for TOON encoding and ILP delivery.\n * Standard relay write fee applies: basePricePerByte * toonData.length.\n *\n * @param requestEventId - 64-char hex event ID of the original Kind 5xxx request\n * @param customerPubkey - 64-char hex pubkey of the customer who posted the request\n * @param amount - Compute cost in USDC micro-units as string\n * @param content - Result data (text, URL, etc.)\n * @param options - Must include destination; optional kind (default: 6100)\n * @returns Result with success/failure info and event ID\n */\n publishResult(\n requestEventId: string,\n customerPubkey: string,\n amount: string,\n content: string,\n options?: { destination: string; kind?: number }\n ): Promise<PublishEventResult>;\n\n /**\n * Returns the computed skill descriptor for this node's DVM capabilities.\n * Returns `undefined` if no DVM handlers (kinds 5000-5999) are registered.\n * The descriptor is computed from the handler registry and node config.\n */\n getSkillDescriptor(): SkillDescriptor | undefined;\n\n /**\n * Send an ILP payment to a provider for compute settlement.\n *\n * Extracts the compute cost from the result event's `amount` tag via\n * parseJobResult(), optionally validates against the original bid amount\n * (E5-R005 bid validation), and sends a pure ILP value transfer\n * (empty data field) to the provider's ILP address.\n *\n * This is a payment-only operation -- no TOON encoding, no relay write.\n * The payment routes through the ILP mesh using the same infrastructure\n * as relay write fees.\n *\n * @deprecated Use publishEvent() with amount option instead. Prepaid model:\n * send job request + payment in one packet via\n * `publishEvent(event, { destination, amount: computePrice })`.\n *\n * @param resultEvent - Kind 6xxx result event with amount tag\n * @param providerIlpAddress - Provider's ILP address from kind:10035\n * @param options - Optional originalBid for bid validation (E5-R005)\n * @returns ILP send result with accepted/rejected status\n */\n settleCompute(\n resultEvent: NostrEvent,\n providerIlpAddress: string,\n options?: { originalBid?: string }\n ): Promise<IlpSendResult>;\n\n /**\n * Add a new upstream peer, deriving and registering its ILP address.\n *\n * Updates the AddressRegistry, registers a self-route in the embedded\n * connector, and triggers kind:10032 republication via BootstrapService.\n *\n * @param upstreamPrefix - The upstream peer's ILP address prefix\n */\n addUpstreamPeer(upstreamPrefix: string): void;\n\n /**\n * Remove an upstream peer, unregistering its derived ILP address.\n *\n * Updates the AddressRegistry, removes the self-route from the embedded\n * connector, and triggers kind:10032 republication via BootstrapService.\n *\n * @param upstreamPrefix - The upstream prefix to remove\n */\n removeUpstreamPeer(upstreamPrefix: string): void;\n\n /**\n * Claim a prefix from an upstream peer by sending a prefix claim event\n * with payment via ILP.\n *\n * Builds a Kind 10034 prefix claim event, reads the upstream's prefix\n * pricing from its kind:10032 advertisement, and calls publishEvent()\n * with the appropriate amount.\n *\n * @param prefix - The prefix string to claim (e.g., 'useast')\n * @param upstreamDestination - ILP address of the upstream node\n * @param options - Optional prefixPrice override (defaults to upstream's prefixPricing.basePrice from discovery)\n * @returns Result with success/failure info and event ID\n */\n claimPrefix(\n prefix: string,\n upstreamDestination: string,\n options?: { prefixPrice?: bigint }\n ): Promise<PublishEventResult>;\n}\n\n/**\n * Creates a fully wired ServiceNode from configuration.\n *\n * The returned node has the full ILP packet processing pipeline wired in the\n * correct order:\n * 1. Shallow TOON parse (extract routing metadata)\n * 2. Schnorr signature verification (reject with F06 if invalid)\n * 3. Pricing validation (reject with F04 if underpaid)\n * 4. Handler dispatch (route to kind-specific or default handler)\n *\n * Supports two deployment modes:\n * - **Embedded** (`connector`): Uses createToonNode for zero-latency\n * packet delivery via direct function calls.\n * - **Standalone** (`connectorUrl` + `handlerPort`): Starts an HTTP server\n * to receive packets and uses HTTP clients for the connector API.\n *\n * Handlers can be registered via config or post-creation .on()/.onDefault().\n */\nexport function createNode(config: NodeConfig): ServiceNode {\n // 0. Validate connector mode\n const hasConnector = config.connector !== undefined;\n const hasConnectorUrl = config.connectorUrl !== undefined;\n\n if (hasConnector && hasConnectorUrl) {\n throw new NodeError(\n 'NodeConfig: provide either connector or connectorUrl, not both'\n );\n }\n if (hasConnectorUrl && config.handlerPort === undefined) {\n throw new NodeError(\n 'NodeConfig: handlerPort is required when using connectorUrl (standalone mode)'\n );\n }\n const autoCreateConnector = !hasConnector && !hasConnectorUrl;\n const embeddedMode = hasConnector || autoCreateConnector;\n\n // 0b. Resolve chain config and auto-populate settlementInfo if not set\n const chainConfig = resolveChainConfig(config.chain);\n let effectiveSettlementInfo = config.settlementInfo;\n if (!effectiveSettlementInfo) {\n const chainKey = `evm:base:${chainConfig.chainId}`;\n const supportedChains = [chainKey];\n const preferredTokens: Record<string, string> = {\n [chainKey]: chainConfig.usdcAddress,\n };\n const tokenNetworks: Record<string, string> | undefined =\n chainConfig.tokenNetworkAddress\n ? { [chainKey]: chainConfig.tokenNetworkAddress }\n : undefined;\n\n effectiveSettlementInfo = {\n supportedChains,\n preferredTokens,\n ...(tokenNetworks && { tokenNetworks }),\n };\n }\n\n // 1. Derive identity from secretKey\n let identity;\n try {\n identity = fromSecretKey(config.secretKey);\n } catch (error: unknown) {\n throw new NodeError(\n `Invalid secretKey: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n const { pubkey, evmAddress } = identity;\n\n // 2. Create handler registry\n const registry = new HandlerRegistry();\n\n // 3. Register config-based handlers (with kind validation matching node.on())\n if (config.handlers) {\n for (const [kind, handler] of Object.entries(config.handlers)) {\n const kindNum = Number(kind);\n if (!Number.isInteger(kindNum) || kindNum < 0) {\n throw new NodeError(\n `Invalid event kind in handlers config: expected a non-negative integer, got '${kind}'`\n );\n }\n registry.on(kindNum, handler);\n }\n }\n\n // 4. Register config-based default handler\n if (config.defaultHandler) {\n registry.onDefault(config.defaultHandler);\n }\n\n // 5. Create verification pipeline\n const verifier = createVerificationPipeline({\n devMode: config.devMode ?? false,\n });\n\n // 6. Create pricing validator\n const pricer = createPricingValidator({\n basePricePerByte: config.basePricePerByte ?? 10n,\n ownPubkey: pubkey,\n kindPricing: config.kindPricing,\n });\n\n // 7. Set up TOON codec with defaults\n const encoder = config.toonEncoder ?? encodeEventToToon;\n const decoder = config.toonDecoder ?? decodeEventFromToon;\n\n // Context decoder: converts base64 TOON string to NostrEvent\n const contextDecoder = (toon: string): NostrEvent => {\n const bytes = Buffer.from(toon, 'base64');\n return decoder(bytes);\n };\n\n // Mutable ref so the packet handler closure can access the tracker after it's created.\n const trackerRef: { current?: { processEvent(event: NostrEvent): void } } =\n {};\n\n // 8. Build the pipelined packet handler (Option A: directly on HandlePacketRequest)\n const pipelinedHandler = async (\n request: HandlePacketRequest\n ): Promise<HandlePacketResponse> => {\n // Step 0: Reject oversized payloads before allocating memory (DoS mitigation)\n if (request.data.length > MAX_PAYLOAD_BASE64_LENGTH) {\n return {\n accept: false,\n code: 'F06',\n message: `Payload too large: ${request.data.length} bytes exceeds maximum ${MAX_PAYLOAD_BASE64_LENGTH}`,\n };\n }\n\n // Step 1: Shallow TOON parse\n const toonBytes = Buffer.from(request.data, 'base64');\n let meta;\n try {\n meta = shallowParseToon(toonBytes);\n } catch {\n // Corrupted TOON data cannot be parsed -- reject as invalid payload\n return {\n accept: false,\n code: 'F06',\n message: 'Invalid TOON payload: failed to parse routing metadata',\n };\n }\n\n // Dev mode: log packet details\n // Sanitize user-controlled fields (amount, destination) to prevent log injection\n // via newlines or control characters. meta.kind (integer) and meta.pubkey (validated\n // hex) are safe; request.data preview is base64 (safe character set).\n if (config.devMode ?? false) {\n const toonPreview =\n request.data.length > 80\n ? request.data.substring(0, 80) + '...'\n : request.data;\n // eslint-disable-next-line no-control-regex\n const sanitize = (s: string) => s.replace(/[\\x00-\\x1f\\x7f]/g, '');\n console.log(\n '[toon:dev]',\n `kind=${meta.kind}`,\n `pubkey=${meta.pubkey.substring(0, 16)}...`,\n `amount=${sanitize(request.amount)}`,\n `dest=${sanitize(request.destination)}`,\n `toon=${toonPreview}`\n );\n }\n\n // Step 2: Verify signature\n const verifyResult = await verifier.verify(meta, request.data);\n if (!verifyResult.verified) {\n // VerificationResult.rejection is always set when verified=false per\n // createVerificationPipeline contract. Guard defensively anyway.\n if (verifyResult.rejection) {\n return verifyResult.rejection;\n }\n return { accept: false, code: 'F06', message: 'Verification failed' };\n }\n\n // Step 3: Validate pricing (skip in dev mode)\n let amount: bigint;\n try {\n amount = BigInt(request.amount);\n } catch {\n if (config.devMode ?? false) {\n amount = 0n;\n } else {\n return {\n accept: false,\n code: 'T00',\n message: 'Invalid payment amount',\n };\n }\n }\n if (!(config.devMode ?? false)) {\n const priceResult = pricer.validate(meta, amount);\n if (!priceResult.accepted) {\n // PricingValidationResult.rejection is always set when accepted=false per\n // createPricingValidator contract. Guard defensively anyway.\n if (priceResult.rejection) {\n return priceResult.rejection;\n }\n return {\n accept: false,\n code: 'F04',\n message: 'Pricing validation failed',\n };\n }\n }\n\n // Step 4: Build HandlerContext with real metadata\n const ctx = createHandlerContext({\n toon: request.data,\n meta,\n amount,\n destination: request.destination,\n toonDecoder: contextDecoder,\n });\n\n // Step 5: Dispatch to handler (T00 error boundary)\n try {\n const result = await registry.dispatch(ctx);\n\n // Feed accepted kind:10032 events to discovery tracker for peer discovery.\n // Uses late-binding reference since the tracker is created after this handler.\n if (result.accept && meta.kind === 10032 && trackerRef.current) {\n const decoded = ctx.decode();\n if (decoded) {\n trackerRef.current.processEvent(decoded);\n }\n }\n\n // Connector v3.3.2 breaking change: the embedded ConnectorNode's\n // PaymentHandlerAdapter consumes `response.data` (base64) and ignores\n // `response.metadata`. The SDK's `ctx.accept(metadata)` historically\n // returned `{ accept: true, metadata }`. To preserve handler ergonomics\n // (handlers still pass an object), serialize `metadata` → base64 JSON\n // into `data` here when the handler did not already populate `data`.\n // The streamSwap FULFILL decoder expects this exact base64-JSON shape.\n if (result.accept && result.metadata && !result.data) {\n try {\n const metadataJson = JSON.stringify(result.metadata);\n (result as { data?: string }).data = Buffer.from(\n metadataJson,\n 'utf8'\n ).toString('base64');\n } catch (err) {\n console.error(\n 'Failed to serialize handler metadata to FULFILL data:',\n err instanceof Error ? err.message : err\n );\n }\n }\n\n // Connector v3.3.2 breaking change: the adapter's reject path reads\n // `response.rejectReason.{code,message}` and feeds `rejectReason.code`\n // through `mapRejectCode()` — a SEMANTIC-reason → ILP-code lookup\n // (e.g. `'internal_error'` → `'T00'`). The SDK's `ctx.reject(ilpCode,\n // message)` returns the ILP code directly, so without translation\n // every reject collapses to F99 (the `mapRejectCode()` fallback).\n //\n // Reverse-map the handler's ILP code back to the connector's expected\n // semantic reason. Canonical mapping lives in\n // `@toon-protocol/core` (utils/reject-code.ts).\n if (\n !result.accept &&\n !(result as { rejectReason?: unknown }).rejectReason &&\n (result as { code?: string }).code\n ) {\n const ilpCode = (result as { code: string }).code;\n (\n result as {\n rejectReason?: { code: string; message: string };\n }\n ).rejectReason = {\n code: ilpCodeToSemantic(ilpCode),\n message:\n (result as { message?: string }).message ?? 'Payment rejected',\n };\n }\n\n return result;\n } catch (err: unknown) {\n // Log only the error message, not the full error object (which may contain payload data)\n const errMsg = err instanceof Error ? err.message : 'Unknown error';\n console.error('Handler dispatch failed:', errMsg);\n return { accept: false, code: 'T00', message: 'Internal error' };\n }\n };\n\n // ILP info shared between both modes\n // Address resolution priority (Story 7.3 extends with upstreamPrefixes):\n // 1. upstreamPrefixes set -> derive one address per prefix, primary = first\n // 2. upstreamPrefix set -> derive single address (ignore ilpAddress)\n // 3. ilpAddress set -> use directly (includes genesis node with 'g.toon')\n // 4. neither set -> derive from ILP_ROOT_PREFIX + pubkey (default)\n let resolvedIlpAddress: string;\n let resolvedIlpAddresses: string[];\n\n if (\n config.upstreamPrefixes !== undefined &&\n config.upstreamPrefixes.length > 0\n ) {\n // upstreamPrefixes takes priority over upstreamPrefix (singular)\n if (config.upstreamPrefix !== undefined) {\n console.warn(\n '[toon:warn] Both upstreamPrefixes and upstreamPrefix set; upstreamPrefixes takes priority'\n );\n }\n resolvedIlpAddresses = config.upstreamPrefixes.map((p) =>\n deriveChildAddress(p, pubkey)\n );\n // Task 4.4: Check for truncation collisions across upstream prefixes\n for (let i = 0; i < resolvedIlpAddresses.length; i++) {\n const others = resolvedIlpAddresses.filter((_, j) => j !== i);\n checkAddressCollision(resolvedIlpAddresses[i] as string, others);\n }\n resolvedIlpAddress = resolvedIlpAddresses[0] as string;\n } else if (config.upstreamPrefix !== undefined) {\n resolvedIlpAddress = deriveChildAddress(config.upstreamPrefix, pubkey);\n resolvedIlpAddresses = [resolvedIlpAddress];\n } else if (config.ilpAddress !== undefined) {\n resolvedIlpAddress = config.ilpAddress;\n resolvedIlpAddresses = [resolvedIlpAddress];\n } else {\n resolvedIlpAddress = deriveChildAddress(ILP_ROOT_PREFIX, pubkey);\n resolvedIlpAddresses = [resolvedIlpAddress];\n }\n\n // Task 6.2: Initialize AddressRegistry with initial upstream prefixes\n const addressRegistry = new AddressRegistry();\n if (\n config.upstreamPrefixes !== undefined &&\n config.upstreamPrefixes.length > 0\n ) {\n for (let i = 0; i < config.upstreamPrefixes.length; i++) {\n addressRegistry.addAddress(\n config.upstreamPrefixes[i] as string,\n resolvedIlpAddresses[i] as string\n );\n }\n } else if (config.upstreamPrefix !== undefined) {\n addressRegistry.addAddress(config.upstreamPrefix, resolvedIlpAddress);\n } else if (config.ilpAddress !== undefined) {\n addressRegistry.addAddress('explicit', resolvedIlpAddress);\n } else {\n addressRegistry.addAddress(ILP_ROOT_PREFIX, resolvedIlpAddress);\n }\n\n const ilpInfo = {\n ilpAddress: resolvedIlpAddress,\n ilpAddresses: resolvedIlpAddresses,\n btpEndpoint: config.btpEndpoint ?? '',\n ...(config.httpEndpoint !== undefined && { httpEndpoint: config.httpEndpoint }),\n ...(config.supportsUpgrade !== undefined && { supportsUpgrade: config.supportsUpgrade }),\n assetCode: config.assetCode ?? 'USD',\n assetScale: config.assetScale ?? 6,\n feePerByte: String(config.feePerByte ?? 0n),\n };\n\n // 9. Branch: embedded mode vs standalone mode\n let ilpClient: IlpClient;\n let adminClient: ConnectorAdminClient;\n let channelClient: ConnectorChannelClient | null = null;\n let bootstrapServiceInstance: BootstrapService;\n let discoveryTrackerInstance: DiscoveryTracker;\n let httpServer: HttpServer | null = null;\n\n // Lifecycle delegates (start/stop differ per mode)\n let doStart: () => Promise<{\n bootstrapResults: BootstrapResult[];\n peerCount: number;\n channelCount: number;\n }>;\n let doStop: () => Promise<void>;\n\n // Track auto-created connector for cleanup\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let autoCreatedConnector: any = null;\n\n if (embeddedMode && hasConnector) {\n // --- EMBEDDED MODE (user-provided connector): delegate to createToonNode ---\n const toonNode = createToonNode({\n connector: config.connector as NonNullable<typeof config.connector>,\n handlePacket: pipelinedHandler,\n secretKey: config.secretKey,\n ilpInfo,\n toonEncoder: encoder,\n toonDecoder: decoder,\n relayUrl: config.relayUrl,\n knownPeers: config.knownPeers,\n settlementInfo: effectiveSettlementInfo,\n basePricePerByte: config.basePricePerByte,\n ardriveEnabled: config.ardriveEnabled,\n });\n\n ilpClient = toonNode.ilpClient;\n channelClient = toonNode.channelClient;\n bootstrapServiceInstance = toonNode.bootstrapService;\n discoveryTrackerInstance = toonNode.discoveryTracker;\n adminClient = {\n addPeer: () => Promise.resolve(),\n removePeer: () => Promise.resolve(),\n };\n\n trackerRef.current = discoveryTrackerInstance;\n\n doStart = async () => toonNode.start();\n doStop = async () => toonNode.stop();\n } else if (autoCreateConnector) {\n // --- AUTO-CREATE EMBEDDED MODE: deferred ConnectorNode creation in doStart() ---\n\n // Create placeholder bootstrap/discovery instances (wired during start)\n bootstrapServiceInstance = new BootstrapService(\n {\n knownPeers: config.knownPeers ?? [],\n ardriveEnabled: config.ardriveEnabled ?? false,\n defaultRelayUrl: config.relayUrl ?? '',\n settlementInfo: effectiveSettlementInfo,\n ownIlpAddress: ilpInfo.ilpAddress,\n toonEncoder: encoder,\n toonDecoder: decoder,\n basePricePerByte: config.basePricePerByte ?? 10n,\n },\n config.secretKey,\n ilpInfo\n );\n\n discoveryTrackerInstance = createDiscoveryTracker({\n secretKey: config.secretKey,\n settlementInfo: effectiveSettlementInfo,\n });\n\n // Placeholder clients (replaced during start)\n ilpClient = {\n sendIlpPacket: () =>\n Promise.reject(new NodeError('Node not started. Call start() first.')),\n };\n adminClient = {\n addPeer: () => Promise.resolve(),\n removePeer: () => Promise.resolve(),\n };\n\n trackerRef.current = discoveryTrackerInstance;\n\n doStart = async () => {\n // Dynamic import to keep @toon-protocol/connector as optional peer dep\n // eslint-disable-next-line @typescript-eslint/consistent-type-imports\n let ConnectorNodeClass: typeof import('@toon-protocol/connector').ConnectorNode;\n // eslint-disable-next-line @typescript-eslint/consistent-type-imports\n let createConnectorLogger: typeof import('@toon-protocol/connector').createLogger;\n try {\n const mod = await import('@toon-protocol/connector');\n ConnectorNodeClass = mod.ConnectorNode;\n createConnectorLogger = mod.createLogger;\n } catch {\n throw new NodeError(\n 'Auto-create connector requires @toon-protocol/connector as a peer dependency. ' +\n 'Install it with: pnpm add @toon-protocol/connector'\n );\n }\n\n const nodeId = `toon-${pubkey.slice(0, 16)}`;\n const btpServerPort = config.btpServerPort ?? 3000;\n const connectorLogger = createConnectorLogger(nodeId, 'warn');\n\n // Derive settlement private key from identity if not explicitly set\n let settlementPrivateKey = config.settlementPrivateKey;\n if (!settlementPrivateKey) {\n const keyBuffer = Buffer.from(config.secretKey);\n settlementPrivateKey = `0x${keyBuffer.toString('hex')}`;\n keyBuffer.fill(0);\n }\n\n const hasSettlementAddresses =\n chainConfig.registryAddress && chainConfig.tokenNetworkAddress;\n\n // Build connector config: chainProviders for settlement (v2.3.0+)\n const hasChainProviders =\n config.chainProviders !== undefined && config.chainProviders.length > 0;\n\n // When chainProviders not explicitly provided, build from legacy config\n const effectiveChainProviders = hasChainProviders\n ? config.chainProviders\n : hasSettlementAddresses\n ? [\n {\n chainType: 'evm' as const,\n chainId: `evm:${chainConfig.chainId ?? '31337'}`,\n rpcUrl: chainConfig.rpcUrl,\n registryAddress: chainConfig.registryAddress,\n tokenAddress: chainConfig.usdcAddress,\n privateKey: settlementPrivateKey,\n },\n ]\n : undefined;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const connectorConfig: any = {\n nodeId,\n btpServerPort,\n environment: 'development',\n deploymentMode: 'embedded',\n peers: [],\n routes: [],\n localDelivery: { enabled: false },\n };\n if (effectiveChainProviders) {\n connectorConfig.chainProviders = effectiveChainProviders;\n }\n if (config.transport) {\n connectorConfig.transport = config.transport;\n }\n if (config.nip59) {\n connectorConfig.nip59 = config.nip59;\n }\n\n autoCreatedConnector = new ConnectorNodeClass(\n connectorConfig,\n connectorLogger\n );\n\n await autoCreatedConnector.start();\n\n // Now wire the real connector into createToonNode\n const connector =\n autoCreatedConnector as unknown as EmbeddableConnectorLike;\n const toonNode = createToonNode({\n connector,\n handlePacket: pipelinedHandler,\n secretKey: config.secretKey,\n ilpInfo,\n toonEncoder: encoder,\n toonDecoder: decoder,\n relayUrl: config.relayUrl,\n knownPeers: config.knownPeers,\n settlementInfo: effectiveSettlementInfo,\n basePricePerByte: config.basePricePerByte,\n ardriveEnabled: config.ardriveEnabled,\n });\n\n // Replace placeholder clients with real ones\n ilpClient = toonNode.ilpClient;\n channelClient = toonNode.channelClient;\n\n // Wire bootstrap and discovery with the real admin client\n bootstrapServiceInstance.setIlpClient(toonNode.ilpClient);\n bootstrapServiceInstance.setConnectorAdmin({\n addPeer: () => Promise.resolve(),\n removePeer: () => Promise.resolve(),\n });\n if (toonNode.channelClient) {\n bootstrapServiceInstance.setChannelClient(toonNode.channelClient);\n }\n\n discoveryTrackerInstance.setConnectorAdmin({\n addPeer: () => Promise.resolve(),\n removePeer: () => Promise.resolve(),\n });\n if (toonNode.channelClient) {\n discoveryTrackerInstance.setChannelClient(toonNode.channelClient);\n }\n\n trackerRef.current = discoveryTrackerInstance;\n\n // Add self-routes for all addresses (Task 5.3)\n for (const addr of ilpInfo.ilpAddresses) {\n autoCreatedConnector.addRoute({\n prefix: addr,\n nextHop: nodeId,\n priority: 100,\n });\n }\n\n // Start the toonNode (wires packet handler, runs bootstrap)\n const result = await toonNode.start();\n return result;\n };\n\n doStop = async () => {\n if (autoCreatedConnector) {\n await autoCreatedConnector.stop();\n autoCreatedConnector = null;\n }\n };\n } else {\n // --- STANDALONE MODE: manual composition with HTTP clients ---\n const connectorUrl = config.connectorUrl as string;\n const handlerPort = config.handlerPort as number;\n\n ilpClient = createHttpIlpClient(connectorUrl);\n adminClient = createHttpConnectorAdmin(connectorUrl, '');\n\n // Channel client via HTTP if settlement is configured\n if (effectiveSettlementInfo) {\n channelClient = createHttpChannelClient(connectorUrl);\n }\n\n // Create BootstrapService\n bootstrapServiceInstance = new BootstrapService(\n {\n knownPeers: config.knownPeers ?? [],\n ardriveEnabled: config.ardriveEnabled ?? false,\n defaultRelayUrl: config.relayUrl ?? '',\n settlementInfo: effectiveSettlementInfo,\n ownIlpAddress: ilpInfo.ilpAddress,\n toonEncoder: encoder,\n toonDecoder: decoder,\n basePricePerByte: config.basePricePerByte ?? 10n,\n },\n config.secretKey,\n ilpInfo\n );\n\n bootstrapServiceInstance.setIlpClient(ilpClient);\n bootstrapServiceInstance.setConnectorAdmin(adminClient);\n if (channelClient) {\n bootstrapServiceInstance.setChannelClient(channelClient);\n }\n\n // Create DiscoveryTracker\n discoveryTrackerInstance = createDiscoveryTracker({\n secretKey: config.secretKey,\n settlementInfo: effectiveSettlementInfo,\n });\n discoveryTrackerInstance.setConnectorAdmin(adminClient);\n if (channelClient) {\n discoveryTrackerInstance.setChannelClient(channelClient);\n }\n\n trackerRef.current = discoveryTrackerInstance;\n\n doStart = async () => {\n // Start HTTP server for receiving ILP packets\n httpServer = createServer(async (req, res) => {\n if (req.method === 'GET' && req.url === '/health') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'healthy', pubkey }));\n return;\n }\n\n if (req.method === 'POST' && req.url === '/handle-packet') {\n let body = '';\n req.on('data', (chunk: Buffer) => {\n body += chunk.toString();\n });\n req.on('end', async () => {\n try {\n const request = JSON.parse(body) as HandlePacketRequest;\n if (\n request.amount === undefined ||\n request.amount === null ||\n request.destination === undefined ||\n request.destination === null ||\n request.data === undefined ||\n request.data === null\n ) {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(\n JSON.stringify({\n accept: false,\n code: 'F00',\n message: 'Missing required fields',\n })\n );\n return;\n }\n const result = await pipelinedHandler(request);\n res.writeHead(result.accept ? 200 : 400, {\n 'Content-Type': 'application/json',\n });\n res.end(JSON.stringify(result));\n } catch (err: unknown) {\n console.error('[SDK] handle-packet error:', err);\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(\n JSON.stringify({\n accept: false,\n code: 'T00',\n message: 'Internal server error',\n })\n );\n }\n });\n return;\n }\n\n res.writeHead(404);\n res.end();\n });\n\n await new Promise<void>((resolve) => {\n httpServer?.listen(handlerPort, () => resolve());\n });\n\n // Run bootstrap\n const results = await bootstrapServiceInstance.bootstrap();\n const bootstrapPeerPubkeys = results.map((r) => r.knownPeer.pubkey);\n discoveryTrackerInstance.addExcludedPubkeys(bootstrapPeerPubkeys);\n\n return {\n bootstrapResults: results,\n peerCount: results.length,\n channelCount: results.filter((r) => r.channelId).length,\n };\n };\n\n doStop = async () => {\n const server = httpServer;\n if (server) {\n await new Promise<void>((resolve) => {\n server.close(() => resolve());\n });\n httpServer = null;\n }\n };\n }\n\n // 10. Track SDK-level lifecycle state\n let started = false;\n let lastBootstrapResults: BootstrapResult[] = [];\n\n // 11. Build and return ServiceNode\n const node: ServiceNode = {\n get pubkey() {\n return pubkey;\n },\n get evmAddress() {\n return evmAddress;\n },\n get connector() {\n return (\n config.connector ??\n (autoCreatedConnector as unknown as EmbeddableConnectorLike) ??\n null\n );\n },\n get channelClient() {\n return channelClient;\n },\n\n getSkillDescriptor(): SkillDescriptor | undefined {\n return buildSkillDescriptor(registry, {\n basePricePerByte: config.basePricePerByte ?? 10n,\n kindPricing: config.kindPricing,\n ...config.skillConfig,\n });\n },\n\n on(\n kindOrEvent: number | string,\n handlerOrListener: Handler | BootstrapEventListener\n ): ServiceNode {\n if (typeof kindOrEvent === 'number') {\n // Handler registration (existing behavior)\n if (!Number.isInteger(kindOrEvent) || kindOrEvent < 0) {\n throw new NodeError(\n `Invalid event kind: expected a non-negative integer, got ${String(kindOrEvent)}`\n );\n }\n registry.on(kindOrEvent, handlerOrListener as Handler);\n } else if (kindOrEvent === 'bootstrap') {\n // Lifecycle event listener -- forward to bootstrapService AND discoveryTracker\n const listener = handlerOrListener as BootstrapEventListener;\n bootstrapServiceInstance.on(listener);\n discoveryTrackerInstance.on(listener);\n } else {\n // Sanitize event name to prevent log injection via control characters\n // eslint-disable-next-line no-control-regex\n const sanitized = String(kindOrEvent).replace(/[\\x00-\\x1f\\x7f]/g, '');\n throw new NodeError(\n `Unknown lifecycle event: '${sanitized}'. Supported: 'bootstrap'`\n );\n }\n return node;\n },\n\n onDefault(handler: Handler): ServiceNode {\n registry.onDefault(handler);\n return node;\n },\n\n async start(): Promise<StartResult> {\n if (started) {\n throw new NodeError('Node already started');\n }\n\n try {\n const result = await doStart();\n started = true;\n lastBootstrapResults = result.bootstrapResults;\n return {\n peerCount: result.peerCount,\n channelCount: result.channelCount,\n bootstrapResults: result.bootstrapResults,\n };\n } catch (error: unknown) {\n if (error instanceof NodeError) {\n throw error;\n }\n throw new NodeError(\n `Failed to start node: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n },\n\n async stop(): Promise<void> {\n if (!started) {\n return; // No-op if not started\n }\n\n await doStop();\n started = false;\n },\n\n async peerWith(targetPubkey: string): Promise<void> {\n if (!started) {\n throw new NodeError(\n 'Cannot peer: node not started. Call start() first.'\n );\n }\n // Defense-in-depth: validate pubkey format before delegating to core\n if (\n typeof targetPubkey !== 'string' ||\n targetPubkey.length !== 64 ||\n !/^[0-9a-f]{64}$/.test(targetPubkey)\n ) {\n throw new NodeError(\n 'Invalid pubkey: expected a 64-character lowercase hex string'\n );\n }\n return discoveryTrackerInstance.peerWith(targetPubkey);\n },\n\n async publishEvent(\n event: NostrEvent,\n options?: { destination: string; amount?: bigint; bid?: bigint }\n ): Promise<PublishEventResult> {\n // Guard: node must be started\n if (!started) {\n throw new NodeError(\n 'Cannot publish: node not started. Call start() first.'\n );\n }\n\n // Guard: destination is required\n if (!options?.destination) {\n throw new NodeError(\n \"Cannot publish: destination is required. Pass { destination: 'g.peer.address' }.\"\n );\n }\n\n try {\n // TOON-encode the event\n const toonData = encoder(event);\n\n // Resolve intermediary routing fees from discovered peers\n const { hopFees, warnings } = resolveRouteFees({\n destination: options.destination,\n ownIlpAddress: ilpInfo.ilpAddress,\n discoveredPeers: discoveryTrackerInstance.getAllDiscoveredPeers(),\n });\n\n // Log warnings about unknown intermediaries\n for (const warning of warnings) {\n console.warn(`[publishEvent] ${warning}`);\n }\n\n // Compute destination amount: use override or default basePricePerByte * bytes\n const destinationAmount =\n options.amount ??\n (config.basePricePerByte ?? 10n) * BigInt(toonData.length);\n\n // Bid safety cap: if bid is provided, reject if destination amount exceeds bid\n if (options.bid !== undefined && destinationAmount > options.bid) {\n throw new NodeError(\n `Cannot publish: destination amount ${destinationAmount} exceeds bid safety cap ${options.bid}`\n );\n }\n\n // Compute total amount: destination amount + intermediary route fees\n let amount: bigint;\n if (options.amount !== undefined) {\n // When amount override is provided, add only hop fees (no basePricePerByte)\n const hopFeesTotal = calculateRouteAmount({\n basePricePerByte: 0n,\n packetByteLength: toonData.length,\n hopFees,\n });\n amount = options.amount + hopFeesTotal;\n } else {\n // Default: basePricePerByte * bytes + hop fees\n amount = calculateRouteAmount({\n basePricePerByte: config.basePricePerByte ?? 10n,\n packetByteLength: toonData.length,\n hopFees,\n });\n }\n\n // Build ILP PREPARE packet using shared construction (packet equivalence\n // with x402 rail -- the destination relay cannot distinguish between\n // packets sent via publishEvent() and the x402 /publish endpoint).\n const packet = buildIlpPrepare({\n destination: options.destination,\n amount,\n data: toonData,\n });\n\n // Send via ILP client\n const result = await ilpClient.sendIlpPacket(packet);\n\n // Map IlpSendResult to PublishEventResult\n if (result.accepted) {\n return {\n success: true,\n eventId: event.id,\n ...(result.data !== undefined ? { data: result.data } : {}),\n };\n }\n\n return {\n success: false,\n eventId: event.id,\n code: result.code ?? 'T00',\n message: result.message ?? 'Unknown error',\n };\n } catch (error: unknown) {\n // Propagate NodeError directly\n if (error instanceof NodeError) {\n throw error;\n }\n throw new NodeError(\n `Failed to publish event: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n },\n\n async publishFeedback(\n requestEventId: string,\n customerPubkey: string,\n status: DvmJobStatus,\n content?: string,\n options?: { destination: string }\n ): Promise<PublishEventResult> {\n // Build Kind 7000 feedback event using provider's secretKey\n const feedbackEvent = buildJobFeedbackEvent(\n { requestEventId, customerPubkey, status, content },\n config.secretKey\n );\n\n // Delegate to publishEvent() for TOON encoding, pricing, and ILP delivery\n return node.publishEvent(feedbackEvent, options);\n },\n\n async publishResult(\n requestEventId: string,\n customerPubkey: string,\n amount: string,\n content: string,\n options?: { destination: string; kind?: number }\n ): Promise<PublishEventResult> {\n // Default result kind is 6100 (text generation result = 5100 + 1000)\n const resultKind = options?.kind ?? 6100;\n\n // Build Kind 6xxx result event using provider's secretKey\n const resultEvent = buildJobResultEvent(\n { kind: resultKind, requestEventId, customerPubkey, amount, content },\n config.secretKey\n );\n\n // Delegate to publishEvent() for TOON encoding, pricing, and ILP delivery\n return node.publishEvent(resultEvent, options);\n },\n\n async settleCompute(\n resultEvent: NostrEvent,\n providerIlpAddress: string,\n options?: { originalBid?: string }\n ): Promise<IlpSendResult> {\n // Deprecation warning\n console.warn(\n '[settleCompute] DEPRECATED: Use publishEvent() with { amount } option instead. The prepaid model sends job request + payment in one packet.'\n );\n\n // Guard: node must be started\n if (!started) {\n throw new NodeError(\n 'Cannot settle compute: node not started. Call start() first.'\n );\n }\n\n // Guard: providerIlpAddress must be a non-empty, non-whitespace string\n if (\n !providerIlpAddress ||\n typeof providerIlpAddress !== 'string' ||\n providerIlpAddress.trim() === ''\n ) {\n throw new NodeError(\n \"Cannot settle compute: providerIlpAddress is required. Resolve it from the provider's kind:10035 service discovery event.\"\n );\n }\n\n // Extract amount from result event via parseJobResult()\n const parsed = parseJobResult(resultEvent);\n if (!parsed) {\n throw new NodeError(\n 'Cannot settle compute: failed to parse result event. Ensure the event is a valid Kind 6xxx with an amount tag.'\n );\n }\n\n const computeAmount = parsed.amount;\n\n // Validate computeAmount is a valid non-negative numeric string before any\n // further processing. Without this, a non-numeric amount would propagate to\n // ilpClient.sendIlpPacket() and cause a BootstrapError instead of a clear\n // NodeError. Negative amounts would cause undefined behavior in the ILP layer.\n let amountBigInt: bigint;\n try {\n amountBigInt = BigInt(computeAmount);\n } catch {\n throw new NodeError(\n `Cannot settle compute: result event amount ('${computeAmount}') is not a valid numeric string.`\n );\n }\n if (amountBigInt < 0n) {\n throw new NodeError(\n `Cannot settle compute: result event amount ('${computeAmount}') must be non-negative.`\n );\n }\n\n // E5-R005 bid validation: if originalBid is provided, validate amount <= bid\n if (options?.originalBid !== undefined) {\n let bidBigInt: bigint;\n try {\n bidBigInt = BigInt(options.originalBid);\n } catch {\n throw new NodeError(\n `Cannot settle compute: originalBid ('${options.originalBid}') is not a valid numeric string.`\n );\n }\n if (amountBigInt > bidBigInt) {\n throw new NodeError(\n `Cannot settle compute: result amount (${computeAmount}) exceeds original bid (${options.originalBid}). Potential provider overcharge.`\n );\n }\n }\n\n // Send pure ILP value transfer (empty data = no event payload)\n // This is a payment-only operation -- no TOON encoding, no relay write.\n return ilpClient.sendIlpPacket({\n destination: providerIlpAddress,\n amount: computeAmount,\n data: '',\n });\n },\n\n // Task 6.3: Address lifecycle management for multi-peered nodes\n addUpstreamPeer(upstreamPrefix: string): void {\n const derivedAddress = deriveChildAddress(upstreamPrefix, pubkey);\n\n // Check for collision against existing addresses\n checkAddressCollision(derivedAddress, addressRegistry.getAddresses());\n\n // Update registry\n addressRegistry.addAddress(upstreamPrefix, derivedAddress);\n\n // Update ilpInfo for kind:10032 republication\n ilpInfo.ilpAddresses = addressRegistry.getAddresses();\n ilpInfo.ilpAddress =\n addressRegistry.getPrimaryAddress() ?? ilpInfo.ilpAddress;\n\n // Register self-route in embedded connector (if available)\n if (autoCreatedConnector) {\n const nodeId = `toon-${pubkey.slice(0, 16)}`;\n autoCreatedConnector.addRoute({\n prefix: derivedAddress,\n nextHop: nodeId,\n priority: 100,\n });\n }\n\n // Trigger kind:10032 republication to propagate updated address list\n if (started && lastBootstrapResults.length > 0) {\n void bootstrapServiceInstance.republish(lastBootstrapResults);\n }\n },\n\n removeUpstreamPeer(upstreamPrefix: string): void {\n // Guard: cannot remove the last address -- a node must always have at least one\n // ILP address (AC #3: empty ilpAddresses array is rejected at construction time).\n if (\n addressRegistry.hasPrefix(upstreamPrefix) &&\n addressRegistry.size <= 1\n ) {\n throw new NodeError(\n 'Cannot remove last upstream peer: a node must have at least one ILP address'\n );\n }\n\n const removedAddress = addressRegistry.removeAddress(upstreamPrefix);\n if (removedAddress === undefined) {\n return; // No-op if prefix not found\n }\n\n // Update ilpInfo for kind:10032 republication\n ilpInfo.ilpAddresses = addressRegistry.getAddresses();\n const newPrimary = addressRegistry.getPrimaryAddress();\n if (newPrimary !== undefined) {\n ilpInfo.ilpAddress = newPrimary;\n }\n\n // Remove self-route from embedded connector (if available)\n if (autoCreatedConnector && autoCreatedConnector.removeRoute) {\n autoCreatedConnector.removeRoute(removedAddress);\n }\n\n // Trigger kind:10032 republication to propagate updated address list\n if (started && lastBootstrapResults.length > 0) {\n void bootstrapServiceInstance.republish(lastBootstrapResults);\n }\n },\n\n async claimPrefix(\n prefix: string,\n upstreamDestination: string,\n options?: { prefixPrice?: bigint }\n ): Promise<PublishEventResult> {\n // Guard: node must be started\n if (!started) {\n throw new NodeError(\n 'Cannot claim prefix: node not started. Call start() first.'\n );\n }\n\n // Validate prefix format before sending payment (fail-fast, like bid safety cap)\n const prefixValidation = validatePrefix(prefix);\n if (!prefixValidation.valid) {\n throw new NodeError(\n `Cannot claim prefix: ${prefixValidation.reason ?? 'invalid prefix'}`\n );\n }\n\n // Determine the amount: explicit override, or look up upstream's prefixPricing\n let claimAmount = options?.prefixPrice;\n if (claimAmount === undefined) {\n // Look up from discovered peers\n const peers = discoveryTrackerInstance.getAllDiscoveredPeers();\n const upstreamPeer = peers.find(\n (p) =>\n p.peerInfo.ilpAddress === upstreamDestination ||\n (p.peerInfo.ilpAddresses &&\n p.peerInfo.ilpAddresses.includes(upstreamDestination))\n );\n if (upstreamPeer?.peerInfo.prefixPricing?.basePrice) {\n claimAmount = BigInt(upstreamPeer.peerInfo.prefixPricing.basePrice);\n } else {\n throw new NodeError(\n 'Cannot claim prefix: no amount provided and upstream peer prefix pricing not found in discovery'\n );\n }\n }\n\n // Build the prefix claim event\n const claimEvent = buildPrefixClaimEvent(\n { requestedPrefix: prefix },\n config.secretKey\n );\n\n // Delegate to publishEvent with the amount override\n return node.publishEvent(claimEvent, {\n destination: upstreamDestination,\n amount: claimAmount,\n });\n },\n };\n\n return node;\n}\n","/**\n * Skill descriptor builder for DVM service discovery.\n *\n * Computes a SkillDescriptor from the handler registry and node configuration.\n * The descriptor is embedded in kind:10035 events to advertise DVM capabilities.\n *\n * Returns `undefined` when no DVM handlers are registered (backward compatible\n * with pre-DVM kind:10035 events).\n */\n\nimport { ToonError } from '@toon-protocol/core';\nimport type { SkillDescriptor, ReputationScore } from '@toon-protocol/core';\nimport type { HandlerRegistry } from './handler-registry.js';\n\n/** Regex for 64-char lowercase hex string. */\nconst HEX_64_REGEX = /^[0-9a-f]{64}$/;\n\n/** Configuration for building a skill descriptor. */\nexport interface BuildSkillDescriptorConfig {\n /** Base price per byte in USDC micro-units (default: 10n). */\n basePricePerByte?: bigint;\n /** Per-kind pricing overrides (kind number -> price in USDC micro-units). */\n kindPricing?: Record<number, bigint>;\n /** Service name override (default: 'toon-dvm'). */\n name?: string;\n /** Schema version override (default: '1.0'). */\n version?: string;\n /** Feature list override. */\n features?: string[];\n /** JSON Schema draft-07 object for job request parameters. */\n inputSchema?: Record<string, unknown>;\n /** Available AI models. */\n models?: string[];\n /** Pre-computed reputation score for the skill descriptor. */\n reputation?: ReputationScore;\n /** TEE attestation metadata for the skill descriptor. */\n attestation?: {\n /** 64-char hex event ID of the provider's latest kind:10033 attestation event. */\n eventId: string;\n /** Enclave image hash for the TEE environment. */\n enclaveImageHash: string;\n };\n}\n\n/**\n * Builds a SkillDescriptor from the handler registry and configuration.\n *\n * Returns `undefined` when the registry has no DVM handlers (kinds 5000-5999).\n * When DVM kinds exist, auto-populates the descriptor:\n * - `kinds` from `registry.getDvmKinds()`\n * - `pricing` from `config.kindPricing` overrides or `config.basePricePerByte` fallback\n * - `name`, `version`, `features`, `inputSchema`, `models` from config or defaults\n *\n * @param registry - The handler registry to read DVM kinds from.\n * @param config - Node configuration for pricing and optional overrides.\n * @returns A SkillDescriptor, or undefined if no DVM handlers are registered.\n */\nexport function buildSkillDescriptor(\n registry: HandlerRegistry,\n config: BuildSkillDescriptorConfig = {}\n): SkillDescriptor | undefined {\n const dvmKinds = registry.getDvmKinds();\n if (dvmKinds.length === 0) {\n return undefined;\n }\n\n // Validate attestation eventId if provided\n if (config.attestation !== undefined) {\n if (!HEX_64_REGEX.test(config.attestation.eventId)) {\n throw new ToonError(\n 'Skill descriptor attestation eventId must be a 64-character lowercase hex string',\n 'DVM_SKILL_INVALID_ATTESTATION_EVENT_ID'\n );\n }\n }\n\n const basePricePerByte = config.basePricePerByte ?? 10n;\n const kindPricing = config.kindPricing ?? {};\n\n // Derive pricing: per-kind overrides or basePricePerByte fallback\n const pricing: Record<string, string> = {};\n for (const kind of dvmKinds) {\n if (Object.hasOwn(kindPricing, kind)) {\n pricing[String(kind)] = String(kindPricing[kind]);\n } else {\n pricing[String(kind)] = String(basePricePerByte);\n }\n }\n\n const descriptor: SkillDescriptor = {\n name: config.name ?? 'toon-dvm',\n version: config.version ?? '1.0',\n kinds: dvmKinds,\n features: config.features ?? [],\n inputSchema: config.inputSchema ?? { type: 'object', properties: {} },\n pricing,\n };\n\n if (config.models !== undefined) {\n descriptor.models = config.models;\n }\n\n if (config.reputation !== undefined) {\n descriptor.reputation = config.reputation;\n }\n\n if (config.attestation !== undefined) {\n descriptor.attestation = {\n eventId: config.attestation.eventId,\n enclaveImageHash: config.attestation.enclaveImageHash,\n };\n }\n\n return descriptor;\n}\n","/**\n * Workflow Orchestrator for multi-step DVM pipelines (Story 6.1).\n *\n * Manages the lifecycle of a workflow chain: creates step job requests,\n * detects step completion/failure, advances to the next step, handles\n * timeouts, and settles compute payments per step.\n *\n * The orchestrator uses the TOON relay as the orchestration layer --\n * there is no separate workflow engine. Step completion is detected\n * via relay event subscriptions (Kind 6xxx results, Kind 7000 feedback).\n *\n * State machine states:\n * - `pending`: Workflow created but not yet started\n * - `step_N_running`: Step N's job request published, waiting for result\n * - `step_N_failed`: Step N failed (error or timeout), workflow aborted\n * - `completed`: All steps finished successfully\n *\n * Settlement: Each step settles individually via settleCompute().\n * The orchestrator validates sum(step_amounts) <= total_bid before settlement.\n *\n * Forward-compatible with Epic 7 prepaid protocol: settlement logic is\n * isolated in handleStepResult() so swapping to prepaid per-step payment\n * requires changes only in that method.\n */\n\nimport type { NostrEvent } from 'nostr-tools/pure';\nimport { buildJobRequestEvent, parseJobFeedback } from '@toon-protocol/core';\nimport type { ParsedWorkflowDefinition } from '@toon-protocol/core';\nimport type { ServiceNode } from './create-node.js';\n\n/**\n * Workflow state type. Uses template literal types for step-indexed states.\n * Note: Timeout is represented as `step_N_failed` (timeout is a failure mode,\n * not a separate terminal state). The timeout cause is communicated in the\n * customer notification content.\n */\nexport type WorkflowState =\n | 'pending'\n | `step_${number}_running`\n | `step_${number}_failed`\n | 'completed';\n\n/**\n * Event store interface for workflow state persistence.\n * Minimal interface -- only store() and query() are needed.\n */\nexport interface WorkflowEventStore {\n store(event: NostrEvent): Promise<void>;\n query(filter: { kinds?: number[]; '#e'?: string[] }): Promise<NostrEvent[]>;\n}\n\n/**\n * Configuration options for the WorkflowOrchestrator.\n */\nexport interface WorkflowOrchestratorOptions {\n /** Secret key for signing step job request events (32-byte Uint8Array). */\n secretKey?: Uint8Array;\n /** Per-step timeout in milliseconds (default: 300000 = 5 minutes). */\n stepTimeoutMs?: number;\n /** Injectable time source for deterministic testing. */\n now?: () => number;\n /**\n * Injectable timer factory for deterministic testing.\n * Defaults to global setTimeout. Inject a custom implementation\n * to control timer advancement in tests without vi.useFakeTimers().\n */\n setTimer?: (\n callback: () => void,\n ms: number\n ) => ReturnType<typeof setTimeout>;\n /**\n * Injectable timer cancellation for deterministic testing.\n * Defaults to global clearTimeout. Must pair with setTimer.\n */\n clearTimer?: (handle: ReturnType<typeof setTimeout>) => void;\n /** Optional event store for workflow state persistence. */\n eventStore?: WorkflowEventStore;\n /** Default destination ILP address for publishing events. */\n destination?: string;\n /** Workflow definition event ID (for referencing in notifications). */\n workflowEventId?: string;\n /** Customer pubkey (for directing notifications). */\n customerPubkey?: string;\n}\n\n/**\n * Per-step state tracking.\n */\ninterface StepState {\n /** Index of this step (0-based). */\n index: number;\n /** The job request event ID published for this step. */\n requestEventId?: string;\n /** The result event received for this step. */\n resultEvent?: NostrEvent;\n /** Whether this step has been settled. */\n settled: boolean;\n}\n\n/**\n * Orchestrates a multi-step DVM workflow chain.\n *\n * Each instance manages a single workflow. For concurrent workflows,\n * create multiple WorkflowOrchestrator instances sharing the same\n * ServiceNode.\n */\nexport class WorkflowOrchestrator {\n private readonly node: ServiceNode;\n private readonly options: Required<\n Pick<WorkflowOrchestratorOptions, 'stepTimeoutMs'>\n > &\n WorkflowOrchestratorOptions;\n\n private state: WorkflowState = 'pending';\n private definition: ParsedWorkflowDefinition | null = null;\n private workflowEventId: string = '0'.repeat(64);\n private customerPubkey: string = '0'.repeat(64);\n private currentStepIndex = 0;\n private stepStates: StepState[] = [];\n private timeoutHandle: ReturnType<typeof setTimeout> | null = null;\n private processedResultIds = new Set<string>();\n\n constructor(node: ServiceNode, options?: WorkflowOrchestratorOptions) {\n this.node = node;\n this.options = {\n stepTimeoutMs: 300_000, // 5 minutes default\n ...options,\n };\n if (options?.workflowEventId) {\n this.workflowEventId = options.workflowEventId;\n }\n if (options?.customerPubkey) {\n this.customerPubkey = options.customerPubkey;\n }\n }\n\n /**\n * Returns the current workflow state.\n */\n getState(): WorkflowState {\n return this.state;\n }\n\n /**\n * Returns per-step state tracking data (for testing/debugging).\n */\n getStepStates(): readonly Readonly<StepState>[] {\n return this.stepStates;\n }\n\n /**\n * Starts a workflow: parses the definition, creates and publishes\n * the Kind 5xxx job request for step 1, sets state to step_1_running.\n */\n async startWorkflow(definition: ParsedWorkflowDefinition): Promise<void> {\n // Guard: prevent re-entrance -- calling startWorkflow on an already-started\n // orchestrator would silently overwrite state without cleaning up timeouts.\n if (this.state !== 'pending') {\n throw new Error(\n 'WorkflowOrchestrator.startWorkflow() called on a non-pending orchestrator. ' +\n 'Create a new WorkflowOrchestrator instance for each workflow.'\n );\n }\n\n // Guard: validate steps non-empty (defense-in-depth -- the builder validates\n // this too, but the orchestrator may receive ParsedWorkflowDefinition from\n // sources other than the builder, e.g. deserialized from EventStore).\n if (!definition.steps || definition.steps.length === 0) {\n throw new Error(\n 'WorkflowOrchestrator.startWorkflow() requires at least one step in the definition.'\n );\n }\n\n this.definition = definition;\n\n // Initialize step states\n this.stepStates = definition.steps.map((_, index) => ({\n index,\n settled: false,\n }));\n\n // Store the workflow definition event if eventStore is available\n if (this.options.eventStore) {\n // Create a synthetic event representing the workflow state.\n // Use a hash-like ID derived from pubkey + timestamp to avoid collisions\n // across concurrent workflows.\n const now = this.options.now ? this.options.now() : Date.now();\n const syntheticId = `wf${now.toString(16).padStart(16, '0')}${this.node.pubkey.slice(0, 48)}`;\n const stateEvent: NostrEvent = {\n id: syntheticId.padEnd(64, '0').slice(0, 64),\n pubkey: this.node.pubkey,\n created_at: Math.floor(now / 1000),\n kind: 10040,\n content: JSON.stringify({\n state: 'started',\n definition,\n }),\n tags: [],\n sig: '0'.repeat(128),\n };\n await this.options.eventStore.store(stateEvent);\n }\n\n // Create and publish step 1\n await this.publishStepRequest(\n 0,\n definition.initialInput.data,\n definition.initialInput.type\n );\n\n // Set state\n this.state = 'step_1_running';\n this.currentStepIndex = 0;\n\n // Start timeout for step 1\n this.startStepTimeout();\n }\n\n /**\n * Handles a Kind 6xxx result event for the current step.\n *\n * If the current step matches, extracts the result content and either:\n * - Advances to the next step (publishes Kind 5xxx for step N+1)\n * - Marks workflow as completed (if this was the final step)\n *\n * Idempotent: re-processing a result for an already-advanced step is a no-op.\n */\n async handleStepResult(resultEvent: NostrEvent): Promise<void> {\n if (!this.definition) return;\n\n // Validate result event kind is in the 6000-6999 range (Kind 6xxx).\n // Without this check, any event kind could be injected as a step result.\n if (resultEvent.kind < 6000 || resultEvent.kind > 6999) return;\n\n // Idempotency: skip already-processed result events\n if (this.processedResultIds.has(resultEvent.id)) return;\n\n // Idempotency: ignore if we've already moved past this step\n const currentStep = this.stepStates[this.currentStepIndex];\n if (!currentStep) return;\n if (currentStep.resultEvent) return; // Already processed\n\n // Ignore if workflow is in a terminal state\n if (this.state === 'completed' || this.state.endsWith('_failed')) return;\n\n // NOTE: The orchestrator trusts that the caller pre-filters result events\n // to match the current step's request (via relay subscription with `e` tag\n // filter on currentStep.requestEventId). Defense-in-depth validation of the\n // `e` tag could be added here but would require callers to ensure result\n // events always reference the correct request ID.\n\n // Mark this result as processed\n this.processedResultIds.add(resultEvent.id);\n\n // Clear timeout\n this.clearStepTimeout();\n\n // Record the result\n currentStep.resultEvent = resultEvent;\n\n // Settle compute for this step\n await this.settleStep(this.currentStepIndex, resultEvent);\n\n // Store step result in eventStore if available\n if (this.options.eventStore) {\n await this.options.eventStore.store(resultEvent);\n }\n\n // Check if this was the final step\n const isLastStep =\n this.currentStepIndex === this.definition.steps.length - 1;\n\n if (isLastStep) {\n // Workflow complete\n this.state = 'completed';\n\n // Notify customer with Kind 7000 success\n await this.publishWorkflowNotification(\n 'success',\n 'Workflow completed successfully'\n );\n } else {\n // Advance to next step\n const nextIndex = this.currentStepIndex + 1;\n const resultContent = resultEvent.content;\n\n await this.publishStepRequest(nextIndex, resultContent, 'text');\n\n this.currentStepIndex = nextIndex;\n this.state = `step_${nextIndex + 1}_running`;\n\n // Start timeout for next step\n this.startStepTimeout();\n }\n }\n\n /**\n * Handles a Kind 7000 feedback event for the current step.\n *\n * If the feedback status is 'error', marks the workflow as failed\n * and notifies the customer.\n */\n async handleStepFeedback(feedbackEvent: NostrEvent): Promise<void> {\n if (!this.definition) return;\n\n // Ignore if workflow is in a terminal state\n if (this.state === 'completed' || this.state.endsWith('_failed')) return;\n\n const parsed = parseJobFeedback(feedbackEvent);\n if (!parsed) return;\n\n if (parsed.status === 'error') {\n // Clear timeout\n this.clearStepTimeout();\n\n // Mark failed\n this.state = `step_${this.currentStepIndex + 1}_failed`;\n\n // Store failure in eventStore if available\n if (this.options.eventStore) {\n await this.options.eventStore.store(feedbackEvent);\n }\n\n // Notify customer\n await this.publishWorkflowNotification(\n 'error',\n `Workflow failed at step ${this.currentStepIndex + 1}: ${parsed.content || 'Unknown error'}`\n );\n }\n }\n\n /**\n * Cleans up resources (timeout handles).\n */\n destroy(): void {\n this.clearStepTimeout();\n }\n\n // ---------- Private Methods ----------\n\n /**\n * Publishes a Kind 5xxx job request for the given step.\n */\n private async publishStepRequest(\n stepIndex: number,\n inputData: string,\n inputType: string\n ): Promise<void> {\n if (!this.definition) return;\n\n const step = this.definition.steps[stepIndex];\n if (!step) return;\n\n // Compute bid for this step\n const stepBid = this.getStepBid(stepIndex);\n\n // Build the job request event\n const jobRequest = buildJobRequestEvent(\n {\n kind: step.kind,\n input: { data: inputData, type: inputType },\n bid: stepBid,\n output: 'text/plain',\n content: step.description,\n targetProvider: step.targetProvider,\n },\n this.getSecretKey()\n );\n\n // Publish via the node's publishEvent (which handles TOON encoding + ILP)\n const destination = this.options.destination ?? 'g.toon.local';\n await this.node.publishEvent(jobRequest, { destination });\n\n // Record the request event ID\n const stepState = this.stepStates[stepIndex];\n if (stepState) {\n stepState.requestEventId = jobRequest.id;\n }\n }\n\n /**\n * Settles compute payment for a completed step.\n */\n private async settleStep(\n stepIndex: number,\n resultEvent: NostrEvent\n ): Promise<void> {\n if (!this.definition) return;\n\n const stepState = this.stepStates[stepIndex];\n if (!stepState || stepState.settled) return;\n\n const step = this.definition.steps[stepIndex];\n if (!step) return;\n\n // Determine provider ILP address from result event pubkey or fallback.\n // In production, this would be resolved from the provider's kind:10035\n // service discovery event. For now, derive from the destination prefix.\n const providerIlpAddress = this.options.destination ?? 'g.toon.provider';\n\n try {\n await this.node.settleCompute(resultEvent, providerIlpAddress, {\n originalBid: this.getStepBid(stepIndex),\n });\n stepState.settled = true;\n } catch (_err: unknown) {\n // Settlement failure does not block workflow advancement.\n // The orchestrator prioritizes workflow progress over payment atomicity.\n // In production, the caller should monitor stepState.settled per step\n // and reconcile unsettled steps out-of-band.\n }\n }\n\n /**\n * Returns the bid allocation for a specific step.\n * Uses explicit bidAllocation if set, otherwise proportional split.\n */\n private getStepBid(stepIndex: number): string {\n if (!this.definition) return '0';\n\n const step = this.definition.steps[stepIndex];\n if (!step) return '0';\n\n // Use explicit allocation if available\n if (step.bidAllocation !== undefined) {\n return step.bidAllocation;\n }\n\n // Proportional split -- wrap BigInt conversion in try/catch since\n // totalBid may come from deserialized EventStore data that was not\n // validated by the builder.\n try {\n const totalBid = BigInt(this.definition.totalBid);\n const stepCount = BigInt(this.definition.steps.length);\n const perStep = totalBid / stepCount;\n return perStep.toString();\n } catch {\n return '0';\n }\n }\n\n /**\n * Publishes a Kind 7000 workflow notification to the customer.\n */\n private async publishWorkflowNotification(\n status: 'success' | 'error',\n content: string\n ): Promise<void> {\n const destination = this.options.destination ?? 'g.toon.local';\n try {\n await this.node.publishFeedback(\n this.workflowEventId,\n this.customerPubkey,\n status,\n content,\n { destination }\n );\n } catch (_err: unknown) {\n // Best-effort notification -- don't fail the workflow.\n // Notification delivery is not guaranteed; callers should\n // check orchestrator.getState() for authoritative status.\n }\n }\n\n /**\n * Starts a timeout timer for the current step.\n */\n private startStepTimeout(): void {\n this.clearStepTimeout();\n\n const timeoutMs = this.options.stepTimeoutMs;\n const timerFn = this.options.setTimer ?? setTimeout;\n\n this.timeoutHandle = timerFn(() => {\n // Mark as failed due to timeout\n this.state = `step_${this.currentStepIndex + 1}_failed`;\n\n // Notify customer\n void this.publishWorkflowNotification(\n 'error',\n `Workflow timed out at step ${this.currentStepIndex + 1} after ${timeoutMs}ms`\n );\n }, timeoutMs);\n }\n\n /**\n * Returns the secret key for signing step events.\n * Falls back to a deterministic key derived from the node pubkey\n * (for testing -- production should always pass secretKey in options).\n */\n private getSecretKey(): Uint8Array {\n if (this.options.secretKey) {\n return this.options.secretKey;\n }\n // Fallback: use a fixed test key (this path should not be hit in production)\n throw new Error(\n 'WorkflowOrchestrator requires secretKey in options to sign step events'\n );\n }\n\n /**\n * Clears the current step timeout.\n */\n private clearStepTimeout(): void {\n if (this.timeoutHandle !== null) {\n const cancelFn = this.options.clearTimer ?? clearTimeout;\n cancelFn(this.timeoutHandle);\n this.timeoutHandle = null;\n }\n }\n}\n","/**\n * Swarm Coordinator for competitive DVM bidding (Story 6.2).\n *\n * Manages the lifecycle of a competitive swarm: collects provider\n * submissions, handles timeout-based judging deadlines, validates\n * winner selection, and settles compute payment to the winner only.\n *\n * The coordinator uses the TOON relay as the orchestration layer --\n * there is no separate swarm engine. Submission arrival is detected\n * via relay event subscriptions (Kind 6xxx results).\n *\n * State machine states:\n * - `collecting`: Waiting for provider submissions (Kind 6xxx results)\n * - `judging`: Timeout or max submissions reached; customer selects winner\n * - `settled`: Winner paid via settleCompute(); swarm complete\n * - `failed`: No submissions within timeout or error\n *\n * Settlement: Only the winning provider receives compute payment.\n * Losing providers paid relay write fees (sunk cost) but receive\n * no compute payment -- this is by design to incentivize quality.\n *\n * Forward-compatible with Epic 7 prepaid protocol: settlement logic\n * is isolated in selectWinner() so swapping to prepaid per-winner\n * payment requires changes only in that method.\n */\n\nimport type { NostrEvent } from 'nostr-tools/pure';\nimport {\n ToonError,\n parseSwarmRequest,\n parseSwarmSelection,\n} from '@toon-protocol/core';\nimport type { ServiceNode } from './create-node.js';\nimport type { WorkflowEventStore } from './workflow-orchestrator.js';\n\n/**\n * Swarm state type.\n * - `collecting`: Waiting for provider submissions.\n * - `judging`: Timeout or max submissions reached; awaiting winner selection.\n * - `settled`: Winner paid; swarm complete.\n * - `failed`: No submissions within timeout or error.\n */\nexport type SwarmState = 'collecting' | 'judging' | 'settled' | 'failed';\n\n/**\n * Configuration options for the SwarmCoordinator.\n */\nexport interface SwarmCoordinatorOptions {\n /** Secret key for signing events (32-byte Uint8Array). */\n secretKey?: Uint8Array;\n /** Swarm collection timeout in milliseconds (default: 600000 = 10 minutes). */\n timeoutMs?: number;\n /** Injectable time source for deterministic testing. */\n now?: () => number;\n /**\n * Injectable timer factory for deterministic testing.\n * Defaults to global setTimeout. Inject a custom implementation\n * to control timer advancement in tests without vi.useFakeTimers().\n */\n setTimer?: (\n callback: () => void,\n ms: number\n ) => ReturnType<typeof setTimeout>;\n /**\n * Injectable timer cancellation for deterministic testing.\n * Defaults to global clearTimeout. Must pair with setTimer.\n */\n clearTimer?: (handle: ReturnType<typeof setTimeout>) => void;\n /** Optional event store for swarm state persistence. */\n eventStore?: WorkflowEventStore;\n /** Default destination ILP address for publishing events. */\n destination?: string;\n}\n\n/**\n * Coordinates a competitive DVM swarm.\n *\n * Each instance manages a single swarm. For concurrent swarms,\n * create multiple SwarmCoordinator instances sharing the same\n * ServiceNode.\n */\nexport class SwarmCoordinator {\n private readonly node: ServiceNode;\n private readonly options: Required<\n Pick<SwarmCoordinatorOptions, 'timeoutMs'>\n > &\n SwarmCoordinatorOptions;\n\n private state: SwarmState = 'collecting';\n private swarmRequestId = '';\n private customerPubkey = '';\n private maxProviders = 0;\n private submissions: NostrEvent[] = [];\n private timeoutHandle: ReturnType<typeof setTimeout> | null = null;\n private started = false;\n private settlementSucceeded = false;\n private submissionIds = new Set<string>();\n\n constructor(node: ServiceNode, options?: SwarmCoordinatorOptions) {\n this.node = node;\n this.options = {\n timeoutMs: 600_000, // 10 minutes default\n ...options,\n };\n }\n\n /**\n * Returns the current swarm state.\n */\n getState(): SwarmState {\n return this.state;\n }\n\n /**\n * Returns whether settlement was successfully completed.\n * Only meaningful when state is 'settled' (returns true).\n * If settlement fails, the state remains 'judging' and this returns false.\n */\n isSettlementSucceeded(): boolean {\n return this.settlementSucceeded;\n }\n\n /**\n * Returns the collected eligible submissions.\n */\n getSubmissions(): readonly NostrEvent[] {\n return this.submissions;\n }\n\n /**\n * Starts a swarm: parses the swarm request, initializes collection,\n * and starts the timeout timer.\n */\n async startSwarm(swarmRequest: NostrEvent): Promise<void> {\n const parsed = parseSwarmRequest(swarmRequest);\n if (!parsed) {\n throw new ToonError(\n 'Invalid swarm request event: missing swarm tag or invalid Kind 5xxx',\n 'DVM_INVALID_KIND'\n );\n }\n\n this.swarmRequestId = swarmRequest.id;\n this.customerPubkey = swarmRequest.pubkey;\n this.maxProviders = parsed.maxProviders;\n this.state = 'collecting';\n this.submissions = [];\n this.submissionIds = new Set<string>();\n this.started = true;\n\n // Store the swarm request in EventStore if available\n if (this.options.eventStore) {\n await this.options.eventStore.store(swarmRequest);\n }\n\n // Start timeout timer\n this.startTimeout();\n }\n\n /**\n * Handles a Kind 6xxx result event (provider submission).\n *\n * Validates the `e` tag references the swarm request, adds to the\n * submissions list, and checks if max providers is reached.\n *\n * Late submissions (after timeout or max reached) are ignored.\n * Submissions before startSwarm() are silently ignored.\n */\n async handleSubmission(resultEvent: NostrEvent): Promise<void> {\n if (!this.started) return;\n\n // Only accept submissions during collecting state\n if (this.state !== 'collecting') return;\n\n // Validate result event kind is in the 6000-6999 range\n if (resultEvent.kind < 6000 || resultEvent.kind > 6999) return;\n\n // Validate e tag references our swarm request\n const eTag = resultEvent.tags.find((t: string[]) => t[0] === 'e');\n if (!eTag) return;\n const requestEventId = eTag[1];\n if (requestEventId === undefined || requestEventId !== this.swarmRequestId)\n return;\n\n // Deduplicate by event ID -- relay normally handles dedup, but the\n // coordinator must also guard against duplicate submissions reaching\n // the maxProviders threshold with the same event repeated.\n if (this.submissionIds.has(resultEvent.id)) return;\n this.submissionIds.add(resultEvent.id);\n\n // Add to submissions\n this.submissions.push(resultEvent);\n\n // Store in EventStore if available\n if (this.options.eventStore) {\n await this.options.eventStore.store(resultEvent);\n }\n\n // Check if max providers reached\n if (this.submissions.length >= this.maxProviders) {\n this.clearTimeout();\n this.state = 'judging';\n }\n }\n\n /**\n * Selects the winner and settles compute payment.\n *\n * Validates:\n * - Swarm is in `judging` state (not `collecting`, `settled`, or `failed`)\n * - Selection references a submission in the collected set\n * - Swarm has not already been settled (idempotency guard)\n *\n * @throws ToonError with code DVM_SWARM_ALREADY_SETTLED if already settled\n * @throws ToonError with code DVM_SWARM_INVALID_SELECTION if winner not in submissions\n */\n async selectWinner(selectionEvent: NostrEvent): Promise<void> {\n // Idempotency guard: reject if already settled\n if (this.state === 'settled') {\n throw new ToonError(\n 'Swarm has already been settled; duplicate selection rejected',\n 'DVM_SWARM_ALREADY_SETTLED'\n );\n }\n\n // Must be in judging state\n if (this.state !== 'judging') {\n throw new ToonError(\n `Cannot select winner in state '${this.state}'; swarm must be in 'judging' state`,\n 'DVM_SWARM_INVALID_SELECTION'\n );\n }\n\n // Parse the selection event to extract the winner\n // Validate that the selection event is from the swarm's customer\n if (selectionEvent.pubkey !== this.customerPubkey) {\n throw new ToonError(\n 'Selection event pubkey does not match the swarm request customer pubkey',\n 'DVM_SWARM_INVALID_SELECTION'\n );\n }\n\n const parsed = parseSwarmSelection(selectionEvent);\n if (!parsed) {\n throw new ToonError(\n 'Invalid swarm selection event: missing winner tag or invalid Kind 7000',\n 'DVM_SWARM_INVALID_SELECTION'\n );\n }\n\n // Validate that the selection references this swarm (not a different one)\n if (parsed.swarmRequestEventId !== this.swarmRequestId) {\n throw new ToonError(\n `Selection event references swarm '${parsed.swarmRequestEventId}' but this swarm is '${this.swarmRequestId}'`,\n 'DVM_SWARM_INVALID_SELECTION'\n );\n }\n\n // Validate the winner references a collected submission\n const winnerSubmission = this.submissions.find(\n (s) => s.id === parsed.winnerResultEventId\n );\n if (!winnerSubmission) {\n throw new ToonError(\n `Winner result event ID '${parsed.winnerResultEventId}' not found in collected submissions`,\n 'DVM_SWARM_INVALID_SELECTION'\n );\n }\n\n // Settle compute payment to the winning provider only\n const providerIlpAddress = this.options.destination ?? 'g.toon.provider';\n try {\n await this.node.settleCompute(winnerSubmission, providerIlpAddress);\n this.settlementSucceeded = true;\n } catch (_err: unknown) {\n // Settlement failed -- remain in 'judging' state so the caller can\n // retry selectWinner() with the same or different selection event.\n // Do NOT transition to 'settled' because no payment was made.\n this.settlementSucceeded = false;\n throw new ToonError(\n 'Settlement failed for winning provider; swarm remains in judging state for retry',\n 'DVM_SWARM_SETTLEMENT_FAILED'\n );\n }\n\n // Store selection in EventStore if available\n if (this.options.eventStore) {\n await this.options.eventStore.store(selectionEvent);\n }\n\n this.state = 'settled';\n }\n\n /**\n * Cleans up resources (timeout handles).\n */\n destroy(): void {\n this.clearTimeout();\n }\n\n // ---------- Private Methods ----------\n\n /**\n * Starts the collection timeout timer.\n */\n private startTimeout(): void {\n this.clearTimeout();\n\n const timeoutMs = this.options.timeoutMs;\n const timerFn = this.options.setTimer ?? setTimeout;\n\n this.timeoutHandle = timerFn(() => {\n if (this.state !== 'collecting') return;\n\n if (this.submissions.length === 0) {\n // No submissions -- transition to failed\n this.state = 'failed';\n\n // Publish \"no submissions\" Kind 7000 feedback to customer\n void this.publishNoSubmissionsFeedback();\n } else {\n // Some submissions collected -- transition to judging\n this.state = 'judging';\n }\n }, timeoutMs);\n }\n\n /**\n * Publishes a Kind 7000 feedback event indicating no submissions were received.\n */\n private async publishNoSubmissionsFeedback(): Promise<void> {\n const destination = this.options.destination ?? 'g.toon.local';\n try {\n await this.node.publishFeedback(\n this.swarmRequestId,\n this.customerPubkey,\n 'error',\n 'Swarm timed out with no submissions',\n { destination }\n );\n } catch (_err: unknown) {\n // Best-effort notification -- don't fail the state transition.\n }\n }\n\n /**\n * Clears the current timeout timer.\n */\n private clearTimeout(): void {\n if (this.timeoutHandle !== null) {\n const cancelFn = this.options.clearTimer ?? clearTimeout;\n cancelFn(this.timeoutHandle);\n this.timeoutHandle = null;\n }\n }\n}\n","/**\n * Prefix claim handler for the TOON prefix marketplace.\n *\n * Creates a handler that processes kind 10034 prefix claim events,\n * validates payment and prefix availability, and publishes grant\n * confirmations. Lives in SDK (not core) because it depends on\n * the SDK's Handler type and HandlerContext interface.\n */\n\nimport type { NostrEvent } from 'nostr-tools/pure';\nimport {\n parsePrefixClaimEvent,\n buildPrefixGrantEvent,\n validatePrefix,\n} from '@toon-protocol/core';\nimport type { Handler, HandlerResponse } from './handler-registry.js';\nimport type { HandlerContext } from './handler-context.js';\n\n/**\n * Options for creating a prefix claim handler.\n */\nexport interface PrefixClaimHandlerOptions {\n /** Pricing configuration for prefix claims. */\n prefixPricing: { basePrice: bigint };\n /** Secret key for signing grant events. */\n secretKey: Uint8Array;\n /** Returns the current map of claimed prefixes (prefix -> claimer pubkey). */\n getClaimedPrefixes: () => Map<string, string>;\n /**\n * Atomically claim a prefix for a pubkey. Returns true if the claim\n * succeeded, false if the prefix was already taken. This is the\n * serialization point for race condition defense.\n */\n claimPrefix: (prefix: string, claimerPubkey: string) => boolean;\n /** Publish the grant event (e.g., to the local relay). */\n publishGrant: (grantEvent: NostrEvent) => Promise<void>;\n /** ILP address prefix to include in the grant event (default: 'g.toon'). */\n ilpAddressPrefix?: string;\n}\n\n/**\n * Creates a handler for kind 10034 prefix claim events.\n *\n * The handler validates payment, prefix availability, and prefix format,\n * then atomically claims the prefix and publishes a grant confirmation.\n *\n * @param options - Handler configuration\n * @returns A Handler function for kind 10034 events\n */\nexport function createPrefixClaimHandler(\n options: PrefixClaimHandlerOptions\n): Handler {\n return async (ctx: HandlerContext): Promise<HandlerResponse> => {\n // 1. Parse the incoming event as PrefixClaimContent\n const event = ctx.decode();\n const claim = parsePrefixClaimEvent(event);\n if (!claim) {\n return ctx.reject('F06', 'Invalid prefix claim event: malformed content');\n }\n\n // 2. Validate the prefix format\n const validation = validatePrefix(claim.requestedPrefix);\n if (!validation.valid) {\n return ctx.reject(\n 'F06',\n `Invalid prefix: ${validation.reason ?? 'unknown validation error'}`\n );\n }\n\n // 3. Check payment is sufficient\n if (ctx.amount < options.prefixPricing.basePrice) {\n return ctx.reject(\n 'F06',\n `Insufficient payment: received ${ctx.amount}, required ${options.prefixPricing.basePrice}`\n );\n }\n\n // 4. Check prefix availability\n const claimedPrefixes = options.getClaimedPrefixes();\n if (claimedPrefixes.has(claim.requestedPrefix)) {\n return ctx.reject(\n 'F06',\n `PREFIX_TAKEN: prefix \"${claim.requestedPrefix}\" is already claimed`\n );\n }\n\n // 5. Atomically claim the prefix (serialization point for race conditions)\n const claimed = options.claimPrefix(claim.requestedPrefix, event.pubkey);\n if (!claimed) {\n return ctx.reject(\n 'F06',\n `PREFIX_TAKEN: prefix \"${claim.requestedPrefix}\" is already claimed`\n );\n }\n\n // 6. Build and publish grant event\n const prefix = options.ilpAddressPrefix ?? 'g.toon';\n const ilpAddress = `${prefix}.${claim.requestedPrefix}`;\n const grantEvent = buildPrefixGrantEvent(\n {\n grantedPrefix: claim.requestedPrefix,\n claimerPubkey: event.pubkey,\n ilpAddress,\n },\n options.secretKey\n );\n await options.publishGrant(grantEvent);\n\n // 7. Accept the ILP packet\n return ctx.accept();\n };\n}\n","/**\n * Arweave DVM handler for kind:5094 blob storage requests.\n *\n * Creates a handler function that:\n * 1. Parses the kind:5094 event from the HandlerContext\n * 2. For single-packet uploads: uploads directly to Arweave via the adapter\n * 3. For chunked uploads: accumulates via ChunkManager, uploads on completion\n *\n * The handler does NOT validate pricing -- the SDK pricing validator\n * (packages/sdk/src/pricing-validator.ts) runs BEFORE the handler is invoked.\n *\n * Returns HandlePacketAcceptResponse with `data` field containing the Arweave\n * tx ID -- NOT via ctx.accept() which only supports metadata.\n */\n\nimport { parseBlobStorageRequest } from '@toon-protocol/core';\nimport type { HandlerContext } from '../handler-context.js';\nimport type { HandlerResponse } from '../handler-registry.js';\nimport type { ArweaveUploadAdapter } from './turbo-adapter.js';\nimport type { ChunkManager } from './chunk-manager.js';\n\n/**\n * Validates and sanitizes a MIME content type string.\n * Returns sanitized type or 'application/octet-stream' for invalid values.\n *\n * Defends against header injection (CWE-113) and ensures only well-formed\n * MIME types are forwarded to the Arweave upload adapter.\n */\nconst MIME_REGEX =\n /^[a-zA-Z0-9][a-zA-Z0-9!#$&\\-^_.+]*\\/[a-zA-Z0-9][a-zA-Z0-9!#$&\\-^_.+]*$/;\nfunction sanitizeContentType(contentType: string): string {\n // Strip any parameters (e.g., \"; charset=utf-8\") and whitespace\n const base = (contentType.split(';')[0] ?? '').trim();\n if (base.length > 0 && base.length <= 255 && MIME_REGEX.test(base)) {\n return base;\n }\n return 'application/octet-stream';\n}\n\nexport interface ArweaveDvmConfig {\n /** Arweave upload adapter (wraps @ardrive/turbo-sdk). */\n turboAdapter: ArweaveUploadAdapter;\n /** Chunk state manager for multi-packet uploads. */\n chunkManager: ChunkManager;\n /** Optional default Arweave data item tags (e.g., Content-Type). */\n arweaveTags?: Record<string, string>;\n}\n\n/**\n * Creates an Arweave DVM handler for kind:5094 blob storage requests.\n *\n * @param config - Handler configuration with adapter and chunk manager.\n * @returns A handler function compatible with HandlerRegistry.on().\n */\nexport function createArweaveDvmHandler(\n config: ArweaveDvmConfig\n): (ctx: HandlerContext) => Promise<HandlerResponse> {\n const { turboAdapter, chunkManager, arweaveTags } = config;\n\n return async (ctx: HandlerContext): Promise<HandlerResponse> => {\n // Decode the TOON payload into a full NostrEvent\n const event = ctx.decode();\n\n // Parse the kind:5094 event\n const parsed = parseBlobStorageRequest(event);\n if (!parsed) {\n return {\n accept: false,\n code: 'F00',\n message: 'Malformed kind:5094 blob storage request',\n };\n }\n\n // Build tags for Arweave upload.\n // Event-level Content-Type takes precedence over config-level arweaveTags.\n // Sanitize to prevent header injection (CWE-113) from user-controlled input.\n const uploadTags: Record<string, string> = {\n ...arweaveTags,\n 'Content-Type': sanitizeContentType(parsed.contentType),\n };\n\n // Check if this is a chunked upload\n if (\n parsed.uploadId !== undefined &&\n parsed.chunkIndex !== undefined &&\n parsed.totalChunks !== undefined\n ) {\n try {\n const result = chunkManager.addChunk(\n parsed.uploadId,\n parsed.chunkIndex,\n parsed.totalChunks,\n parsed.blobData\n );\n\n if (!result.complete) {\n // Intermediate chunk -- acknowledge receipt\n return {\n accept: true,\n data: Buffer.from(`ack:${parsed.chunkIndex}`).toString('base64'),\n };\n }\n\n // All chunks received -- upload assembled blob\n // assembled is guaranteed to be present when complete is true\n const assembled = result.assembled ?? Buffer.alloc(0);\n const { txId } = await turboAdapter.upload(assembled, uploadTags);\n\n // Base64-encode txId for ILP FULFILL data field (connector validates base64)\n return {\n accept: true,\n data: Buffer.from(txId).toString('base64'),\n };\n } catch (error) {\n // Log full error internally but return generic message to client (CWE-209)\n console.error(\n '[ArweaveDvmHandler] Chunked upload failed:',\n error instanceof Error ? (error.stack ?? error.message) : error\n );\n const internalMsg =\n error instanceof Error ? error.message : 'Unknown chunk error';\n // Only surface safe, expected error categories to callers\n const safeMsg = internalMsg.includes('Duplicate chunkIndex')\n ? 'Duplicate chunk rejected'\n : internalMsg.includes('Max active uploads')\n ? 'Server busy, too many concurrent uploads'\n : internalMsg.includes('exceeds max bytes')\n ? 'Upload size limit exceeded'\n : 'Chunk processing error';\n return {\n accept: false,\n code: 'F00',\n message: safeMsg,\n };\n }\n }\n\n // Single-packet upload\n try {\n const { txId } = await turboAdapter.upload(parsed.blobData, uploadTags);\n // Base64-encode txId for ILP FULFILL data field (connector validates base64)\n return {\n accept: true,\n data: Buffer.from(txId).toString('base64'),\n };\n } catch (error) {\n // Log the full underlying error server-side for operator/gate diagnostics\n // (e.g. Turbo upload-service rejection, network timeout to upload.ardrive.io,\n // a >100 KB payload exceeding the unauthenticated free-tier limit, or a\n // throwing/no-credential adapter — see #146). The stub adapter used to\n // swallow the cause as a bare \"Arweave upload failed\"; we now always emit\n // the real reason here so the failure self-diagnoses from the dvm logs.\n // The client still only receives a generic message (CWE-209): we never\n // forward SDK internals over the wire.\n const cause =\n error instanceof Error ? (error.stack ?? error.message) : String(error);\n console.error(\n `[ArweaveDvmHandler] Arweave upload failed (${parsed.blobData.length} bytes). ` +\n `Cause: ${cause}`\n );\n return {\n accept: false,\n code: 'T00',\n message: 'Arweave upload failed',\n };\n }\n };\n}\n","/**\n * Arweave upload adapter wrapping @ardrive/turbo-sdk.\n *\n * This is the ONLY file that imports @ardrive/turbo-sdk, isolating the\n * external dependency behind an interface (risk E8-R002).\n */\n\nimport { Readable } from 'node:stream';\n\n/**\n * Interface for uploading data to Arweave.\n * Implementations wrap a specific Arweave upload SDK.\n */\nexport interface ArweaveUploadAdapter {\n upload(\n data: Buffer,\n tags?: Record<string, string>\n ): Promise<{ txId: string }>;\n}\n\n/**\n * Turbo SDK upload adapter using @ardrive/turbo-sdk.\n *\n * - Dev/free tier (<=100KB): pass no turboClient. getClient() lazily generates an\n * ephemeral RSA JWK and builds a TurboFactory.authenticated() client. The upload\n * service grants free small-data-item uploads to any valid signer regardless of\n * balance, so no deposit is required. NOTE: TurboFactory.unauthenticated() is NOT\n * usable here — in @ardrive/turbo-sdk >=1.40 uploadFile() exists only on the\n * authenticated client; the unauthenticated client exposes only uploadSignedDataItem().\n * - Prod (paid, uncapped): pass a TurboAuthenticatedClient from a funded wallet.\n *\n * The turboClient is typed as `unknown` to avoid importing @ardrive/turbo-sdk types\n * in this interface. The actual TurboAuthenticatedClient is duck-typed at runtime.\n */\nexport class TurboUploadAdapter implements ArweaveUploadAdapter {\n private turboClient: unknown;\n private initialized = false;\n\n constructor(turboClient?: unknown) {\n if (turboClient) {\n this.turboClient = turboClient;\n this.initialized = true;\n }\n }\n\n private async getClient(): Promise<unknown> {\n if (!this.initialized) {\n // Lazy-import to avoid loading @ardrive/turbo-sdk unless actually used\n const { TurboFactory } = await import('@ardrive/turbo-sdk/node');\n const Arweave = (await import('arweave')).default;\n const arweave = Arweave.init({});\n const jwk = await arweave.crypto.generateJWK();\n this.turboClient = TurboFactory.authenticated({ privateKey: jwk });\n this.initialized = true;\n }\n return this.turboClient;\n }\n\n async upload(\n data: Buffer,\n tags?: Record<string, string>\n ): Promise<{ txId: string }> {\n const client = (await this.getClient()) as {\n uploadFile(params: {\n fileStreamFactory: () => Readable;\n fileSizeFactory: () => number;\n dataItemOpts?: { tags: { name: string; value: string }[] };\n }): Promise<{ id: string }>;\n };\n\n // Convert Record<string, string> to tag array format\n const tagArray: { name: string; value: string }[] = [];\n if (tags) {\n for (const [name, value] of Object.entries(tags)) {\n tagArray.push({ name, value });\n }\n }\n\n const result = await client.uploadFile({\n fileStreamFactory: () => Readable.from(data),\n fileSizeFactory: () => data.length,\n ...(tagArray.length > 0 ? { dataItemOpts: { tags: tagArray } } : {}),\n });\n\n return { txId: result.id };\n }\n}\n","/**\n * Chunk state manager for multi-packet blob uploads.\n *\n * Accumulates chunks for a given uploadId, assembles them in order\n * when all chunks have arrived, and enforces timeout and memory caps.\n */\n\nexport interface ChunkManagerConfig {\n /** Timeout in milliseconds before partial uploads are discarded (default: 300_000 = 5 min). */\n timeoutMs?: number;\n /** Maximum number of concurrent active uploads (default: 100). */\n maxActiveUploads?: number;\n /** Maximum total bytes accumulated per upload before rejection (default: 50MB). */\n maxBytesPerUpload?: number;\n}\n\nexport interface AddChunkResult {\n /** Whether all chunks have been received and the blob is fully assembled. */\n complete: boolean;\n /** The assembled blob (only present when complete is true). */\n assembled?: Buffer;\n /** Error message if the chunk was rejected. */\n error?: string;\n}\n\ninterface UploadState {\n chunks: Map<number, Buffer>;\n totalChunks: number;\n timer: ReturnType<typeof setTimeout>;\n}\n\n/**\n * Manages chunked upload state for the Arweave DVM handler.\n */\nexport class ChunkManager {\n private uploads = new Map<string, UploadState>();\n private readonly timeoutMs: number;\n private readonly maxActiveUploads: number;\n private readonly maxBytesPerUpload: number;\n\n constructor(config: ChunkManagerConfig = {}) {\n this.timeoutMs = config.timeoutMs ?? 300_000;\n this.maxActiveUploads = config.maxActiveUploads ?? 100;\n this.maxBytesPerUpload = config.maxBytesPerUpload ?? 50 * 1024 * 1024; // 50MB\n }\n\n /**\n * Add a chunk for a given uploadId.\n *\n * @param uploadId - Unique identifier for the upload session.\n * @param chunkIndex - Zero-based index of this chunk.\n * @param totalChunks - Total number of chunks expected.\n * @param data - The chunk data.\n * @returns Result indicating completion status or error.\n * @throws Error if memory cap reached or duplicate chunk.\n */\n addChunk(\n uploadId: string,\n chunkIndex: number,\n totalChunks: number,\n data: Buffer\n ): AddChunkResult {\n let state = this.uploads.get(uploadId);\n\n if (!state) {\n // Check memory cap before creating new upload\n if (this.uploads.size >= this.maxActiveUploads) {\n throw new Error(\n `Max active uploads reached (${this.maxActiveUploads}). Cannot accept new uploadId.`\n );\n }\n\n // Create new upload state with timeout\n const timer = setTimeout(() => {\n this.cleanup(uploadId);\n }, this.timeoutMs);\n\n state = {\n chunks: new Map(),\n totalChunks,\n timer,\n };\n this.uploads.set(uploadId, state);\n }\n\n // Validate chunkIndex is within bounds\n if (chunkIndex < 0 || chunkIndex >= state.totalChunks) {\n throw new Error(\n `chunkIndex ${chunkIndex} out of bounds for totalChunks ${state.totalChunks} (uploadId ${uploadId})`\n );\n }\n\n // Check for duplicate chunk\n if (state.chunks.has(chunkIndex)) {\n throw new Error(\n `Duplicate chunkIndex ${chunkIndex} for uploadId ${uploadId}`\n );\n }\n\n // Check per-upload byte limit before storing\n let currentBytes = 0;\n for (const chunk of state.chunks.values()) {\n currentBytes += chunk.length;\n }\n if (currentBytes + data.length > this.maxBytesPerUpload) {\n this.cleanup(uploadId);\n throw new Error(\n `Upload ${uploadId} exceeds max bytes per upload (${this.maxBytesPerUpload})`\n );\n }\n\n // Store chunk\n state.chunks.set(chunkIndex, data);\n\n // Check if all chunks received\n if (state.chunks.size === state.totalChunks) {\n // Assemble in correct order (0, 1, 2, ...)\n const orderedChunks: Buffer[] = [];\n for (let i = 0; i < state.totalChunks; i++) {\n const chunk = state.chunks.get(i);\n if (!chunk) {\n throw new Error(`Missing chunk ${i} for upload`);\n }\n orderedChunks.push(chunk);\n }\n const assembled = Buffer.concat(orderedChunks);\n\n // Clean up state\n this.cleanup(uploadId);\n\n return { complete: true, assembled };\n }\n\n return { complete: false };\n }\n\n /**\n * Check if an upload is complete (all chunks received).\n * Returns false if the uploadId is unknown (cleaned up or never started).\n */\n isComplete(uploadId: string): boolean {\n const state = this.uploads.get(uploadId);\n if (!state) return false;\n return state.chunks.size === state.totalChunks;\n }\n\n /**\n * Clean up state for a given uploadId, clearing the timeout timer.\n */\n cleanup(uploadId: string): void {\n const state = this.uploads.get(uploadId);\n if (state) {\n clearTimeout(state.timer);\n this.uploads.delete(uploadId);\n }\n }\n\n /**\n * Clean up all active uploads and their timers.\n * Call this on node shutdown to prevent timer leaks.\n */\n destroyAll(): void {\n for (const [, state] of this.uploads) {\n clearTimeout(state.timer);\n }\n this.uploads.clear();\n }\n}\n","/**\n * Client-side helpers for uploading blobs to an Arweave DVM provider.\n *\n * - uploadBlob(): single-packet upload for blobs under the chunk threshold\n * - uploadBlobChunked(): multi-packet upload that splits large blobs into chunks\n *\n * Both use publishEvent() with the amount override (Story 7.6, D7-007).\n */\n\nimport { randomUUID } from 'node:crypto';\nimport type { NostrEvent } from 'nostr-tools/pure';\nimport { buildBlobStorageRequest } from '@toon-protocol/core';\nimport type { PublishEventResult } from '../create-node.js';\n\n/**\n * Minimal interface for publishing events. Matches ServiceNode.publishEvent().\n */\nexport interface PublishableNode {\n publishEvent(\n event: NostrEvent,\n options?: { destination: string; amount?: bigint }\n ): Promise<\n Pick<PublishEventResult, 'success' | 'eventId' | 'data' | 'message'>\n >;\n}\n\n/**\n * Options for single-packet blob upload.\n */\nexport interface UploadBlobOptions {\n /** MIME type of the blob (default: 'application/octet-stream'). */\n contentType?: string;\n /** Secret key for signing the event. */\n secretKey: Uint8Array;\n /** Price per byte in USDC micro-units for amount calculation. */\n pricePerByte?: bigint;\n}\n\n/**\n * Options for chunked blob upload.\n */\nexport interface UploadBlobChunkedOptions {\n /** Chunk size in bytes (default: 500_000, under 512KB threshold). */\n chunkSize?: number;\n /** MIME type of the blob (default: 'application/octet-stream'). */\n contentType?: string;\n /** Secret key for signing events. */\n secretKey: Uint8Array;\n /** Price per byte in USDC micro-units for amount calculation. */\n pricePerByte?: bigint;\n}\n\n/**\n * Upload a blob to an Arweave DVM provider in a single ILP packet.\n *\n * Uses publishEvent() with amount override (D7-007) to send the blob\n * and payment in one ILP PREPARE.\n *\n * @param node - The ServiceNode (or compatible) to publish through.\n * @param blob - The raw blob data to upload.\n * @param destination - The provider's ILP address.\n * @param options - Upload options including secretKey for signing.\n * @returns The Arweave transaction ID from the FULFILL data field.\n */\nexport async function uploadBlob(\n node: PublishableNode,\n blob: Buffer,\n destination: string,\n options: UploadBlobOptions\n): Promise<string> {\n const contentType = options.contentType ?? 'application/octet-stream';\n const pricePerByte = options.pricePerByte ?? 10n;\n const amount = BigInt(blob.length) * pricePerByte;\n\n const event = buildBlobStorageRequest(\n {\n blobData: blob,\n contentType,\n bid: amount.toString(),\n },\n options.secretKey\n );\n\n const result = await node.publishEvent(event, { destination, amount });\n\n if (!result.success) {\n throw new Error(`Blob upload failed: ${result.message ?? 'unknown error'}`);\n }\n\n // The tx ID is returned base64-encoded in the FULFILL data field.\n // Decode it to get the raw Arweave tx ID string.\n if (result.data) {\n return Buffer.from(result.data, 'base64').toString('utf-8');\n }\n return result.eventId;\n}\n\n/**\n * Upload a large blob to an Arweave DVM provider using chunked uploads.\n *\n * Splits the blob into chunks, each sent as a separate kind:5094 ILP PREPARE\n * with uploadId, chunkIndex, and totalChunks params. Each chunk carries its\n * own payment. The final chunk's FULFILL data contains the Arweave tx ID.\n *\n * @param node - The ServiceNode (or compatible) to publish through.\n * @param blob - The raw blob data to upload.\n * @param destination - The provider's ILP address.\n * @param options - Chunked upload options including secretKey for signing.\n * @returns The Arweave transaction ID from the final chunk's FULFILL data.\n */\nexport async function uploadBlobChunked(\n node: PublishableNode,\n blob: Buffer,\n destination: string,\n options: UploadBlobChunkedOptions\n): Promise<string> {\n if (blob.length === 0) {\n throw new Error('Cannot upload empty blob via chunked upload');\n }\n\n const chunkSize = options.chunkSize ?? 500_000;\n const contentType = options.contentType ?? 'application/octet-stream';\n const pricePerByte = options.pricePerByte ?? 10n;\n const uploadId = randomUUID();\n\n // Split blob into chunks\n const totalChunks = Math.ceil(blob.length / chunkSize);\n let lastResult: PublishEventResult | undefined;\n\n for (let i = 0; i < totalChunks; i++) {\n const start = i * chunkSize;\n const end = Math.min(start + chunkSize, blob.length);\n const chunkData = blob.subarray(start, end);\n const amount = BigInt(chunkData.length) * pricePerByte;\n\n const event = buildBlobStorageRequest(\n {\n blobData: Buffer.from(chunkData),\n contentType,\n bid: amount.toString(),\n params: [\n { key: 'uploadId', value: uploadId },\n { key: 'chunkIndex', value: String(i) },\n { key: 'totalChunks', value: String(totalChunks) },\n ],\n },\n options.secretKey\n );\n\n lastResult = await node.publishEvent(event, { destination, amount });\n\n if (!lastResult.success) {\n throw new Error(\n `Chunk ${i}/${totalChunks} upload failed: ${lastResult.message ?? 'unknown error'}`\n );\n }\n }\n\n // The final chunk's FULFILL data contains the Arweave tx ID (base64-encoded)\n // lastResult is guaranteed to be set since totalChunks >= 1\n if (!lastResult) {\n throw new Error('No chunks were uploaded');\n }\n if (lastResult.data) {\n return Buffer.from(lastResult.data, 'base64').toString('utf-8');\n }\n return lastResult.eventId;\n}\n","/**\n * EVM-specific settlement tx construction + signature verification\n * (Story 12.6 AC-7).\n *\n * @module\n * @since 12.6\n * @see _bmad-output/implementation-artifacts/12-6-build-settlement-tx.md\n */\n\nimport { secp256k1 } from '@noble/curves/secp256k1.js';\nimport { keccak_256 } from '@noble/hashes/sha3.js';\nimport { bytesToHex } from '@noble/hashes/utils.js';\n\nimport { SettlementTxError } from '../errors.js';\nimport type { AccumulatedClaim } from '../stream-swap.js';\nimport {\n balanceProofHashEvm,\n bigintToBytes32BE,\n concatBytes,\n hexToBytes,\n} from './hashes.js';\nimport type { SwapSignerConfig, SettlementBundle } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Function selector + event signature\n// ---------------------------------------------------------------------------\n\n// TODO(12.6 follow-up): verify against the TokenNetwork contract in\n// ../connector/packages/contracts/. Story 12.8 E2E will catch drift if the\n// real contract uses a different name/arity.\nconst EVM_SETTLEMENT_FUNCTION_SIGNATURE =\n 'updateBalance(bytes32,uint256,uint256,address,bytes)';\nconst EVM_SETTLEMENT_EVENT_SIGNATURE =\n 'SettlementSucceeded(bytes32,uint256,uint256,address)';\n\n/** 4-byte keccak256 function selector for the settlement call. */\nexport const EVM_SETTLEMENT_FUNCTION_SELECTOR: Uint8Array = keccak_256(\n new TextEncoder().encode(EVM_SETTLEMENT_FUNCTION_SIGNATURE)\n).slice(0, 4);\n\nconst EVM_SETTLEMENT_EVENT_TOPIC: string =\n '0x' +\n bytesToHex(\n keccak_256(new TextEncoder().encode(EVM_SETTLEMENT_EVENT_SIGNATURE))\n );\n\n// ---------------------------------------------------------------------------\n// Recover EVM signer address\n// ---------------------------------------------------------------------------\n\n/**\n * Recover the EVM signer address from an `AccumulatedClaim`'s 65-byte\n * `r||s||v` signature. Returns lowercase `0x`-prefixed 40-hex-char address.\n *\n * Reconstructs the balance-proof message hash via `balanceProofHashEvm` using\n * the claim's settlement-context fields (channelId, cumulativeAmount, nonce,\n * recipient) and recovers the secp256k1 public key.\n *\n * @throws {SettlementTxError} INVALID_SIGNATURE_LENGTH / INVALID_SIGNATURE_V /\n * MISSING_SETTLEMENT_METADATA.\n * @since 12.6\n */\nexport function recoverEvmSignerAddress(claim: AccumulatedClaim): string {\n if (\n claim.channelId === undefined ||\n claim.cumulativeAmount === undefined ||\n claim.nonce === undefined ||\n claim.recipient === undefined\n ) {\n throw new SettlementTxError(\n 'MISSING_SETTLEMENT_METADATA',\n 'Claim missing channelId/cumulativeAmount/nonce/recipient for EVM signer recovery'\n );\n }\n if (claim.claimBytes.length !== 65) {\n throw new SettlementTxError(\n 'INVALID_SIGNATURE_LENGTH',\n `EVM signature must be 65 bytes (r||s||v), got ${claim.claimBytes.length}`\n );\n }\n const v = claim.claimBytes[64];\n if (v !== 27 && v !== 28) {\n throw new SettlementTxError(\n 'INVALID_SIGNATURE_V',\n `EVM signature v must be 27 or 28, got ${v}`\n );\n }\n const recovery = v - 27;\n const compactRS = claim.claimBytes.slice(0, 64);\n\n let msgHash: Uint8Array;\n let uncompressedPubkey: Uint8Array;\n try {\n const channelIdBytes = hexToBytes(claim.channelId);\n const recipientBytes = hexToBytes(claim.recipient);\n if (channelIdBytes.length !== 32) {\n throw new Error(\n `channelId must be 32 bytes (got ${channelIdBytes.length})`\n );\n }\n if (recipientBytes.length !== 20) {\n throw new Error(\n `recipient must be 20 bytes (got ${recipientBytes.length})`\n );\n }\n msgHash = balanceProofHashEvm(\n channelIdBytes,\n BigInt(claim.cumulativeAmount),\n BigInt(claim.nonce),\n recipientBytes\n );\n const sig = secp256k1.Signature.fromBytes(\n compactRS,\n 'compact'\n ).addRecoveryBit(recovery);\n const point = sig.recoverPublicKey(msgHash);\n uncompressedPubkey = point.toBytes(false);\n } catch (err) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n `EVM signer recovery failed: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err }\n );\n }\n\n // Uncompressed pubkey is 65 bytes: 0x04 || X(32) || Y(32). Address =\n // last 20 bytes of keccak256(X||Y).\n const addrHash = keccak_256(uncompressedPubkey.slice(1));\n return '0x' + bytesToHex(addrHash.slice(-20)).toLowerCase();\n}\n\n// ---------------------------------------------------------------------------\n// Minimal ABI encoder (bytes32, uint256, uint256, address, bytes)\n// ---------------------------------------------------------------------------\n\nfunction padLeft32(bytes: Uint8Array): Uint8Array {\n if (bytes.length > 32) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n `cannot pad bytes of length ${bytes.length} to 32`\n );\n }\n const out = new Uint8Array(32);\n out.set(bytes, 32 - bytes.length);\n return out;\n}\n\nfunction padRight32(bytes: Uint8Array): Uint8Array {\n const padded = Math.ceil(bytes.length / 32) * 32;\n const out = new Uint8Array(padded);\n out.set(bytes, 0);\n return out;\n}\n\nfunction encodeUpdateBalanceCallData(\n channelIdBytes: Uint8Array,\n cumulativeAmount: bigint,\n nonce: bigint,\n recipientBytes: Uint8Array,\n signature: Uint8Array\n): Uint8Array {\n if (channelIdBytes.length !== 32) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n `channelId must be 32 bytes (got ${channelIdBytes.length})`\n );\n }\n if (recipientBytes.length !== 20) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n `recipient must be 20 bytes (got ${recipientBytes.length})`\n );\n }\n\n // Solidity ABI layout for `(bytes32, uint256, uint256, address, bytes)`:\n // head = channelId(32) || cumulative(32) || nonce(32) || recipient(32) || offset(32) = 160 bytes\n // tail = sigLen(32) || sigPadded(ceil(sigLen/32)*32)\n // Only `bytes` is dynamic; its head slot is the offset (160) into the\n // head+tail region (selector is NOT counted in the offset per spec).\n const channelIdWord = channelIdBytes; // already 32\n const cumulativeWord = bigintToBytes32BE(cumulativeAmount);\n const nonceWord = bigintToBytes32BE(nonce);\n const recipientWord = padLeft32(recipientBytes);\n const offsetWord = bigintToBytes32BE(160n);\n const sigLenWord = bigintToBytes32BE(BigInt(signature.length));\n const sigPadded = padRight32(signature);\n\n return concatBytes(\n EVM_SETTLEMENT_FUNCTION_SELECTOR,\n channelIdWord,\n cumulativeWord,\n nonceWord,\n recipientWord,\n offsetWord,\n sigLenWord,\n sigPadded\n );\n}\n\n// ---------------------------------------------------------------------------\n// Minimal RLP encoder (for EIP-155 unsigned tx)\n// ---------------------------------------------------------------------------\n\nfunction rlpEncodeBytes(bytes: Uint8Array): Uint8Array {\n if (bytes.length === 1 && bytes[0] !== undefined && bytes[0] < 0x80) {\n return bytes;\n }\n if (bytes.length < 56) {\n return concatBytes(new Uint8Array([0x80 + bytes.length]), bytes);\n }\n const lenBytes = bigintToMinimalBytes(BigInt(bytes.length));\n return concatBytes(new Uint8Array([0xb7 + lenBytes.length]), lenBytes, bytes);\n}\n\nfunction rlpEncodeList(items: Uint8Array[]): Uint8Array {\n const payload = concatBytes(...items);\n if (payload.length < 56) {\n return concatBytes(new Uint8Array([0xc0 + payload.length]), payload);\n }\n const lenBytes = bigintToMinimalBytes(BigInt(payload.length));\n return concatBytes(\n new Uint8Array([0xf7 + lenBytes.length]),\n lenBytes,\n payload\n );\n}\n\nfunction bigintToMinimalBytes(x: bigint): Uint8Array {\n if (x < 0n) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n 'bigintToMinimalBytes: negative input'\n );\n }\n if (x === 0n) return new Uint8Array(0);\n let hex = x.toString(16);\n if (hex.length % 2) hex = '0' + hex;\n const out = new Uint8Array(hex.length / 2);\n for (let i = 0; i < out.length; i++) {\n out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n }\n return out;\n}\n\nfunction rlpEncodeUint(x: bigint): Uint8Array {\n return rlpEncodeBytes(bigintToMinimalBytes(x));\n}\n\n/**\n * RLP-encode an EIP-155 unsigned transaction:\n * [nonce, gasPrice, gasLimit, to, value, data, chainId, 0, 0]\n */\nfunction rlpEncodeUnsignedTx(params: {\n nonce: bigint;\n gasPrice: bigint;\n gasLimit: bigint;\n to: Uint8Array; // 20 bytes\n value: bigint;\n data: Uint8Array;\n chainId: bigint;\n}): Uint8Array {\n return rlpEncodeList([\n rlpEncodeUint(params.nonce),\n rlpEncodeUint(params.gasPrice),\n rlpEncodeUint(params.gasLimit),\n rlpEncodeBytes(params.to),\n rlpEncodeUint(params.value),\n rlpEncodeBytes(params.data),\n rlpEncodeUint(params.chainId),\n rlpEncodeUint(0n),\n rlpEncodeUint(0n),\n ]);\n}\n\n// ---------------------------------------------------------------------------\n// buildEvmSettlementTx\n// ---------------------------------------------------------------------------\n\n/**\n * Produce a `SettlementBundle` for a winning EVM `AccumulatedClaim`.\n *\n * The bundle's `unsignedTxBytes` is RLP-encoded with placeholder gas fields\n * (tx-nonce / gasPrice / gasLimit = 0). Callers (direct sender OR a Chain\n * Bridge DVM) fill in real gas via {@link fillEvmSettlementTxGas} before\n * signing + broadcasting.\n *\n * @stable\n * @since 12.6\n */\nexport function buildEvmSettlementTx(\n winner: AccumulatedClaim,\n signer: SwapSignerConfig,\n recipient: string,\n selectedClaimIndex: number,\n claimsMerged: number\n): SettlementBundle {\n if (\n winner.channelId === undefined ||\n winner.cumulativeAmount === undefined ||\n winner.nonce === undefined ||\n winner.recipient === undefined ||\n winner.swapSignerAddress === undefined\n ) {\n throw new SettlementTxError(\n 'MISSING_SETTLEMENT_METADATA',\n 'EVM winner claim missing settlement-context fields'\n );\n }\n if (!signer.contractAddress) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `EVM SwapSignerConfig.contractAddress is required for chain ${winner.pair.to.chain}`\n );\n }\n if (\n typeof signer.chainId !== 'number' ||\n !Number.isInteger(signer.chainId) ||\n signer.chainId <= 0\n ) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `EVM SwapSignerConfig.chainId must be a positive integer, got ${signer.chainId}`\n );\n }\n\n const channelIdBytes = hexToBytes(winner.channelId);\n const recipientBytes = hexToBytes(recipient);\n const contractBytes = hexToBytes(signer.contractAddress);\n\n const calldata = encodeUpdateBalanceCallData(\n channelIdBytes,\n BigInt(winner.cumulativeAmount),\n BigInt(winner.nonce),\n recipientBytes,\n winner.claimBytes\n );\n\n const unsignedTxBytes = rlpEncodeUnsignedTx({\n nonce: 0n,\n gasPrice: 0n,\n gasLimit: 0n,\n to: contractBytes,\n value: 0n,\n data: calldata,\n chainId: BigInt(signer.chainId),\n });\n\n return {\n chain: winner.pair.to.chain,\n chainKind: 'evm',\n channelId: winner.channelId,\n cumulativeAmount: winner.cumulativeAmount,\n nonce: winner.nonce,\n recipient,\n swapSignerAddress: winner.swapSignerAddress,\n unsignedTxBytes,\n expectedEventSignature: EVM_SETTLEMENT_EVENT_TOPIC,\n claimsMerged,\n selectedClaimIndex,\n sourceChain: winner.pair.from.chain,\n sourceAssetCode: winner.pair.from.assetCode,\n };\n}\n\n// ---------------------------------------------------------------------------\n// fillEvmSettlementTxGas\n// ---------------------------------------------------------------------------\n\n/**\n * Re-encode an EVM settlement bundle's RLP with real tx-nonce / gasPrice /\n * gasLimit values. The `to`, `value`, `data`, `chainId` fields are preserved\n * from the original bundle.\n *\n * @stable\n * @since 12.6\n */\nexport function fillEvmSettlementTxGas(\n bundle: SettlementBundle,\n gas: { nonce: bigint; gasPrice: bigint; gasLimit: bigint },\n signer: SwapSignerConfig\n): Uint8Array {\n if (bundle.chainKind !== 'evm') {\n throw new SettlementTxError(\n 'UNSUPPORTED_CHAIN',\n `fillEvmSettlementTxGas requires chainKind=evm, got ${bundle.chainKind}`\n );\n }\n if (!signer.contractAddress) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n 'EVM SwapSignerConfig.contractAddress is required for gas-fill'\n );\n }\n if (typeof signer.chainId !== 'number' || signer.chainId <= 0) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n 'EVM SwapSignerConfig.chainId must be a positive integer'\n );\n }\n // Decode calldata from the original bundle's RLP: we know calldata starts\n // at a fixed offset in our own encoding, but safer to re-construct from\n // bundle fields using encodeUpdateBalanceCallData again.\n const channelIdBytes = hexToBytes(bundle.channelId);\n const recipientBytes = hexToBytes(bundle.recipient);\n const contractBytes = hexToBytes(signer.contractAddress);\n // We need the 65-byte signature back; extract it from the original bundle's\n // calldata payload, which is the last `signature.length` bytes before the\n // zero-padding. Simplest: re-build from settlement fields alone cannot\n // reproduce the signature — so we require the caller to pass it via the\n // bundle's embedded claim. Instead of complicating the API, parse the\n // signature out of bundle.unsignedTxBytes calldata.\n const sig = extractSignatureFromBundle(bundle);\n const calldata = encodeUpdateBalanceCallData(\n channelIdBytes,\n BigInt(bundle.cumulativeAmount),\n BigInt(bundle.nonce),\n recipientBytes,\n sig\n );\n return rlpEncodeUnsignedTx({\n nonce: gas.nonce,\n gasPrice: gas.gasPrice,\n gasLimit: gas.gasLimit,\n to: contractBytes,\n value: 0n,\n data: calldata,\n chainId: BigInt(signer.chainId),\n });\n}\n\n/**\n * Decode the 65-byte balance-proof signature from a bundle's RLP-encoded\n * calldata. The calldata layout is:\n * selector(4) || channelId(32) || cumulative(32) || nonce(32) ||\n * recipient(32) || offset(32) || sigLen(32) || sigPadded(ceil(sigLen/32)*32)\n */\nfunction extractSignatureFromBundle(bundle: SettlementBundle): Uint8Array {\n // We re-parse our own RLP encoding. For robustness, locate the data field\n // via a minimal RLP walk.\n const tx = bundle.unsignedTxBytes;\n const list = rlpDecodeList(tx);\n if (list.length < 6) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n 'unsignedTxBytes is not a 9-element RLP list'\n );\n }\n const data = list[5];\n if (!data) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n 'unsignedTxBytes missing data field'\n );\n }\n // data = selector(4) || head(5 * 32) || sigLen(32) || sigPadded\n const sigLenOffset = 4 + 5 * 32;\n if (data.length < sigLenOffset + 32) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n 'calldata too short to contain signature length word'\n );\n }\n const sigLenBytes = data.slice(sigLenOffset, sigLenOffset + 32);\n let sigLen = 0n;\n for (const b of sigLenBytes) sigLen = (sigLen << 8n) | BigInt(b);\n const sigStart = sigLenOffset + 32;\n const sigEnd = sigStart + Number(sigLen);\n if (sigEnd > data.length) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n 'calldata truncated before end of signature bytes'\n );\n }\n return data.slice(sigStart, sigEnd);\n}\n\n// Minimal RLP decoder (items as raw bytes; does NOT recurse into sublists).\nfunction rlpDecodeList(buf: Uint8Array): Uint8Array[] {\n if (buf.length === 0) {\n throw new SettlementTxError('ENCODING_FAILED', 'empty RLP input');\n }\n const first = buf[0] ?? 0;\n let offset: number;\n let listEnd: number;\n if (first >= 0xc0 && first <= 0xf7) {\n offset = 1;\n listEnd = 1 + (first - 0xc0);\n } else if (first >= 0xf8 && first <= 0xff) {\n const lenOfLen = first - 0xf7;\n let listLen = 0;\n for (let i = 0; i < lenOfLen; i++) {\n listLen = (listLen << 8) | (buf[1 + i] ?? 0);\n }\n offset = 1 + lenOfLen;\n listEnd = offset + listLen;\n } else {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n `rlpDecodeList: input is not a list (first byte 0x${first.toString(16)})`\n );\n }\n const items: Uint8Array[] = [];\n let p = offset;\n while (p < listEnd) {\n const b = buf[p] ?? 0;\n if (b < 0x80) {\n items.push(buf.slice(p, p + 1));\n p += 1;\n } else if (b <= 0xb7) {\n const len = b - 0x80;\n items.push(buf.slice(p + 1, p + 1 + len));\n p += 1 + len;\n } else if (b <= 0xbf) {\n const lenOfLen = b - 0xb7;\n let len = 0;\n for (let i = 0; i < lenOfLen; i++) {\n len = (len << 8) | (buf[p + 1 + i] ?? 0);\n }\n items.push(buf.slice(p + 1 + lenOfLen, p + 1 + lenOfLen + len));\n p += 1 + lenOfLen + len;\n } else {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n 'rlpDecodeList: nested list not supported in minimal decoder'\n );\n }\n }\n return items;\n}\n\n/**\n * Verify an EVM claim's signature by recovering the signer and comparing to\n * `expectedAddress`. Returns `{ valid, recovered }`.\n *\n * @since 12.6\n */\nexport function verifyEvmClaimSignature(\n claim: AccumulatedClaim,\n expectedAddress: string\n): { valid: boolean; recovered: string } {\n const recovered = recoverEvmSignerAddress(claim);\n const expected = expectedAddress.toLowerCase();\n return {\n valid: recovered.toLowerCase() === expected,\n recovered,\n };\n}\n","/**\n * Shared balance-proof hash helpers — re-exported from `@toon-protocol/core`.\n *\n * The canonical hash/field layouts (the single source of truth for every\n * signer and verifier) now live in `@toon-protocol/core`'s settlement module so\n * the `@toon-protocol/client` package can consume them without depending on the\n * SDK. This file preserves the historical `@toon-protocol/sdk` import surface\n * (Story 12.6 AC-6) so existing SDK consumers and `@toon-protocol/swap` (which\n * import these names via the SDK root export) are unaffected.\n *\n * Any change to a hash layout must be made in `@toon-protocol/core` — it\n * applies to the Swap signer, the SDK verifiers, and the client signers at once.\n *\n * @module\n * @since 12.6\n * @see _bmad-output/epics/epic-12-token-swap-primitive.md\n */\n\nexport {\n hexToBytes,\n bigintToBytes32BE,\n concatBytes,\n balanceProofHashEvm,\n balanceProofHashSolana,\n minaHashToField,\n balanceProofFieldsMina,\n} from '@toon-protocol/core';\n","/**\n * Mina-specific settlement: balance-proof signature verification +\n * settlement-bundle construction (Story 12.8 follow-up to 12.6 AC-9).\n *\n * ## What is implemented (and unit-tested)\n *\n * - {@link verifyMinaSignature}: verifies the Swap's off-chain balance-proof\n * signature using `mina-signer` (`verifyFields`). This is the EXACT inverse\n * of the Swap's {@link MinaPaymentChannelSigner} (`packages/swap/src/payment-channel-signer.ts`),\n * which signs `balanceProofFieldsMina(channelId, cumulativeAmount, nonce, recipient)`\n * via `mina-signer`'s `signFields` and emits the base58 signature string as\n * the claim's `claimBytes` (UTF-8). The field-element derivation lives in\n * `hashes.ts` so signer and verifier cannot drift (same single-source-of-\n * truth pattern as EVM/Solana).\n * - {@link buildMinaSettlementTx}: constructs a {@link SettlementBundle}\n * envelope (chain metadata + the verified balance proof bytes) so a Chain\n * Bridge DVM / direct sender has everything needed to drive the on-chain\n * claim. The signature is re-emitted verbatim in `unsignedTxBytes`.\n *\n * ## What remains (documented gap — NOT silently stubbed)\n *\n * The final on-chain step — submitting a Mina zkApp `claimFromChannel`\n * transaction that settles the channel — requires **o1js circuit compilation\n * + zk-SNARK proof generation** (Poseidon balance-commitment, zkApp method\n * proving). The connector already implements this in\n * `MinaPaymentChannelSDK.claimFromChannel()` (o1js, heavyweight). That proof\n * generation is intentionally NOT done here: o1js circuit compilation is far\n * too heavy for a unit-testable SDK helper and would pull a multi-hundred-MB\n * WASM dependency into every SDK consumer. Instead, the bundle carries the\n * verified balance proof so a Mina-capable settler (the connector's\n * `MinaPaymentChannelProvider`, or a future o1js-backed Chain Bridge DVM) can\n * generate the proof + broadcast. See the on-chain settlement note below\n * (connector 3.8.1 wires the dual-party claim path — connector#84).\n *\n * Note also: the Swap↔sender wire proof here (a Schnorr signature over four\n * field elements) is a DIFFERENT object than the connector's on-chain\n * `MinaPaymentChannelSDK` Poseidon-commitment proof shape\n * (`{ commitment, signature, nonce }`). The verifier matches the Swap's\n * actual emitted format (`MinaPaymentChannelSigner`), which is what a sender\n * receives on-wire.\n *\n * @module\n * @since 12.8\n */\n\nimport { SettlementTxError } from '../errors.js';\nimport type { AccumulatedClaim } from '../stream-swap.js';\nimport { balanceProofFieldsMina } from './hashes.js';\nimport type { SwapSignerConfig, SettlementBundle } from './types.js';\n\n/**\n * Network id the Swap signs with (`MinaPaymentChannelSigner` uses\n * `network: 'mainnet'`). The signature itself is network-agnostic for the\n * `signFields`/`verifyFields` path — `mina-signer` only folds the network id\n * into message-string hashing, not pre-hashed field arrays — but we keep this\n * aligned with the Swap for clarity and future-proofing.\n */\nconst MINA_NETWORK = 'mainnet';\n\n/**\n * Minimal structural type for the slice of the `mina-signer` `Client` we use.\n * Declared locally so the SDK does not hard-depend on the optional peer dep's\n * full type surface (the ambient `mina-signer.d.ts` only declares\n * `derivePublicKey`). `verifyFields` is synchronous.\n */\nexport interface MinaSignerClientLike {\n verifyFields(input: {\n data: bigint[];\n signature: string;\n publicKey: string;\n }): boolean;\n}\n\n/**\n * Constructor shape of the `mina-signer` default export.\n */\ntype MinaSignerClientCtor = new (opts: {\n network: string;\n}) => MinaSignerClientLike;\n\n/**\n * Lazily load `mina-signer` (optional peer dep) and instantiate a `Client`\n * bound to {@link MINA_NETWORK}. Returns `undefined` when the peer dep is not\n * installed — callers MUST treat that as \"cannot verify Mina claims\" rather\n * than \"claim is valid\".\n *\n * Mirrors the dynamic-import pattern in `identity.ts` (`deriveMinaIdentity`).\n *\n * @since 12.8\n */\nexport async function loadMinaSignerClient(): Promise<\n MinaSignerClientLike | undefined\n> {\n try {\n // `mina-signer` is an optional peer dep — dynamic import so the SDK\n // type-checks and runs without it installed.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const lib: any = await import('mina-signer');\n const Ctor: MinaSignerClientCtor = 'default' in lib ? lib.default : lib;\n return new Ctor({ network: MINA_NETWORK });\n } catch {\n return undefined;\n }\n}\n\n/**\n * Verify a Mina balance-proof Schnorr signature carried in an\n * `AccumulatedClaim`.\n *\n * Re-derives the signed field-element message via {@link balanceProofFieldsMina}\n * (identical to the Swap signer), decodes the claim's `claimBytes` to the\n * base58 signature string the Swap emitted, and verifies it against\n * `expectedSignerAddress` using a `mina-signer` `Client`.\n *\n * `client` MUST be a pre-loaded `mina-signer` `Client` (see\n * {@link loadMinaSignerClient}). It is injected (rather than imported inline)\n * so the synchronous `buildSettlementTx()` pipeline can verify Mina claims\n * without becoming async.\n *\n * @returns `true` iff the signature is valid for the given signer address.\n * @throws {SettlementTxError} on missing settlement metadata or a malformed\n * (non-UTF-8 / empty) signature payload.\n * @since 12.8\n */\nexport function verifyMinaSignature(\n claim: AccumulatedClaim,\n expectedSignerAddress: string,\n client: MinaSignerClientLike\n): boolean {\n if (\n claim.channelId === undefined ||\n claim.cumulativeAmount === undefined ||\n claim.nonce === undefined ||\n claim.recipient === undefined\n ) {\n throw new SettlementTxError(\n 'MISSING_SETTLEMENT_METADATA',\n 'Claim missing channelId/cumulativeAmount/nonce/recipient for Mina signature verify'\n );\n }\n if (claim.claimBytes.length === 0) {\n throw new SettlementTxError(\n 'INVALID_SIGNATURE_LENGTH',\n 'Mina claimBytes is empty (expected UTF-8 base58 signature string)'\n );\n }\n\n // The Swap emits the `mina-signer` base58 signature string as UTF-8 bytes.\n const signature = new TextDecoder().decode(claim.claimBytes);\n\n const fields = balanceProofFieldsMina(\n claim.channelId,\n BigInt(claim.cumulativeAmount),\n BigInt(claim.nonce),\n claim.recipient\n );\n\n try {\n return client.verifyFields({\n data: fields,\n signature,\n publicKey: expectedSignerAddress,\n });\n } catch {\n // `verifyFields` throws on a structurally invalid signature / publicKey.\n // Treat that as \"not valid\" rather than crashing the settlement pipeline.\n return false;\n }\n}\n\n/**\n * Discriminator for the Mina on-chain `claimFromChannel` zkApp method.\n *\n * On-chain settlement: the actual transaction is a Mina zkApp method call\n * requiring o1js circuit compilation + zk-SNARK proof generation. As of\n * `@toon-protocol/connector` 3.8.1 the connector's settlement executor wires\n * the dual-party `MinaPaymentChannelSDK.claimFromChannel()` path with the real\n * counterparty balance, salt, and both party signatures (connector#84 — earlier\n * builds passed single-sig/zeroed placeholders that left Mina claims\n * un-settleable). Proof generation + broadcast remain the connector-side\n * settler's responsibility and are intentionally out of scope for this SDK\n * helper — see the module docblock. The bundle below therefore carries the\n * verified balance-proof bytes + channel metadata for that settler to consume.\n */\n\n/**\n * Build a Mina {@link SettlementBundle} from a winning `AccumulatedClaim`.\n *\n * Unlike the EVM/Solana builders (which produce a chain-native unsigned tx),\n * this builder produces a settlement ENVELOPE: it validates the winning\n * claim's settlement context and re-emits the Swap's verified balance-proof\n * signature bytes in `unsignedTxBytes`. The actual on-chain `claimFromChannel`\n * zkApp transaction (o1js proof generation) is the responsibility of a\n * Mina-capable settler — see the module docblock + the on-chain settlement note.\n *\n * The signature carried here is the SAME `claimBytes` the sender already\n * verified via {@link verifyMinaSignature}, so a downstream settler does not\n * need to re-derive it.\n *\n * @since 12.8\n */\nexport function buildMinaSettlementTx(\n winner: AccumulatedClaim,\n signer: SwapSignerConfig,\n recipient: string,\n selectedClaimIndex: number,\n claimsMerged: number\n): SettlementBundle {\n if (\n winner.channelId === undefined ||\n winner.cumulativeAmount === undefined ||\n winner.nonce === undefined ||\n winner.recipient === undefined ||\n winner.swapSignerAddress === undefined\n ) {\n throw new SettlementTxError(\n 'MISSING_SETTLEMENT_METADATA',\n 'Mina winner claim missing settlement-context fields'\n );\n }\n if (!signer.address) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `Mina SwapSignerConfig.address is required for chain ${winner.pair.to.chain}`\n );\n }\n if (winner.claimBytes.length === 0) {\n throw new SettlementTxError(\n 'INVALID_SIGNATURE_LENGTH',\n 'Mina winner claimBytes is empty (expected UTF-8 base58 signature string)'\n );\n }\n\n // Envelope: the verified balance-proof signature bytes. A Mina-capable\n // settler generates the o1js claimFromChannel proof from (channelId,\n // cumulativeAmount, nonce, recipient, signature) — connector 3.8.1, #84.\n const unsignedTxBytes = winner.claimBytes;\n\n return {\n chain: winner.pair.to.chain,\n chainKind: 'mina',\n channelId: winner.channelId,\n cumulativeAmount: winner.cumulativeAmount,\n nonce: winner.nonce,\n recipient,\n swapSignerAddress: winner.swapSignerAddress,\n unsignedTxBytes,\n claimsMerged,\n selectedClaimIndex,\n sourceChain: winner.pair.from.chain,\n sourceAssetCode: winner.pair.from.assetCode,\n };\n}\n","/**\n * Solana-specific settlement tx construction + signature verification\n * (Story 12.6 AC-9).\n *\n * @module\n * @since 12.6\n * @see _bmad-output/implementation-artifacts/12-6-build-settlement-tx.md\n */\n\nimport { ed25519 } from '@noble/curves/ed25519.js';\nimport { sha256 } from '@noble/hashes/sha2.js';\n\nimport { SettlementTxError } from '../errors.js';\nimport { base58Decode, base58Encode } from '../identity.js';\nimport type { AccumulatedClaim } from '../stream-swap.js';\nimport { balanceProofHashSolana, concatBytes } from './hashes.js';\nimport type { SwapSignerConfig, SettlementBundle } from './types.js';\n\n/**\n * Verify a Solana Ed25519 balance-proof signature.\n *\n * @since 12.6\n */\nexport function verifyEd25519Signature(\n claim: AccumulatedClaim,\n expectedSignerAddress: string\n): boolean {\n if (\n claim.channelId === undefined ||\n claim.cumulativeAmount === undefined ||\n claim.nonce === undefined ||\n claim.recipient === undefined\n ) {\n throw new SettlementTxError(\n 'MISSING_SETTLEMENT_METADATA',\n 'Claim missing channelId/cumulativeAmount/nonce/recipient for Solana signature verify'\n );\n }\n if (claim.claimBytes.length !== 64) {\n throw new SettlementTxError(\n 'INVALID_SIGNATURE_LENGTH',\n `Solana signature must be 64 bytes, got ${claim.claimBytes.length}`\n );\n }\n const msgHash = balanceProofHashSolana(\n claim.channelId,\n BigInt(claim.cumulativeAmount),\n BigInt(claim.nonce),\n claim.recipient\n );\n let pubkeyBytes: Uint8Array;\n try {\n pubkeyBytes = base58Decode(expectedSignerAddress);\n } catch (err) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `Solana expected signer address is not valid base58: ${expectedSignerAddress}`,\n { cause: err }\n );\n }\n if (pubkeyBytes.length !== 32) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `Solana expected signer pubkey must be 32 bytes, got ${pubkeyBytes.length}`\n );\n }\n try {\n return ed25519.verify(claim.claimBytes, msgHash, pubkeyBytes);\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Anchor discriminator (default — may be overridden if the real program is\n// non-Anchor. TODO(12.6 follow-up): confirm via ../connector/packages/solana-program/.\n// Story 12.8 E2E against a real program will catch drift.)\n// ---------------------------------------------------------------------------\n\nconst SOLANA_UPDATE_BALANCE_DISCRIMINATOR: Uint8Array = sha256(\n new TextEncoder().encode('global:update_balance')\n).slice(0, 8);\n\n/**\n * Write an 8-byte little-endian representation of a non-negative bigint.\n * Throws if value > 2^64 - 1.\n */\nfunction bigintToBytes8LE(x: bigint): Uint8Array {\n if (x < 0n) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n 'bigintToBytes8LE: negative input'\n );\n }\n if (x > 0xffffffffffffffffn) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n 'bigintToBytes8LE: value exceeds 64 bits'\n );\n }\n const out = new Uint8Array(8);\n let v = x;\n for (let i = 0; i < 8; i++) {\n out[i] = Number(v & 0xffn);\n v >>= 8n;\n }\n return out;\n}\n\n/**\n * Build a Solana `SettlementBundle` from a winning AccumulatedClaim.\n *\n * Produces a serialized legacy `Message` (NOT `Transaction`, since Transaction\n * requires signatures). The Message header + account keys + placeholder\n * blockhash + single instruction invoking `signer.programId`.\n *\n * NOTE: This Message is a TEMPLATE. The caller (direct sender OR a Chain\n * Bridge DVM) MUST patch in a real recent blockhash before signing — the\n * current bundle carries an all-zero blockhash placeholder.\n *\n * @stable\n * @since 12.6\n */\nexport function buildSolanaSettlementTx(\n winner: AccumulatedClaim,\n signer: SwapSignerConfig,\n recipient: string,\n selectedClaimIndex: number,\n claimsMerged: number\n): SettlementBundle {\n if (\n winner.channelId === undefined ||\n winner.cumulativeAmount === undefined ||\n winner.nonce === undefined ||\n winner.recipient === undefined ||\n winner.swapSignerAddress === undefined\n ) {\n throw new SettlementTxError(\n 'MISSING_SETTLEMENT_METADATA',\n 'Solana winner claim missing settlement-context fields'\n );\n }\n if (!signer.programId) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `Solana SwapSignerConfig.programId is required for chain ${winner.pair.to.chain}`\n );\n }\n\n let programIdBytes: Uint8Array;\n let recipientBytes: Uint8Array;\n let swapBytes: Uint8Array;\n let channelIdBytes: Uint8Array;\n try {\n programIdBytes = base58Decode(signer.programId);\n recipientBytes = base58Decode(recipient);\n swapBytes = base58Decode(winner.swapSignerAddress);\n channelIdBytes = base58Decode(winner.channelId);\n } catch (err) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n `Solana settlement tx: base58 decode failed (${err instanceof Error ? err.message : String(err)})`,\n { cause: err }\n );\n }\n if (programIdBytes.length !== 32) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `Solana programId must be 32 bytes, got ${programIdBytes.length}`\n );\n }\n if (recipientBytes.length !== 32) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `Solana recipient must decode to 32 bytes, got ${recipientBytes.length}`\n );\n }\n if (swapBytes.length !== 32) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `Solana swapSignerAddress must decode to 32 bytes, got ${swapBytes.length}`\n );\n }\n if (channelIdBytes.length !== 32) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `Solana channelId must decode to 32 bytes, got ${channelIdBytes.length}`\n );\n }\n\n // Instruction data: discriminator(8) || cumulative(8 LE) || nonce(8 LE) || signature(64)\n const instructionData = concatBytes(\n SOLANA_UPDATE_BALANCE_DISCRIMINATOR,\n bigintToBytes8LE(BigInt(winner.cumulativeAmount)),\n bigintToBytes8LE(BigInt(winner.nonce)),\n winner.claimBytes\n );\n\n // Accounts: [recipient (signer), swap, channel-state, system-program, programId]\n // We construct a minimal Message. For simplicity, use 4 accounts:\n // [0] recipient (signer, writable)\n // [1] swap (writable)\n // [2] channel-state (writable, derived — we approximate via channelIdBytes)\n // [3] program (readonly)\n const accounts = [recipientBytes, swapBytes, channelIdBytes, programIdBytes];\n\n // Message header: [numRequiredSignatures, numReadonlySigned, numReadonlyUnsigned]\n const header = new Uint8Array([1, 0, 1]); // 1 signer (recipient), 1 readonly unsigned (program)\n\n // Compact-u16 length encoding (for small counts, one byte suffices)\n const accountsCountByte = new Uint8Array([accounts.length]);\n const recentBlockhash = new Uint8Array(32); // placeholder — caller patches\n const instructionsCountByte = new Uint8Array([1]);\n\n // Instruction: programIdIndex (u8) || accountsLen (compact-u16) ||\n // accountIndices(u8 each) || dataLen (compact-u16) || data\n const programIdIndex = new Uint8Array([3]); // index of program in accounts\n const instrAccountsLen = new Uint8Array([3]); // recipient, swap, channel-state\n const instrAccountIndices = new Uint8Array([0, 1, 2]);\n // Instruction data length as compact-u16 (assume <127)\n if (instructionData.length >= 0x80) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n `Solana instruction data too large for simple compact-u16 encoding: ${instructionData.length}`\n );\n }\n const instrDataLen = new Uint8Array([instructionData.length]);\n\n const instruction = concatBytes(\n programIdIndex,\n instrAccountsLen,\n instrAccountIndices,\n instrDataLen,\n instructionData\n );\n\n const unsignedTxBytes = concatBytes(\n header,\n accountsCountByte,\n ...accounts,\n recentBlockhash,\n instructionsCountByte,\n instruction\n );\n\n return {\n chain: winner.pair.to.chain,\n chainKind: 'solana',\n channelId: winner.channelId,\n cumulativeAmount: winner.cumulativeAmount,\n nonce: winner.nonce,\n recipient,\n swapSignerAddress: winner.swapSignerAddress,\n unsignedTxBytes,\n claimsMerged,\n selectedClaimIndex,\n sourceChain: winner.pair.from.chain,\n sourceAssetCode: winner.pair.from.assetCode,\n };\n}\n\n/**\n * Re-export base58 helpers so callers (e.g., a Chain Bridge DVM) can round-\n * trip Solana addresses without a second base58 impl.\n *\n * @since 12.6\n */\nexport { base58Decode, base58Encode };\n","/**\n * `buildSettlementTx()` — public entrypoint for Story 12.6.\n *\n * Takes the output `claims` array from `streamSwap()` + per-chain signer\n * config + per-chain recipient addresses, and produces one\n * {@link SettlementBundle} per unique `(chain, channelId)` group.\n *\n * @module\n * @since 12.6\n * @see _bmad-output/implementation-artifacts/12-6-build-settlement-tx.md\n */\n\nimport { SettlementTxError } from '../errors.js';\nimport { base58Decode } from '../identity.js';\nimport type { AccumulatedClaim } from '../stream-swap.js';\nimport {\n buildEvmSettlementTx,\n recoverEvmSignerAddress,\n verifyEvmClaimSignature,\n} from './evm.js';\nimport { buildMinaSettlementTx, verifyMinaSignature } from './mina.js';\nimport type { MinaSignerClientLike } from './mina.js';\nimport { buildSolanaSettlementTx, verifyEd25519Signature } from './solana.js';\nimport type {\n BuildSettlementTxParams,\n BuildSettlementTxResult,\n SwapSignerConfig,\n SettlementBundle,\n} from './types.js';\n\nconst EVM_ADDRESS_REGEX = /^0x[0-9a-f]{40}$/;\n\nfunction chainKindOf(chain: string): 'evm' | 'solana' | 'mina' | 'unknown' {\n if (chain.startsWith('evm:')) return 'evm';\n if (chain.startsWith('solana:')) return 'solana';\n if (chain.startsWith('mina:')) return 'mina';\n return 'unknown';\n}\n\n/**\n * Build raw unsigned settlement transactions from a bag of accumulated\n * swap claims.\n *\n * Algorithm:\n * 1. Validate params (synchronous throws on malformed input).\n * 2. Optionally verify each claim's signature. Rejected claims land in\n * `result.rejected[]` and are dropped from further processing.\n * 3. Group surviving claims by `(chain, channelId)`. Within each group,\n * assert recipient + swapSignerAddress consensus, unique nonces, and\n * non-decreasing cumulativeAmount with nonce.\n * 4. Pick the winning claim per group (highest nonce).\n * 5. Dispatch each winner to its chain-specific tx builder.\n *\n * @stable\n * @since 12.6\n * @throws {SettlementTxError} Synchronously on malformed input, group-level\n * inconsistency, or chain dispatch failures.\n *\n * @example\n * ```ts\n * const result = await streamSwap({ ... });\n * const { bundles } = buildSettlementTx({\n * claims: result.claims,\n * signers: {\n * 'evm:base:8453': {\n * address: '0xswap...',\n * contractAddress: '0xtokennetwork...',\n * chainId: 8453,\n * },\n * },\n * recipients: { 'evm:base:8453': '0xsender...' },\n * });\n * // Feed bundles[0].unsignedTxBytes into fillEvmSettlementTxGas → sign → eth_sendRawTransaction.\n * ```\n */\nexport function buildSettlementTx(\n params: BuildSettlementTxParams\n): BuildSettlementTxResult {\n // ---- 1. Validate ----\n if (!Array.isArray(params.claims) || params.claims.length === 0) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n 'claims array is empty or not an array'\n );\n }\n if (!params.signers || typeof params.signers !== 'object') {\n throw new SettlementTxError('INVALID_INPUT', 'signers map is required');\n }\n if (!params.recipients || typeof params.recipients !== 'object') {\n throw new SettlementTxError('INVALID_INPUT', 'recipients map is required');\n }\n\n const logger = params.logger;\n const verifySignatures = params.verifySignatures ?? true;\n\n // Every claim must carry the five settlement-context fields.\n for (let i = 0; i < params.claims.length; i++) {\n const c = params.claims[i];\n if (c === undefined) continue; // bounded by loop — defensive for TS narrowing\n if (\n c.channelId === undefined ||\n c.nonce === undefined ||\n c.cumulativeAmount === undefined ||\n c.recipient === undefined ||\n c.swapSignerAddress === undefined\n ) {\n throw new SettlementTxError(\n 'MISSING_SETTLEMENT_METADATA',\n `claims[${i}] missing one or more of { channelId, nonce, cumulativeAmount, recipient, swapSignerAddress }`\n );\n }\n }\n\n // Every distinct chain must have a signer + recipient + per-chain config validity.\n const distinctChains = new Set<string>();\n for (const c of params.claims) distinctChains.add(c.pair.to.chain);\n for (const chain of distinctChains) {\n const signer = params.signers[chain];\n if (!signer) {\n throw new SettlementTxError(\n 'UNSUPPORTED_CHAIN',\n `signers map missing entry for chain ${chain}`\n );\n }\n if (!(chain in params.recipients)) {\n throw new SettlementTxError(\n 'MISSING_RECIPIENT',\n `recipients map missing entry for chain ${chain}`\n );\n }\n const kind = chainKindOf(chain);\n if (kind === 'evm') {\n if (\n !signer.contractAddress ||\n !EVM_ADDRESS_REGEX.test(signer.contractAddress)\n ) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `EVM signers[${chain}].contractAddress must match 0x + 40 lowercase hex`\n );\n }\n if (\n typeof signer.chainId !== 'number' ||\n !Number.isInteger(signer.chainId) ||\n signer.chainId <= 0\n ) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `EVM signers[${chain}].chainId must be a positive integer`\n );\n }\n } else if (kind === 'solana') {\n if (!signer.programId || signer.programId.length === 0) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `Solana signers[${chain}].programId is required`\n );\n }\n // AC-4: programId MUST be a valid base58 string that decodes to 32 bytes.\n let programIdLen: number;\n try {\n programIdLen = base58Decode(signer.programId).length;\n } catch (err) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `Solana signers[${chain}].programId is not valid base58`,\n { cause: err }\n );\n }\n if (programIdLen !== 32) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `Solana signers[${chain}].programId must decode to 32 bytes (got ${programIdLen})`\n );\n }\n }\n }\n\n // ---- 2. Verify signatures (optional) ----\n const rejected: BuildSettlementTxResult['rejected'] = [];\n const survivors: AccumulatedClaim[] = [];\n for (const claim of params.claims) {\n if (!verifySignatures) {\n survivors.push(claim);\n continue;\n }\n const chain = claim.pair.to.chain;\n const signer = params.signers[chain];\n if (!signer) {\n // Already validated above; this branch is defensive for TS narrowing.\n throw new SettlementTxError(\n 'UNSUPPORTED_CHAIN',\n `signers map missing entry for chain ${chain} (unreachable — validated)`\n );\n }\n const kind = chainKindOf(chain);\n if (kind === 'evm') {\n try {\n const { valid, recovered } = verifyEvmClaimSignature(\n claim,\n signer.address\n );\n if (!valid) {\n rejected.push({\n claim,\n reason: 'SIGNER_MISMATCH',\n details: `recovered=${recovered} expected=${signer.address.toLowerCase()}`,\n });\n continue;\n }\n survivors.push(claim);\n } catch (err) {\n rejected.push({\n claim,\n reason: 'SIGNATURE_INVALID',\n details: err instanceof Error ? err.message : String(err),\n });\n }\n } else if (kind === 'solana') {\n try {\n const ok = verifyEd25519Signature(claim, signer.address);\n if (!ok) {\n rejected.push({\n claim,\n reason: 'SIGNER_MISMATCH',\n details: `ed25519.verify returned false against ${signer.address}`,\n });\n continue;\n }\n survivors.push(claim);\n } catch (err) {\n rejected.push({\n claim,\n reason: 'SIGNATURE_INVALID',\n details: err instanceof Error ? err.message : String(err),\n });\n }\n } else if (kind === 'mina') {\n if (!params.minaSignerClient) {\n // mina-signer is an optional peer dep loaded via async import; the\n // sync pipeline cannot load it itself. Reject (rather than pass\n // through unverified) so an absent client never lets an unverified\n // Mina claim settle.\n rejected.push({\n claim,\n reason: 'MINA_VERIFICATION_UNSUPPORTED',\n details:\n 'minaSignerClient not provided — load mina-signer via loadMinaSignerClient() and pass it in params.minaSignerClient to verify mina:* claims',\n });\n continue;\n }\n try {\n const ok = verifyMinaSignature(\n claim,\n signer.address,\n params.minaSignerClient\n );\n if (!ok) {\n rejected.push({\n claim,\n reason: 'SIGNER_MISMATCH',\n details: `mina-signer verifyFields returned false against ${signer.address}`,\n });\n continue;\n }\n survivors.push(claim);\n } catch (err) {\n rejected.push({\n claim,\n reason: 'SIGNATURE_INVALID',\n details: err instanceof Error ? err.message : String(err),\n });\n }\n } else {\n throw new SettlementTxError(\n 'UNSUPPORTED_CHAIN',\n `Unknown chain kind for ${chain}`\n );\n }\n }\n\n logger?.debug?.({\n event: 'build_settlement_tx.verified',\n survivorCount: survivors.length,\n rejectedCount: rejected.length,\n });\n\n // ---- 3. Group by (chain, channelId) ----\n interface Group {\n chain: string;\n channelId: string;\n claims: { claim: AccumulatedClaim; originalIndex: number }[];\n }\n const groups = new Map<string, Group>();\n // Need the original input index for bundle.selectedClaimIndex.\n const originalIndex = new Map<AccumulatedClaim, number>();\n for (let i = 0; i < params.claims.length; i++) {\n const c = params.claims[i];\n if (c !== undefined) originalIndex.set(c, i);\n }\n for (const claim of survivors) {\n const chain = claim.pair.to.chain;\n const channelId = claim.channelId;\n if (channelId === undefined) {\n // Validated earlier — defensive branch.\n throw new SettlementTxError(\n 'MISSING_SETTLEMENT_METADATA',\n 'claim.channelId undefined after validation (unreachable)'\n );\n }\n const key = `${chain}::${channelId}`;\n let g = groups.get(key);\n if (!g) {\n g = { chain, channelId, claims: [] };\n groups.set(key, g);\n }\n const idx = originalIndex.get(claim);\n g.claims.push({ claim, originalIndex: idx ?? -1 });\n }\n\n // ---- 3b. Validate each group ----\n const superseded: AccumulatedClaim[] = [];\n const bundles: SettlementBundle[] = [];\n for (const g of groups.values()) {\n if (g.claims.length === 0) continue;\n const first = g.claims[0];\n if (!first) continue;\n const firstRecipient = first.claim.recipient;\n const firstSwapSigner = first.claim.swapSignerAddress;\n if (firstRecipient === undefined || firstSwapSigner === undefined) {\n throw new SettlementTxError(\n 'MISSING_SETTLEMENT_METADATA',\n 'winner claim missing recipient/swapSignerAddress (unreachable after validation)'\n );\n }\n for (let i = 1; i < g.claims.length; i++) {\n const entry = g.claims[i];\n if (!entry) continue;\n const c = entry.claim;\n if (c.recipient !== firstRecipient) {\n throw new SettlementTxError(\n 'RECIPIENT_MISMATCH',\n `claims in channel ${g.channelId} disagree on recipient: ${firstRecipient} vs ${String(c.recipient)} (claim indices ${first.originalIndex}, ${entry.originalIndex})`\n );\n }\n if (c.swapSignerAddress !== firstSwapSigner) {\n throw new SettlementTxError(\n 'SWAP_SIGNER_MISMATCH',\n `claims in channel ${g.channelId} disagree on swapSignerAddress: ${firstSwapSigner} vs ${String(c.swapSignerAddress)}`\n );\n }\n }\n // Nonces strictly unique within group.\n const nonceSeen = new Set<string>();\n for (const entry of g.claims) {\n const nonceStr = entry.claim.nonce;\n if (nonceStr === undefined) {\n throw new SettlementTxError(\n 'MISSING_SETTLEMENT_METADATA',\n 'claim.nonce undefined (unreachable after validation)'\n );\n }\n if (nonceSeen.has(nonceStr)) {\n throw new SettlementTxError(\n 'DUPLICATE_NONCE',\n `channel ${g.channelId} has two claims with nonce ${nonceStr}`\n );\n }\n nonceSeen.add(nonceStr);\n }\n // Sort by nonce ascending, then enforce non-decreasing cumulativeAmount.\n const sorted = [...g.claims].sort((a, b) => {\n const an = BigInt(a.claim.nonce ?? '0');\n const bn = BigInt(b.claim.nonce ?? '0');\n return an < bn ? -1 : an > bn ? 1 : 0;\n });\n for (let i = 1; i < sorted.length; i++) {\n const prevE = sorted[i - 1];\n const currE = sorted[i];\n if (!prevE || !currE) continue;\n const prev = BigInt(prevE.claim.cumulativeAmount ?? '0');\n const curr = BigInt(currE.claim.cumulativeAmount ?? '0');\n if (curr < prev) {\n throw new SettlementTxError(\n 'NON_MONOTONIC_CUMULATIVE',\n `channel ${g.channelId} nonce ${String(currE.claim.nonce)} has cumulativeAmount ${curr} < previous nonce ${String(prevE.claim.nonce)} cumulativeAmount ${prev}`\n );\n }\n }\n\n // Winner = highest nonce = last entry in sorted array.\n const winnerEntry = sorted[sorted.length - 1];\n if (!winnerEntry) continue;\n const winner = winnerEntry.claim;\n\n if (params.includeSuperseded) {\n for (let i = 0; i < sorted.length - 1; i++) {\n const e = sorted[i];\n if (e) superseded.push(e.claim);\n }\n }\n\n // Dispatch per chain kind.\n const signer = params.signers[g.chain];\n const recipient = params.recipients[g.chain];\n if (!signer || recipient === undefined) {\n throw new SettlementTxError(\n 'UNSUPPORTED_CHAIN',\n `signers/recipients missing entry for ${g.chain} (unreachable after validation)`\n );\n }\n const kind = chainKindOf(g.chain);\n let bundle: SettlementBundle;\n if (kind === 'evm') {\n // Recipient address consistency check: bundle recipient MUST match\n // the claim's recipient. The recipients map is the sender's declared\n // address — any disagreement with the Swap-reported recipient signals\n // an adversarial Swap or caller misconfiguration.\n if (recipient.toLowerCase() !== firstRecipient.toLowerCase()) {\n throw new SettlementTxError(\n 'RECIPIENT_MISMATCH',\n `recipients[${g.chain}] (${recipient}) does not match Swap-reported recipient (${firstRecipient})`\n );\n }\n bundle = buildEvmSettlementTx(\n winner,\n signer,\n recipient.toLowerCase(),\n winnerEntry.originalIndex,\n g.claims.length\n );\n } else if (kind === 'solana') {\n if (recipient !== firstRecipient) {\n throw new SettlementTxError(\n 'RECIPIENT_MISMATCH',\n `recipients[${g.chain}] (${recipient}) does not match Swap-reported recipient (${firstRecipient})`\n );\n }\n bundle = buildSolanaSettlementTx(\n winner,\n signer,\n recipient,\n winnerEntry.originalIndex,\n g.claims.length\n );\n } else if (kind === 'mina') {\n // Mina addresses are case-sensitive base58 (no lowercasing, like Solana).\n if (recipient !== firstRecipient) {\n throw new SettlementTxError(\n 'RECIPIENT_MISMATCH',\n `recipients[${g.chain}] (${recipient}) does not match Swap-reported recipient (${firstRecipient})`\n );\n }\n bundle = buildMinaSettlementTx(\n winner,\n signer,\n recipient,\n winnerEntry.originalIndex,\n g.claims.length\n );\n } else {\n throw new SettlementTxError(\n 'UNSUPPORTED_CHAIN',\n `Unknown chain kind for ${g.chain}`\n );\n }\n bundles.push(bundle);\n }\n\n logger?.info?.({\n event: 'build_settlement_tx.complete',\n bundleCount: bundles.length,\n rejectedCount: rejected.length,\n supersededCount: superseded.length,\n });\n\n return { bundles, rejected, superseded };\n}\n\n/**\n * Standalone utility: verify a single `AccumulatedClaim`'s signature against\n * a `SwapSignerConfig` without running the full grouping/winner pipeline.\n *\n * Useful inside a `streamSwap()` `onPacket` callback for mid-stream claim\n * validation.\n *\n * For `mina:*` claims, pass a pre-loaded `mina-signer` `Client` as\n * `minaSignerClient` (see `loadMinaSignerClient()`); without it, Mina claims\n * return `MINA_VERIFICATION_UNSUPPORTED` (same contract as\n * `buildSettlementTx`).\n *\n * @stable\n * @since 12.6\n */\nexport function verifyAccumulatedClaim(\n claim: AccumulatedClaim,\n signer: SwapSignerConfig,\n minaSignerClient?: MinaSignerClientLike\n): { valid: true } | { valid: false; reason: string } {\n const kind = chainKindOf(claim.pair.to.chain);\n if (kind === 'evm') {\n try {\n const { valid, recovered } = verifyEvmClaimSignature(\n claim,\n signer.address\n );\n if (valid) return { valid: true };\n return {\n valid: false,\n reason: `SIGNER_MISMATCH: recovered=${recovered} expected=${signer.address.toLowerCase()}`,\n };\n } catch (err) {\n return {\n valid: false,\n reason: `SIGNATURE_INVALID: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n }\n if (kind === 'solana') {\n try {\n const ok = verifyEd25519Signature(claim, signer.address);\n return ok\n ? { valid: true }\n : {\n valid: false,\n reason: 'SIGNER_MISMATCH: ed25519.verify returned false',\n };\n } catch (err) {\n return {\n valid: false,\n reason: `SIGNATURE_INVALID: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n }\n if (kind === 'mina') {\n if (!minaSignerClient) {\n return {\n valid: false,\n reason:\n 'MINA_VERIFICATION_UNSUPPORTED: pass a mina-signer Client (loadMinaSignerClient()) as minaSignerClient to verify mina:* claims',\n };\n }\n try {\n const ok = verifyMinaSignature(claim, signer.address, minaSignerClient);\n return ok\n ? { valid: true }\n : {\n valid: false,\n reason: 'SIGNER_MISMATCH: mina-signer verifyFields returned false',\n };\n } catch (err) {\n return {\n valid: false,\n reason: `SIGNATURE_INVALID: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n }\n return {\n valid: false,\n reason: `UNSUPPORTED_CHAIN: ${claim.pair.to.chain}`,\n };\n}\n\n// Re-export helper for tests + external callers that need address recovery\n// without running the full pipeline.\nexport { recoverEvmSignerAddress };\n"],"mappings":"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","names":["ToonError","validatePrefix","hexToBytes","hexToBytes"]}
|
|
1
|
+
{"version":3,"sources":["../src/handler-context.ts","../src/handler-registry.ts","../src/pricing-validator.ts","../src/verification-pipeline.ts","../src/payment-handler-bridge.ts","../src/event-storage-handler.ts","../src/create-node.ts","../src/skill-descriptor.ts","../src/workflow-orchestrator.ts","../src/swarm-coordinator.ts","../src/prefix-claim-handler.ts","../src/arweave/arweave-dvm-handler.ts","../src/arweave/turbo-adapter.ts","../src/arweave/chunk-manager.ts","../src/arweave/chunked-upload.ts","../src/adaptive-controller.ts","../src/settlement/evm.ts","../src/settlement/hashes.ts","../src/settlement/mina.ts","../src/settlement/solana.ts","../src/settlement/build-settlement-tx.ts"],"sourcesContent":["/**\n * Handler context for @toon-protocol/sdk.\n *\n * Provides a context object passed to kind-based handlers with methods\n * for accessing TOON data, shallow-parsed metadata, and accept/reject actions.\n */\n\nimport type { NostrEvent } from 'nostr-tools/pure';\nimport type { ToonRoutingMeta } from '@toon-protocol/core/toon';\nimport type {\n HandlePacketAcceptResponse,\n HandlePacketRejectResponse,\n} from '@toon-protocol/core';\n\n// Re-export core response types so SDK consumers don't need to import from core\nexport type { HandlePacketAcceptResponse, HandlePacketRejectResponse };\n\n/**\n * The handler context passed to each kind-based handler.\n */\nexport interface HandlerContext {\n /** Raw TOON string (base64-encoded). */\n readonly toon: string;\n /** Event kind from shallow parse. */\n readonly kind: number;\n /** Event pubkey from shallow parse. */\n readonly pubkey: string;\n /** Payment amount in the ILP packet. */\n readonly amount: bigint;\n /** ILP destination address. */\n readonly destination: string;\n /** Lazy-decode the TOON payload into a full NostrEvent. */\n decode(): NostrEvent;\n /** Accept the packet with optional metadata. */\n accept(metadata?: Record<string, unknown>): HandlePacketAcceptResponse;\n /** Reject the packet with an ILP error code and message. */\n reject(code: string, message: string): HandlePacketRejectResponse;\n}\n\nexport interface CreateHandlerContextOptions {\n toon: string;\n meta: ToonRoutingMeta;\n amount: bigint;\n destination: string;\n toonDecoder: (toon: string) => NostrEvent;\n}\n\n/**\n * Creates a HandlerContext from the given options.\n */\nexport function createHandlerContext(\n options: CreateHandlerContextOptions\n): HandlerContext {\n let cachedEvent: NostrEvent | undefined;\n\n return {\n get toon() {\n return options.toon;\n },\n get kind() {\n return options.meta.kind;\n },\n get pubkey() {\n return options.meta.pubkey;\n },\n get amount() {\n return options.amount;\n },\n get destination() {\n return options.destination;\n },\n decode(): NostrEvent {\n if (!cachedEvent) {\n cachedEvent = options.toonDecoder(options.toon);\n }\n return cachedEvent;\n },\n accept(metadata?: Record<string, unknown>): HandlePacketAcceptResponse {\n return {\n accept: true,\n ...(metadata ? { metadata } : {}),\n };\n },\n reject(code: string, message: string): HandlePacketRejectResponse {\n return {\n accept: false,\n code,\n message,\n };\n },\n };\n}\n","/**\n * Handler registry for @toon-protocol/sdk.\n *\n * Maps event kinds to handler functions for dispatching incoming ILP packets.\n */\n\nimport { JOB_REQUEST_KIND_BASE } from '@toon-protocol/core';\nimport type {\n HandlerContext,\n HandlePacketAcceptResponse,\n HandlePacketRejectResponse,\n} from './handler-context.js';\n\nexport type HandlerResponse =\n | HandlePacketAcceptResponse\n | HandlePacketRejectResponse;\n\nexport type Handler = (ctx: HandlerContext) => Promise<HandlerResponse>;\n\n/**\n * Registry that maps Nostr event kinds to handler functions.\n */\nexport class HandlerRegistry {\n private handlers = new Map<number, Handler>();\n private defaultHandler: Handler | undefined;\n\n /**\n * Register a handler for a specific event kind.\n * Replaces any existing handler for that kind.\n */\n on(kind: number, handler: Handler): this {\n this.handlers.set(kind, handler);\n return this;\n }\n\n /**\n * Register a default handler for unrecognized kinds.\n */\n onDefault(handler: Handler): this {\n this.defaultHandler = handler;\n return this;\n }\n\n /**\n * Returns all registered kind numbers, sorted ascending.\n */\n getRegisteredKinds(): number[] {\n return [...this.handlers.keys()].sort((a, b) => a - b);\n }\n\n /**\n * Returns the handler registered for `kind`, or `undefined` if none.\n * Added for Story 12.7 AC-10 (handler-registration verification).\n */\n get(kind: number): Handler | undefined {\n return this.handlers.get(kind);\n }\n\n /**\n * Returns registered kinds in the DVM request range (5000-5999), sorted ascending.\n * Uses JOB_REQUEST_KIND_BASE (5000) as the range start.\n */\n getDvmKinds(): number[] {\n const dvmRangeStart = JOB_REQUEST_KIND_BASE;\n const dvmRangeEnd = JOB_REQUEST_KIND_BASE + 999;\n return this.getRegisteredKinds().filter(\n (k) => k >= dvmRangeStart && k <= dvmRangeEnd\n );\n }\n\n /**\n * Dispatch a context to the appropriate handler based on kind.\n */\n async dispatch(ctx: HandlerContext): Promise<HandlerResponse> {\n const handler = this.handlers.get(ctx.kind);\n if (handler) {\n return handler(ctx);\n }\n if (this.defaultHandler) {\n return this.defaultHandler(ctx);\n }\n return {\n accept: false,\n code: 'F00',\n message: `No handler registered for kind ${ctx.kind}`,\n };\n }\n}\n","/**\n * Pricing validator for @toon-protocol/sdk.\n *\n * Validates that incoming ILP payments meet the required price based on\n * TOON payload size and configured price per byte.\n */\n\nimport type { ToonRoutingMeta } from '@toon-protocol/core/toon';\n\nexport interface PricingValidatorConfig {\n basePricePerByte?: bigint;\n ownPubkey: string;\n kindPricing?: Record<number, bigint>;\n}\n\nexport interface PricingValidationResult {\n accepted: boolean;\n rejection?: {\n accept: false;\n code: string;\n message: string;\n metadata?: Record<string, string>;\n };\n}\n\n/**\n * Creates a pricing validator that checks payment amounts against TOON size.\n */\nexport function createPricingValidator(config: PricingValidatorConfig) {\n const basePricePerByte = config.basePricePerByte ?? 10n;\n\n return {\n validate(meta: ToonRoutingMeta, amount: bigint): PricingValidationResult {\n // Self-write bypass\n if (meta.pubkey === config.ownPubkey) {\n return { accepted: true };\n }\n\n // Kind-specific pricing override (use Object.hasOwn for prototype-safe lookup)\n const kindOverride =\n config.kindPricing && Object.hasOwn(config.kindPricing, meta.kind)\n ? config.kindPricing[meta.kind]\n : undefined;\n const pricePerByte = kindOverride ?? basePricePerByte;\n\n const requiredAmount = BigInt(meta.rawBytes.length) * pricePerByte;\n\n if (amount < requiredAmount) {\n return {\n accepted: false,\n rejection: {\n accept: false,\n code: 'F04',\n message: 'Insufficient payment',\n metadata: {\n required: requiredAmount.toString(),\n received: amount.toString(),\n },\n },\n };\n }\n\n return { accepted: true };\n },\n };\n}\n","/**\n * Verification pipeline for @toon-protocol/sdk.\n *\n * Verifies Schnorr signatures on incoming TOON payloads before dispatching\n * to handlers. In dev mode, verification is skipped.\n */\n\nimport type { ToonRoutingMeta } from '@toon-protocol/core/toon';\nimport { schnorr } from '@noble/curves/secp256k1.js';\nimport { hexToBytes } from '@noble/hashes/utils.js';\n\nexport interface VerificationResult {\n verified: boolean;\n rejection?: {\n accept: false;\n code: string;\n message: string;\n };\n}\n\nexport interface VerificationPipelineConfig {\n devMode: boolean;\n}\n\n/**\n * Creates a verification pipeline that checks Schnorr signatures.\n */\nexport function createVerificationPipeline(config: VerificationPipelineConfig) {\n return {\n async verify(\n meta: ToonRoutingMeta,\n _toonData: string\n ): Promise<VerificationResult> {\n if (config.devMode) {\n return { verified: true };\n }\n\n try {\n const sigBytes = hexToBytes(meta.sig);\n const msgBytes = hexToBytes(meta.id);\n const pubkeyBytes = hexToBytes(meta.pubkey);\n const valid = schnorr.verify(sigBytes, msgBytes, pubkeyBytes);\n if (!valid) {\n return {\n verified: false,\n rejection: {\n accept: false,\n code: 'F06',\n message: 'Invalid Schnorr signature',\n },\n };\n }\n return { verified: true };\n } catch {\n return {\n verified: false,\n rejection: {\n accept: false,\n code: 'F06',\n message: 'Signature verification failed',\n },\n };\n }\n },\n };\n}\n","/**\n * Payment handler bridge for @toon-protocol/sdk.\n *\n * Bridges between ILP packet handling and the handler registry,\n * using isTransit to distinguish fire-and-forget from await semantics.\n */\n\nimport type { HandlerRegistry } from './handler-registry.js';\nimport { createHandlerContext } from './handler-context.js';\nimport type { ToonRoutingMeta } from '@toon-protocol/core/toon';\n\nexport interface PaymentHandlerBridgeConfig {\n registry: HandlerRegistry;\n devMode: boolean;\n ownPubkey: string;\n basePricePerByte: bigint;\n}\n\nexport interface PaymentRequest {\n paymentId: string;\n destination: string;\n amount: string;\n data: string;\n isTransit: boolean;\n}\n\nexport interface PaymentResponse {\n accept: boolean;\n code?: string;\n message?: string;\n data?: string;\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Creates a payment handler bridge that connects ILP packets to the handler registry.\n */\nexport function createPaymentHandlerBridge(config: PaymentHandlerBridgeConfig) {\n const { registry } = config;\n\n return {\n async handlePayment(request: PaymentRequest): Promise<PaymentResponse> {\n let amount: bigint;\n try {\n amount = BigInt(request.amount);\n } catch {\n return {\n accept: false,\n code: 'T00',\n message: 'Invalid payment amount',\n };\n }\n\n const ctx = createHandlerContext({\n toon: request.data,\n meta: {\n kind: 0,\n pubkey: '',\n id: '',\n sig: '',\n rawBytes: new Uint8Array(0),\n } as ToonRoutingMeta,\n amount,\n destination: request.destination,\n toonDecoder: () => {\n throw new Error('decode not available in bridge');\n },\n });\n\n if (request.isTransit) {\n void registry.dispatch(ctx).catch((err: unknown) => {\n // Log only the error message, not the full error object (which may contain payload data)\n const errMsg = err instanceof Error ? err.message : 'Unknown error';\n console.error('Transit handler error:', errMsg);\n });\n return { accept: true };\n }\n\n try {\n return await registry.dispatch(ctx);\n } catch (err: unknown) {\n // Log only the error message, not the full error object (which may contain payload data)\n const errMsg = err instanceof Error ? err.message : 'Unknown error';\n console.error('Handler error:', errMsg);\n return {\n accept: false,\n code: 'T00',\n message: 'Internal error',\n };\n }\n },\n };\n}\n","/**\n * Event storage handler stub for @toon-protocol/sdk.\n *\n * This is a stub that throws. The real implementation lives in\n * `@toon-protocol/relay` -- see `createEventStorageHandler` from that package.\n *\n * The SDK is the framework; Relay is the relay implementation. SDK consumers\n * building relay functionality should use `@toon-protocol/relay` directly.\n */\n\n/**\n * Creates an event storage handler.\n *\n * **Stub** -- throws \"not yet implemented\". See `@toon-protocol/relay` for the\n * real relay implementation of this handler.\n *\n * @see {@link https://github.com/toon-protocol/relay | @toon-protocol/relay}\n */\nexport function createEventStorageHandler(_config: unknown): unknown {\n throw new Error(\n 'createEventStorageHandler is not yet implemented in @toon-protocol/sdk. ' +\n 'Use @toon-protocol/relay for the relay implementation.'\n );\n}\n","/**\n * createNode() composition for @toon-protocol/sdk.\n *\n * Wires the full ILP packet processing pipeline:\n * shallow TOON parse -> Schnorr signature verification -> pricing validation -> handler dispatch\n *\n * Provides start() / stop() lifecycle management by delegating to\n * the core ToonNode composition (embedded mode) or manual HTTP\n * composition (standalone mode).\n *\n * ## Deployment Modes\n *\n * - **Embedded** (`connector`): Pass an EmbeddableConnectorLike directly.\n * Zero-latency packet delivery via direct function calls.\n * - **Standalone** (`connectorUrl` + `handlerPort`): Connect to an external\n * connector via HTTP. The SDK starts an HTTP server to receive packets.\n */\n\nimport { createServer, type Server as HttpServer } from 'node:http';\nimport type { NostrEvent } from 'nostr-tools/pure';\nimport type {\n EmbeddableConnectorLike,\n HandlePacketRequest,\n HandlePacketResponse,\n ConnectorChannelClient,\n} from '@toon-protocol/core';\nimport type { TransportConfig } from '@toon-protocol/connector';\nimport type {\n KnownPeer,\n BootstrapResult,\n BootstrapEventListener,\n} from '@toon-protocol/core';\nimport type { SettlementConfig } from '@toon-protocol/core';\nimport {\n createToonNode,\n BootstrapService,\n createDiscoveryTracker,\n createHttpIlpClient,\n createHttpConnectorAdmin,\n createHttpChannelClient,\n resolveChainConfig,\n buildIlpPrepare,\n buildJobFeedbackEvent,\n buildJobResultEvent,\n parseJobResult,\n deriveChildAddress,\n checkAddressCollision,\n AddressRegistry,\n ILP_ROOT_PREFIX,\n calculateRouteAmount,\n resolveRouteFees,\n buildPrefixClaimEvent,\n validatePrefix,\n ilpCodeToSemantic,\n} from '@toon-protocol/core';\nimport type { ChainProviderConfigEntry } from '@toon-protocol/core';\nimport type { DvmJobStatus, IlpSendResult } from '@toon-protocol/core';\nimport type {\n IlpClient,\n ConnectorAdminClient,\n DiscoveryTracker,\n} from '@toon-protocol/core';\nimport {\n shallowParseToon,\n decodeEventFromToon,\n encodeEventToToon,\n} from '@toon-protocol/core/toon';\n\nimport type { SkillDescriptor } from '@toon-protocol/core';\nimport { fromSecretKey } from './identity.js';\nimport { HandlerRegistry, type Handler } from './handler-registry.js';\nimport { createHandlerContext } from './handler-context.js';\nimport { createVerificationPipeline } from './verification-pipeline.js';\nimport { createPricingValidator } from './pricing-validator.js';\nimport {\n buildSkillDescriptor,\n type BuildSkillDescriptorConfig,\n} from './skill-descriptor.js';\nimport { NodeError } from './errors.js';\n\n/**\n * Maximum base64-encoded payload size (in characters) accepted by the pipeline.\n * Defense-in-depth against DoS via oversized payloads. 1MB of base64 decodes\n * to ~750KB of raw TOON data, which is far beyond any legitimate Nostr event.\n * The pay-per-byte pricing model provides an additional economic disincentive.\n */\nconst MAX_PAYLOAD_BASE64_LENGTH = 1_048_576;\n\n/**\n * Configuration for creating a ServiceNode via createNode().\n *\n * Supports three deployment modes:\n * - **Default** (no connector args): Auto-creates an embedded ConnectorNode via\n * dynamic import. Requires `@toon-protocol/connector` as peer dependency.\n * - **Embedded** (`connector`): Pass a pre-configured EmbeddableConnectorLike.\n * - **Standalone** (`connectorUrl` + `handlerPort`): Connect to an external\n * connector via HTTP. The SDK starts an HTTP server to receive packets.\n *\n * Provide `connector` OR (`connectorUrl` + `handlerPort`), or neither (auto-create).\n */\nexport interface NodeConfig {\n /** 32-byte secp256k1 secret key */\n secretKey: Uint8Array;\n\n /** Chain preset name (default: 'anvil'). See resolveChainConfig(). */\n chain?: string;\n\n // --- Connector (optional — defaults to auto-created embedded connector) ---\n\n /**\n * Embedded connector instance for zero-latency mode.\n * If neither `connector` nor `connectorUrl` is provided, an embedded\n * ConnectorNode is auto-created (requires @toon-protocol/connector peer dep).\n */\n connector?: EmbeddableConnectorLike;\n /**\n * External connector admin URL for standalone mode (e.g., \"http://localhost:8081\").\n * Must be provided with `handlerPort`.\n */\n connectorUrl?: string;\n /**\n * Port for the HTTP server that receives ILP packets from the external connector.\n * Must be provided with `connectorUrl`.\n */\n handlerPort?: number;\n\n /**\n * BTP server port for the auto-created embedded connector (default: 3000).\n * Only used when neither `connector` nor `connectorUrl` is provided.\n */\n btpServerPort?: number;\n\n /**\n * EVM private key for settlement infrastructure.\n * Only used when auto-creating an embedded connector.\n * If not set, the identity's secp256k1 key is used.\n */\n settlementPrivateKey?: string;\n\n // --- Network ---\n\n /**\n * Upstream peer's ILP address prefix for address derivation.\n * When set, the node derives its ILP address as\n * `deriveChildAddress(upstreamPrefix, pubkey)` and ignores `ilpAddress`.\n */\n upstreamPrefix?: string;\n /**\n * Multiple upstream peer prefixes for multi-peered nodes (Story 7.3).\n * When set, derives one ILP address per upstream prefix. Takes priority\n * over `upstreamPrefix` (singular) when both are set.\n */\n upstreamPrefixes?: string[];\n /** ILP address (default: derived from pubkey under ILP_ROOT_PREFIX) */\n ilpAddress?: string;\n /** BTP endpoint URL advertised in kind:10032 announcements */\n btpEndpoint?: string;\n /**\n * ILP-over-HTTP endpoint URL (RFC-0035) advertised in kind:10032, for\n * stateless one-shot writes (`POST /ilp`). With the shared transport server,\n * this is typically the same host/port as `btpEndpoint` (e.g.\n * `http://host:3000/ilp`).\n */\n httpEndpoint?: string;\n /** Whether `httpEndpoint` accepts an HTTP Upgrade to BTP (default: false). */\n supportsUpgrade?: boolean;\n /** Asset code (default: 'USD') */\n assetCode?: string;\n /** Asset scale (default: 6) */\n assetScale?: number;\n /**\n * Base price per byte for pricing validation (default: 10n).\n *\n * Amounts are in USDC micro-units (6 decimals) for production.\n * Default 10n = 10 micro-USDC per byte = $0.00001/byte.\n * A 1KB event costs 10,240 micro-USDC = ~$0.01.\n */\n basePricePerByte?: bigint;\n /**\n * Routing fee per byte charged by this node as an intermediary (default: 0n = free routing).\n * Advertised in kind:10032 peer info events as a non-negative integer string.\n */\n feePerByte?: bigint;\n /** Dev mode skips signature verification (default: false) */\n devMode?: boolean;\n /** TOON encoder function */\n toonEncoder?: (event: NostrEvent) => Uint8Array;\n /** TOON decoder function */\n toonDecoder?: (bytes: Uint8Array) => NostrEvent;\n /** Initial known peers for bootstrap */\n knownPeers?: KnownPeer[];\n /** Relay WebSocket URL */\n relayUrl?: string;\n /** Settlement info for peer registration */\n settlementInfo?: SettlementConfig;\n /** Enable ArDrive peer lookup */\n ardriveEnabled?: boolean;\n /** Per-kind pricing overrides */\n kindPricing?: Record<number, bigint>;\n /**\n * Multi-chain provider configuration for the embedded connector.\n * When provided, passed directly to ConnectorNode as `chainProviders`.\n * Takes priority over the legacy `settlementInfra` auto-configuration.\n * Only used when auto-creating an embedded connector.\n */\n chainProviders?: ChainProviderConfigEntry[];\n\n /**\n * Transport configuration for the embedded connector (ator/SOCKS5 privacy overlay).\n * When provided, passed directly to ConnectorNode as `transport`.\n * Only used when auto-creating an embedded connector.\n */\n transport?: TransportConfig;\n\n /**\n * NIP-59 transport privacy configuration for the embedded connector.\n * When enabled, per-packet claims are wrapped in three-layer encryption.\n * Only used when auto-creating an embedded connector.\n */\n nip59?: { enabled: boolean };\n /**\n * Per-peer NIP-59 public keys for claim encryption.\n * Map of peer ID to compressed secp256k1 public key (hex, 66 chars).\n * Applied to peer configs when registering peers with the connector.\n * Only used when auto-creating an embedded connector.\n */\n peerNip59PublicKeys?: Record<string, string>;\n /** Config-based handler registration (alternative to post-creation .on()) */\n handlers?: Record<number, Handler>;\n /** Config-based default handler (alternative to post-creation .onDefault()) */\n defaultHandler?: Handler;\n /**\n * Optional skill descriptor configuration overrides.\n * When DVM handlers are registered, these values override the auto-derived\n * defaults in the skill descriptor. See buildSkillDescriptor().\n */\n skillConfig?: Omit<\n BuildSkillDescriptorConfig,\n 'basePricePerByte' | 'kindPricing'\n >;\n}\n\n/**\n * Result returned by ServiceNode.start().\n */\nexport interface StartResult {\n /** Number of peers successfully bootstrapped */\n peerCount: number;\n /** Number of payment channels opened */\n channelCount: number;\n /** Detailed results from the bootstrap phase */\n bootstrapResults: BootstrapResult[];\n}\n\n/**\n * Result returned by ServiceNode.publishEvent().\n */\nexport interface PublishEventResult {\n success: boolean;\n eventId: string;\n /** Response data from the ILP FULFILL packet (e.g., Arweave tx ID for kind:5094). */\n data?: string;\n code?: string;\n message?: string;\n}\n\n/**\n * A fully wired TOON node with lifecycle management.\n */\nexport interface ServiceNode {\n /** Nostr x-only public key (32 bytes, 64 hex chars) */\n readonly pubkey: string;\n /** EVM address derived from the same secp256k1 key */\n readonly evmAddress: string;\n /** Pass-through to the underlying connector (null in standalone mode) */\n readonly connector: EmbeddableConnectorLike | null;\n /** Channel client (null if connector lacks channel support or in standalone mode) */\n readonly channelClient: ConnectorChannelClient | null;\n /** Register a handler for a specific event kind (builder pattern) */\n on(kind: number, handler: Handler): ServiceNode;\n /** Register a lifecycle event listener */\n on(event: 'bootstrap', listener: BootstrapEventListener): ServiceNode;\n /** Register a default handler for unrecognized kinds (builder pattern) */\n onDefault(handler: Handler): ServiceNode;\n /** Start the node: wire packet handler, run bootstrap, start discovery */\n start(): Promise<StartResult>;\n /** Stop the node: clean up lifecycle state */\n stop(): Promise<void>;\n /** Initiate peering with a discovered peer (register + settlement) */\n peerWith(pubkey: string): Promise<void>;\n /**\n * Publish a Nostr event to a remote peer via the embedded connector.\n *\n * TOON-encodes the event, computes payment amount, and sends as an\n * ILP PREPARE packet via the runtime client.\n *\n * @param event - The Nostr event to publish\n * @param options - Must include destination ILP address. Optional `amount`\n * overrides the default basePricePerByte * toonData.length calculation\n * (prepaid model: send exact destination amount). Optional `bid` is a\n * client-side safety cap: if the destination amount exceeds `bid`, the\n * SDK throws before sending any ILP packet.\n * @returns Result with success/failure info and event ID\n */\n publishEvent(\n event: NostrEvent,\n options?: { destination: string; amount?: bigint; bid?: bigint }\n ): Promise<PublishEventResult>;\n\n /**\n * Publish a Kind 7000 DVM job feedback event via ILP PREPARE.\n *\n * Builds a signed Kind 7000 feedback event with NIP-90 tags (e, p, status)\n * and delegates to publishEvent() for TOON encoding and ILP delivery.\n * Standard relay write fee applies: basePricePerByte * toonData.length.\n *\n * @param requestEventId - 64-char hex event ID of the original Kind 5xxx request\n * @param customerPubkey - 64-char hex pubkey of the customer who posted the request\n * @param status - Job status value ('processing', 'error', 'success', 'partial')\n * @param content - Optional status details or error message\n * @param options - Must include destination ILP address\n * @returns Result with success/failure info and event ID\n */\n publishFeedback(\n requestEventId: string,\n customerPubkey: string,\n status: DvmJobStatus,\n content?: string,\n options?: { destination: string }\n ): Promise<PublishEventResult>;\n\n /**\n * Publish a Kind 6xxx DVM job result event via ILP PREPARE.\n *\n * Builds a signed Kind 6xxx result event with NIP-90 tags (e, p, amount)\n * and delegates to publishEvent() for TOON encoding and ILP delivery.\n * Standard relay write fee applies: basePricePerByte * toonData.length.\n *\n * @param requestEventId - 64-char hex event ID of the original Kind 5xxx request\n * @param customerPubkey - 64-char hex pubkey of the customer who posted the request\n * @param amount - Compute cost in USDC micro-units as string\n * @param content - Result data (text, URL, etc.)\n * @param options - Must include destination; optional kind (default: 6100)\n * @returns Result with success/failure info and event ID\n */\n publishResult(\n requestEventId: string,\n customerPubkey: string,\n amount: string,\n content: string,\n options?: { destination: string; kind?: number }\n ): Promise<PublishEventResult>;\n\n /**\n * Returns the computed skill descriptor for this node's DVM capabilities.\n * Returns `undefined` if no DVM handlers (kinds 5000-5999) are registered.\n * The descriptor is computed from the handler registry and node config.\n */\n getSkillDescriptor(): SkillDescriptor | undefined;\n\n /**\n * Send an ILP payment to a provider for compute settlement.\n *\n * Extracts the compute cost from the result event's `amount` tag via\n * parseJobResult(), optionally validates against the original bid amount\n * (E5-R005 bid validation), and sends a pure ILP value transfer\n * (empty data field) to the provider's ILP address.\n *\n * This is a payment-only operation -- no TOON encoding, no relay write.\n * The payment routes through the ILP mesh using the same infrastructure\n * as relay write fees.\n *\n * @deprecated Use publishEvent() with amount option instead. Prepaid model:\n * send job request + payment in one packet via\n * `publishEvent(event, { destination, amount: computePrice })`.\n *\n * @param resultEvent - Kind 6xxx result event with amount tag\n * @param providerIlpAddress - Provider's ILP address from kind:10035\n * @param options - Optional originalBid for bid validation (E5-R005)\n * @returns ILP send result with accepted/rejected status\n */\n settleCompute(\n resultEvent: NostrEvent,\n providerIlpAddress: string,\n options?: { originalBid?: string }\n ): Promise<IlpSendResult>;\n\n /**\n * Add a new upstream peer, deriving and registering its ILP address.\n *\n * Updates the AddressRegistry, registers a self-route in the embedded\n * connector, and triggers kind:10032 republication via BootstrapService.\n *\n * @param upstreamPrefix - The upstream peer's ILP address prefix\n */\n addUpstreamPeer(upstreamPrefix: string): void;\n\n /**\n * Remove an upstream peer, unregistering its derived ILP address.\n *\n * Updates the AddressRegistry, removes the self-route from the embedded\n * connector, and triggers kind:10032 republication via BootstrapService.\n *\n * @param upstreamPrefix - The upstream prefix to remove\n */\n removeUpstreamPeer(upstreamPrefix: string): void;\n\n /**\n * Claim a prefix from an upstream peer by sending a prefix claim event\n * with payment via ILP.\n *\n * Builds a Kind 10034 prefix claim event, reads the upstream's prefix\n * pricing from its kind:10032 advertisement, and calls publishEvent()\n * with the appropriate amount.\n *\n * @param prefix - The prefix string to claim (e.g., 'useast')\n * @param upstreamDestination - ILP address of the upstream node\n * @param options - Optional prefixPrice override (defaults to upstream's prefixPricing.basePrice from discovery)\n * @returns Result with success/failure info and event ID\n */\n claimPrefix(\n prefix: string,\n upstreamDestination: string,\n options?: { prefixPrice?: bigint }\n ): Promise<PublishEventResult>;\n}\n\n/**\n * Creates a fully wired ServiceNode from configuration.\n *\n * The returned node has the full ILP packet processing pipeline wired in the\n * correct order:\n * 1. Shallow TOON parse (extract routing metadata)\n * 2. Schnorr signature verification (reject with F06 if invalid)\n * 3. Pricing validation (reject with F04 if underpaid)\n * 4. Handler dispatch (route to kind-specific or default handler)\n *\n * Supports two deployment modes:\n * - **Embedded** (`connector`): Uses createToonNode for zero-latency\n * packet delivery via direct function calls.\n * - **Standalone** (`connectorUrl` + `handlerPort`): Starts an HTTP server\n * to receive packets and uses HTTP clients for the connector API.\n *\n * Handlers can be registered via config or post-creation .on()/.onDefault().\n */\nexport function createNode(config: NodeConfig): ServiceNode {\n // 0. Validate connector mode\n const hasConnector = config.connector !== undefined;\n const hasConnectorUrl = config.connectorUrl !== undefined;\n\n if (hasConnector && hasConnectorUrl) {\n throw new NodeError(\n 'NodeConfig: provide either connector or connectorUrl, not both'\n );\n }\n if (hasConnectorUrl && config.handlerPort === undefined) {\n throw new NodeError(\n 'NodeConfig: handlerPort is required when using connectorUrl (standalone mode)'\n );\n }\n const autoCreateConnector = !hasConnector && !hasConnectorUrl;\n const embeddedMode = hasConnector || autoCreateConnector;\n\n // 0b. Resolve chain config and auto-populate settlementInfo if not set\n const chainConfig = resolveChainConfig(config.chain);\n let effectiveSettlementInfo = config.settlementInfo;\n if (!effectiveSettlementInfo) {\n const chainKey = `evm:base:${chainConfig.chainId}`;\n const supportedChains = [chainKey];\n const preferredTokens: Record<string, string> = {\n [chainKey]: chainConfig.usdcAddress,\n };\n const tokenNetworks: Record<string, string> | undefined =\n chainConfig.tokenNetworkAddress\n ? { [chainKey]: chainConfig.tokenNetworkAddress }\n : undefined;\n\n effectiveSettlementInfo = {\n supportedChains,\n preferredTokens,\n ...(tokenNetworks && { tokenNetworks }),\n };\n }\n\n // 1. Derive identity from secretKey\n let identity;\n try {\n identity = fromSecretKey(config.secretKey);\n } catch (error: unknown) {\n throw new NodeError(\n `Invalid secretKey: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n const { pubkey, evmAddress } = identity;\n\n // 2. Create handler registry\n const registry = new HandlerRegistry();\n\n // 3. Register config-based handlers (with kind validation matching node.on())\n if (config.handlers) {\n for (const [kind, handler] of Object.entries(config.handlers)) {\n const kindNum = Number(kind);\n if (!Number.isInteger(kindNum) || kindNum < 0) {\n throw new NodeError(\n `Invalid event kind in handlers config: expected a non-negative integer, got '${kind}'`\n );\n }\n registry.on(kindNum, handler);\n }\n }\n\n // 4. Register config-based default handler\n if (config.defaultHandler) {\n registry.onDefault(config.defaultHandler);\n }\n\n // 5. Create verification pipeline\n const verifier = createVerificationPipeline({\n devMode: config.devMode ?? false,\n });\n\n // 6. Create pricing validator\n const pricer = createPricingValidator({\n basePricePerByte: config.basePricePerByte ?? 10n,\n ownPubkey: pubkey,\n kindPricing: config.kindPricing,\n });\n\n // 7. Set up TOON codec with defaults\n const encoder = config.toonEncoder ?? encodeEventToToon;\n const decoder = config.toonDecoder ?? decodeEventFromToon;\n\n // Context decoder: converts base64 TOON string to NostrEvent\n const contextDecoder = (toon: string): NostrEvent => {\n const bytes = Buffer.from(toon, 'base64');\n return decoder(bytes);\n };\n\n // Mutable ref so the packet handler closure can access the tracker after it's created.\n const trackerRef: { current?: { processEvent(event: NostrEvent): void } } =\n {};\n\n // 8. Build the pipelined packet handler (Option A: directly on HandlePacketRequest)\n const pipelinedHandler = async (\n request: HandlePacketRequest\n ): Promise<HandlePacketResponse> => {\n // Step 0: Reject oversized payloads before allocating memory (DoS mitigation)\n if (request.data.length > MAX_PAYLOAD_BASE64_LENGTH) {\n return {\n accept: false,\n code: 'F06',\n message: `Payload too large: ${request.data.length} bytes exceeds maximum ${MAX_PAYLOAD_BASE64_LENGTH}`,\n };\n }\n\n // Step 1: Shallow TOON parse\n const toonBytes = Buffer.from(request.data, 'base64');\n let meta;\n try {\n meta = shallowParseToon(toonBytes);\n } catch {\n // Corrupted TOON data cannot be parsed -- reject as invalid payload\n return {\n accept: false,\n code: 'F06',\n message: 'Invalid TOON payload: failed to parse routing metadata',\n };\n }\n\n // Dev mode: log packet details\n // Sanitize user-controlled fields (amount, destination) to prevent log injection\n // via newlines or control characters. meta.kind (integer) and meta.pubkey (validated\n // hex) are safe; request.data preview is base64 (safe character set).\n if (config.devMode ?? false) {\n const toonPreview =\n request.data.length > 80\n ? request.data.substring(0, 80) + '...'\n : request.data;\n // eslint-disable-next-line no-control-regex\n const sanitize = (s: string) => s.replace(/[\\x00-\\x1f\\x7f]/g, '');\n console.log(\n '[toon:dev]',\n `kind=${meta.kind}`,\n `pubkey=${meta.pubkey.substring(0, 16)}...`,\n `amount=${sanitize(request.amount)}`,\n `dest=${sanitize(request.destination)}`,\n `toon=${toonPreview}`\n );\n }\n\n // Step 2: Verify signature\n const verifyResult = await verifier.verify(meta, request.data);\n if (!verifyResult.verified) {\n // VerificationResult.rejection is always set when verified=false per\n // createVerificationPipeline contract. Guard defensively anyway.\n if (verifyResult.rejection) {\n return verifyResult.rejection;\n }\n return { accept: false, code: 'F06', message: 'Verification failed' };\n }\n\n // Step 3: Validate pricing (skip in dev mode)\n let amount: bigint;\n try {\n amount = BigInt(request.amount);\n } catch {\n if (config.devMode ?? false) {\n amount = 0n;\n } else {\n return {\n accept: false,\n code: 'T00',\n message: 'Invalid payment amount',\n };\n }\n }\n if (!(config.devMode ?? false)) {\n const priceResult = pricer.validate(meta, amount);\n if (!priceResult.accepted) {\n // PricingValidationResult.rejection is always set when accepted=false per\n // createPricingValidator contract. Guard defensively anyway.\n if (priceResult.rejection) {\n return priceResult.rejection;\n }\n return {\n accept: false,\n code: 'F04',\n message: 'Pricing validation failed',\n };\n }\n }\n\n // Step 4: Build HandlerContext with real metadata\n const ctx = createHandlerContext({\n toon: request.data,\n meta,\n amount,\n destination: request.destination,\n toonDecoder: contextDecoder,\n });\n\n // Step 5: Dispatch to handler (T00 error boundary)\n try {\n const result = await registry.dispatch(ctx);\n\n // Feed accepted kind:10032 events to discovery tracker for peer discovery.\n // Uses late-binding reference since the tracker is created after this handler.\n if (result.accept && meta.kind === 10032 && trackerRef.current) {\n const decoded = ctx.decode();\n if (decoded) {\n trackerRef.current.processEvent(decoded);\n }\n }\n\n // Connector v3.3.2 breaking change: the embedded ConnectorNode's\n // PaymentHandlerAdapter consumes `response.data` (base64) and ignores\n // `response.metadata`. The SDK's `ctx.accept(metadata)` historically\n // returned `{ accept: true, metadata }`. To preserve handler ergonomics\n // (handlers still pass an object), serialize `metadata` → base64 JSON\n // into `data` here when the handler did not already populate `data`.\n // The streamSwap FULFILL decoder expects this exact base64-JSON shape.\n if (result.accept && result.metadata && !result.data) {\n try {\n const metadataJson = JSON.stringify(result.metadata);\n (result as { data?: string }).data = Buffer.from(\n metadataJson,\n 'utf8'\n ).toString('base64');\n } catch (err) {\n console.error(\n 'Failed to serialize handler metadata to FULFILL data:',\n err instanceof Error ? err.message : err\n );\n }\n }\n\n // Connector v3.3.2 breaking change: the adapter's reject path reads\n // `response.rejectReason.{code,message}` and feeds `rejectReason.code`\n // through `mapRejectCode()` — a SEMANTIC-reason → ILP-code lookup\n // (e.g. `'internal_error'` → `'T00'`). The SDK's `ctx.reject(ilpCode,\n // message)` returns the ILP code directly, so without translation\n // every reject collapses to F99 (the `mapRejectCode()` fallback).\n //\n // Reverse-map the handler's ILP code back to the connector's expected\n // semantic reason. Canonical mapping lives in\n // `@toon-protocol/core` (utils/reject-code.ts).\n if (\n !result.accept &&\n !(result as { rejectReason?: unknown }).rejectReason &&\n (result as { code?: string }).code\n ) {\n const ilpCode = (result as { code: string }).code;\n (\n result as {\n rejectReason?: { code: string; message: string };\n }\n ).rejectReason = {\n code: ilpCodeToSemantic(ilpCode),\n message:\n (result as { message?: string }).message ?? 'Payment rejected',\n };\n }\n\n return result;\n } catch (err: unknown) {\n // Log only the error message, not the full error object (which may contain payload data)\n const errMsg = err instanceof Error ? err.message : 'Unknown error';\n console.error('Handler dispatch failed:', errMsg);\n return { accept: false, code: 'T00', message: 'Internal error' };\n }\n };\n\n // ILP info shared between both modes\n // Address resolution priority (Story 7.3 extends with upstreamPrefixes):\n // 1. upstreamPrefixes set -> derive one address per prefix, primary = first\n // 2. upstreamPrefix set -> derive single address (ignore ilpAddress)\n // 3. ilpAddress set -> use directly (includes genesis node with 'g.toon')\n // 4. neither set -> derive from ILP_ROOT_PREFIX + pubkey (default)\n let resolvedIlpAddress: string;\n let resolvedIlpAddresses: string[];\n\n if (\n config.upstreamPrefixes !== undefined &&\n config.upstreamPrefixes.length > 0\n ) {\n // upstreamPrefixes takes priority over upstreamPrefix (singular)\n if (config.upstreamPrefix !== undefined) {\n console.warn(\n '[toon:warn] Both upstreamPrefixes and upstreamPrefix set; upstreamPrefixes takes priority'\n );\n }\n resolvedIlpAddresses = config.upstreamPrefixes.map((p) =>\n deriveChildAddress(p, pubkey)\n );\n // Task 4.4: Check for truncation collisions across upstream prefixes\n for (let i = 0; i < resolvedIlpAddresses.length; i++) {\n const others = resolvedIlpAddresses.filter((_, j) => j !== i);\n checkAddressCollision(resolvedIlpAddresses[i] as string, others);\n }\n resolvedIlpAddress = resolvedIlpAddresses[0] as string;\n } else if (config.upstreamPrefix !== undefined) {\n resolvedIlpAddress = deriveChildAddress(config.upstreamPrefix, pubkey);\n resolvedIlpAddresses = [resolvedIlpAddress];\n } else if (config.ilpAddress !== undefined) {\n resolvedIlpAddress = config.ilpAddress;\n resolvedIlpAddresses = [resolvedIlpAddress];\n } else {\n resolvedIlpAddress = deriveChildAddress(ILP_ROOT_PREFIX, pubkey);\n resolvedIlpAddresses = [resolvedIlpAddress];\n }\n\n // Task 6.2: Initialize AddressRegistry with initial upstream prefixes\n const addressRegistry = new AddressRegistry();\n if (\n config.upstreamPrefixes !== undefined &&\n config.upstreamPrefixes.length > 0\n ) {\n for (let i = 0; i < config.upstreamPrefixes.length; i++) {\n addressRegistry.addAddress(\n config.upstreamPrefixes[i] as string,\n resolvedIlpAddresses[i] as string\n );\n }\n } else if (config.upstreamPrefix !== undefined) {\n addressRegistry.addAddress(config.upstreamPrefix, resolvedIlpAddress);\n } else if (config.ilpAddress !== undefined) {\n addressRegistry.addAddress('explicit', resolvedIlpAddress);\n } else {\n addressRegistry.addAddress(ILP_ROOT_PREFIX, resolvedIlpAddress);\n }\n\n const ilpInfo = {\n ilpAddress: resolvedIlpAddress,\n ilpAddresses: resolvedIlpAddresses,\n btpEndpoint: config.btpEndpoint ?? '',\n ...(config.httpEndpoint !== undefined && { httpEndpoint: config.httpEndpoint }),\n ...(config.supportsUpgrade !== undefined && { supportsUpgrade: config.supportsUpgrade }),\n assetCode: config.assetCode ?? 'USD',\n assetScale: config.assetScale ?? 6,\n feePerByte: String(config.feePerByte ?? 0n),\n };\n\n // 9. Branch: embedded mode vs standalone mode\n let ilpClient: IlpClient;\n let adminClient: ConnectorAdminClient;\n let channelClient: ConnectorChannelClient | null = null;\n let bootstrapServiceInstance: BootstrapService;\n let discoveryTrackerInstance: DiscoveryTracker;\n let httpServer: HttpServer | null = null;\n\n // Lifecycle delegates (start/stop differ per mode)\n let doStart: () => Promise<{\n bootstrapResults: BootstrapResult[];\n peerCount: number;\n channelCount: number;\n }>;\n let doStop: () => Promise<void>;\n\n // Track auto-created connector for cleanup\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let autoCreatedConnector: any = null;\n\n if (embeddedMode && hasConnector) {\n // --- EMBEDDED MODE (user-provided connector): delegate to createToonNode ---\n const toonNode = createToonNode({\n connector: config.connector as NonNullable<typeof config.connector>,\n handlePacket: pipelinedHandler,\n secretKey: config.secretKey,\n ilpInfo,\n toonEncoder: encoder,\n toonDecoder: decoder,\n relayUrl: config.relayUrl,\n knownPeers: config.knownPeers,\n settlementInfo: effectiveSettlementInfo,\n basePricePerByte: config.basePricePerByte,\n ardriveEnabled: config.ardriveEnabled,\n });\n\n ilpClient = toonNode.ilpClient;\n channelClient = toonNode.channelClient;\n bootstrapServiceInstance = toonNode.bootstrapService;\n discoveryTrackerInstance = toonNode.discoveryTracker;\n adminClient = {\n addPeer: () => Promise.resolve(),\n removePeer: () => Promise.resolve(),\n };\n\n trackerRef.current = discoveryTrackerInstance;\n\n doStart = async () => toonNode.start();\n doStop = async () => toonNode.stop();\n } else if (autoCreateConnector) {\n // --- AUTO-CREATE EMBEDDED MODE: deferred ConnectorNode creation in doStart() ---\n\n // Create placeholder bootstrap/discovery instances (wired during start)\n bootstrapServiceInstance = new BootstrapService(\n {\n knownPeers: config.knownPeers ?? [],\n ardriveEnabled: config.ardriveEnabled ?? false,\n defaultRelayUrl: config.relayUrl ?? '',\n settlementInfo: effectiveSettlementInfo,\n ownIlpAddress: ilpInfo.ilpAddress,\n toonEncoder: encoder,\n toonDecoder: decoder,\n basePricePerByte: config.basePricePerByte ?? 10n,\n },\n config.secretKey,\n ilpInfo\n );\n\n discoveryTrackerInstance = createDiscoveryTracker({\n secretKey: config.secretKey,\n settlementInfo: effectiveSettlementInfo,\n });\n\n // Placeholder clients (replaced during start)\n ilpClient = {\n sendIlpPacket: () =>\n Promise.reject(new NodeError('Node not started. Call start() first.')),\n };\n adminClient = {\n addPeer: () => Promise.resolve(),\n removePeer: () => Promise.resolve(),\n };\n\n trackerRef.current = discoveryTrackerInstance;\n\n doStart = async () => {\n // Dynamic import to keep @toon-protocol/connector as optional peer dep\n // eslint-disable-next-line @typescript-eslint/consistent-type-imports\n let ConnectorNodeClass: typeof import('@toon-protocol/connector').ConnectorNode;\n // eslint-disable-next-line @typescript-eslint/consistent-type-imports\n let createConnectorLogger: typeof import('@toon-protocol/connector').createLogger;\n try {\n const mod = await import('@toon-protocol/connector');\n ConnectorNodeClass = mod.ConnectorNode;\n createConnectorLogger = mod.createLogger;\n } catch {\n throw new NodeError(\n 'Auto-create connector requires @toon-protocol/connector as a peer dependency. ' +\n 'Install it with: pnpm add @toon-protocol/connector'\n );\n }\n\n const nodeId = `toon-${pubkey.slice(0, 16)}`;\n const btpServerPort = config.btpServerPort ?? 3000;\n const connectorLogger = createConnectorLogger(nodeId, 'warn');\n\n // Derive settlement private key from identity if not explicitly set\n let settlementPrivateKey = config.settlementPrivateKey;\n if (!settlementPrivateKey) {\n const keyBuffer = Buffer.from(config.secretKey);\n settlementPrivateKey = `0x${keyBuffer.toString('hex')}`;\n keyBuffer.fill(0);\n }\n\n const hasSettlementAddresses =\n chainConfig.registryAddress && chainConfig.tokenNetworkAddress;\n\n // Build connector config: chainProviders for settlement (v2.3.0+)\n const hasChainProviders =\n config.chainProviders !== undefined && config.chainProviders.length > 0;\n\n // When chainProviders not explicitly provided, build from legacy config\n const effectiveChainProviders = hasChainProviders\n ? config.chainProviders\n : hasSettlementAddresses\n ? [\n {\n chainType: 'evm' as const,\n chainId: `evm:${chainConfig.chainId ?? '31337'}`,\n rpcUrl: chainConfig.rpcUrl,\n registryAddress: chainConfig.registryAddress,\n tokenAddress: chainConfig.usdcAddress,\n privateKey: settlementPrivateKey,\n },\n ]\n : undefined;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const connectorConfig: any = {\n nodeId,\n btpServerPort,\n environment: 'development',\n deploymentMode: 'embedded',\n peers: [],\n routes: [],\n localDelivery: { enabled: false },\n };\n if (effectiveChainProviders) {\n connectorConfig.chainProviders = effectiveChainProviders;\n }\n if (config.transport) {\n connectorConfig.transport = config.transport;\n }\n if (config.nip59) {\n connectorConfig.nip59 = config.nip59;\n }\n\n autoCreatedConnector = new ConnectorNodeClass(\n connectorConfig,\n connectorLogger\n );\n\n await autoCreatedConnector.start();\n\n // Now wire the real connector into createToonNode\n const connector =\n autoCreatedConnector as unknown as EmbeddableConnectorLike;\n const toonNode = createToonNode({\n connector,\n handlePacket: pipelinedHandler,\n secretKey: config.secretKey,\n ilpInfo,\n toonEncoder: encoder,\n toonDecoder: decoder,\n relayUrl: config.relayUrl,\n knownPeers: config.knownPeers,\n settlementInfo: effectiveSettlementInfo,\n basePricePerByte: config.basePricePerByte,\n ardriveEnabled: config.ardriveEnabled,\n });\n\n // Replace placeholder clients with real ones\n ilpClient = toonNode.ilpClient;\n channelClient = toonNode.channelClient;\n\n // Wire bootstrap and discovery with the real admin client\n bootstrapServiceInstance.setIlpClient(toonNode.ilpClient);\n bootstrapServiceInstance.setConnectorAdmin({\n addPeer: () => Promise.resolve(),\n removePeer: () => Promise.resolve(),\n });\n if (toonNode.channelClient) {\n bootstrapServiceInstance.setChannelClient(toonNode.channelClient);\n }\n\n discoveryTrackerInstance.setConnectorAdmin({\n addPeer: () => Promise.resolve(),\n removePeer: () => Promise.resolve(),\n });\n if (toonNode.channelClient) {\n discoveryTrackerInstance.setChannelClient(toonNode.channelClient);\n }\n\n trackerRef.current = discoveryTrackerInstance;\n\n // Add self-routes for all addresses (Task 5.3)\n for (const addr of ilpInfo.ilpAddresses) {\n autoCreatedConnector.addRoute({\n prefix: addr,\n nextHop: nodeId,\n priority: 100,\n });\n }\n\n // Start the toonNode (wires packet handler, runs bootstrap)\n const result = await toonNode.start();\n return result;\n };\n\n doStop = async () => {\n if (autoCreatedConnector) {\n await autoCreatedConnector.stop();\n autoCreatedConnector = null;\n }\n };\n } else {\n // --- STANDALONE MODE: manual composition with HTTP clients ---\n const connectorUrl = config.connectorUrl as string;\n const handlerPort = config.handlerPort as number;\n\n ilpClient = createHttpIlpClient(connectorUrl);\n adminClient = createHttpConnectorAdmin(connectorUrl, '');\n\n // Channel client via HTTP if settlement is configured\n if (effectiveSettlementInfo) {\n channelClient = createHttpChannelClient(connectorUrl);\n }\n\n // Create BootstrapService\n bootstrapServiceInstance = new BootstrapService(\n {\n knownPeers: config.knownPeers ?? [],\n ardriveEnabled: config.ardriveEnabled ?? false,\n defaultRelayUrl: config.relayUrl ?? '',\n settlementInfo: effectiveSettlementInfo,\n ownIlpAddress: ilpInfo.ilpAddress,\n toonEncoder: encoder,\n toonDecoder: decoder,\n basePricePerByte: config.basePricePerByte ?? 10n,\n },\n config.secretKey,\n ilpInfo\n );\n\n bootstrapServiceInstance.setIlpClient(ilpClient);\n bootstrapServiceInstance.setConnectorAdmin(adminClient);\n if (channelClient) {\n bootstrapServiceInstance.setChannelClient(channelClient);\n }\n\n // Create DiscoveryTracker\n discoveryTrackerInstance = createDiscoveryTracker({\n secretKey: config.secretKey,\n settlementInfo: effectiveSettlementInfo,\n });\n discoveryTrackerInstance.setConnectorAdmin(adminClient);\n if (channelClient) {\n discoveryTrackerInstance.setChannelClient(channelClient);\n }\n\n trackerRef.current = discoveryTrackerInstance;\n\n doStart = async () => {\n // Start HTTP server for receiving ILP packets\n httpServer = createServer(async (req, res) => {\n if (req.method === 'GET' && req.url === '/health') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'healthy', pubkey }));\n return;\n }\n\n if (req.method === 'POST' && req.url === '/handle-packet') {\n let body = '';\n req.on('data', (chunk: Buffer) => {\n body += chunk.toString();\n });\n req.on('end', async () => {\n try {\n const request = JSON.parse(body) as HandlePacketRequest;\n if (\n request.amount === undefined ||\n request.amount === null ||\n request.destination === undefined ||\n request.destination === null ||\n request.data === undefined ||\n request.data === null\n ) {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(\n JSON.stringify({\n accept: false,\n code: 'F00',\n message: 'Missing required fields',\n })\n );\n return;\n }\n const result = await pipelinedHandler(request);\n res.writeHead(result.accept ? 200 : 400, {\n 'Content-Type': 'application/json',\n });\n res.end(JSON.stringify(result));\n } catch (err: unknown) {\n console.error('[SDK] handle-packet error:', err);\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(\n JSON.stringify({\n accept: false,\n code: 'T00',\n message: 'Internal server error',\n })\n );\n }\n });\n return;\n }\n\n res.writeHead(404);\n res.end();\n });\n\n await new Promise<void>((resolve) => {\n httpServer?.listen(handlerPort, () => resolve());\n });\n\n // Run bootstrap\n const results = await bootstrapServiceInstance.bootstrap();\n const bootstrapPeerPubkeys = results.map((r) => r.knownPeer.pubkey);\n discoveryTrackerInstance.addExcludedPubkeys(bootstrapPeerPubkeys);\n\n return {\n bootstrapResults: results,\n peerCount: results.length,\n channelCount: results.filter((r) => r.channelId).length,\n };\n };\n\n doStop = async () => {\n const server = httpServer;\n if (server) {\n await new Promise<void>((resolve) => {\n server.close(() => resolve());\n });\n httpServer = null;\n }\n };\n }\n\n // 10. Track SDK-level lifecycle state\n let started = false;\n let lastBootstrapResults: BootstrapResult[] = [];\n\n // 11. Build and return ServiceNode\n const node: ServiceNode = {\n get pubkey() {\n return pubkey;\n },\n get evmAddress() {\n return evmAddress;\n },\n get connector() {\n return (\n config.connector ??\n (autoCreatedConnector as unknown as EmbeddableConnectorLike) ??\n null\n );\n },\n get channelClient() {\n return channelClient;\n },\n\n getSkillDescriptor(): SkillDescriptor | undefined {\n return buildSkillDescriptor(registry, {\n basePricePerByte: config.basePricePerByte ?? 10n,\n kindPricing: config.kindPricing,\n ...config.skillConfig,\n });\n },\n\n on(\n kindOrEvent: number | string,\n handlerOrListener: Handler | BootstrapEventListener\n ): ServiceNode {\n if (typeof kindOrEvent === 'number') {\n // Handler registration (existing behavior)\n if (!Number.isInteger(kindOrEvent) || kindOrEvent < 0) {\n throw new NodeError(\n `Invalid event kind: expected a non-negative integer, got ${String(kindOrEvent)}`\n );\n }\n registry.on(kindOrEvent, handlerOrListener as Handler);\n } else if (kindOrEvent === 'bootstrap') {\n // Lifecycle event listener -- forward to bootstrapService AND discoveryTracker\n const listener = handlerOrListener as BootstrapEventListener;\n bootstrapServiceInstance.on(listener);\n discoveryTrackerInstance.on(listener);\n } else {\n // Sanitize event name to prevent log injection via control characters\n // eslint-disable-next-line no-control-regex\n const sanitized = String(kindOrEvent).replace(/[\\x00-\\x1f\\x7f]/g, '');\n throw new NodeError(\n `Unknown lifecycle event: '${sanitized}'. Supported: 'bootstrap'`\n );\n }\n return node;\n },\n\n onDefault(handler: Handler): ServiceNode {\n registry.onDefault(handler);\n return node;\n },\n\n async start(): Promise<StartResult> {\n if (started) {\n throw new NodeError('Node already started');\n }\n\n try {\n const result = await doStart();\n started = true;\n lastBootstrapResults = result.bootstrapResults;\n return {\n peerCount: result.peerCount,\n channelCount: result.channelCount,\n bootstrapResults: result.bootstrapResults,\n };\n } catch (error: unknown) {\n if (error instanceof NodeError) {\n throw error;\n }\n throw new NodeError(\n `Failed to start node: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n },\n\n async stop(): Promise<void> {\n if (!started) {\n return; // No-op if not started\n }\n\n await doStop();\n started = false;\n },\n\n async peerWith(targetPubkey: string): Promise<void> {\n if (!started) {\n throw new NodeError(\n 'Cannot peer: node not started. Call start() first.'\n );\n }\n // Defense-in-depth: validate pubkey format before delegating to core\n if (\n typeof targetPubkey !== 'string' ||\n targetPubkey.length !== 64 ||\n !/^[0-9a-f]{64}$/.test(targetPubkey)\n ) {\n throw new NodeError(\n 'Invalid pubkey: expected a 64-character lowercase hex string'\n );\n }\n return discoveryTrackerInstance.peerWith(targetPubkey);\n },\n\n async publishEvent(\n event: NostrEvent,\n options?: { destination: string; amount?: bigint; bid?: bigint }\n ): Promise<PublishEventResult> {\n // Guard: node must be started\n if (!started) {\n throw new NodeError(\n 'Cannot publish: node not started. Call start() first.'\n );\n }\n\n // Guard: destination is required\n if (!options?.destination) {\n throw new NodeError(\n \"Cannot publish: destination is required. Pass { destination: 'g.peer.address' }.\"\n );\n }\n\n try {\n // TOON-encode the event\n const toonData = encoder(event);\n\n // Resolve intermediary routing fees from discovered peers\n const { hopFees, warnings } = resolveRouteFees({\n destination: options.destination,\n ownIlpAddress: ilpInfo.ilpAddress,\n discoveredPeers: discoveryTrackerInstance.getAllDiscoveredPeers(),\n });\n\n // Log warnings about unknown intermediaries\n for (const warning of warnings) {\n console.warn(`[publishEvent] ${warning}`);\n }\n\n // Compute destination amount: use override or default basePricePerByte * bytes\n const destinationAmount =\n options.amount ??\n (config.basePricePerByte ?? 10n) * BigInt(toonData.length);\n\n // Bid safety cap: if bid is provided, reject if destination amount exceeds bid\n if (options.bid !== undefined && destinationAmount > options.bid) {\n throw new NodeError(\n `Cannot publish: destination amount ${destinationAmount} exceeds bid safety cap ${options.bid}`\n );\n }\n\n // Compute total amount: destination amount + intermediary route fees\n let amount: bigint;\n if (options.amount !== undefined) {\n // When amount override is provided, add only hop fees (no basePricePerByte)\n const hopFeesTotal = calculateRouteAmount({\n basePricePerByte: 0n,\n packetByteLength: toonData.length,\n hopFees,\n });\n amount = options.amount + hopFeesTotal;\n } else {\n // Default: basePricePerByte * bytes + hop fees\n amount = calculateRouteAmount({\n basePricePerByte: config.basePricePerByte ?? 10n,\n packetByteLength: toonData.length,\n hopFees,\n });\n }\n\n // Build ILP PREPARE packet using shared construction (packet equivalence\n // with x402 rail -- the destination relay cannot distinguish between\n // packets sent via publishEvent() and the x402 /publish endpoint).\n const packet = buildIlpPrepare({\n destination: options.destination,\n amount,\n data: toonData,\n });\n\n // Send via ILP client\n const result = await ilpClient.sendIlpPacket(packet);\n\n // Map IlpSendResult to PublishEventResult\n if (result.accepted) {\n return {\n success: true,\n eventId: event.id,\n ...(result.data !== undefined ? { data: result.data } : {}),\n };\n }\n\n return {\n success: false,\n eventId: event.id,\n code: result.code ?? 'T00',\n message: result.message ?? 'Unknown error',\n };\n } catch (error: unknown) {\n // Propagate NodeError directly\n if (error instanceof NodeError) {\n throw error;\n }\n throw new NodeError(\n `Failed to publish event: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : undefined\n );\n }\n },\n\n async publishFeedback(\n requestEventId: string,\n customerPubkey: string,\n status: DvmJobStatus,\n content?: string,\n options?: { destination: string }\n ): Promise<PublishEventResult> {\n // Build Kind 7000 feedback event using provider's secretKey\n const feedbackEvent = buildJobFeedbackEvent(\n { requestEventId, customerPubkey, status, content },\n config.secretKey\n );\n\n // Delegate to publishEvent() for TOON encoding, pricing, and ILP delivery\n return node.publishEvent(feedbackEvent, options);\n },\n\n async publishResult(\n requestEventId: string,\n customerPubkey: string,\n amount: string,\n content: string,\n options?: { destination: string; kind?: number }\n ): Promise<PublishEventResult> {\n // Default result kind is 6100 (text generation result = 5100 + 1000)\n const resultKind = options?.kind ?? 6100;\n\n // Build Kind 6xxx result event using provider's secretKey\n const resultEvent = buildJobResultEvent(\n { kind: resultKind, requestEventId, customerPubkey, amount, content },\n config.secretKey\n );\n\n // Delegate to publishEvent() for TOON encoding, pricing, and ILP delivery\n return node.publishEvent(resultEvent, options);\n },\n\n async settleCompute(\n resultEvent: NostrEvent,\n providerIlpAddress: string,\n options?: { originalBid?: string }\n ): Promise<IlpSendResult> {\n // Deprecation warning\n console.warn(\n '[settleCompute] DEPRECATED: Use publishEvent() with { amount } option instead. The prepaid model sends job request + payment in one packet.'\n );\n\n // Guard: node must be started\n if (!started) {\n throw new NodeError(\n 'Cannot settle compute: node not started. Call start() first.'\n );\n }\n\n // Guard: providerIlpAddress must be a non-empty, non-whitespace string\n if (\n !providerIlpAddress ||\n typeof providerIlpAddress !== 'string' ||\n providerIlpAddress.trim() === ''\n ) {\n throw new NodeError(\n \"Cannot settle compute: providerIlpAddress is required. Resolve it from the provider's kind:10035 service discovery event.\"\n );\n }\n\n // Extract amount from result event via parseJobResult()\n const parsed = parseJobResult(resultEvent);\n if (!parsed) {\n throw new NodeError(\n 'Cannot settle compute: failed to parse result event. Ensure the event is a valid Kind 6xxx with an amount tag.'\n );\n }\n\n const computeAmount = parsed.amount;\n\n // Validate computeAmount is a valid non-negative numeric string before any\n // further processing. Without this, a non-numeric amount would propagate to\n // ilpClient.sendIlpPacket() and cause a BootstrapError instead of a clear\n // NodeError. Negative amounts would cause undefined behavior in the ILP layer.\n let amountBigInt: bigint;\n try {\n amountBigInt = BigInt(computeAmount);\n } catch {\n throw new NodeError(\n `Cannot settle compute: result event amount ('${computeAmount}') is not a valid numeric string.`\n );\n }\n if (amountBigInt < 0n) {\n throw new NodeError(\n `Cannot settle compute: result event amount ('${computeAmount}') must be non-negative.`\n );\n }\n\n // E5-R005 bid validation: if originalBid is provided, validate amount <= bid\n if (options?.originalBid !== undefined) {\n let bidBigInt: bigint;\n try {\n bidBigInt = BigInt(options.originalBid);\n } catch {\n throw new NodeError(\n `Cannot settle compute: originalBid ('${options.originalBid}') is not a valid numeric string.`\n );\n }\n if (amountBigInt > bidBigInt) {\n throw new NodeError(\n `Cannot settle compute: result amount (${computeAmount}) exceeds original bid (${options.originalBid}). Potential provider overcharge.`\n );\n }\n }\n\n // Send pure ILP value transfer (empty data = no event payload)\n // This is a payment-only operation -- no TOON encoding, no relay write.\n return ilpClient.sendIlpPacket({\n destination: providerIlpAddress,\n amount: computeAmount,\n data: '',\n });\n },\n\n // Task 6.3: Address lifecycle management for multi-peered nodes\n addUpstreamPeer(upstreamPrefix: string): void {\n const derivedAddress = deriveChildAddress(upstreamPrefix, pubkey);\n\n // Check for collision against existing addresses\n checkAddressCollision(derivedAddress, addressRegistry.getAddresses());\n\n // Update registry\n addressRegistry.addAddress(upstreamPrefix, derivedAddress);\n\n // Update ilpInfo for kind:10032 republication\n ilpInfo.ilpAddresses = addressRegistry.getAddresses();\n ilpInfo.ilpAddress =\n addressRegistry.getPrimaryAddress() ?? ilpInfo.ilpAddress;\n\n // Register self-route in embedded connector (if available)\n if (autoCreatedConnector) {\n const nodeId = `toon-${pubkey.slice(0, 16)}`;\n autoCreatedConnector.addRoute({\n prefix: derivedAddress,\n nextHop: nodeId,\n priority: 100,\n });\n }\n\n // Trigger kind:10032 republication to propagate updated address list\n if (started && lastBootstrapResults.length > 0) {\n void bootstrapServiceInstance.republish(lastBootstrapResults);\n }\n },\n\n removeUpstreamPeer(upstreamPrefix: string): void {\n // Guard: cannot remove the last address -- a node must always have at least one\n // ILP address (AC #3: empty ilpAddresses array is rejected at construction time).\n if (\n addressRegistry.hasPrefix(upstreamPrefix) &&\n addressRegistry.size <= 1\n ) {\n throw new NodeError(\n 'Cannot remove last upstream peer: a node must have at least one ILP address'\n );\n }\n\n const removedAddress = addressRegistry.removeAddress(upstreamPrefix);\n if (removedAddress === undefined) {\n return; // No-op if prefix not found\n }\n\n // Update ilpInfo for kind:10032 republication\n ilpInfo.ilpAddresses = addressRegistry.getAddresses();\n const newPrimary = addressRegistry.getPrimaryAddress();\n if (newPrimary !== undefined) {\n ilpInfo.ilpAddress = newPrimary;\n }\n\n // Remove self-route from embedded connector (if available)\n if (autoCreatedConnector && autoCreatedConnector.removeRoute) {\n autoCreatedConnector.removeRoute(removedAddress);\n }\n\n // Trigger kind:10032 republication to propagate updated address list\n if (started && lastBootstrapResults.length > 0) {\n void bootstrapServiceInstance.republish(lastBootstrapResults);\n }\n },\n\n async claimPrefix(\n prefix: string,\n upstreamDestination: string,\n options?: { prefixPrice?: bigint }\n ): Promise<PublishEventResult> {\n // Guard: node must be started\n if (!started) {\n throw new NodeError(\n 'Cannot claim prefix: node not started. Call start() first.'\n );\n }\n\n // Validate prefix format before sending payment (fail-fast, like bid safety cap)\n const prefixValidation = validatePrefix(prefix);\n if (!prefixValidation.valid) {\n throw new NodeError(\n `Cannot claim prefix: ${prefixValidation.reason ?? 'invalid prefix'}`\n );\n }\n\n // Determine the amount: explicit override, or look up upstream's prefixPricing\n let claimAmount = options?.prefixPrice;\n if (claimAmount === undefined) {\n // Look up from discovered peers\n const peers = discoveryTrackerInstance.getAllDiscoveredPeers();\n const upstreamPeer = peers.find(\n (p) =>\n p.peerInfo.ilpAddress === upstreamDestination ||\n (p.peerInfo.ilpAddresses &&\n p.peerInfo.ilpAddresses.includes(upstreamDestination))\n );\n if (upstreamPeer?.peerInfo.prefixPricing?.basePrice) {\n claimAmount = BigInt(upstreamPeer.peerInfo.prefixPricing.basePrice);\n } else {\n throw new NodeError(\n 'Cannot claim prefix: no amount provided and upstream peer prefix pricing not found in discovery'\n );\n }\n }\n\n // Build the prefix claim event\n const claimEvent = buildPrefixClaimEvent(\n { requestedPrefix: prefix },\n config.secretKey\n );\n\n // Delegate to publishEvent with the amount override\n return node.publishEvent(claimEvent, {\n destination: upstreamDestination,\n amount: claimAmount,\n });\n },\n };\n\n return node;\n}\n","/**\n * Skill descriptor builder for DVM service discovery.\n *\n * Computes a SkillDescriptor from the handler registry and node configuration.\n * The descriptor is embedded in kind:10035 events to advertise DVM capabilities.\n *\n * Returns `undefined` when no DVM handlers are registered (backward compatible\n * with pre-DVM kind:10035 events).\n */\n\nimport { ToonError } from '@toon-protocol/core';\nimport type { SkillDescriptor, ReputationScore } from '@toon-protocol/core';\nimport type { HandlerRegistry } from './handler-registry.js';\n\n/** Regex for 64-char lowercase hex string. */\nconst HEX_64_REGEX = /^[0-9a-f]{64}$/;\n\n/** Configuration for building a skill descriptor. */\nexport interface BuildSkillDescriptorConfig {\n /** Base price per byte in USDC micro-units (default: 10n). */\n basePricePerByte?: bigint;\n /** Per-kind pricing overrides (kind number -> price in USDC micro-units). */\n kindPricing?: Record<number, bigint>;\n /** Service name override (default: 'toon-dvm'). */\n name?: string;\n /** Schema version override (default: '1.0'). */\n version?: string;\n /** Feature list override. */\n features?: string[];\n /** JSON Schema draft-07 object for job request parameters. */\n inputSchema?: Record<string, unknown>;\n /** Available AI models. */\n models?: string[];\n /** Pre-computed reputation score for the skill descriptor. */\n reputation?: ReputationScore;\n /** TEE attestation metadata for the skill descriptor. */\n attestation?: {\n /** 64-char hex event ID of the provider's latest kind:10033 attestation event. */\n eventId: string;\n /** Enclave image hash for the TEE environment. */\n enclaveImageHash: string;\n };\n}\n\n/**\n * Builds a SkillDescriptor from the handler registry and configuration.\n *\n * Returns `undefined` when the registry has no DVM handlers (kinds 5000-5999).\n * When DVM kinds exist, auto-populates the descriptor:\n * - `kinds` from `registry.getDvmKinds()`\n * - `pricing` from `config.kindPricing` overrides or `config.basePricePerByte` fallback\n * - `name`, `version`, `features`, `inputSchema`, `models` from config or defaults\n *\n * @param registry - The handler registry to read DVM kinds from.\n * @param config - Node configuration for pricing and optional overrides.\n * @returns A SkillDescriptor, or undefined if no DVM handlers are registered.\n */\nexport function buildSkillDescriptor(\n registry: HandlerRegistry,\n config: BuildSkillDescriptorConfig = {}\n): SkillDescriptor | undefined {\n const dvmKinds = registry.getDvmKinds();\n if (dvmKinds.length === 0) {\n return undefined;\n }\n\n // Validate attestation eventId if provided\n if (config.attestation !== undefined) {\n if (!HEX_64_REGEX.test(config.attestation.eventId)) {\n throw new ToonError(\n 'Skill descriptor attestation eventId must be a 64-character lowercase hex string',\n 'DVM_SKILL_INVALID_ATTESTATION_EVENT_ID'\n );\n }\n }\n\n const basePricePerByte = config.basePricePerByte ?? 10n;\n const kindPricing = config.kindPricing ?? {};\n\n // Derive pricing: per-kind overrides or basePricePerByte fallback\n const pricing: Record<string, string> = {};\n for (const kind of dvmKinds) {\n if (Object.hasOwn(kindPricing, kind)) {\n pricing[String(kind)] = String(kindPricing[kind]);\n } else {\n pricing[String(kind)] = String(basePricePerByte);\n }\n }\n\n const descriptor: SkillDescriptor = {\n name: config.name ?? 'toon-dvm',\n version: config.version ?? '1.0',\n kinds: dvmKinds,\n features: config.features ?? [],\n inputSchema: config.inputSchema ?? { type: 'object', properties: {} },\n pricing,\n };\n\n if (config.models !== undefined) {\n descriptor.models = config.models;\n }\n\n if (config.reputation !== undefined) {\n descriptor.reputation = config.reputation;\n }\n\n if (config.attestation !== undefined) {\n descriptor.attestation = {\n eventId: config.attestation.eventId,\n enclaveImageHash: config.attestation.enclaveImageHash,\n };\n }\n\n return descriptor;\n}\n","/**\n * Workflow Orchestrator for multi-step DVM pipelines (Story 6.1).\n *\n * Manages the lifecycle of a workflow chain: creates step job requests,\n * detects step completion/failure, advances to the next step, handles\n * timeouts, and settles compute payments per step.\n *\n * The orchestrator uses the TOON relay as the orchestration layer --\n * there is no separate workflow engine. Step completion is detected\n * via relay event subscriptions (Kind 6xxx results, Kind 7000 feedback).\n *\n * State machine states:\n * - `pending`: Workflow created but not yet started\n * - `step_N_running`: Step N's job request published, waiting for result\n * - `step_N_failed`: Step N failed (error or timeout), workflow aborted\n * - `completed`: All steps finished successfully\n *\n * Settlement: Each step settles individually via settleCompute().\n * The orchestrator validates sum(step_amounts) <= total_bid before settlement.\n *\n * Forward-compatible with Epic 7 prepaid protocol: settlement logic is\n * isolated in handleStepResult() so swapping to prepaid per-step payment\n * requires changes only in that method.\n */\n\nimport type { NostrEvent } from 'nostr-tools/pure';\nimport { buildJobRequestEvent, parseJobFeedback } from '@toon-protocol/core';\nimport type { ParsedWorkflowDefinition } from '@toon-protocol/core';\nimport type { ServiceNode } from './create-node.js';\n\n/**\n * Workflow state type. Uses template literal types for step-indexed states.\n * Note: Timeout is represented as `step_N_failed` (timeout is a failure mode,\n * not a separate terminal state). The timeout cause is communicated in the\n * customer notification content.\n */\nexport type WorkflowState =\n | 'pending'\n | `step_${number}_running`\n | `step_${number}_failed`\n | 'completed';\n\n/**\n * Event store interface for workflow state persistence.\n * Minimal interface -- only store() and query() are needed.\n */\nexport interface WorkflowEventStore {\n store(event: NostrEvent): Promise<void>;\n query(filter: { kinds?: number[]; '#e'?: string[] }): Promise<NostrEvent[]>;\n}\n\n/**\n * Configuration options for the WorkflowOrchestrator.\n */\nexport interface WorkflowOrchestratorOptions {\n /** Secret key for signing step job request events (32-byte Uint8Array). */\n secretKey?: Uint8Array;\n /** Per-step timeout in milliseconds (default: 300000 = 5 minutes). */\n stepTimeoutMs?: number;\n /** Injectable time source for deterministic testing. */\n now?: () => number;\n /**\n * Injectable timer factory for deterministic testing.\n * Defaults to global setTimeout. Inject a custom implementation\n * to control timer advancement in tests without vi.useFakeTimers().\n */\n setTimer?: (\n callback: () => void,\n ms: number\n ) => ReturnType<typeof setTimeout>;\n /**\n * Injectable timer cancellation for deterministic testing.\n * Defaults to global clearTimeout. Must pair with setTimer.\n */\n clearTimer?: (handle: ReturnType<typeof setTimeout>) => void;\n /** Optional event store for workflow state persistence. */\n eventStore?: WorkflowEventStore;\n /** Default destination ILP address for publishing events. */\n destination?: string;\n /** Workflow definition event ID (for referencing in notifications). */\n workflowEventId?: string;\n /** Customer pubkey (for directing notifications). */\n customerPubkey?: string;\n}\n\n/**\n * Per-step state tracking.\n */\ninterface StepState {\n /** Index of this step (0-based). */\n index: number;\n /** The job request event ID published for this step. */\n requestEventId?: string;\n /** The result event received for this step. */\n resultEvent?: NostrEvent;\n /** Whether this step has been settled. */\n settled: boolean;\n}\n\n/**\n * Orchestrates a multi-step DVM workflow chain.\n *\n * Each instance manages a single workflow. For concurrent workflows,\n * create multiple WorkflowOrchestrator instances sharing the same\n * ServiceNode.\n */\nexport class WorkflowOrchestrator {\n private readonly node: ServiceNode;\n private readonly options: Required<\n Pick<WorkflowOrchestratorOptions, 'stepTimeoutMs'>\n > &\n WorkflowOrchestratorOptions;\n\n private state: WorkflowState = 'pending';\n private definition: ParsedWorkflowDefinition | null = null;\n private workflowEventId: string = '0'.repeat(64);\n private customerPubkey: string = '0'.repeat(64);\n private currentStepIndex = 0;\n private stepStates: StepState[] = [];\n private timeoutHandle: ReturnType<typeof setTimeout> | null = null;\n private processedResultIds = new Set<string>();\n\n constructor(node: ServiceNode, options?: WorkflowOrchestratorOptions) {\n this.node = node;\n this.options = {\n stepTimeoutMs: 300_000, // 5 minutes default\n ...options,\n };\n if (options?.workflowEventId) {\n this.workflowEventId = options.workflowEventId;\n }\n if (options?.customerPubkey) {\n this.customerPubkey = options.customerPubkey;\n }\n }\n\n /**\n * Returns the current workflow state.\n */\n getState(): WorkflowState {\n return this.state;\n }\n\n /**\n * Returns per-step state tracking data (for testing/debugging).\n */\n getStepStates(): readonly Readonly<StepState>[] {\n return this.stepStates;\n }\n\n /**\n * Starts a workflow: parses the definition, creates and publishes\n * the Kind 5xxx job request for step 1, sets state to step_1_running.\n */\n async startWorkflow(definition: ParsedWorkflowDefinition): Promise<void> {\n // Guard: prevent re-entrance -- calling startWorkflow on an already-started\n // orchestrator would silently overwrite state without cleaning up timeouts.\n if (this.state !== 'pending') {\n throw new Error(\n 'WorkflowOrchestrator.startWorkflow() called on a non-pending orchestrator. ' +\n 'Create a new WorkflowOrchestrator instance for each workflow.'\n );\n }\n\n // Guard: validate steps non-empty (defense-in-depth -- the builder validates\n // this too, but the orchestrator may receive ParsedWorkflowDefinition from\n // sources other than the builder, e.g. deserialized from EventStore).\n if (!definition.steps || definition.steps.length === 0) {\n throw new Error(\n 'WorkflowOrchestrator.startWorkflow() requires at least one step in the definition.'\n );\n }\n\n this.definition = definition;\n\n // Initialize step states\n this.stepStates = definition.steps.map((_, index) => ({\n index,\n settled: false,\n }));\n\n // Store the workflow definition event if eventStore is available\n if (this.options.eventStore) {\n // Create a synthetic event representing the workflow state.\n // Use a hash-like ID derived from pubkey + timestamp to avoid collisions\n // across concurrent workflows.\n const now = this.options.now ? this.options.now() : Date.now();\n const syntheticId = `wf${now.toString(16).padStart(16, '0')}${this.node.pubkey.slice(0, 48)}`;\n const stateEvent: NostrEvent = {\n id: syntheticId.padEnd(64, '0').slice(0, 64),\n pubkey: this.node.pubkey,\n created_at: Math.floor(now / 1000),\n kind: 10040,\n content: JSON.stringify({\n state: 'started',\n definition,\n }),\n tags: [],\n sig: '0'.repeat(128),\n };\n await this.options.eventStore.store(stateEvent);\n }\n\n // Create and publish step 1\n await this.publishStepRequest(\n 0,\n definition.initialInput.data,\n definition.initialInput.type\n );\n\n // Set state\n this.state = 'step_1_running';\n this.currentStepIndex = 0;\n\n // Start timeout for step 1\n this.startStepTimeout();\n }\n\n /**\n * Handles a Kind 6xxx result event for the current step.\n *\n * If the current step matches, extracts the result content and either:\n * - Advances to the next step (publishes Kind 5xxx for step N+1)\n * - Marks workflow as completed (if this was the final step)\n *\n * Idempotent: re-processing a result for an already-advanced step is a no-op.\n */\n async handleStepResult(resultEvent: NostrEvent): Promise<void> {\n if (!this.definition) return;\n\n // Validate result event kind is in the 6000-6999 range (Kind 6xxx).\n // Without this check, any event kind could be injected as a step result.\n if (resultEvent.kind < 6000 || resultEvent.kind > 6999) return;\n\n // Idempotency: skip already-processed result events\n if (this.processedResultIds.has(resultEvent.id)) return;\n\n // Idempotency: ignore if we've already moved past this step\n const currentStep = this.stepStates[this.currentStepIndex];\n if (!currentStep) return;\n if (currentStep.resultEvent) return; // Already processed\n\n // Ignore if workflow is in a terminal state\n if (this.state === 'completed' || this.state.endsWith('_failed')) return;\n\n // NOTE: The orchestrator trusts that the caller pre-filters result events\n // to match the current step's request (via relay subscription with `e` tag\n // filter on currentStep.requestEventId). Defense-in-depth validation of the\n // `e` tag could be added here but would require callers to ensure result\n // events always reference the correct request ID.\n\n // Mark this result as processed\n this.processedResultIds.add(resultEvent.id);\n\n // Clear timeout\n this.clearStepTimeout();\n\n // Record the result\n currentStep.resultEvent = resultEvent;\n\n // Settle compute for this step\n await this.settleStep(this.currentStepIndex, resultEvent);\n\n // Store step result in eventStore if available\n if (this.options.eventStore) {\n await this.options.eventStore.store(resultEvent);\n }\n\n // Check if this was the final step\n const isLastStep =\n this.currentStepIndex === this.definition.steps.length - 1;\n\n if (isLastStep) {\n // Workflow complete\n this.state = 'completed';\n\n // Notify customer with Kind 7000 success\n await this.publishWorkflowNotification(\n 'success',\n 'Workflow completed successfully'\n );\n } else {\n // Advance to next step\n const nextIndex = this.currentStepIndex + 1;\n const resultContent = resultEvent.content;\n\n await this.publishStepRequest(nextIndex, resultContent, 'text');\n\n this.currentStepIndex = nextIndex;\n this.state = `step_${nextIndex + 1}_running`;\n\n // Start timeout for next step\n this.startStepTimeout();\n }\n }\n\n /**\n * Handles a Kind 7000 feedback event for the current step.\n *\n * If the feedback status is 'error', marks the workflow as failed\n * and notifies the customer.\n */\n async handleStepFeedback(feedbackEvent: NostrEvent): Promise<void> {\n if (!this.definition) return;\n\n // Ignore if workflow is in a terminal state\n if (this.state === 'completed' || this.state.endsWith('_failed')) return;\n\n const parsed = parseJobFeedback(feedbackEvent);\n if (!parsed) return;\n\n if (parsed.status === 'error') {\n // Clear timeout\n this.clearStepTimeout();\n\n // Mark failed\n this.state = `step_${this.currentStepIndex + 1}_failed`;\n\n // Store failure in eventStore if available\n if (this.options.eventStore) {\n await this.options.eventStore.store(feedbackEvent);\n }\n\n // Notify customer\n await this.publishWorkflowNotification(\n 'error',\n `Workflow failed at step ${this.currentStepIndex + 1}: ${parsed.content || 'Unknown error'}`\n );\n }\n }\n\n /**\n * Cleans up resources (timeout handles).\n */\n destroy(): void {\n this.clearStepTimeout();\n }\n\n // ---------- Private Methods ----------\n\n /**\n * Publishes a Kind 5xxx job request for the given step.\n */\n private async publishStepRequest(\n stepIndex: number,\n inputData: string,\n inputType: string\n ): Promise<void> {\n if (!this.definition) return;\n\n const step = this.definition.steps[stepIndex];\n if (!step) return;\n\n // Compute bid for this step\n const stepBid = this.getStepBid(stepIndex);\n\n // Build the job request event\n const jobRequest = buildJobRequestEvent(\n {\n kind: step.kind,\n input: { data: inputData, type: inputType },\n bid: stepBid,\n output: 'text/plain',\n content: step.description,\n targetProvider: step.targetProvider,\n },\n this.getSecretKey()\n );\n\n // Publish via the node's publishEvent (which handles TOON encoding + ILP)\n const destination = this.options.destination ?? 'g.toon.local';\n await this.node.publishEvent(jobRequest, { destination });\n\n // Record the request event ID\n const stepState = this.stepStates[stepIndex];\n if (stepState) {\n stepState.requestEventId = jobRequest.id;\n }\n }\n\n /**\n * Settles compute payment for a completed step.\n */\n private async settleStep(\n stepIndex: number,\n resultEvent: NostrEvent\n ): Promise<void> {\n if (!this.definition) return;\n\n const stepState = this.stepStates[stepIndex];\n if (!stepState || stepState.settled) return;\n\n const step = this.definition.steps[stepIndex];\n if (!step) return;\n\n // Determine provider ILP address from result event pubkey or fallback.\n // In production, this would be resolved from the provider's kind:10035\n // service discovery event. For now, derive from the destination prefix.\n const providerIlpAddress = this.options.destination ?? 'g.toon.provider';\n\n try {\n await this.node.settleCompute(resultEvent, providerIlpAddress, {\n originalBid: this.getStepBid(stepIndex),\n });\n stepState.settled = true;\n } catch (_err: unknown) {\n // Settlement failure does not block workflow advancement.\n // The orchestrator prioritizes workflow progress over payment atomicity.\n // In production, the caller should monitor stepState.settled per step\n // and reconcile unsettled steps out-of-band.\n }\n }\n\n /**\n * Returns the bid allocation for a specific step.\n * Uses explicit bidAllocation if set, otherwise proportional split.\n */\n private getStepBid(stepIndex: number): string {\n if (!this.definition) return '0';\n\n const step = this.definition.steps[stepIndex];\n if (!step) return '0';\n\n // Use explicit allocation if available\n if (step.bidAllocation !== undefined) {\n return step.bidAllocation;\n }\n\n // Proportional split -- wrap BigInt conversion in try/catch since\n // totalBid may come from deserialized EventStore data that was not\n // validated by the builder.\n try {\n const totalBid = BigInt(this.definition.totalBid);\n const stepCount = BigInt(this.definition.steps.length);\n const perStep = totalBid / stepCount;\n return perStep.toString();\n } catch {\n return '0';\n }\n }\n\n /**\n * Publishes a Kind 7000 workflow notification to the customer.\n */\n private async publishWorkflowNotification(\n status: 'success' | 'error',\n content: string\n ): Promise<void> {\n const destination = this.options.destination ?? 'g.toon.local';\n try {\n await this.node.publishFeedback(\n this.workflowEventId,\n this.customerPubkey,\n status,\n content,\n { destination }\n );\n } catch (_err: unknown) {\n // Best-effort notification -- don't fail the workflow.\n // Notification delivery is not guaranteed; callers should\n // check orchestrator.getState() for authoritative status.\n }\n }\n\n /**\n * Starts a timeout timer for the current step.\n */\n private startStepTimeout(): void {\n this.clearStepTimeout();\n\n const timeoutMs = this.options.stepTimeoutMs;\n const timerFn = this.options.setTimer ?? setTimeout;\n\n this.timeoutHandle = timerFn(() => {\n // Mark as failed due to timeout\n this.state = `step_${this.currentStepIndex + 1}_failed`;\n\n // Notify customer\n void this.publishWorkflowNotification(\n 'error',\n `Workflow timed out at step ${this.currentStepIndex + 1} after ${timeoutMs}ms`\n );\n }, timeoutMs);\n }\n\n /**\n * Returns the secret key for signing step events.\n * Falls back to a deterministic key derived from the node pubkey\n * (for testing -- production should always pass secretKey in options).\n */\n private getSecretKey(): Uint8Array {\n if (this.options.secretKey) {\n return this.options.secretKey;\n }\n // Fallback: use a fixed test key (this path should not be hit in production)\n throw new Error(\n 'WorkflowOrchestrator requires secretKey in options to sign step events'\n );\n }\n\n /**\n * Clears the current step timeout.\n */\n private clearStepTimeout(): void {\n if (this.timeoutHandle !== null) {\n const cancelFn = this.options.clearTimer ?? clearTimeout;\n cancelFn(this.timeoutHandle);\n this.timeoutHandle = null;\n }\n }\n}\n","/**\n * Swarm Coordinator for competitive DVM bidding (Story 6.2).\n *\n * Manages the lifecycle of a competitive swarm: collects provider\n * submissions, handles timeout-based judging deadlines, validates\n * winner selection, and settles compute payment to the winner only.\n *\n * The coordinator uses the TOON relay as the orchestration layer --\n * there is no separate swarm engine. Submission arrival is detected\n * via relay event subscriptions (Kind 6xxx results).\n *\n * State machine states:\n * - `collecting`: Waiting for provider submissions (Kind 6xxx results)\n * - `judging`: Timeout or max submissions reached; customer selects winner\n * - `settled`: Winner paid via settleCompute(); swarm complete\n * - `failed`: No submissions within timeout or error\n *\n * Settlement: Only the winning provider receives compute payment.\n * Losing providers paid relay write fees (sunk cost) but receive\n * no compute payment -- this is by design to incentivize quality.\n *\n * Forward-compatible with Epic 7 prepaid protocol: settlement logic\n * is isolated in selectWinner() so swapping to prepaid per-winner\n * payment requires changes only in that method.\n */\n\nimport type { NostrEvent } from 'nostr-tools/pure';\nimport {\n ToonError,\n parseSwarmRequest,\n parseSwarmSelection,\n} from '@toon-protocol/core';\nimport type { ServiceNode } from './create-node.js';\nimport type { WorkflowEventStore } from './workflow-orchestrator.js';\n\n/**\n * Swarm state type.\n * - `collecting`: Waiting for provider submissions.\n * - `judging`: Timeout or max submissions reached; awaiting winner selection.\n * - `settled`: Winner paid; swarm complete.\n * - `failed`: No submissions within timeout or error.\n */\nexport type SwarmState = 'collecting' | 'judging' | 'settled' | 'failed';\n\n/**\n * Configuration options for the SwarmCoordinator.\n */\nexport interface SwarmCoordinatorOptions {\n /** Secret key for signing events (32-byte Uint8Array). */\n secretKey?: Uint8Array;\n /** Swarm collection timeout in milliseconds (default: 600000 = 10 minutes). */\n timeoutMs?: number;\n /** Injectable time source for deterministic testing. */\n now?: () => number;\n /**\n * Injectable timer factory for deterministic testing.\n * Defaults to global setTimeout. Inject a custom implementation\n * to control timer advancement in tests without vi.useFakeTimers().\n */\n setTimer?: (\n callback: () => void,\n ms: number\n ) => ReturnType<typeof setTimeout>;\n /**\n * Injectable timer cancellation for deterministic testing.\n * Defaults to global clearTimeout. Must pair with setTimer.\n */\n clearTimer?: (handle: ReturnType<typeof setTimeout>) => void;\n /** Optional event store for swarm state persistence. */\n eventStore?: WorkflowEventStore;\n /** Default destination ILP address for publishing events. */\n destination?: string;\n}\n\n/**\n * Coordinates a competitive DVM swarm.\n *\n * Each instance manages a single swarm. For concurrent swarms,\n * create multiple SwarmCoordinator instances sharing the same\n * ServiceNode.\n */\nexport class SwarmCoordinator {\n private readonly node: ServiceNode;\n private readonly options: Required<\n Pick<SwarmCoordinatorOptions, 'timeoutMs'>\n > &\n SwarmCoordinatorOptions;\n\n private state: SwarmState = 'collecting';\n private swarmRequestId = '';\n private customerPubkey = '';\n private maxProviders = 0;\n private submissions: NostrEvent[] = [];\n private timeoutHandle: ReturnType<typeof setTimeout> | null = null;\n private started = false;\n private settlementSucceeded = false;\n private submissionIds = new Set<string>();\n\n constructor(node: ServiceNode, options?: SwarmCoordinatorOptions) {\n this.node = node;\n this.options = {\n timeoutMs: 600_000, // 10 minutes default\n ...options,\n };\n }\n\n /**\n * Returns the current swarm state.\n */\n getState(): SwarmState {\n return this.state;\n }\n\n /**\n * Returns whether settlement was successfully completed.\n * Only meaningful when state is 'settled' (returns true).\n * If settlement fails, the state remains 'judging' and this returns false.\n */\n isSettlementSucceeded(): boolean {\n return this.settlementSucceeded;\n }\n\n /**\n * Returns the collected eligible submissions.\n */\n getSubmissions(): readonly NostrEvent[] {\n return this.submissions;\n }\n\n /**\n * Starts a swarm: parses the swarm request, initializes collection,\n * and starts the timeout timer.\n */\n async startSwarm(swarmRequest: NostrEvent): Promise<void> {\n const parsed = parseSwarmRequest(swarmRequest);\n if (!parsed) {\n throw new ToonError(\n 'Invalid swarm request event: missing swarm tag or invalid Kind 5xxx',\n 'DVM_INVALID_KIND'\n );\n }\n\n this.swarmRequestId = swarmRequest.id;\n this.customerPubkey = swarmRequest.pubkey;\n this.maxProviders = parsed.maxProviders;\n this.state = 'collecting';\n this.submissions = [];\n this.submissionIds = new Set<string>();\n this.started = true;\n\n // Store the swarm request in EventStore if available\n if (this.options.eventStore) {\n await this.options.eventStore.store(swarmRequest);\n }\n\n // Start timeout timer\n this.startTimeout();\n }\n\n /**\n * Handles a Kind 6xxx result event (provider submission).\n *\n * Validates the `e` tag references the swarm request, adds to the\n * submissions list, and checks if max providers is reached.\n *\n * Late submissions (after timeout or max reached) are ignored.\n * Submissions before startSwarm() are silently ignored.\n */\n async handleSubmission(resultEvent: NostrEvent): Promise<void> {\n if (!this.started) return;\n\n // Only accept submissions during collecting state\n if (this.state !== 'collecting') return;\n\n // Validate result event kind is in the 6000-6999 range\n if (resultEvent.kind < 6000 || resultEvent.kind > 6999) return;\n\n // Validate e tag references our swarm request\n const eTag = resultEvent.tags.find((t: string[]) => t[0] === 'e');\n if (!eTag) return;\n const requestEventId = eTag[1];\n if (requestEventId === undefined || requestEventId !== this.swarmRequestId)\n return;\n\n // Deduplicate by event ID -- relay normally handles dedup, but the\n // coordinator must also guard against duplicate submissions reaching\n // the maxProviders threshold with the same event repeated.\n if (this.submissionIds.has(resultEvent.id)) return;\n this.submissionIds.add(resultEvent.id);\n\n // Add to submissions\n this.submissions.push(resultEvent);\n\n // Store in EventStore if available\n if (this.options.eventStore) {\n await this.options.eventStore.store(resultEvent);\n }\n\n // Check if max providers reached\n if (this.submissions.length >= this.maxProviders) {\n this.clearTimeout();\n this.state = 'judging';\n }\n }\n\n /**\n * Selects the winner and settles compute payment.\n *\n * Validates:\n * - Swarm is in `judging` state (not `collecting`, `settled`, or `failed`)\n * - Selection references a submission in the collected set\n * - Swarm has not already been settled (idempotency guard)\n *\n * @throws ToonError with code DVM_SWARM_ALREADY_SETTLED if already settled\n * @throws ToonError with code DVM_SWARM_INVALID_SELECTION if winner not in submissions\n */\n async selectWinner(selectionEvent: NostrEvent): Promise<void> {\n // Idempotency guard: reject if already settled\n if (this.state === 'settled') {\n throw new ToonError(\n 'Swarm has already been settled; duplicate selection rejected',\n 'DVM_SWARM_ALREADY_SETTLED'\n );\n }\n\n // Must be in judging state\n if (this.state !== 'judging') {\n throw new ToonError(\n `Cannot select winner in state '${this.state}'; swarm must be in 'judging' state`,\n 'DVM_SWARM_INVALID_SELECTION'\n );\n }\n\n // Parse the selection event to extract the winner\n // Validate that the selection event is from the swarm's customer\n if (selectionEvent.pubkey !== this.customerPubkey) {\n throw new ToonError(\n 'Selection event pubkey does not match the swarm request customer pubkey',\n 'DVM_SWARM_INVALID_SELECTION'\n );\n }\n\n const parsed = parseSwarmSelection(selectionEvent);\n if (!parsed) {\n throw new ToonError(\n 'Invalid swarm selection event: missing winner tag or invalid Kind 7000',\n 'DVM_SWARM_INVALID_SELECTION'\n );\n }\n\n // Validate that the selection references this swarm (not a different one)\n if (parsed.swarmRequestEventId !== this.swarmRequestId) {\n throw new ToonError(\n `Selection event references swarm '${parsed.swarmRequestEventId}' but this swarm is '${this.swarmRequestId}'`,\n 'DVM_SWARM_INVALID_SELECTION'\n );\n }\n\n // Validate the winner references a collected submission\n const winnerSubmission = this.submissions.find(\n (s) => s.id === parsed.winnerResultEventId\n );\n if (!winnerSubmission) {\n throw new ToonError(\n `Winner result event ID '${parsed.winnerResultEventId}' not found in collected submissions`,\n 'DVM_SWARM_INVALID_SELECTION'\n );\n }\n\n // Settle compute payment to the winning provider only\n const providerIlpAddress = this.options.destination ?? 'g.toon.provider';\n try {\n await this.node.settleCompute(winnerSubmission, providerIlpAddress);\n this.settlementSucceeded = true;\n } catch (_err: unknown) {\n // Settlement failed -- remain in 'judging' state so the caller can\n // retry selectWinner() with the same or different selection event.\n // Do NOT transition to 'settled' because no payment was made.\n this.settlementSucceeded = false;\n throw new ToonError(\n 'Settlement failed for winning provider; swarm remains in judging state for retry',\n 'DVM_SWARM_SETTLEMENT_FAILED'\n );\n }\n\n // Store selection in EventStore if available\n if (this.options.eventStore) {\n await this.options.eventStore.store(selectionEvent);\n }\n\n this.state = 'settled';\n }\n\n /**\n * Cleans up resources (timeout handles).\n */\n destroy(): void {\n this.clearTimeout();\n }\n\n // ---------- Private Methods ----------\n\n /**\n * Starts the collection timeout timer.\n */\n private startTimeout(): void {\n this.clearTimeout();\n\n const timeoutMs = this.options.timeoutMs;\n const timerFn = this.options.setTimer ?? setTimeout;\n\n this.timeoutHandle = timerFn(() => {\n if (this.state !== 'collecting') return;\n\n if (this.submissions.length === 0) {\n // No submissions -- transition to failed\n this.state = 'failed';\n\n // Publish \"no submissions\" Kind 7000 feedback to customer\n void this.publishNoSubmissionsFeedback();\n } else {\n // Some submissions collected -- transition to judging\n this.state = 'judging';\n }\n }, timeoutMs);\n }\n\n /**\n * Publishes a Kind 7000 feedback event indicating no submissions were received.\n */\n private async publishNoSubmissionsFeedback(): Promise<void> {\n const destination = this.options.destination ?? 'g.toon.local';\n try {\n await this.node.publishFeedback(\n this.swarmRequestId,\n this.customerPubkey,\n 'error',\n 'Swarm timed out with no submissions',\n { destination }\n );\n } catch (_err: unknown) {\n // Best-effort notification -- don't fail the state transition.\n }\n }\n\n /**\n * Clears the current timeout timer.\n */\n private clearTimeout(): void {\n if (this.timeoutHandle !== null) {\n const cancelFn = this.options.clearTimer ?? clearTimeout;\n cancelFn(this.timeoutHandle);\n this.timeoutHandle = null;\n }\n }\n}\n","/**\n * Prefix claim handler for the TOON prefix marketplace.\n *\n * Creates a handler that processes kind 10034 prefix claim events,\n * validates payment and prefix availability, and publishes grant\n * confirmations. Lives in SDK (not core) because it depends on\n * the SDK's Handler type and HandlerContext interface.\n */\n\nimport type { NostrEvent } from 'nostr-tools/pure';\nimport {\n parsePrefixClaimEvent,\n buildPrefixGrantEvent,\n validatePrefix,\n} from '@toon-protocol/core';\nimport type { Handler, HandlerResponse } from './handler-registry.js';\nimport type { HandlerContext } from './handler-context.js';\n\n/**\n * Options for creating a prefix claim handler.\n */\nexport interface PrefixClaimHandlerOptions {\n /** Pricing configuration for prefix claims. */\n prefixPricing: { basePrice: bigint };\n /** Secret key for signing grant events. */\n secretKey: Uint8Array;\n /** Returns the current map of claimed prefixes (prefix -> claimer pubkey). */\n getClaimedPrefixes: () => Map<string, string>;\n /**\n * Atomically claim a prefix for a pubkey. Returns true if the claim\n * succeeded, false if the prefix was already taken. This is the\n * serialization point for race condition defense.\n */\n claimPrefix: (prefix: string, claimerPubkey: string) => boolean;\n /** Publish the grant event (e.g., to the local relay). */\n publishGrant: (grantEvent: NostrEvent) => Promise<void>;\n /** ILP address prefix to include in the grant event (default: 'g.toon'). */\n ilpAddressPrefix?: string;\n}\n\n/**\n * Creates a handler for kind 10034 prefix claim events.\n *\n * The handler validates payment, prefix availability, and prefix format,\n * then atomically claims the prefix and publishes a grant confirmation.\n *\n * @param options - Handler configuration\n * @returns A Handler function for kind 10034 events\n */\nexport function createPrefixClaimHandler(\n options: PrefixClaimHandlerOptions\n): Handler {\n return async (ctx: HandlerContext): Promise<HandlerResponse> => {\n // 1. Parse the incoming event as PrefixClaimContent\n const event = ctx.decode();\n const claim = parsePrefixClaimEvent(event);\n if (!claim) {\n return ctx.reject('F06', 'Invalid prefix claim event: malformed content');\n }\n\n // 2. Validate the prefix format\n const validation = validatePrefix(claim.requestedPrefix);\n if (!validation.valid) {\n return ctx.reject(\n 'F06',\n `Invalid prefix: ${validation.reason ?? 'unknown validation error'}`\n );\n }\n\n // 3. Check payment is sufficient\n if (ctx.amount < options.prefixPricing.basePrice) {\n return ctx.reject(\n 'F06',\n `Insufficient payment: received ${ctx.amount}, required ${options.prefixPricing.basePrice}`\n );\n }\n\n // 4. Check prefix availability\n const claimedPrefixes = options.getClaimedPrefixes();\n if (claimedPrefixes.has(claim.requestedPrefix)) {\n return ctx.reject(\n 'F06',\n `PREFIX_TAKEN: prefix \"${claim.requestedPrefix}\" is already claimed`\n );\n }\n\n // 5. Atomically claim the prefix (serialization point for race conditions)\n const claimed = options.claimPrefix(claim.requestedPrefix, event.pubkey);\n if (!claimed) {\n return ctx.reject(\n 'F06',\n `PREFIX_TAKEN: prefix \"${claim.requestedPrefix}\" is already claimed`\n );\n }\n\n // 6. Build and publish grant event\n const prefix = options.ilpAddressPrefix ?? 'g.toon';\n const ilpAddress = `${prefix}.${claim.requestedPrefix}`;\n const grantEvent = buildPrefixGrantEvent(\n {\n grantedPrefix: claim.requestedPrefix,\n claimerPubkey: event.pubkey,\n ilpAddress,\n },\n options.secretKey\n );\n await options.publishGrant(grantEvent);\n\n // 7. Accept the ILP packet\n return ctx.accept();\n };\n}\n","/**\n * Arweave DVM handler for kind:5094 blob storage requests.\n *\n * Creates a handler function that:\n * 1. Parses the kind:5094 event from the HandlerContext\n * 2. For single-packet uploads: uploads directly to Arweave via the adapter\n * 3. For chunked uploads: accumulates via ChunkManager, uploads on completion\n *\n * The handler does NOT validate pricing -- the SDK pricing validator\n * (packages/sdk/src/pricing-validator.ts) runs BEFORE the handler is invoked.\n *\n * Returns HandlePacketAcceptResponse with `data` field containing the Arweave\n * tx ID -- NOT via ctx.accept() which only supports metadata.\n */\n\nimport { parseBlobStorageRequest } from '@toon-protocol/core';\nimport type { HandlerContext } from '../handler-context.js';\nimport type { HandlerResponse } from '../handler-registry.js';\nimport type { ArweaveUploadAdapter } from './turbo-adapter.js';\nimport type { ChunkManager } from './chunk-manager.js';\n\n/**\n * Validates and sanitizes a MIME content type string.\n * Returns sanitized type or 'application/octet-stream' for invalid values.\n *\n * Defends against header injection (CWE-113) and ensures only well-formed\n * MIME types are forwarded to the Arweave upload adapter.\n */\nconst MIME_REGEX =\n /^[a-zA-Z0-9][a-zA-Z0-9!#$&\\-^_.+]*\\/[a-zA-Z0-9][a-zA-Z0-9!#$&\\-^_.+]*$/;\nfunction sanitizeContentType(contentType: string): string {\n // Strip any parameters (e.g., \"; charset=utf-8\") and whitespace\n const base = (contentType.split(';')[0] ?? '').trim();\n if (base.length > 0 && base.length <= 255 && MIME_REGEX.test(base)) {\n return base;\n }\n return 'application/octet-stream';\n}\n\nexport interface ArweaveDvmConfig {\n /** Arweave upload adapter (wraps @ardrive/turbo-sdk). */\n turboAdapter: ArweaveUploadAdapter;\n /** Chunk state manager for multi-packet uploads. */\n chunkManager: ChunkManager;\n /** Optional default Arweave data item tags (e.g., Content-Type). */\n arweaveTags?: Record<string, string>;\n}\n\n/**\n * Creates an Arweave DVM handler for kind:5094 blob storage requests.\n *\n * @param config - Handler configuration with adapter and chunk manager.\n * @returns A handler function compatible with HandlerRegistry.on().\n */\nexport function createArweaveDvmHandler(\n config: ArweaveDvmConfig\n): (ctx: HandlerContext) => Promise<HandlerResponse> {\n const { turboAdapter, chunkManager, arweaveTags } = config;\n\n return async (ctx: HandlerContext): Promise<HandlerResponse> => {\n // Decode the TOON payload into a full NostrEvent\n const event = ctx.decode();\n\n // Parse the kind:5094 event\n const parsed = parseBlobStorageRequest(event);\n if (!parsed) {\n return {\n accept: false,\n code: 'F00',\n message: 'Malformed kind:5094 blob storage request',\n };\n }\n\n // Build tags for Arweave upload.\n // Event-level Content-Type takes precedence over config-level arweaveTags.\n // Sanitize to prevent header injection (CWE-113) from user-controlled input.\n const uploadTags: Record<string, string> = {\n ...arweaveTags,\n 'Content-Type': sanitizeContentType(parsed.contentType),\n };\n\n // Check if this is a chunked upload\n if (\n parsed.uploadId !== undefined &&\n parsed.chunkIndex !== undefined &&\n parsed.totalChunks !== undefined\n ) {\n try {\n const result = chunkManager.addChunk(\n parsed.uploadId,\n parsed.chunkIndex,\n parsed.totalChunks,\n parsed.blobData\n );\n\n if (!result.complete) {\n // Intermediate chunk -- acknowledge receipt\n return {\n accept: true,\n data: Buffer.from(`ack:${parsed.chunkIndex}`).toString('base64'),\n };\n }\n\n // All chunks received -- upload assembled blob\n // assembled is guaranteed to be present when complete is true\n const assembled = result.assembled ?? Buffer.alloc(0);\n const { txId } = await turboAdapter.upload(assembled, uploadTags);\n\n // Base64-encode txId for ILP FULFILL data field (connector validates base64)\n return {\n accept: true,\n data: Buffer.from(txId).toString('base64'),\n };\n } catch (error) {\n // Log full error internally but return generic message to client (CWE-209)\n console.error(\n '[ArweaveDvmHandler] Chunked upload failed:',\n error instanceof Error ? (error.stack ?? error.message) : error\n );\n const internalMsg =\n error instanceof Error ? error.message : 'Unknown chunk error';\n // Only surface safe, expected error categories to callers\n const safeMsg = internalMsg.includes('Duplicate chunkIndex')\n ? 'Duplicate chunk rejected'\n : internalMsg.includes('Max active uploads')\n ? 'Server busy, too many concurrent uploads'\n : internalMsg.includes('exceeds max bytes')\n ? 'Upload size limit exceeded'\n : 'Chunk processing error';\n return {\n accept: false,\n code: 'F00',\n message: safeMsg,\n };\n }\n }\n\n // Single-packet upload\n try {\n const { txId } = await turboAdapter.upload(parsed.blobData, uploadTags);\n // Base64-encode txId for ILP FULFILL data field (connector validates base64)\n return {\n accept: true,\n data: Buffer.from(txId).toString('base64'),\n };\n } catch (error) {\n // Log the full underlying error server-side for operator/gate diagnostics\n // (e.g. Turbo upload-service rejection, network timeout to upload.ardrive.io,\n // a >100 KB payload exceeding the unauthenticated free-tier limit, or a\n // throwing/no-credential adapter — see #146). The stub adapter used to\n // swallow the cause as a bare \"Arweave upload failed\"; we now always emit\n // the real reason here so the failure self-diagnoses from the dvm logs.\n // The client still only receives a generic message (CWE-209): we never\n // forward SDK internals over the wire.\n const cause =\n error instanceof Error ? (error.stack ?? error.message) : String(error);\n console.error(\n `[ArweaveDvmHandler] Arweave upload failed (${parsed.blobData.length} bytes). ` +\n `Cause: ${cause}`\n );\n return {\n accept: false,\n code: 'T00',\n message: 'Arweave upload failed',\n };\n }\n };\n}\n","/**\n * Arweave upload adapter wrapping @ardrive/turbo-sdk.\n *\n * This is the ONLY file that imports @ardrive/turbo-sdk, isolating the\n * external dependency behind an interface (risk E8-R002).\n */\n\nimport { Readable } from 'node:stream';\n\n/**\n * Interface for uploading data to Arweave.\n * Implementations wrap a specific Arweave upload SDK.\n */\nexport interface ArweaveUploadAdapter {\n upload(\n data: Buffer,\n tags?: Record<string, string>\n ): Promise<{ txId: string }>;\n}\n\n/**\n * Turbo SDK upload adapter using @ardrive/turbo-sdk.\n *\n * - Dev/free tier (<=100KB): pass no turboClient. getClient() lazily generates an\n * ephemeral RSA JWK and builds a TurboFactory.authenticated() client. The upload\n * service grants free small-data-item uploads to any valid signer regardless of\n * balance, so no deposit is required. NOTE: TurboFactory.unauthenticated() is NOT\n * usable here — in @ardrive/turbo-sdk >=1.40 uploadFile() exists only on the\n * authenticated client; the unauthenticated client exposes only uploadSignedDataItem().\n * - Prod (paid, uncapped): pass a TurboAuthenticatedClient from a funded wallet.\n *\n * The turboClient is typed as `unknown` to avoid importing @ardrive/turbo-sdk types\n * in this interface. The actual TurboAuthenticatedClient is duck-typed at runtime.\n */\nexport class TurboUploadAdapter implements ArweaveUploadAdapter {\n private turboClient: unknown;\n private initialized = false;\n\n constructor(turboClient?: unknown) {\n if (turboClient) {\n this.turboClient = turboClient;\n this.initialized = true;\n }\n }\n\n private async getClient(): Promise<unknown> {\n if (!this.initialized) {\n // Lazy-import to avoid loading @ardrive/turbo-sdk unless actually used\n const { TurboFactory } = await import('@ardrive/turbo-sdk/node');\n const Arweave = (await import('arweave')).default;\n const arweave = Arweave.init({});\n const jwk = await arweave.crypto.generateJWK();\n this.turboClient = TurboFactory.authenticated({ privateKey: jwk });\n this.initialized = true;\n }\n return this.turboClient;\n }\n\n async upload(\n data: Buffer,\n tags?: Record<string, string>\n ): Promise<{ txId: string }> {\n const client = (await this.getClient()) as {\n uploadFile(params: {\n fileStreamFactory: () => Readable;\n fileSizeFactory: () => number;\n dataItemOpts?: { tags: { name: string; value: string }[] };\n }): Promise<{ id: string }>;\n };\n\n // Convert Record<string, string> to tag array format\n const tagArray: { name: string; value: string }[] = [];\n if (tags) {\n for (const [name, value] of Object.entries(tags)) {\n tagArray.push({ name, value });\n }\n }\n\n const result = await client.uploadFile({\n fileStreamFactory: () => Readable.from(data),\n fileSizeFactory: () => data.length,\n ...(tagArray.length > 0 ? { dataItemOpts: { tags: tagArray } } : {}),\n });\n\n return { txId: result.id };\n }\n}\n","/**\n * Chunk state manager for multi-packet blob uploads.\n *\n * Accumulates chunks for a given uploadId, assembles them in order\n * when all chunks have arrived, and enforces timeout and memory caps.\n */\n\nexport interface ChunkManagerConfig {\n /** Timeout in milliseconds before partial uploads are discarded (default: 300_000 = 5 min). */\n timeoutMs?: number;\n /** Maximum number of concurrent active uploads (default: 100). */\n maxActiveUploads?: number;\n /** Maximum total bytes accumulated per upload before rejection (default: 50MB). */\n maxBytesPerUpload?: number;\n}\n\nexport interface AddChunkResult {\n /** Whether all chunks have been received and the blob is fully assembled. */\n complete: boolean;\n /** The assembled blob (only present when complete is true). */\n assembled?: Buffer;\n /** Error message if the chunk was rejected. */\n error?: string;\n}\n\ninterface UploadState {\n chunks: Map<number, Buffer>;\n totalChunks: number;\n timer: ReturnType<typeof setTimeout>;\n}\n\n/**\n * Manages chunked upload state for the Arweave DVM handler.\n */\nexport class ChunkManager {\n private uploads = new Map<string, UploadState>();\n private readonly timeoutMs: number;\n private readonly maxActiveUploads: number;\n private readonly maxBytesPerUpload: number;\n\n constructor(config: ChunkManagerConfig = {}) {\n this.timeoutMs = config.timeoutMs ?? 300_000;\n this.maxActiveUploads = config.maxActiveUploads ?? 100;\n this.maxBytesPerUpload = config.maxBytesPerUpload ?? 50 * 1024 * 1024; // 50MB\n }\n\n /**\n * Add a chunk for a given uploadId.\n *\n * @param uploadId - Unique identifier for the upload session.\n * @param chunkIndex - Zero-based index of this chunk.\n * @param totalChunks - Total number of chunks expected.\n * @param data - The chunk data.\n * @returns Result indicating completion status or error.\n * @throws Error if memory cap reached or duplicate chunk.\n */\n addChunk(\n uploadId: string,\n chunkIndex: number,\n totalChunks: number,\n data: Buffer\n ): AddChunkResult {\n let state = this.uploads.get(uploadId);\n\n if (!state) {\n // Check memory cap before creating new upload\n if (this.uploads.size >= this.maxActiveUploads) {\n throw new Error(\n `Max active uploads reached (${this.maxActiveUploads}). Cannot accept new uploadId.`\n );\n }\n\n // Create new upload state with timeout\n const timer = setTimeout(() => {\n this.cleanup(uploadId);\n }, this.timeoutMs);\n\n state = {\n chunks: new Map(),\n totalChunks,\n timer,\n };\n this.uploads.set(uploadId, state);\n }\n\n // Validate chunkIndex is within bounds\n if (chunkIndex < 0 || chunkIndex >= state.totalChunks) {\n throw new Error(\n `chunkIndex ${chunkIndex} out of bounds for totalChunks ${state.totalChunks} (uploadId ${uploadId})`\n );\n }\n\n // Check for duplicate chunk\n if (state.chunks.has(chunkIndex)) {\n throw new Error(\n `Duplicate chunkIndex ${chunkIndex} for uploadId ${uploadId}`\n );\n }\n\n // Check per-upload byte limit before storing\n let currentBytes = 0;\n for (const chunk of state.chunks.values()) {\n currentBytes += chunk.length;\n }\n if (currentBytes + data.length > this.maxBytesPerUpload) {\n this.cleanup(uploadId);\n throw new Error(\n `Upload ${uploadId} exceeds max bytes per upload (${this.maxBytesPerUpload})`\n );\n }\n\n // Store chunk\n state.chunks.set(chunkIndex, data);\n\n // Check if all chunks received\n if (state.chunks.size === state.totalChunks) {\n // Assemble in correct order (0, 1, 2, ...)\n const orderedChunks: Buffer[] = [];\n for (let i = 0; i < state.totalChunks; i++) {\n const chunk = state.chunks.get(i);\n if (!chunk) {\n throw new Error(`Missing chunk ${i} for upload`);\n }\n orderedChunks.push(chunk);\n }\n const assembled = Buffer.concat(orderedChunks);\n\n // Clean up state\n this.cleanup(uploadId);\n\n return { complete: true, assembled };\n }\n\n return { complete: false };\n }\n\n /**\n * Check if an upload is complete (all chunks received).\n * Returns false if the uploadId is unknown (cleaned up or never started).\n */\n isComplete(uploadId: string): boolean {\n const state = this.uploads.get(uploadId);\n if (!state) return false;\n return state.chunks.size === state.totalChunks;\n }\n\n /**\n * Clean up state for a given uploadId, clearing the timeout timer.\n */\n cleanup(uploadId: string): void {\n const state = this.uploads.get(uploadId);\n if (state) {\n clearTimeout(state.timer);\n this.uploads.delete(uploadId);\n }\n }\n\n /**\n * Clean up all active uploads and their timers.\n * Call this on node shutdown to prevent timer leaks.\n */\n destroyAll(): void {\n for (const [, state] of this.uploads) {\n clearTimeout(state.timer);\n }\n this.uploads.clear();\n }\n}\n","/**\n * Client-side helpers for uploading blobs to an Arweave DVM provider.\n *\n * - uploadBlob(): single-packet upload for blobs under the chunk threshold\n * - uploadBlobChunked(): multi-packet upload that splits large blobs into chunks\n *\n * Both use publishEvent() with the amount override (Story 7.6, D7-007).\n */\n\nimport { randomUUID } from 'node:crypto';\nimport type { NostrEvent } from 'nostr-tools/pure';\nimport { buildBlobStorageRequest } from '@toon-protocol/core';\nimport type { PublishEventResult } from '../create-node.js';\n\n/**\n * Minimal interface for publishing events. Matches ServiceNode.publishEvent().\n */\nexport interface PublishableNode {\n publishEvent(\n event: NostrEvent,\n options?: { destination: string; amount?: bigint }\n ): Promise<\n Pick<PublishEventResult, 'success' | 'eventId' | 'data' | 'message'>\n >;\n}\n\n/**\n * Options for single-packet blob upload.\n */\nexport interface UploadBlobOptions {\n /** MIME type of the blob (default: 'application/octet-stream'). */\n contentType?: string;\n /** Secret key for signing the event. */\n secretKey: Uint8Array;\n /** Price per byte in USDC micro-units for amount calculation. */\n pricePerByte?: bigint;\n}\n\n/**\n * Options for chunked blob upload.\n */\nexport interface UploadBlobChunkedOptions {\n /** Chunk size in bytes (default: 500_000, under 512KB threshold). */\n chunkSize?: number;\n /** MIME type of the blob (default: 'application/octet-stream'). */\n contentType?: string;\n /** Secret key for signing events. */\n secretKey: Uint8Array;\n /** Price per byte in USDC micro-units for amount calculation. */\n pricePerByte?: bigint;\n}\n\n/**\n * Upload a blob to an Arweave DVM provider in a single ILP packet.\n *\n * Uses publishEvent() with amount override (D7-007) to send the blob\n * and payment in one ILP PREPARE.\n *\n * @param node - The ServiceNode (or compatible) to publish through.\n * @param blob - The raw blob data to upload.\n * @param destination - The provider's ILP address.\n * @param options - Upload options including secretKey for signing.\n * @returns The Arweave transaction ID from the FULFILL data field.\n */\nexport async function uploadBlob(\n node: PublishableNode,\n blob: Buffer,\n destination: string,\n options: UploadBlobOptions\n): Promise<string> {\n const contentType = options.contentType ?? 'application/octet-stream';\n const pricePerByte = options.pricePerByte ?? 10n;\n const amount = BigInt(blob.length) * pricePerByte;\n\n const event = buildBlobStorageRequest(\n {\n blobData: blob,\n contentType,\n bid: amount.toString(),\n },\n options.secretKey\n );\n\n const result = await node.publishEvent(event, { destination, amount });\n\n if (!result.success) {\n throw new Error(`Blob upload failed: ${result.message ?? 'unknown error'}`);\n }\n\n // The tx ID is returned base64-encoded in the FULFILL data field.\n // Decode it to get the raw Arweave tx ID string.\n if (result.data) {\n return Buffer.from(result.data, 'base64').toString('utf-8');\n }\n return result.eventId;\n}\n\n/**\n * Upload a large blob to an Arweave DVM provider using chunked uploads.\n *\n * Splits the blob into chunks, each sent as a separate kind:5094 ILP PREPARE\n * with uploadId, chunkIndex, and totalChunks params. Each chunk carries its\n * own payment. The final chunk's FULFILL data contains the Arweave tx ID.\n *\n * @param node - The ServiceNode (or compatible) to publish through.\n * @param blob - The raw blob data to upload.\n * @param destination - The provider's ILP address.\n * @param options - Chunked upload options including secretKey for signing.\n * @returns The Arweave transaction ID from the final chunk's FULFILL data.\n */\nexport async function uploadBlobChunked(\n node: PublishableNode,\n blob: Buffer,\n destination: string,\n options: UploadBlobChunkedOptions\n): Promise<string> {\n if (blob.length === 0) {\n throw new Error('Cannot upload empty blob via chunked upload');\n }\n\n const chunkSize = options.chunkSize ?? 500_000;\n const contentType = options.contentType ?? 'application/octet-stream';\n const pricePerByte = options.pricePerByte ?? 10n;\n const uploadId = randomUUID();\n\n // Split blob into chunks\n const totalChunks = Math.ceil(blob.length / chunkSize);\n let lastResult: PublishEventResult | undefined;\n\n for (let i = 0; i < totalChunks; i++) {\n const start = i * chunkSize;\n const end = Math.min(start + chunkSize, blob.length);\n const chunkData = blob.subarray(start, end);\n const amount = BigInt(chunkData.length) * pricePerByte;\n\n const event = buildBlobStorageRequest(\n {\n blobData: Buffer.from(chunkData),\n contentType,\n bid: amount.toString(),\n params: [\n { key: 'uploadId', value: uploadId },\n { key: 'chunkIndex', value: String(i) },\n { key: 'totalChunks', value: String(totalChunks) },\n ],\n },\n options.secretKey\n );\n\n lastResult = await node.publishEvent(event, { destination, amount });\n\n if (!lastResult.success) {\n throw new Error(\n `Chunk ${i}/${totalChunks} upload failed: ${lastResult.message ?? 'unknown error'}`\n );\n }\n }\n\n // The final chunk's FULFILL data contains the Arweave tx ID (base64-encoded)\n // lastResult is guaranteed to be set since totalChunks >= 1\n if (!lastResult) {\n throw new Error('No chunks were uploaded');\n }\n if (lastResult.data) {\n return Buffer.from(lastResult.data, 'base64').toString('utf-8');\n }\n return lastResult.eventId;\n}\n","/**\n * Adaptive δ/W controller for rolling swaps (issue #83, rolling-swap spec §6).\n *\n * Two knobs, managed separately:\n * - `δ` (delta) — packet size in source micro-units — bounds *per-packet\n * pick-off risk*.\n * - `W` (window) — max unfulfilled packets in flight — bounds *timing /\n * liveness risk* and the worst-case unrecovered exposure `δ·W`.\n *\n * Normative behavior implemented here (spec §6):\n * - **The cap.** `delta_cap = ε / (v·τ)`, recomputed per packet from measured\n * state, and `δ ≤ delta_cap` always. δ and `delta_cap` are fractions of the\n * session's remaining notional; `v·τ` is the expected fractional rate drift\n * while one packet is in flight; ε is the per-packet slippage budget as a\n * fraction of the maker's advertised half-spread (default `ε = 0.5 ×\n * halfSpread`). ε is spread-denominated, never an absolute rate.\n * - **Inputs are measured, not trusted.** `v` is an EWMA of\n * `abs(R_i − R_{i−1})/R_{i−1}` per second read off the quote tape\n * (`PacketProgress.rate` / `rateTimestamp`, issue #82); `τ` is an EWMA of\n * observed round-trip times. Both update on every observed packet.\n * - **Asymmetric adjustment, one knob per step.** On a shrink signal (a\n * `stale_rate` reject, any other reject/verification failure, or realized\n * per-packet slip `> ε`): multiplicative — `δ ← max(δ_min, δ/2)`; if the\n * signal was a timeout/expiry, `W ← max(1, ⌈W/2⌉)` instead. On a clean\n * streak of `K = 16` consecutive fulfills: additive — `δ ← min(caps, δ +\n * δ_0)` or `W ← min(W_max, W + 1)`, alternating, never both in one step.\n * - **Cold start ramps.** With no persisted state for the tuple:\n * `δ_0 = min(delta_cap, notional/256, maker maxAmount)`, `W_0 = 1`. Until\n * the first shrink signal ever observed for the tuple, the δ widen step is\n * multiplicative (`δ ← min(caps, 2δ)` per clean streak) — slow-start —\n * dropping to additive permanently after the first loss event.\n * - **State is per-(chain, maker, pair) and persisted** via a pluggable\n * {@link SwapControllerStateStore} (the SDK is isomorphic; the JSON-file\n * implementation is Node-only and lazily imports `node:fs`).\n *\n * INVARIANT (spec §5): the controller is an *efficiency* mechanism only. It\n * never sees, computes, or relaxes the `minExchangeRate` floor — the floor\n * check in `stream-swap.ts` runs before the controller observes anything and\n * consults nothing but the floor itself. A calm (or adversarially painted)\n * tape can widen δ up to the caps, but can never worsen the sender's declared\n * worst case. NOTE: the adversarial-quote-tape question (toon-meta#146 — a\n * maker quoting calm to coax δ wide, then gapping one large packet) is open;\n * its resolution may tighten the cap logic here. The floor plus the absolute\n * `maxPacketAmount` cap are the uncompromising backstops in the meantime.\n *\n * @module\n */\n\nimport type { SwapPair } from '@toon-protocol/core';\nimport { ToonError } from '@toon-protocol/core';\n\nimport { applyRate } from './swap-handler.js';\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\n/**\n * Error thrown for invalid adaptive-controller configuration or a\n * persistence-layer failure surfaced through {@link SwapControllerStateStore}.\n */\nexport class SwapControllerError extends ToonError {\n constructor(message: string, cause?: Error) {\n super(message, 'SWAP_CONTROLLER_ERROR', cause);\n this.name = 'SwapControllerError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Persisted state\n// ---------------------------------------------------------------------------\n\n/**\n * Persisted per-(chain, maker, pair) controller state (spec §6 value shape\n * `{delta, W, vEwma, tauEwma, cleanStreak, everShrunk, updatedAt}` plus the\n * additive `lastWidened` alternation marker).\n *\n * JSON-serializable by construction: `delta` is a decimal string because\n * `bigint` does not survive `JSON.stringify`.\n */\nexport interface SwapControllerState {\n /** Schema version for forward migration. Currently `1`. */\n v: 1;\n /**\n * Current ramp value of δ in source micro-units (decimal string).\n * `'0'` means \"not yet initialized\" — the first `nextDelta()` call seeds it\n * with the cold-start `δ_0`.\n */\n delta: string;\n /** In-flight window W: max unfulfilled packets outstanding. Always ≥ 1. */\n W: number;\n /** EWMA of `abs(ΔR)/R` per second, measured from the quote tape. */\n vEwma: number;\n /** EWMA of observed packet round-trip time, in seconds. */\n tauEwma: number;\n /** Consecutive clean fulfills since the last knob adjustment. */\n cleanStreak: number;\n /**\n * True once ANY shrink signal has ever been observed for this tuple.\n * Ends the multiplicative slow-start widen ramp permanently (spec §6).\n */\n everShrunk: boolean;\n /** Which knob the most recent widen step adjusted (alternation marker). */\n lastWidened: 'delta' | 'window';\n /** Unix ms of the last state mutation. */\n updatedAt: number;\n}\n\nconst DECIMAL_UINT_REGEX = /^(0|[1-9]\\d*)$/;\n\n/** Runtime shape check for a (possibly foreign) persisted state blob. */\nexport function isSwapControllerState(\n value: unknown\n): value is SwapControllerState {\n if (!value || typeof value !== 'object') return false;\n const s = value as Record<string, unknown>;\n return (\n s['v'] === 1 &&\n typeof s['delta'] === 'string' &&\n DECIMAL_UINT_REGEX.test(s['delta'] as string) &&\n typeof s['W'] === 'number' &&\n Number.isInteger(s['W']) &&\n (s['W'] as number) >= 1 &&\n typeof s['vEwma'] === 'number' &&\n Number.isFinite(s['vEwma']) &&\n (s['vEwma'] as number) >= 0 &&\n typeof s['tauEwma'] === 'number' &&\n Number.isFinite(s['tauEwma']) &&\n (s['tauEwma'] as number) >= 0 &&\n typeof s['cleanStreak'] === 'number' &&\n Number.isInteger(s['cleanStreak']) &&\n (s['cleanStreak'] as number) >= 0 &&\n typeof s['everShrunk'] === 'boolean' &&\n (s['lastWidened'] === 'delta' || s['lastWidened'] === 'window') &&\n typeof s['updatedAt'] === 'number'\n );\n}\n\n/**\n * Canonical persistence key for a controller tuple (spec §6):\n * `${chain}:${makerPubkey}:${from}:${to}` with `from`/`to` as\n * `assetCode@chain` so cross-chain pairs sharing an asset code stay distinct.\n *\n * The leading `chain` segment is the SOURCE chain — per-tuple state is how\n * the same code runs fast on Base and cautious on Mina.\n */\nexport function swapControllerStateKey(params: {\n makerPubkey: string;\n pair: SwapPair;\n}): string {\n const { makerPubkey, pair } = params;\n const from = `${pair.from.assetCode}@${pair.from.chain}`;\n const to = `${pair.to.assetCode}@${pair.to.chain}`;\n return `${pair.from.chain}:${makerPubkey}:${from}:${to}`;\n}\n\n// ---------------------------------------------------------------------------\n// Pluggable state store\n// ---------------------------------------------------------------------------\n\n/**\n * Pluggable persistence seam for controller state, keyed by\n * {@link swapControllerStateKey}. The SDK is isomorphic, so the interface is\n * environment-neutral (same pattern as `WorkflowEventStore`); pick\n * {@link JsonFileSwapControllerStateStore} on Node or supply your own\n * (e.g. IndexedDB / daemon-side store in toon-client).\n */\nexport interface SwapControllerStateStore {\n load(key: string): Promise<SwapControllerState | undefined>;\n save(key: string, state: SwapControllerState): Promise<void>;\n}\n\n/** Volatile in-memory store — the default when no store is configured. */\nexport class InMemorySwapControllerStateStore implements SwapControllerStateStore {\n private readonly states = new Map<string, SwapControllerState>();\n\n async load(key: string): Promise<SwapControllerState | undefined> {\n const hit = this.states.get(key);\n return hit === undefined ? undefined : { ...hit };\n }\n\n async save(key: string, state: SwapControllerState): Promise<void> {\n this.states.set(key, { ...state });\n }\n}\n\n/**\n * Node-only JSON-file store (the toon-client `JsonFileChannelStore` pattern —\n * controller state persists beside the channel store). One JSON file holds a\n * `{ [key]: SwapControllerState }` map; writes are atomic\n * (temp-file + rename). `node:fs` / `node:path` are imported lazily so\n * bundling this module for the browser stays safe as long as the class is\n * not instantiated there.\n */\nexport class JsonFileSwapControllerStateStore implements SwapControllerStateStore {\n constructor(private readonly filePath: string) {\n if (typeof filePath !== 'string' || filePath.length === 0) {\n throw new SwapControllerError(\n 'JsonFileSwapControllerStateStore requires a non-empty filePath'\n );\n }\n }\n\n private async readAll(): Promise<Record<string, unknown>> {\n const fs = await import('node:fs/promises');\n let raw: string;\n try {\n raw = await fs.readFile(this.filePath, 'utf8');\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return {};\n throw new SwapControllerError(\n `Failed to read controller state file ${this.filePath}`,\n err instanceof Error ? err : undefined\n );\n }\n try {\n const parsed: unknown = JSON.parse(raw);\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n // Corrupt file → treat as empty (cold start) rather than bricking every\n // future swap on this box. The next save rewrites it whole.\n }\n return {};\n }\n\n async load(key: string): Promise<SwapControllerState | undefined> {\n const all = await this.readAll();\n const candidate = all[key];\n return isSwapControllerState(candidate) ? { ...candidate } : undefined;\n }\n\n async save(key: string, state: SwapControllerState): Promise<void> {\n const fs = await import('node:fs/promises');\n const path = await import('node:path');\n const all = await this.readAll();\n all[key] = { ...state };\n const dir = path.dirname(this.filePath);\n await fs.mkdir(dir, { recursive: true });\n const tmp = `${this.filePath}.${process.pid}.${Date.now()}.tmp`;\n await fs.writeFile(tmp, JSON.stringify(all, null, 2), 'utf8');\n await fs.rename(tmp, this.filePath);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Observations\n// ---------------------------------------------------------------------------\n\n/**\n * Resolution class of one packet, as measured by the sender (spec §6):\n *\n * - `'fulfill'` — packet fulfilled and its claim accepted. Clean unless the\n * realized slip exceeds ε (computed internally from `rate` +\n * `sourceAmount` + `targetAmount`), in which case it is a shrink signal.\n * - `'reject-stale'` — maker staleness reject (`T99 stale_rate`, spec §4).\n * Shrink signal → δ halves.\n * - `'reject'` — any other reject or verification failure (R5-class: floor\n * breach, recipient substitution, bad claim). Shrink signal → δ halves.\n * - `'timeout'` — packet expiry / transport timeout. Shrink signal → W\n * halves (timing risk, not pricing risk).\n * - `'error'` — transport or decode error. Shrink signal → δ halves.\n */\nexport type PacketResolution =\n | 'fulfill'\n | 'reject-stale'\n | 'reject'\n | 'timeout'\n | 'error';\n\n/** One per-packet observation fed to {@link AdaptiveDeltaController.observe}. */\nexport interface PacketObservation {\n /** Resolution class for this packet (see {@link PacketResolution}). */\n resolution: PacketResolution;\n /** Measured round-trip time for this packet in ms (send → resolve). */\n rttMs?: number;\n /** Quote-tape rate `R_i` for this packet (decimal string, issue #82). */\n rate?: string;\n /** Unix ms when the maker's rate source produced {@link rate}. */\n rateTimestamp?: number;\n /** Source amount sent for this packet (micro-units). Used for slip. */\n sourceAmount?: bigint;\n /** Delivered target amount (micro-units). Used for slip. */\n targetAmount?: bigint;\n /**\n * Remaining session notional AFTER this packet (source micro-units).\n * When provided, widen steps are additionally clamped to the current\n * `delta_cap` fraction of it (spec §6 `δ ← min(delta_cap, δ + δ_0)`).\n * `nextDelta()` re-enforces the cap at issuance regardless.\n */\n remaining?: bigint;\n}\n\n// ---------------------------------------------------------------------------\n// The controller\n// ---------------------------------------------------------------------------\n\n/**\n * The seam `streamSwap` consumes (kept structural so alternative controller\n * implementations — or a test double — can be plugged in).\n */\nexport interface StreamSwapAdaptiveController {\n /**\n * Decide the size of the next packet, in source micro-units, given the\n * session's remaining notional. MUST return a value in `[1, remaining]`\n * (callers clamp defensively). Enforces `δ ≤ delta_cap` at issuance.\n */\n nextDelta(remaining: bigint): bigint;\n /** Current in-flight window W (max unfulfilled packets outstanding, ≥ 1). */\n readonly window: number;\n /** Feed one packet observation. May persist state (hence async). */\n observe(observation: PacketObservation): void | Promise<void>;\n}\n\n/** Configuration for {@link AdaptiveDeltaController.create}. */\nexport interface AdaptiveDeltaControllerConfig {\n /** Maker's 64-char hex pubkey — part of the persistence key. */\n makerPubkey: string;\n /** The pair being executed — chain + asset parts of the persistence key. */\n pair: SwapPair;\n /**\n * Maker's advertised two-sided spread as a fraction (e.g. `0.004` = 40 bps).\n * ε is denominated off this — NEVER an absolute rate (spec §6): the\n * half-spread self-calibrates ε per chain and per maker. Today the spread\n * is caller-supplied (from the maker's board / RFQ response once toon#145's\n * RFQ lands); there is deliberately no default — an invented spread would\n * rot and silently mis-size ε.\n */\n advertisedSpread: number;\n /**\n * ε as a fraction of the advertised HALF-spread. Default `0.5`\n * (spec §6 default `ε = 0.5 × halfSpread`).\n */\n epsilonHalfSpreadFraction?: number;\n /**\n * Absolute per-packet ceiling in source micro-units (the maker's advertised\n * `maxAmount`). Applied to δ at issuance AND to every widen step — the\n * uncompromising absolute cap alongside the measured `v·τ` bound.\n */\n maxPacketAmount?: bigint;\n /** Floor on δ in source micro-units (`δ_min`). Default `1n`. */\n minPacketAmount?: bigint;\n /** Ceiling on W (`W_max`). Default `8`. */\n maxWindow?: number;\n /** Clean-fulfill streak length K per widen step. Default `16` (spec §6). */\n cleanStreakLength?: number;\n /**\n * Cold-start divisor: `δ_0 = notional / coldStartDivisor` (further clamped\n * by `delta_cap` and `maxPacketAmount`). Default `256` (spec §6).\n */\n coldStartDivisor?: number;\n /** EWMA smoothing factor α for both `v` and `τ`, in (0, 1]. Default `0.2`. */\n ewmaAlpha?: number;\n /**\n * Pluggable persistence. Default: a fresh volatile\n * {@link InMemorySwapControllerStateStore} (state survives the session\n * only). Pass {@link JsonFileSwapControllerStateStore} (Node) or a custom\n * store to persist ramp/trust across swaps.\n */\n store?: SwapControllerStateStore;\n /** Injectable clock for deterministic tests. Default `Date.now`. */\n now?: () => number;\n}\n\nconst DEFAULT_EPSILON_HALF_SPREAD_FRACTION = 0.5;\nconst DEFAULT_MAX_WINDOW = 8;\nconst DEFAULT_CLEAN_STREAK_LENGTH = 16;\nconst DEFAULT_COLD_START_DIVISOR = 256;\nconst DEFAULT_EWMA_ALPHA = 0.2;\n/** Fixed-point scale for applying the fractional delta_cap to a bigint. */\nconst CAP_SCALE = 1_000_000_000_000n; // 1e12\nconst CAP_SCALE_NUM = 1e12;\n\n/** Parse a positive decimal-string rate to a JS number (measurement-grade). */\nfunction rateToNumber(rate: string): number {\n const n = Number(rate);\n return Number.isFinite(n) && n > 0 ? n : NaN;\n}\n\n/**\n * Per-(chain, maker, pair) adaptive δ/W controller (spec §6). Construct via\n * {@link AdaptiveDeltaController.create} (async: loads persisted state).\n *\n * Concurrency note: one instance drives one swap session. Running two\n * concurrent sessions against the same tuple is last-write-wins on the\n * persisted state (same as concurrent channel use).\n */\nexport class AdaptiveDeltaController implements StreamSwapAdaptiveController {\n /** Persistence key for this tuple (see {@link swapControllerStateKey}). */\n readonly key: string;\n\n private readonly store: SwapControllerStateStore;\n private readonly pair: SwapPair;\n private readonly epsilon: number;\n private readonly maxPacketAmount: bigint | undefined;\n private readonly minPacketAmount: bigint;\n private readonly maxWindow: number;\n private readonly cleanStreakLength: number;\n private readonly coldStartDivisor: bigint;\n private readonly alpha: number;\n private readonly now: () => number;\n\n private readonly stateInternal: SwapControllerState;\n /** δ in-memory as bigint (mirrors `stateInternal.delta`). */\n private delta: bigint;\n /** Cold-start δ_0 — also the additive widen increment. Set on first nextDelta(). */\n private delta0: bigint | null = null;\n /** Previous tape entry for the per-second volatility measurement. */\n private lastRate: number | null = null;\n private lastRateTimestamp: number | null = null;\n\n private constructor(\n config: AdaptiveDeltaControllerConfig,\n key: string,\n store: SwapControllerStateStore,\n state: SwapControllerState\n ) {\n this.key = key;\n this.store = store;\n this.pair = config.pair;\n const epsilonFraction =\n config.epsilonHalfSpreadFraction ?? DEFAULT_EPSILON_HALF_SPREAD_FRACTION;\n this.epsilon = epsilonFraction * (config.advertisedSpread / 2);\n this.maxPacketAmount = config.maxPacketAmount;\n this.minPacketAmount = config.minPacketAmount ?? 1n;\n this.maxWindow = config.maxWindow ?? DEFAULT_MAX_WINDOW;\n this.cleanStreakLength =\n config.cleanStreakLength ?? DEFAULT_CLEAN_STREAK_LENGTH;\n this.coldStartDivisor = BigInt(\n config.coldStartDivisor ?? DEFAULT_COLD_START_DIVISOR\n );\n this.alpha = config.ewmaAlpha ?? DEFAULT_EWMA_ALPHA;\n this.now = config.now ?? Date.now;\n this.stateInternal = state;\n this.delta = BigInt(state.delta);\n }\n\n /**\n * Create a controller for one swap session: loads the persisted state for\n * the tuple from the store, or starts cold (`δ` unset until the first\n * `nextDelta()`, `W = 1`).\n */\n static async create(\n config: AdaptiveDeltaControllerConfig\n ): Promise<AdaptiveDeltaController> {\n validateConfig(config);\n const key = swapControllerStateKey({\n makerPubkey: config.makerPubkey,\n pair: config.pair,\n });\n const store = config.store ?? new InMemorySwapControllerStateStore();\n const now = config.now ?? Date.now;\n const loaded = await store.load(key);\n const state: SwapControllerState = isSwapControllerState(loaded)\n ? loaded\n : {\n v: 1,\n delta: '0',\n W: 1,\n vEwma: 0,\n tauEwma: 0,\n cleanStreak: 0,\n everShrunk: false,\n // 'window' so the FIRST widen step hits δ (alternation starts on δ).\n lastWidened: 'window',\n updatedAt: now(),\n };\n return new AdaptiveDeltaController(config, key, store, state);\n }\n\n /** Defensive snapshot of the current controller state. */\n get state(): SwapControllerState {\n return { ...this.stateInternal, delta: this.delta.toString() };\n }\n\n /** Current in-flight window W. */\n get window(): number {\n return this.stateInternal.W;\n }\n\n /**\n * Current `delta_cap = ε/(v·τ)` as a fraction of remaining notional.\n * `Infinity` while `v·τ` has no measurement yet (cold tape) — the\n * cold-start `δ_0` and absolute caps bound δ in that regime.\n */\n get deltaCapFraction(): number {\n const vTau = this.stateInternal.vEwma * this.stateInternal.tauEwma;\n if (!(vTau > 0)) return Infinity;\n return this.epsilon / vTau;\n }\n\n /** `delta_cap` in absolute source micro-units for a given remaining notional. */\n private deltaCapAbsolute(remaining: bigint): bigint {\n const frac = this.deltaCapFraction;\n if (!Number.isFinite(frac) || frac >= 1) return remaining;\n if (frac <= 0) return this.minPacketAmount;\n const scaled = BigInt(Math.floor(frac * CAP_SCALE_NUM));\n return (remaining * scaled) / CAP_SCALE;\n }\n\n /**\n * Decide the next packet size (source micro-units) for a session with\n * `remaining` notional left. Enforces, in order: the measured\n * `delta_cap = ε/(v·τ)` bound, the absolute `maxPacketAmount` cap, the\n * remaining notional, and the `minPacketAmount` floor. Seeds the\n * cold-start `δ_0 = min(delta_cap, notional/256, maxPacketAmount)` on\n * first call.\n */\n nextDelta(remaining: bigint): bigint {\n if (typeof remaining !== 'bigint' || remaining <= 0n) return 0n;\n\n if (this.delta0 === null) {\n // δ_0 is derived from the FIRST observed remaining (the session\n // notional) and reused as the additive widen increment all session.\n let d0 = remaining / this.coldStartDivisor;\n const capAbs = this.deltaCapAbsolute(remaining);\n if (d0 > capAbs) d0 = capAbs;\n if (this.maxPacketAmount !== undefined && d0 > this.maxPacketAmount) {\n d0 = this.maxPacketAmount;\n }\n if (d0 < this.minPacketAmount) d0 = this.minPacketAmount;\n this.delta0 = d0;\n if (this.delta <= 0n) {\n // Cold start: seed the ramp value. Persisted on the next observe().\n this.delta = d0;\n this.stateInternal.delta = d0.toString();\n }\n }\n\n let d = this.delta;\n const capAbs = this.deltaCapAbsolute(remaining);\n if (d > capAbs) d = capAbs;\n if (this.maxPacketAmount !== undefined && d > this.maxPacketAmount) {\n d = this.maxPacketAmount;\n }\n if (d > remaining) d = remaining;\n const floor =\n this.minPacketAmount < remaining ? this.minPacketAmount : remaining;\n if (d < floor) d = floor;\n return d;\n }\n\n /**\n * Feed one packet observation: updates the measured EWMAs (`v`, `τ`),\n * applies at most ONE knob adjustment (asymmetric: multiplicative shrink /\n * additive widen), and persists the state.\n */\n async observe(observation: PacketObservation): Promise<void> {\n const s = this.stateInternal;\n\n // --- Measurements (not knobs): τ and v EWMAs update on every packet ---\n if (\n typeof observation.rttMs === 'number' &&\n Number.isFinite(observation.rttMs) &&\n observation.rttMs > 0\n ) {\n const tauSec = observation.rttMs / 1000;\n s.tauEwma =\n s.tauEwma === 0\n ? tauSec\n : this.alpha * tauSec + (1 - this.alpha) * s.tauEwma;\n }\n if (\n typeof observation.rate === 'string' &&\n typeof observation.rateTimestamp === 'number' &&\n Number.isFinite(observation.rateTimestamp)\n ) {\n const r = rateToNumber(observation.rate);\n if (!Number.isNaN(r)) {\n if (\n this.lastRate !== null &&\n this.lastRateTimestamp !== null &&\n observation.rateTimestamp > this.lastRateTimestamp\n ) {\n const dtSec =\n (observation.rateTimestamp - this.lastRateTimestamp) / 1000;\n const inst = Math.abs(r - this.lastRate) / this.lastRate / dtSec;\n if (Number.isFinite(inst)) {\n s.vEwma =\n s.vEwma === 0\n ? inst\n : this.alpha * inst + (1 - this.alpha) * s.vEwma;\n }\n }\n if (\n this.lastRateTimestamp === null ||\n observation.rateTimestamp >= this.lastRateTimestamp\n ) {\n this.lastRate = r;\n this.lastRateTimestamp = observation.rateTimestamp;\n }\n }\n }\n\n // --- Classify: shrink signal? (spec §6) ---\n const slip = this.realizedSlip(observation);\n const isShrink =\n observation.resolution !== 'fulfill' || slip > this.epsilon;\n\n // --- One knob per step ---\n if (isShrink) {\n if (observation.resolution === 'timeout') {\n // Timing/liveness signal → the window knob: W ← max(1, ⌈W/2⌉).\n s.W = Math.max(1, Math.ceil(s.W / 2));\n } else {\n // Pricing signal → the size knob: δ ← max(δ_min, δ/2).\n let d = this.delta / 2n;\n if (d < this.minPacketAmount) d = this.minPacketAmount;\n this.delta = d;\n s.delta = d.toString();\n }\n s.cleanStreak = 0;\n s.everShrunk = true;\n } else {\n s.cleanStreak += 1;\n if (s.cleanStreak >= this.cleanStreakLength) {\n s.cleanStreak = 0;\n const knob: 'delta' | 'window' =\n s.lastWidened === 'delta' ? 'window' : 'delta';\n if (knob === 'window') {\n s.W = Math.min(this.maxWindow, s.W + 1);\n } else {\n // Slow-start (never shrunk for this tuple): multiplicative ×2.\n // After the first loss event ever: additive +δ_0, permanently.\n const increment = this.delta0 ?? this.minPacketAmount;\n let d = s.everShrunk ? this.delta + increment : this.delta * 2n;\n if (this.maxPacketAmount !== undefined && d > this.maxPacketAmount) {\n d = this.maxPacketAmount;\n }\n if (\n observation.remaining !== undefined &&\n observation.remaining > 0n\n ) {\n const capAbs = this.deltaCapAbsolute(observation.remaining);\n if (d > capAbs) d = capAbs;\n }\n if (d < this.minPacketAmount) d = this.minPacketAmount;\n // Widen must never shrink (cap clamps can undershoot current δ).\n if (d < this.delta) d = this.delta;\n this.delta = d;\n s.delta = d.toString();\n }\n s.lastWidened = knob;\n }\n }\n\n s.updatedAt = this.now();\n await this.store.save(this.key, { ...s });\n }\n\n /**\n * Realized per-packet slip as a fraction: shortfall of the delivered\n * target amount vs the amount implied by the packet's own tape rate `R_i`\n * (spec §7.1 `Δcumulative` vs `⌊δ · R_i⌋` cross-check). `0` when the\n * inputs are unavailable or delivery met the tape.\n */\n private realizedSlip(observation: PacketObservation): number {\n if (\n observation.resolution !== 'fulfill' ||\n observation.rate === undefined ||\n observation.sourceAmount === undefined ||\n observation.targetAmount === undefined ||\n observation.sourceAmount <= 0n\n ) {\n return 0;\n }\n let expected: bigint;\n try {\n expected = applyRate({\n sourceAmount: observation.sourceAmount,\n fromScale: this.pair.from.assetScale,\n toScale: this.pair.to.assetScale,\n rate: observation.rate,\n });\n } catch {\n return 0;\n }\n if (expected <= 0n || observation.targetAmount >= expected) return 0;\n const shortfall = expected - observation.targetAmount;\n return Number((shortfall * 1_000_000n) / expected) / 1_000_000;\n }\n}\n\nfunction validateConfig(config: AdaptiveDeltaControllerConfig): void {\n if (\n typeof config.makerPubkey !== 'string' ||\n config.makerPubkey.length === 0\n ) {\n throw new SwapControllerError('makerPubkey must be a non-empty string');\n }\n if (\n !config.pair ||\n typeof config.pair !== 'object' ||\n !config.pair.from ||\n !config.pair.to ||\n typeof config.pair.from.chain !== 'string' ||\n typeof config.pair.to.chain !== 'string'\n ) {\n throw new SwapControllerError(\n 'pair must be a SwapPair with from/to chain descriptors'\n );\n }\n if (\n typeof config.advertisedSpread !== 'number' ||\n !Number.isFinite(config.advertisedSpread) ||\n config.advertisedSpread <= 0\n ) {\n throw new SwapControllerError(\n `advertisedSpread must be a positive finite fraction, got ${String(\n config.advertisedSpread\n )}`\n );\n }\n if (\n config.epsilonHalfSpreadFraction !== undefined &&\n (typeof config.epsilonHalfSpreadFraction !== 'number' ||\n !Number.isFinite(config.epsilonHalfSpreadFraction) ||\n config.epsilonHalfSpreadFraction <= 0)\n ) {\n throw new SwapControllerError(\n 'epsilonHalfSpreadFraction must be a positive finite number'\n );\n }\n if (\n config.maxPacketAmount !== undefined &&\n (typeof config.maxPacketAmount !== 'bigint' || config.maxPacketAmount <= 0n)\n ) {\n throw new SwapControllerError('maxPacketAmount must be a positive bigint');\n }\n if (\n config.minPacketAmount !== undefined &&\n (typeof config.minPacketAmount !== 'bigint' || config.minPacketAmount <= 0n)\n ) {\n throw new SwapControllerError('minPacketAmount must be a positive bigint');\n }\n if (\n config.minPacketAmount !== undefined &&\n config.maxPacketAmount !== undefined &&\n config.minPacketAmount > config.maxPacketAmount\n ) {\n throw new SwapControllerError('minPacketAmount must be <= maxPacketAmount');\n }\n if (\n config.maxWindow !== undefined &&\n (!Number.isInteger(config.maxWindow) || config.maxWindow < 1)\n ) {\n throw new SwapControllerError('maxWindow must be an integer >= 1');\n }\n if (\n config.cleanStreakLength !== undefined &&\n (!Number.isInteger(config.cleanStreakLength) ||\n config.cleanStreakLength < 1)\n ) {\n throw new SwapControllerError('cleanStreakLength must be an integer >= 1');\n }\n if (\n config.coldStartDivisor !== undefined &&\n (!Number.isInteger(config.coldStartDivisor) || config.coldStartDivisor < 1)\n ) {\n throw new SwapControllerError('coldStartDivisor must be an integer >= 1');\n }\n if (\n config.ewmaAlpha !== undefined &&\n (typeof config.ewmaAlpha !== 'number' ||\n !(config.ewmaAlpha > 0) ||\n config.ewmaAlpha > 1)\n ) {\n throw new SwapControllerError('ewmaAlpha must be in (0, 1]');\n }\n}\n","/**\n * EVM-specific settlement tx construction + signature verification\n * (Story 12.6 AC-7).\n *\n * @module\n * @since 12.6\n * @see _bmad-output/implementation-artifacts/12-6-build-settlement-tx.md\n */\n\nimport { secp256k1 } from '@noble/curves/secp256k1.js';\nimport { keccak_256 } from '@noble/hashes/sha3.js';\nimport { bytesToHex } from '@noble/hashes/utils.js';\n\nimport { SettlementTxError } from '../errors.js';\nimport type { AccumulatedClaim } from '../stream-swap.js';\nimport {\n balanceProofHashEvm,\n bigintToBytes32BE,\n concatBytes,\n hexToBytes,\n} from './hashes.js';\nimport type { SwapSignerConfig, SettlementBundle } from './types.js';\n\n// ---------------------------------------------------------------------------\n// Function selector + event signature\n// ---------------------------------------------------------------------------\n\n// TODO(12.6 follow-up): verify against the TokenNetwork contract in\n// ../connector/packages/contracts/. Story 12.8 E2E will catch drift if the\n// real contract uses a different name/arity.\nconst EVM_SETTLEMENT_FUNCTION_SIGNATURE =\n 'updateBalance(bytes32,uint256,uint256,address,bytes)';\nconst EVM_SETTLEMENT_EVENT_SIGNATURE =\n 'SettlementSucceeded(bytes32,uint256,uint256,address)';\n\n/** 4-byte keccak256 function selector for the settlement call. */\nexport const EVM_SETTLEMENT_FUNCTION_SELECTOR: Uint8Array = keccak_256(\n new TextEncoder().encode(EVM_SETTLEMENT_FUNCTION_SIGNATURE)\n).slice(0, 4);\n\nconst EVM_SETTLEMENT_EVENT_TOPIC: string =\n '0x' +\n bytesToHex(\n keccak_256(new TextEncoder().encode(EVM_SETTLEMENT_EVENT_SIGNATURE))\n );\n\n// ---------------------------------------------------------------------------\n// Recover EVM signer address\n// ---------------------------------------------------------------------------\n\n/**\n * Recover the EVM signer address from an `AccumulatedClaim`'s 65-byte\n * `r||s||v` signature. Returns lowercase `0x`-prefixed 40-hex-char address.\n *\n * Reconstructs the balance-proof message hash via `balanceProofHashEvm` using\n * the claim's settlement-context fields (channelId, cumulativeAmount, nonce,\n * recipient) and recovers the secp256k1 public key.\n *\n * @throws {SettlementTxError} INVALID_SIGNATURE_LENGTH / INVALID_SIGNATURE_V /\n * MISSING_SETTLEMENT_METADATA.\n * @since 12.6\n */\nexport function recoverEvmSignerAddress(claim: AccumulatedClaim): string {\n if (\n claim.channelId === undefined ||\n claim.cumulativeAmount === undefined ||\n claim.nonce === undefined ||\n claim.recipient === undefined\n ) {\n throw new SettlementTxError(\n 'MISSING_SETTLEMENT_METADATA',\n 'Claim missing channelId/cumulativeAmount/nonce/recipient for EVM signer recovery'\n );\n }\n if (claim.claimBytes.length !== 65) {\n throw new SettlementTxError(\n 'INVALID_SIGNATURE_LENGTH',\n `EVM signature must be 65 bytes (r||s||v), got ${claim.claimBytes.length}`\n );\n }\n const v = claim.claimBytes[64];\n if (v !== 27 && v !== 28) {\n throw new SettlementTxError(\n 'INVALID_SIGNATURE_V',\n `EVM signature v must be 27 or 28, got ${v}`\n );\n }\n const recovery = v - 27;\n const compactRS = claim.claimBytes.slice(0, 64);\n\n let msgHash: Uint8Array;\n let uncompressedPubkey: Uint8Array;\n try {\n const channelIdBytes = hexToBytes(claim.channelId);\n const recipientBytes = hexToBytes(claim.recipient);\n if (channelIdBytes.length !== 32) {\n throw new Error(\n `channelId must be 32 bytes (got ${channelIdBytes.length})`\n );\n }\n if (recipientBytes.length !== 20) {\n throw new Error(\n `recipient must be 20 bytes (got ${recipientBytes.length})`\n );\n }\n msgHash = balanceProofHashEvm(\n channelIdBytes,\n BigInt(claim.cumulativeAmount),\n BigInt(claim.nonce),\n recipientBytes\n );\n const sig = secp256k1.Signature.fromBytes(\n compactRS,\n 'compact'\n ).addRecoveryBit(recovery);\n const point = sig.recoverPublicKey(msgHash);\n uncompressedPubkey = point.toBytes(false);\n } catch (err) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n `EVM signer recovery failed: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err }\n );\n }\n\n // Uncompressed pubkey is 65 bytes: 0x04 || X(32) || Y(32). Address =\n // last 20 bytes of keccak256(X||Y).\n const addrHash = keccak_256(uncompressedPubkey.slice(1));\n return '0x' + bytesToHex(addrHash.slice(-20)).toLowerCase();\n}\n\n// ---------------------------------------------------------------------------\n// Minimal ABI encoder (bytes32, uint256, uint256, address, bytes)\n// ---------------------------------------------------------------------------\n\nfunction padLeft32(bytes: Uint8Array): Uint8Array {\n if (bytes.length > 32) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n `cannot pad bytes of length ${bytes.length} to 32`\n );\n }\n const out = new Uint8Array(32);\n out.set(bytes, 32 - bytes.length);\n return out;\n}\n\nfunction padRight32(bytes: Uint8Array): Uint8Array {\n const padded = Math.ceil(bytes.length / 32) * 32;\n const out = new Uint8Array(padded);\n out.set(bytes, 0);\n return out;\n}\n\nfunction encodeUpdateBalanceCallData(\n channelIdBytes: Uint8Array,\n cumulativeAmount: bigint,\n nonce: bigint,\n recipientBytes: Uint8Array,\n signature: Uint8Array\n): Uint8Array {\n if (channelIdBytes.length !== 32) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n `channelId must be 32 bytes (got ${channelIdBytes.length})`\n );\n }\n if (recipientBytes.length !== 20) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n `recipient must be 20 bytes (got ${recipientBytes.length})`\n );\n }\n\n // Solidity ABI layout for `(bytes32, uint256, uint256, address, bytes)`:\n // head = channelId(32) || cumulative(32) || nonce(32) || recipient(32) || offset(32) = 160 bytes\n // tail = sigLen(32) || sigPadded(ceil(sigLen/32)*32)\n // Only `bytes` is dynamic; its head slot is the offset (160) into the\n // head+tail region (selector is NOT counted in the offset per spec).\n const channelIdWord = channelIdBytes; // already 32\n const cumulativeWord = bigintToBytes32BE(cumulativeAmount);\n const nonceWord = bigintToBytes32BE(nonce);\n const recipientWord = padLeft32(recipientBytes);\n const offsetWord = bigintToBytes32BE(160n);\n const sigLenWord = bigintToBytes32BE(BigInt(signature.length));\n const sigPadded = padRight32(signature);\n\n return concatBytes(\n EVM_SETTLEMENT_FUNCTION_SELECTOR,\n channelIdWord,\n cumulativeWord,\n nonceWord,\n recipientWord,\n offsetWord,\n sigLenWord,\n sigPadded\n );\n}\n\n// ---------------------------------------------------------------------------\n// Minimal RLP encoder (for EIP-155 unsigned tx)\n// ---------------------------------------------------------------------------\n\nfunction rlpEncodeBytes(bytes: Uint8Array): Uint8Array {\n if (bytes.length === 1 && bytes[0] !== undefined && bytes[0] < 0x80) {\n return bytes;\n }\n if (bytes.length < 56) {\n return concatBytes(new Uint8Array([0x80 + bytes.length]), bytes);\n }\n const lenBytes = bigintToMinimalBytes(BigInt(bytes.length));\n return concatBytes(new Uint8Array([0xb7 + lenBytes.length]), lenBytes, bytes);\n}\n\nfunction rlpEncodeList(items: Uint8Array[]): Uint8Array {\n const payload = concatBytes(...items);\n if (payload.length < 56) {\n return concatBytes(new Uint8Array([0xc0 + payload.length]), payload);\n }\n const lenBytes = bigintToMinimalBytes(BigInt(payload.length));\n return concatBytes(\n new Uint8Array([0xf7 + lenBytes.length]),\n lenBytes,\n payload\n );\n}\n\nfunction bigintToMinimalBytes(x: bigint): Uint8Array {\n if (x < 0n) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n 'bigintToMinimalBytes: negative input'\n );\n }\n if (x === 0n) return new Uint8Array(0);\n let hex = x.toString(16);\n if (hex.length % 2) hex = '0' + hex;\n const out = new Uint8Array(hex.length / 2);\n for (let i = 0; i < out.length; i++) {\n out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n }\n return out;\n}\n\nfunction rlpEncodeUint(x: bigint): Uint8Array {\n return rlpEncodeBytes(bigintToMinimalBytes(x));\n}\n\n/**\n * RLP-encode an EIP-155 unsigned transaction:\n * [nonce, gasPrice, gasLimit, to, value, data, chainId, 0, 0]\n */\nfunction rlpEncodeUnsignedTx(params: {\n nonce: bigint;\n gasPrice: bigint;\n gasLimit: bigint;\n to: Uint8Array; // 20 bytes\n value: bigint;\n data: Uint8Array;\n chainId: bigint;\n}): Uint8Array {\n return rlpEncodeList([\n rlpEncodeUint(params.nonce),\n rlpEncodeUint(params.gasPrice),\n rlpEncodeUint(params.gasLimit),\n rlpEncodeBytes(params.to),\n rlpEncodeUint(params.value),\n rlpEncodeBytes(params.data),\n rlpEncodeUint(params.chainId),\n rlpEncodeUint(0n),\n rlpEncodeUint(0n),\n ]);\n}\n\n// ---------------------------------------------------------------------------\n// buildEvmSettlementTx\n// ---------------------------------------------------------------------------\n\n/**\n * Produce a `SettlementBundle` for a winning EVM `AccumulatedClaim`.\n *\n * The bundle's `unsignedTxBytes` is RLP-encoded with placeholder gas fields\n * (tx-nonce / gasPrice / gasLimit = 0). Callers (direct sender OR a Chain\n * Bridge DVM) fill in real gas via {@link fillEvmSettlementTxGas} before\n * signing + broadcasting.\n *\n * @stable\n * @since 12.6\n */\nexport function buildEvmSettlementTx(\n winner: AccumulatedClaim,\n signer: SwapSignerConfig,\n recipient: string,\n selectedClaimIndex: number,\n claimsMerged: number\n): SettlementBundle {\n if (\n winner.channelId === undefined ||\n winner.cumulativeAmount === undefined ||\n winner.nonce === undefined ||\n winner.recipient === undefined ||\n winner.swapSignerAddress === undefined\n ) {\n throw new SettlementTxError(\n 'MISSING_SETTLEMENT_METADATA',\n 'EVM winner claim missing settlement-context fields'\n );\n }\n if (!signer.contractAddress) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `EVM SwapSignerConfig.contractAddress is required for chain ${winner.pair.to.chain}`\n );\n }\n if (\n typeof signer.chainId !== 'number' ||\n !Number.isInteger(signer.chainId) ||\n signer.chainId <= 0\n ) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `EVM SwapSignerConfig.chainId must be a positive integer, got ${signer.chainId}`\n );\n }\n\n const channelIdBytes = hexToBytes(winner.channelId);\n const recipientBytes = hexToBytes(recipient);\n const contractBytes = hexToBytes(signer.contractAddress);\n\n const calldata = encodeUpdateBalanceCallData(\n channelIdBytes,\n BigInt(winner.cumulativeAmount),\n BigInt(winner.nonce),\n recipientBytes,\n winner.claimBytes\n );\n\n const unsignedTxBytes = rlpEncodeUnsignedTx({\n nonce: 0n,\n gasPrice: 0n,\n gasLimit: 0n,\n to: contractBytes,\n value: 0n,\n data: calldata,\n chainId: BigInt(signer.chainId),\n });\n\n return {\n chain: winner.pair.to.chain,\n chainKind: 'evm',\n channelId: winner.channelId,\n cumulativeAmount: winner.cumulativeAmount,\n nonce: winner.nonce,\n recipient,\n swapSignerAddress: winner.swapSignerAddress,\n unsignedTxBytes,\n expectedEventSignature: EVM_SETTLEMENT_EVENT_TOPIC,\n claimsMerged,\n selectedClaimIndex,\n sourceChain: winner.pair.from.chain,\n sourceAssetCode: winner.pair.from.assetCode,\n };\n}\n\n// ---------------------------------------------------------------------------\n// fillEvmSettlementTxGas\n// ---------------------------------------------------------------------------\n\n/**\n * Re-encode an EVM settlement bundle's RLP with real tx-nonce / gasPrice /\n * gasLimit values. The `to`, `value`, `data`, `chainId` fields are preserved\n * from the original bundle.\n *\n * @stable\n * @since 12.6\n */\nexport function fillEvmSettlementTxGas(\n bundle: SettlementBundle,\n gas: { nonce: bigint; gasPrice: bigint; gasLimit: bigint },\n signer: SwapSignerConfig\n): Uint8Array {\n if (bundle.chainKind !== 'evm') {\n throw new SettlementTxError(\n 'UNSUPPORTED_CHAIN',\n `fillEvmSettlementTxGas requires chainKind=evm, got ${bundle.chainKind}`\n );\n }\n if (!signer.contractAddress) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n 'EVM SwapSignerConfig.contractAddress is required for gas-fill'\n );\n }\n if (typeof signer.chainId !== 'number' || signer.chainId <= 0) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n 'EVM SwapSignerConfig.chainId must be a positive integer'\n );\n }\n // Decode calldata from the original bundle's RLP: we know calldata starts\n // at a fixed offset in our own encoding, but safer to re-construct from\n // bundle fields using encodeUpdateBalanceCallData again.\n const channelIdBytes = hexToBytes(bundle.channelId);\n const recipientBytes = hexToBytes(bundle.recipient);\n const contractBytes = hexToBytes(signer.contractAddress);\n // We need the 65-byte signature back; extract it from the original bundle's\n // calldata payload, which is the last `signature.length` bytes before the\n // zero-padding. Simplest: re-build from settlement fields alone cannot\n // reproduce the signature — so we require the caller to pass it via the\n // bundle's embedded claim. Instead of complicating the API, parse the\n // signature out of bundle.unsignedTxBytes calldata.\n const sig = extractSignatureFromBundle(bundle);\n const calldata = encodeUpdateBalanceCallData(\n channelIdBytes,\n BigInt(bundle.cumulativeAmount),\n BigInt(bundle.nonce),\n recipientBytes,\n sig\n );\n return rlpEncodeUnsignedTx({\n nonce: gas.nonce,\n gasPrice: gas.gasPrice,\n gasLimit: gas.gasLimit,\n to: contractBytes,\n value: 0n,\n data: calldata,\n chainId: BigInt(signer.chainId),\n });\n}\n\n/**\n * Decode the 65-byte balance-proof signature from a bundle's RLP-encoded\n * calldata. The calldata layout is:\n * selector(4) || channelId(32) || cumulative(32) || nonce(32) ||\n * recipient(32) || offset(32) || sigLen(32) || sigPadded(ceil(sigLen/32)*32)\n */\nfunction extractSignatureFromBundle(bundle: SettlementBundle): Uint8Array {\n // We re-parse our own RLP encoding. For robustness, locate the data field\n // via a minimal RLP walk.\n const tx = bundle.unsignedTxBytes;\n const list = rlpDecodeList(tx);\n if (list.length < 6) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n 'unsignedTxBytes is not a 9-element RLP list'\n );\n }\n const data = list[5];\n if (!data) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n 'unsignedTxBytes missing data field'\n );\n }\n // data = selector(4) || head(5 * 32) || sigLen(32) || sigPadded\n const sigLenOffset = 4 + 5 * 32;\n if (data.length < sigLenOffset + 32) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n 'calldata too short to contain signature length word'\n );\n }\n const sigLenBytes = data.slice(sigLenOffset, sigLenOffset + 32);\n let sigLen = 0n;\n for (const b of sigLenBytes) sigLen = (sigLen << 8n) | BigInt(b);\n const sigStart = sigLenOffset + 32;\n const sigEnd = sigStart + Number(sigLen);\n if (sigEnd > data.length) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n 'calldata truncated before end of signature bytes'\n );\n }\n return data.slice(sigStart, sigEnd);\n}\n\n// Minimal RLP decoder (items as raw bytes; does NOT recurse into sublists).\nfunction rlpDecodeList(buf: Uint8Array): Uint8Array[] {\n if (buf.length === 0) {\n throw new SettlementTxError('ENCODING_FAILED', 'empty RLP input');\n }\n const first = buf[0] ?? 0;\n let offset: number;\n let listEnd: number;\n if (first >= 0xc0 && first <= 0xf7) {\n offset = 1;\n listEnd = 1 + (first - 0xc0);\n } else if (first >= 0xf8 && first <= 0xff) {\n const lenOfLen = first - 0xf7;\n let listLen = 0;\n for (let i = 0; i < lenOfLen; i++) {\n listLen = (listLen << 8) | (buf[1 + i] ?? 0);\n }\n offset = 1 + lenOfLen;\n listEnd = offset + listLen;\n } else {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n `rlpDecodeList: input is not a list (first byte 0x${first.toString(16)})`\n );\n }\n const items: Uint8Array[] = [];\n let p = offset;\n while (p < listEnd) {\n const b = buf[p] ?? 0;\n if (b < 0x80) {\n items.push(buf.slice(p, p + 1));\n p += 1;\n } else if (b <= 0xb7) {\n const len = b - 0x80;\n items.push(buf.slice(p + 1, p + 1 + len));\n p += 1 + len;\n } else if (b <= 0xbf) {\n const lenOfLen = b - 0xb7;\n let len = 0;\n for (let i = 0; i < lenOfLen; i++) {\n len = (len << 8) | (buf[p + 1 + i] ?? 0);\n }\n items.push(buf.slice(p + 1 + lenOfLen, p + 1 + lenOfLen + len));\n p += 1 + lenOfLen + len;\n } else {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n 'rlpDecodeList: nested list not supported in minimal decoder'\n );\n }\n }\n return items;\n}\n\n/**\n * Verify an EVM claim's signature by recovering the signer and comparing to\n * `expectedAddress`. Returns `{ valid, recovered }`.\n *\n * @since 12.6\n */\nexport function verifyEvmClaimSignature(\n claim: AccumulatedClaim,\n expectedAddress: string\n): { valid: boolean; recovered: string } {\n const recovered = recoverEvmSignerAddress(claim);\n const expected = expectedAddress.toLowerCase();\n return {\n valid: recovered.toLowerCase() === expected,\n recovered,\n };\n}\n","/**\n * Shared balance-proof hash helpers — re-exported from `@toon-protocol/core`.\n *\n * The canonical hash/field layouts (the single source of truth for every\n * signer and verifier) now live in `@toon-protocol/core`'s settlement module so\n * the `@toon-protocol/client` package can consume them without depending on the\n * SDK. This file preserves the historical `@toon-protocol/sdk` import surface\n * (Story 12.6 AC-6) so existing SDK consumers and `@toon-protocol/swap` (which\n * import these names via the SDK root export) are unaffected.\n *\n * Any change to a hash layout must be made in `@toon-protocol/core` — it\n * applies to the Swap signer, the SDK verifiers, and the client signers at once.\n *\n * @module\n * @since 12.6\n * @see _bmad-output/epics/epic-12-token-swap-primitive.md\n */\n\nexport {\n hexToBytes,\n bigintToBytes32BE,\n concatBytes,\n balanceProofHashEvm,\n balanceProofHashSolana,\n minaHashToField,\n balanceProofFieldsMina,\n} from '@toon-protocol/core';\n","/**\n * Mina-specific settlement: balance-proof signature verification +\n * settlement-bundle construction (Story 12.8 follow-up to 12.6 AC-9).\n *\n * ## What is implemented (and unit-tested)\n *\n * - {@link verifyMinaSignature}: verifies the Swap's off-chain balance-proof\n * signature using `mina-signer` (`verifyFields`). This is the EXACT inverse\n * of the Swap's {@link MinaPaymentChannelSigner} (`packages/swap/src/payment-channel-signer.ts`),\n * which signs `balanceProofFieldsMina(channelId, cumulativeAmount, nonce, recipient)`\n * via `mina-signer`'s `signFields` and emits the base58 signature string as\n * the claim's `claimBytes` (UTF-8). The field-element derivation lives in\n * `hashes.ts` so signer and verifier cannot drift (same single-source-of-\n * truth pattern as EVM/Solana).\n * - {@link buildMinaSettlementTx}: constructs a {@link SettlementBundle}\n * envelope (chain metadata + the verified balance proof bytes) so a Chain\n * Bridge DVM / direct sender has everything needed to drive the on-chain\n * claim. The signature is re-emitted verbatim in `unsignedTxBytes`.\n *\n * ## What remains (documented gap — NOT silently stubbed)\n *\n * The final on-chain step — submitting a Mina zkApp `claimFromChannel`\n * transaction that settles the channel — requires **o1js circuit compilation\n * + zk-SNARK proof generation** (Poseidon balance-commitment, zkApp method\n * proving). The connector already implements this in\n * `MinaPaymentChannelSDK.claimFromChannel()` (o1js, heavyweight). That proof\n * generation is intentionally NOT done here: o1js circuit compilation is far\n * too heavy for a unit-testable SDK helper and would pull a multi-hundred-MB\n * WASM dependency into every SDK consumer. Instead, the bundle carries the\n * verified balance proof so a Mina-capable settler (the connector's\n * `MinaPaymentChannelProvider`, or a future o1js-backed Chain Bridge DVM) can\n * generate the proof + broadcast. See the on-chain settlement note below\n * (connector 3.8.1 wires the dual-party claim path — connector#84).\n *\n * Note also: the Swap↔sender wire proof here (a Schnorr signature over four\n * field elements) is a DIFFERENT object than the connector's on-chain\n * `MinaPaymentChannelSDK` Poseidon-commitment proof shape\n * (`{ commitment, signature, nonce }`). The verifier matches the Swap's\n * actual emitted format (`MinaPaymentChannelSigner`), which is what a sender\n * receives on-wire.\n *\n * @module\n * @since 12.8\n */\n\nimport { SettlementTxError } from '../errors.js';\nimport type { AccumulatedClaim } from '../stream-swap.js';\nimport { balanceProofFieldsMina } from './hashes.js';\nimport type { SwapSignerConfig, SettlementBundle } from './types.js';\n\n/**\n * Network id the Swap signs with (`MinaPaymentChannelSigner` uses\n * `network: 'mainnet'`). The signature itself is network-agnostic for the\n * `signFields`/`verifyFields` path — `mina-signer` only folds the network id\n * into message-string hashing, not pre-hashed field arrays — but we keep this\n * aligned with the Swap for clarity and future-proofing.\n */\nconst MINA_NETWORK = 'mainnet';\n\n/**\n * Minimal structural type for the slice of the `mina-signer` `Client` we use.\n * Declared locally so the SDK does not hard-depend on the optional peer dep's\n * full type surface (the ambient `mina-signer.d.ts` only declares\n * `derivePublicKey`). `verifyFields` is synchronous.\n */\nexport interface MinaSignerClientLike {\n verifyFields(input: {\n data: bigint[];\n signature: string;\n publicKey: string;\n }): boolean;\n}\n\n/**\n * Constructor shape of the `mina-signer` default export.\n */\ntype MinaSignerClientCtor = new (opts: {\n network: string;\n}) => MinaSignerClientLike;\n\n/**\n * Lazily load `mina-signer` (optional peer dep) and instantiate a `Client`\n * bound to {@link MINA_NETWORK}. Returns `undefined` when the peer dep is not\n * installed — callers MUST treat that as \"cannot verify Mina claims\" rather\n * than \"claim is valid\".\n *\n * Mirrors the dynamic-import pattern in `identity.ts` (`deriveMinaIdentity`).\n *\n * @since 12.8\n */\nexport async function loadMinaSignerClient(): Promise<\n MinaSignerClientLike | undefined\n> {\n try {\n // `mina-signer` is an optional peer dep — dynamic import so the SDK\n // type-checks and runs without it installed.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const lib: any = await import('mina-signer');\n const Ctor: MinaSignerClientCtor = 'default' in lib ? lib.default : lib;\n return new Ctor({ network: MINA_NETWORK });\n } catch {\n return undefined;\n }\n}\n\n/**\n * Verify a Mina balance-proof Schnorr signature carried in an\n * `AccumulatedClaim`.\n *\n * Re-derives the signed field-element message via {@link balanceProofFieldsMina}\n * (identical to the Swap signer), decodes the claim's `claimBytes` to the\n * base58 signature string the Swap emitted, and verifies it against\n * `expectedSignerAddress` using a `mina-signer` `Client`.\n *\n * `client` MUST be a pre-loaded `mina-signer` `Client` (see\n * {@link loadMinaSignerClient}). It is injected (rather than imported inline)\n * so the synchronous `buildSettlementTx()` pipeline can verify Mina claims\n * without becoming async.\n *\n * @returns `true` iff the signature is valid for the given signer address.\n * @throws {SettlementTxError} on missing settlement metadata or a malformed\n * (non-UTF-8 / empty) signature payload.\n * @since 12.8\n */\nexport function verifyMinaSignature(\n claim: AccumulatedClaim,\n expectedSignerAddress: string,\n client: MinaSignerClientLike\n): boolean {\n if (\n claim.channelId === undefined ||\n claim.cumulativeAmount === undefined ||\n claim.nonce === undefined ||\n claim.recipient === undefined\n ) {\n throw new SettlementTxError(\n 'MISSING_SETTLEMENT_METADATA',\n 'Claim missing channelId/cumulativeAmount/nonce/recipient for Mina signature verify'\n );\n }\n if (claim.claimBytes.length === 0) {\n throw new SettlementTxError(\n 'INVALID_SIGNATURE_LENGTH',\n 'Mina claimBytes is empty (expected UTF-8 base58 signature string)'\n );\n }\n\n // The Swap emits the `mina-signer` base58 signature string as UTF-8 bytes.\n const signature = new TextDecoder().decode(claim.claimBytes);\n\n const fields = balanceProofFieldsMina(\n claim.channelId,\n BigInt(claim.cumulativeAmount),\n BigInt(claim.nonce),\n claim.recipient\n );\n\n try {\n return client.verifyFields({\n data: fields,\n signature,\n publicKey: expectedSignerAddress,\n });\n } catch {\n // `verifyFields` throws on a structurally invalid signature / publicKey.\n // Treat that as \"not valid\" rather than crashing the settlement pipeline.\n return false;\n }\n}\n\n/**\n * Discriminator for the Mina on-chain `claimFromChannel` zkApp method.\n *\n * On-chain settlement: the actual transaction is a Mina zkApp method call\n * requiring o1js circuit compilation + zk-SNARK proof generation. As of\n * `@toon-protocol/connector` 3.8.1 the connector's settlement executor wires\n * the dual-party `MinaPaymentChannelSDK.claimFromChannel()` path with the real\n * counterparty balance, salt, and both party signatures (connector#84 — earlier\n * builds passed single-sig/zeroed placeholders that left Mina claims\n * un-settleable). Proof generation + broadcast remain the connector-side\n * settler's responsibility and are intentionally out of scope for this SDK\n * helper — see the module docblock. The bundle below therefore carries the\n * verified balance-proof bytes + channel metadata for that settler to consume.\n */\n\n/**\n * Build a Mina {@link SettlementBundle} from a winning `AccumulatedClaim`.\n *\n * Unlike the EVM/Solana builders (which produce a chain-native unsigned tx),\n * this builder produces a settlement ENVELOPE: it validates the winning\n * claim's settlement context and re-emits the Swap's verified balance-proof\n * signature bytes in `unsignedTxBytes`. The actual on-chain `claimFromChannel`\n * zkApp transaction (o1js proof generation) is the responsibility of a\n * Mina-capable settler — see the module docblock + the on-chain settlement note.\n *\n * The signature carried here is the SAME `claimBytes` the sender already\n * verified via {@link verifyMinaSignature}, so a downstream settler does not\n * need to re-derive it.\n *\n * @since 12.8\n */\nexport function buildMinaSettlementTx(\n winner: AccumulatedClaim,\n signer: SwapSignerConfig,\n recipient: string,\n selectedClaimIndex: number,\n claimsMerged: number\n): SettlementBundle {\n if (\n winner.channelId === undefined ||\n winner.cumulativeAmount === undefined ||\n winner.nonce === undefined ||\n winner.recipient === undefined ||\n winner.swapSignerAddress === undefined\n ) {\n throw new SettlementTxError(\n 'MISSING_SETTLEMENT_METADATA',\n 'Mina winner claim missing settlement-context fields'\n );\n }\n if (!signer.address) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `Mina SwapSignerConfig.address is required for chain ${winner.pair.to.chain}`\n );\n }\n if (winner.claimBytes.length === 0) {\n throw new SettlementTxError(\n 'INVALID_SIGNATURE_LENGTH',\n 'Mina winner claimBytes is empty (expected UTF-8 base58 signature string)'\n );\n }\n\n // Envelope: the verified balance-proof signature bytes. A Mina-capable\n // settler generates the o1js claimFromChannel proof from (channelId,\n // cumulativeAmount, nonce, recipient, signature) — connector 3.8.1, #84.\n const unsignedTxBytes = winner.claimBytes;\n\n return {\n chain: winner.pair.to.chain,\n chainKind: 'mina',\n channelId: winner.channelId,\n cumulativeAmount: winner.cumulativeAmount,\n nonce: winner.nonce,\n recipient,\n swapSignerAddress: winner.swapSignerAddress,\n unsignedTxBytes,\n claimsMerged,\n selectedClaimIndex,\n sourceChain: winner.pair.from.chain,\n sourceAssetCode: winner.pair.from.assetCode,\n };\n}\n","/**\n * Solana-specific settlement tx construction + signature verification\n * (Story 12.6 AC-9).\n *\n * @module\n * @since 12.6\n * @see _bmad-output/implementation-artifacts/12-6-build-settlement-tx.md\n */\n\nimport { ed25519 } from '@noble/curves/ed25519.js';\nimport { sha256 } from '@noble/hashes/sha2.js';\n\nimport { SettlementTxError } from '../errors.js';\nimport { base58Decode, base58Encode } from '../identity.js';\nimport type { AccumulatedClaim } from '../stream-swap.js';\nimport { balanceProofHashSolana, concatBytes } from './hashes.js';\nimport type { SwapSignerConfig, SettlementBundle } from './types.js';\n\n/**\n * Verify a Solana Ed25519 balance-proof signature.\n *\n * @since 12.6\n */\nexport function verifyEd25519Signature(\n claim: AccumulatedClaim,\n expectedSignerAddress: string\n): boolean {\n if (\n claim.channelId === undefined ||\n claim.cumulativeAmount === undefined ||\n claim.nonce === undefined ||\n claim.recipient === undefined\n ) {\n throw new SettlementTxError(\n 'MISSING_SETTLEMENT_METADATA',\n 'Claim missing channelId/cumulativeAmount/nonce/recipient for Solana signature verify'\n );\n }\n if (claim.claimBytes.length !== 64) {\n throw new SettlementTxError(\n 'INVALID_SIGNATURE_LENGTH',\n `Solana signature must be 64 bytes, got ${claim.claimBytes.length}`\n );\n }\n const msgHash = balanceProofHashSolana(\n claim.channelId,\n BigInt(claim.cumulativeAmount),\n BigInt(claim.nonce),\n claim.recipient\n );\n let pubkeyBytes: Uint8Array;\n try {\n pubkeyBytes = base58Decode(expectedSignerAddress);\n } catch (err) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `Solana expected signer address is not valid base58: ${expectedSignerAddress}`,\n { cause: err }\n );\n }\n if (pubkeyBytes.length !== 32) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `Solana expected signer pubkey must be 32 bytes, got ${pubkeyBytes.length}`\n );\n }\n try {\n return ed25519.verify(claim.claimBytes, msgHash, pubkeyBytes);\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Anchor discriminator (default — may be overridden if the real program is\n// non-Anchor. TODO(12.6 follow-up): confirm via ../connector/packages/solana-program/.\n// Story 12.8 E2E against a real program will catch drift.)\n// ---------------------------------------------------------------------------\n\nconst SOLANA_UPDATE_BALANCE_DISCRIMINATOR: Uint8Array = sha256(\n new TextEncoder().encode('global:update_balance')\n).slice(0, 8);\n\n/**\n * Write an 8-byte little-endian representation of a non-negative bigint.\n * Throws if value > 2^64 - 1.\n */\nfunction bigintToBytes8LE(x: bigint): Uint8Array {\n if (x < 0n) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n 'bigintToBytes8LE: negative input'\n );\n }\n if (x > 0xffffffffffffffffn) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n 'bigintToBytes8LE: value exceeds 64 bits'\n );\n }\n const out = new Uint8Array(8);\n let v = x;\n for (let i = 0; i < 8; i++) {\n out[i] = Number(v & 0xffn);\n v >>= 8n;\n }\n return out;\n}\n\n/**\n * Build a Solana `SettlementBundle` from a winning AccumulatedClaim.\n *\n * Produces a serialized legacy `Message` (NOT `Transaction`, since Transaction\n * requires signatures). The Message header + account keys + placeholder\n * blockhash + single instruction invoking `signer.programId`.\n *\n * NOTE: This Message is a TEMPLATE. The caller (direct sender OR a Chain\n * Bridge DVM) MUST patch in a real recent blockhash before signing — the\n * current bundle carries an all-zero blockhash placeholder.\n *\n * @stable\n * @since 12.6\n */\nexport function buildSolanaSettlementTx(\n winner: AccumulatedClaim,\n signer: SwapSignerConfig,\n recipient: string,\n selectedClaimIndex: number,\n claimsMerged: number\n): SettlementBundle {\n if (\n winner.channelId === undefined ||\n winner.cumulativeAmount === undefined ||\n winner.nonce === undefined ||\n winner.recipient === undefined ||\n winner.swapSignerAddress === undefined\n ) {\n throw new SettlementTxError(\n 'MISSING_SETTLEMENT_METADATA',\n 'Solana winner claim missing settlement-context fields'\n );\n }\n if (!signer.programId) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `Solana SwapSignerConfig.programId is required for chain ${winner.pair.to.chain}`\n );\n }\n\n let programIdBytes: Uint8Array;\n let recipientBytes: Uint8Array;\n let swapBytes: Uint8Array;\n let channelIdBytes: Uint8Array;\n try {\n programIdBytes = base58Decode(signer.programId);\n recipientBytes = base58Decode(recipient);\n swapBytes = base58Decode(winner.swapSignerAddress);\n channelIdBytes = base58Decode(winner.channelId);\n } catch (err) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n `Solana settlement tx: base58 decode failed (${err instanceof Error ? err.message : String(err)})`,\n { cause: err }\n );\n }\n if (programIdBytes.length !== 32) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `Solana programId must be 32 bytes, got ${programIdBytes.length}`\n );\n }\n if (recipientBytes.length !== 32) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `Solana recipient must decode to 32 bytes, got ${recipientBytes.length}`\n );\n }\n if (swapBytes.length !== 32) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `Solana swapSignerAddress must decode to 32 bytes, got ${swapBytes.length}`\n );\n }\n if (channelIdBytes.length !== 32) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `Solana channelId must decode to 32 bytes, got ${channelIdBytes.length}`\n );\n }\n\n // Instruction data: discriminator(8) || cumulative(8 LE) || nonce(8 LE) || signature(64)\n const instructionData = concatBytes(\n SOLANA_UPDATE_BALANCE_DISCRIMINATOR,\n bigintToBytes8LE(BigInt(winner.cumulativeAmount)),\n bigintToBytes8LE(BigInt(winner.nonce)),\n winner.claimBytes\n );\n\n // Accounts: [recipient (signer), swap, channel-state, system-program, programId]\n // We construct a minimal Message. For simplicity, use 4 accounts:\n // [0] recipient (signer, writable)\n // [1] swap (writable)\n // [2] channel-state (writable, derived — we approximate via channelIdBytes)\n // [3] program (readonly)\n const accounts = [recipientBytes, swapBytes, channelIdBytes, programIdBytes];\n\n // Message header: [numRequiredSignatures, numReadonlySigned, numReadonlyUnsigned]\n const header = new Uint8Array([1, 0, 1]); // 1 signer (recipient), 1 readonly unsigned (program)\n\n // Compact-u16 length encoding (for small counts, one byte suffices)\n const accountsCountByte = new Uint8Array([accounts.length]);\n const recentBlockhash = new Uint8Array(32); // placeholder — caller patches\n const instructionsCountByte = new Uint8Array([1]);\n\n // Instruction: programIdIndex (u8) || accountsLen (compact-u16) ||\n // accountIndices(u8 each) || dataLen (compact-u16) || data\n const programIdIndex = new Uint8Array([3]); // index of program in accounts\n const instrAccountsLen = new Uint8Array([3]); // recipient, swap, channel-state\n const instrAccountIndices = new Uint8Array([0, 1, 2]);\n // Instruction data length as compact-u16 (assume <127)\n if (instructionData.length >= 0x80) {\n throw new SettlementTxError(\n 'ENCODING_FAILED',\n `Solana instruction data too large for simple compact-u16 encoding: ${instructionData.length}`\n );\n }\n const instrDataLen = new Uint8Array([instructionData.length]);\n\n const instruction = concatBytes(\n programIdIndex,\n instrAccountsLen,\n instrAccountIndices,\n instrDataLen,\n instructionData\n );\n\n const unsignedTxBytes = concatBytes(\n header,\n accountsCountByte,\n ...accounts,\n recentBlockhash,\n instructionsCountByte,\n instruction\n );\n\n return {\n chain: winner.pair.to.chain,\n chainKind: 'solana',\n channelId: winner.channelId,\n cumulativeAmount: winner.cumulativeAmount,\n nonce: winner.nonce,\n recipient,\n swapSignerAddress: winner.swapSignerAddress,\n unsignedTxBytes,\n claimsMerged,\n selectedClaimIndex,\n sourceChain: winner.pair.from.chain,\n sourceAssetCode: winner.pair.from.assetCode,\n };\n}\n\n/**\n * Re-export base58 helpers so callers (e.g., a Chain Bridge DVM) can round-\n * trip Solana addresses without a second base58 impl.\n *\n * @since 12.6\n */\nexport { base58Decode, base58Encode };\n","/**\n * `buildSettlementTx()` — public entrypoint for Story 12.6.\n *\n * Takes the output `claims` array from `streamSwap()` + per-chain signer\n * config + per-chain recipient addresses, and produces one\n * {@link SettlementBundle} per unique `(chain, channelId)` group.\n *\n * @module\n * @since 12.6\n * @see _bmad-output/implementation-artifacts/12-6-build-settlement-tx.md\n */\n\nimport { SettlementTxError } from '../errors.js';\nimport { base58Decode } from '../identity.js';\nimport type { AccumulatedClaim } from '../stream-swap.js';\nimport {\n buildEvmSettlementTx,\n recoverEvmSignerAddress,\n verifyEvmClaimSignature,\n} from './evm.js';\nimport { buildMinaSettlementTx, verifyMinaSignature } from './mina.js';\nimport type { MinaSignerClientLike } from './mina.js';\nimport { buildSolanaSettlementTx, verifyEd25519Signature } from './solana.js';\nimport type {\n BuildSettlementTxParams,\n BuildSettlementTxResult,\n SwapSignerConfig,\n SettlementBundle,\n} from './types.js';\n\nconst EVM_ADDRESS_REGEX = /^0x[0-9a-f]{40}$/;\n\nfunction chainKindOf(chain: string): 'evm' | 'solana' | 'mina' | 'unknown' {\n if (chain.startsWith('evm:')) return 'evm';\n if (chain.startsWith('solana:')) return 'solana';\n if (chain.startsWith('mina:')) return 'mina';\n return 'unknown';\n}\n\n/**\n * Build raw unsigned settlement transactions from a bag of accumulated\n * swap claims.\n *\n * Algorithm:\n * 1. Validate params (synchronous throws on malformed input).\n * 2. Optionally verify each claim's signature. Rejected claims land in\n * `result.rejected[]` and are dropped from further processing.\n * 3. Group surviving claims by `(chain, channelId)`. Within each group,\n * assert recipient + swapSignerAddress consensus, unique nonces, and\n * non-decreasing cumulativeAmount with nonce.\n * 4. Pick the winning claim per group (highest nonce).\n * 5. Dispatch each winner to its chain-specific tx builder.\n *\n * @stable\n * @since 12.6\n * @throws {SettlementTxError} Synchronously on malformed input, group-level\n * inconsistency, or chain dispatch failures.\n *\n * @example\n * ```ts\n * const result = await streamSwap({ ... });\n * const { bundles } = buildSettlementTx({\n * claims: result.claims,\n * signers: {\n * 'evm:base:8453': {\n * address: '0xswap...',\n * contractAddress: '0xtokennetwork...',\n * chainId: 8453,\n * },\n * },\n * recipients: { 'evm:base:8453': '0xsender...' },\n * });\n * // Feed bundles[0].unsignedTxBytes into fillEvmSettlementTxGas → sign → eth_sendRawTransaction.\n * ```\n */\nexport function buildSettlementTx(\n params: BuildSettlementTxParams\n): BuildSettlementTxResult {\n // ---- 1. Validate ----\n if (!Array.isArray(params.claims) || params.claims.length === 0) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n 'claims array is empty or not an array'\n );\n }\n if (!params.signers || typeof params.signers !== 'object') {\n throw new SettlementTxError('INVALID_INPUT', 'signers map is required');\n }\n if (!params.recipients || typeof params.recipients !== 'object') {\n throw new SettlementTxError('INVALID_INPUT', 'recipients map is required');\n }\n\n const logger = params.logger;\n const verifySignatures = params.verifySignatures ?? true;\n\n // Every claim must carry the five settlement-context fields.\n for (let i = 0; i < params.claims.length; i++) {\n const c = params.claims[i];\n if (c === undefined) continue; // bounded by loop — defensive for TS narrowing\n if (\n c.channelId === undefined ||\n c.nonce === undefined ||\n c.cumulativeAmount === undefined ||\n c.recipient === undefined ||\n c.swapSignerAddress === undefined\n ) {\n throw new SettlementTxError(\n 'MISSING_SETTLEMENT_METADATA',\n `claims[${i}] missing one or more of { channelId, nonce, cumulativeAmount, recipient, swapSignerAddress }`\n );\n }\n }\n\n // Every distinct chain must have a signer + recipient + per-chain config validity.\n const distinctChains = new Set<string>();\n for (const c of params.claims) distinctChains.add(c.pair.to.chain);\n for (const chain of distinctChains) {\n const signer = params.signers[chain];\n if (!signer) {\n throw new SettlementTxError(\n 'UNSUPPORTED_CHAIN',\n `signers map missing entry for chain ${chain}`\n );\n }\n if (!(chain in params.recipients)) {\n throw new SettlementTxError(\n 'MISSING_RECIPIENT',\n `recipients map missing entry for chain ${chain}`\n );\n }\n const kind = chainKindOf(chain);\n if (kind === 'evm') {\n if (\n !signer.contractAddress ||\n !EVM_ADDRESS_REGEX.test(signer.contractAddress)\n ) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `EVM signers[${chain}].contractAddress must match 0x + 40 lowercase hex`\n );\n }\n if (\n typeof signer.chainId !== 'number' ||\n !Number.isInteger(signer.chainId) ||\n signer.chainId <= 0\n ) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `EVM signers[${chain}].chainId must be a positive integer`\n );\n }\n } else if (kind === 'solana') {\n if (!signer.programId || signer.programId.length === 0) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `Solana signers[${chain}].programId is required`\n );\n }\n // AC-4: programId MUST be a valid base58 string that decodes to 32 bytes.\n let programIdLen: number;\n try {\n programIdLen = base58Decode(signer.programId).length;\n } catch (err) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `Solana signers[${chain}].programId is not valid base58`,\n { cause: err }\n );\n }\n if (programIdLen !== 32) {\n throw new SettlementTxError(\n 'INVALID_INPUT',\n `Solana signers[${chain}].programId must decode to 32 bytes (got ${programIdLen})`\n );\n }\n }\n }\n\n // ---- 2. Verify signatures (optional) ----\n const rejected: BuildSettlementTxResult['rejected'] = [];\n const survivors: AccumulatedClaim[] = [];\n for (const claim of params.claims) {\n if (!verifySignatures) {\n survivors.push(claim);\n continue;\n }\n const chain = claim.pair.to.chain;\n const signer = params.signers[chain];\n if (!signer) {\n // Already validated above; this branch is defensive for TS narrowing.\n throw new SettlementTxError(\n 'UNSUPPORTED_CHAIN',\n `signers map missing entry for chain ${chain} (unreachable — validated)`\n );\n }\n const kind = chainKindOf(chain);\n if (kind === 'evm') {\n try {\n const { valid, recovered } = verifyEvmClaimSignature(\n claim,\n signer.address\n );\n if (!valid) {\n rejected.push({\n claim,\n reason: 'SIGNER_MISMATCH',\n details: `recovered=${recovered} expected=${signer.address.toLowerCase()}`,\n });\n continue;\n }\n survivors.push(claim);\n } catch (err) {\n rejected.push({\n claim,\n reason: 'SIGNATURE_INVALID',\n details: err instanceof Error ? err.message : String(err),\n });\n }\n } else if (kind === 'solana') {\n try {\n const ok = verifyEd25519Signature(claim, signer.address);\n if (!ok) {\n rejected.push({\n claim,\n reason: 'SIGNER_MISMATCH',\n details: `ed25519.verify returned false against ${signer.address}`,\n });\n continue;\n }\n survivors.push(claim);\n } catch (err) {\n rejected.push({\n claim,\n reason: 'SIGNATURE_INVALID',\n details: err instanceof Error ? err.message : String(err),\n });\n }\n } else if (kind === 'mina') {\n if (!params.minaSignerClient) {\n // mina-signer is an optional peer dep loaded via async import; the\n // sync pipeline cannot load it itself. Reject (rather than pass\n // through unverified) so an absent client never lets an unverified\n // Mina claim settle.\n rejected.push({\n claim,\n reason: 'MINA_VERIFICATION_UNSUPPORTED',\n details:\n 'minaSignerClient not provided — load mina-signer via loadMinaSignerClient() and pass it in params.minaSignerClient to verify mina:* claims',\n });\n continue;\n }\n try {\n const ok = verifyMinaSignature(\n claim,\n signer.address,\n params.minaSignerClient\n );\n if (!ok) {\n rejected.push({\n claim,\n reason: 'SIGNER_MISMATCH',\n details: `mina-signer verifyFields returned false against ${signer.address}`,\n });\n continue;\n }\n survivors.push(claim);\n } catch (err) {\n rejected.push({\n claim,\n reason: 'SIGNATURE_INVALID',\n details: err instanceof Error ? err.message : String(err),\n });\n }\n } else {\n throw new SettlementTxError(\n 'UNSUPPORTED_CHAIN',\n `Unknown chain kind for ${chain}`\n );\n }\n }\n\n logger?.debug?.({\n event: 'build_settlement_tx.verified',\n survivorCount: survivors.length,\n rejectedCount: rejected.length,\n });\n\n // ---- 3. Group by (chain, channelId) ----\n interface Group {\n chain: string;\n channelId: string;\n claims: { claim: AccumulatedClaim; originalIndex: number }[];\n }\n const groups = new Map<string, Group>();\n // Need the original input index for bundle.selectedClaimIndex.\n const originalIndex = new Map<AccumulatedClaim, number>();\n for (let i = 0; i < params.claims.length; i++) {\n const c = params.claims[i];\n if (c !== undefined) originalIndex.set(c, i);\n }\n for (const claim of survivors) {\n const chain = claim.pair.to.chain;\n const channelId = claim.channelId;\n if (channelId === undefined) {\n // Validated earlier — defensive branch.\n throw new SettlementTxError(\n 'MISSING_SETTLEMENT_METADATA',\n 'claim.channelId undefined after validation (unreachable)'\n );\n }\n const key = `${chain}::${channelId}`;\n let g = groups.get(key);\n if (!g) {\n g = { chain, channelId, claims: [] };\n groups.set(key, g);\n }\n const idx = originalIndex.get(claim);\n g.claims.push({ claim, originalIndex: idx ?? -1 });\n }\n\n // ---- 3b. Validate each group ----\n const superseded: AccumulatedClaim[] = [];\n const bundles: SettlementBundle[] = [];\n for (const g of groups.values()) {\n if (g.claims.length === 0) continue;\n const first = g.claims[0];\n if (!first) continue;\n const firstRecipient = first.claim.recipient;\n const firstSwapSigner = first.claim.swapSignerAddress;\n if (firstRecipient === undefined || firstSwapSigner === undefined) {\n throw new SettlementTxError(\n 'MISSING_SETTLEMENT_METADATA',\n 'winner claim missing recipient/swapSignerAddress (unreachable after validation)'\n );\n }\n for (let i = 1; i < g.claims.length; i++) {\n const entry = g.claims[i];\n if (!entry) continue;\n const c = entry.claim;\n if (c.recipient !== firstRecipient) {\n throw new SettlementTxError(\n 'RECIPIENT_MISMATCH',\n `claims in channel ${g.channelId} disagree on recipient: ${firstRecipient} vs ${String(c.recipient)} (claim indices ${first.originalIndex}, ${entry.originalIndex})`\n );\n }\n if (c.swapSignerAddress !== firstSwapSigner) {\n throw new SettlementTxError(\n 'SWAP_SIGNER_MISMATCH',\n `claims in channel ${g.channelId} disagree on swapSignerAddress: ${firstSwapSigner} vs ${String(c.swapSignerAddress)}`\n );\n }\n }\n // Nonces strictly unique within group.\n const nonceSeen = new Set<string>();\n for (const entry of g.claims) {\n const nonceStr = entry.claim.nonce;\n if (nonceStr === undefined) {\n throw new SettlementTxError(\n 'MISSING_SETTLEMENT_METADATA',\n 'claim.nonce undefined (unreachable after validation)'\n );\n }\n if (nonceSeen.has(nonceStr)) {\n throw new SettlementTxError(\n 'DUPLICATE_NONCE',\n `channel ${g.channelId} has two claims with nonce ${nonceStr}`\n );\n }\n nonceSeen.add(nonceStr);\n }\n // Sort by nonce ascending, then enforce non-decreasing cumulativeAmount.\n const sorted = [...g.claims].sort((a, b) => {\n const an = BigInt(a.claim.nonce ?? '0');\n const bn = BigInt(b.claim.nonce ?? '0');\n return an < bn ? -1 : an > bn ? 1 : 0;\n });\n for (let i = 1; i < sorted.length; i++) {\n const prevE = sorted[i - 1];\n const currE = sorted[i];\n if (!prevE || !currE) continue;\n const prev = BigInt(prevE.claim.cumulativeAmount ?? '0');\n const curr = BigInt(currE.claim.cumulativeAmount ?? '0');\n if (curr < prev) {\n throw new SettlementTxError(\n 'NON_MONOTONIC_CUMULATIVE',\n `channel ${g.channelId} nonce ${String(currE.claim.nonce)} has cumulativeAmount ${curr} < previous nonce ${String(prevE.claim.nonce)} cumulativeAmount ${prev}`\n );\n }\n }\n\n // Winner = highest nonce = last entry in sorted array.\n const winnerEntry = sorted[sorted.length - 1];\n if (!winnerEntry) continue;\n const winner = winnerEntry.claim;\n\n if (params.includeSuperseded) {\n for (let i = 0; i < sorted.length - 1; i++) {\n const e = sorted[i];\n if (e) superseded.push(e.claim);\n }\n }\n\n // Dispatch per chain kind.\n const signer = params.signers[g.chain];\n const recipient = params.recipients[g.chain];\n if (!signer || recipient === undefined) {\n throw new SettlementTxError(\n 'UNSUPPORTED_CHAIN',\n `signers/recipients missing entry for ${g.chain} (unreachable after validation)`\n );\n }\n const kind = chainKindOf(g.chain);\n let bundle: SettlementBundle;\n if (kind === 'evm') {\n // Recipient address consistency check: bundle recipient MUST match\n // the claim's recipient. The recipients map is the sender's declared\n // address — any disagreement with the Swap-reported recipient signals\n // an adversarial Swap or caller misconfiguration.\n if (recipient.toLowerCase() !== firstRecipient.toLowerCase()) {\n throw new SettlementTxError(\n 'RECIPIENT_MISMATCH',\n `recipients[${g.chain}] (${recipient}) does not match Swap-reported recipient (${firstRecipient})`\n );\n }\n bundle = buildEvmSettlementTx(\n winner,\n signer,\n recipient.toLowerCase(),\n winnerEntry.originalIndex,\n g.claims.length\n );\n } else if (kind === 'solana') {\n if (recipient !== firstRecipient) {\n throw new SettlementTxError(\n 'RECIPIENT_MISMATCH',\n `recipients[${g.chain}] (${recipient}) does not match Swap-reported recipient (${firstRecipient})`\n );\n }\n bundle = buildSolanaSettlementTx(\n winner,\n signer,\n recipient,\n winnerEntry.originalIndex,\n g.claims.length\n );\n } else if (kind === 'mina') {\n // Mina addresses are case-sensitive base58 (no lowercasing, like Solana).\n if (recipient !== firstRecipient) {\n throw new SettlementTxError(\n 'RECIPIENT_MISMATCH',\n `recipients[${g.chain}] (${recipient}) does not match Swap-reported recipient (${firstRecipient})`\n );\n }\n bundle = buildMinaSettlementTx(\n winner,\n signer,\n recipient,\n winnerEntry.originalIndex,\n g.claims.length\n );\n } else {\n throw new SettlementTxError(\n 'UNSUPPORTED_CHAIN',\n `Unknown chain kind for ${g.chain}`\n );\n }\n bundles.push(bundle);\n }\n\n logger?.info?.({\n event: 'build_settlement_tx.complete',\n bundleCount: bundles.length,\n rejectedCount: rejected.length,\n supersededCount: superseded.length,\n });\n\n return { bundles, rejected, superseded };\n}\n\n/**\n * Standalone utility: verify a single `AccumulatedClaim`'s signature against\n * a `SwapSignerConfig` without running the full grouping/winner pipeline.\n *\n * Useful inside a `streamSwap()` `onPacket` callback for mid-stream claim\n * validation.\n *\n * For `mina:*` claims, pass a pre-loaded `mina-signer` `Client` as\n * `minaSignerClient` (see `loadMinaSignerClient()`); without it, Mina claims\n * return `MINA_VERIFICATION_UNSUPPORTED` (same contract as\n * `buildSettlementTx`).\n *\n * @stable\n * @since 12.6\n */\nexport function verifyAccumulatedClaim(\n claim: AccumulatedClaim,\n signer: SwapSignerConfig,\n minaSignerClient?: MinaSignerClientLike\n): { valid: true } | { valid: false; reason: string } {\n const kind = chainKindOf(claim.pair.to.chain);\n if (kind === 'evm') {\n try {\n const { valid, recovered } = verifyEvmClaimSignature(\n claim,\n signer.address\n );\n if (valid) return { valid: true };\n return {\n valid: false,\n reason: `SIGNER_MISMATCH: recovered=${recovered} expected=${signer.address.toLowerCase()}`,\n };\n } catch (err) {\n return {\n valid: false,\n reason: `SIGNATURE_INVALID: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n }\n if (kind === 'solana') {\n try {\n const ok = verifyEd25519Signature(claim, signer.address);\n return ok\n ? { valid: true }\n : {\n valid: false,\n reason: 'SIGNER_MISMATCH: ed25519.verify returned false',\n };\n } catch (err) {\n return {\n valid: false,\n reason: `SIGNATURE_INVALID: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n }\n if (kind === 'mina') {\n if (!minaSignerClient) {\n return {\n valid: false,\n reason:\n 'MINA_VERIFICATION_UNSUPPORTED: pass a mina-signer Client (loadMinaSignerClient()) as minaSignerClient to verify mina:* claims',\n };\n }\n try {\n const ok = verifyMinaSignature(claim, signer.address, minaSignerClient);\n return ok\n ? { valid: true }\n : {\n valid: false,\n reason: 'SIGNER_MISMATCH: mina-signer verifyFields returned false',\n };\n } catch (err) {\n return {\n valid: false,\n reason: `SIGNATURE_INVALID: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n }\n return {\n valid: false,\n reason: `UNSUPPORTED_CHAIN: ${claim.pair.to.chain}`,\n };\n}\n\n// Re-export helper for tests + external callers that need address recovery\n// without running the full pipeline.\nexport { recoverEvmSignerAddress };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDO,SAAS,qBACd,SACgB;AAChB,MAAI;AAEJ,SAAO;AAAA,IACL,IAAI,OAAO;AACT,aAAO,QAAQ;AAAA,IACjB;AAAA,IACA,IAAI,OAAO;AACT,aAAO,QAAQ,KAAK;AAAA,IACtB;AAAA,IACA,IAAI,SAAS;AACX,aAAO,QAAQ,KAAK;AAAA,IACtB;AAAA,IACA,IAAI,SAAS;AACX,aAAO,QAAQ;AAAA,IACjB;AAAA,IACA,IAAI,cAAc;AAChB,aAAO,QAAQ;AAAA,IACjB;AAAA,IACA,SAAqB;AACnB,UAAI,CAAC,aAAa;AAChB,sBAAc,QAAQ,YAAY,QAAQ,IAAI;AAAA,MAChD;AACA,aAAO;AAAA,IACT;AAAA,IACA,OAAO,UAAgE;AACrE,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,MACjC;AAAA,IACF;AAAA,IACA,OAAO,MAAc,SAA6C;AAChE,aAAO;AAAA,QACL,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;ACrFA,SAAS,6BAA6B;AAgB/B,IAAM,kBAAN,MAAsB;AAAA,EACnB,WAAW,oBAAI,IAAqB;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMR,GAAG,MAAc,SAAwB;AACvC,SAAK,SAAS,IAAI,MAAM,OAAO;AAC/B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,SAAwB;AAChC,SAAK,iBAAiB;AACtB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,qBAA+B;AAC7B,WAAO,CAAC,GAAG,KAAK,SAAS,KAAK,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,MAAmC;AACrC,WAAO,KAAK,SAAS,IAAI,IAAI;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,cAAwB;AACtB,UAAM,gBAAgB;AACtB,UAAM,cAAc,wBAAwB;AAC5C,WAAO,KAAK,mBAAmB,EAAE;AAAA,MAC/B,CAAC,MAAM,KAAK,iBAAiB,KAAK;AAAA,IACpC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAS,KAA+C;AAC5D,UAAM,UAAU,KAAK,SAAS,IAAI,IAAI,IAAI;AAC1C,QAAI,SAAS;AACX,aAAO,QAAQ,GAAG;AAAA,IACpB;AACA,QAAI,KAAK,gBAAgB;AACvB,aAAO,KAAK,eAAe,GAAG;AAAA,IAChC;AACA,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,SAAS,kCAAkC,IAAI,IAAI;AAAA,IACrD;AAAA,EACF;AACF;;;AC3DO,SAAS,uBAAuB,QAAgC;AACrE,QAAM,mBAAmB,OAAO,oBAAoB;AAEpD,SAAO;AAAA,IACL,SAAS,MAAuB,QAAyC;AAEvE,UAAI,KAAK,WAAW,OAAO,WAAW;AACpC,eAAO,EAAE,UAAU,KAAK;AAAA,MAC1B;AAGA,YAAM,eACJ,OAAO,eAAe,OAAO,OAAO,OAAO,aAAa,KAAK,IAAI,IAC7D,OAAO,YAAY,KAAK,IAAI,IAC5B;AACN,YAAM,eAAe,gBAAgB;AAErC,YAAM,iBAAiB,OAAO,KAAK,SAAS,MAAM,IAAI;AAEtD,UAAI,SAAS,gBAAgB;AAC3B,eAAO;AAAA,UACL,UAAU;AAAA,UACV,WAAW;AAAA,YACT,QAAQ;AAAA,YACR,MAAM;AAAA,YACN,SAAS;AAAA,YACT,UAAU;AAAA,cACR,UAAU,eAAe,SAAS;AAAA,cAClC,UAAU,OAAO,SAAS;AAAA,YAC5B;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,aAAO,EAAE,UAAU,KAAK;AAAA,IAC1B;AAAA,EACF;AACF;;;ACzDA,SAAS,eAAe;AACxB,SAAS,kBAAkB;AAkBpB,SAAS,2BAA2B,QAAoC;AAC7E,SAAO;AAAA,IACL,MAAM,OACJ,MACA,WAC6B;AAC7B,UAAI,OAAO,SAAS;AAClB,eAAO,EAAE,UAAU,KAAK;AAAA,MAC1B;AAEA,UAAI;AACF,cAAM,WAAW,WAAW,KAAK,GAAG;AACpC,cAAM,WAAW,WAAW,KAAK,EAAE;AACnC,cAAM,cAAc,WAAW,KAAK,MAAM;AAC1C,cAAM,QAAQ,QAAQ,OAAO,UAAU,UAAU,WAAW;AAC5D,YAAI,CAAC,OAAO;AACV,iBAAO;AAAA,YACL,UAAU;AAAA,YACV,WAAW;AAAA,cACT,QAAQ;AAAA,cACR,MAAM;AAAA,cACN,SAAS;AAAA,YACX;AAAA,UACF;AAAA,QACF;AACA,eAAO,EAAE,UAAU,KAAK;AAAA,MAC1B,QAAQ;AACN,eAAO;AAAA,UACL,UAAU;AAAA,UACV,WAAW;AAAA,YACT,QAAQ;AAAA,YACR,MAAM;AAAA,YACN,SAAS;AAAA,UACX;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;AC5BO,SAAS,2BAA2B,QAAoC;AAC7E,QAAM,EAAE,SAAS,IAAI;AAErB,SAAO;AAAA,IACL,MAAM,cAAc,SAAmD;AACrE,UAAI;AACJ,UAAI;AACF,iBAAS,OAAO,QAAQ,MAAM;AAAA,MAChC,QAAQ;AACN,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,SAAS;AAAA,QACX;AAAA,MACF;AAEA,YAAM,MAAM,qBAAqB;AAAA,QAC/B,MAAM,QAAQ;AAAA,QACd,MAAM;AAAA,UACJ,MAAM;AAAA,UACN,QAAQ;AAAA,UACR,IAAI;AAAA,UACJ,KAAK;AAAA,UACL,UAAU,IAAI,WAAW,CAAC;AAAA,QAC5B;AAAA,QACA;AAAA,QACA,aAAa,QAAQ;AAAA,QACrB,aAAa,MAAM;AACjB,gBAAM,IAAI,MAAM,gCAAgC;AAAA,QAClD;AAAA,MACF,CAAC;AAED,UAAI,QAAQ,WAAW;AACrB,aAAK,SAAS,SAAS,GAAG,EAAE,MAAM,CAAC,QAAiB;AAElD,gBAAM,SAAS,eAAe,QAAQ,IAAI,UAAU;AACpD,kBAAQ,MAAM,0BAA0B,MAAM;AAAA,QAChD,CAAC;AACD,eAAO,EAAE,QAAQ,KAAK;AAAA,MACxB;AAEA,UAAI;AACF,eAAO,MAAM,SAAS,SAAS,GAAG;AAAA,MACpC,SAAS,KAAc;AAErB,cAAM,SAAS,eAAe,QAAQ,IAAI,UAAU;AACpD,gBAAQ,MAAM,kBAAkB,MAAM;AACtC,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;AC1EO,SAAS,0BAA0B,SAA2B;AACnE,QAAM,IAAI;AAAA,IACR;AAAA,EAEF;AACF;;;ACLA,SAAS,oBAA+C;AAexD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAQP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;ACxDP,SAAS,iBAAiB;AAK1B,IAAM,eAAe;AA0Cd,SAAS,qBACd,UACA,SAAqC,CAAC,GACT;AAC7B,QAAM,WAAW,SAAS,YAAY;AACtC,MAAI,SAAS,WAAW,GAAG;AACzB,WAAO;AAAA,EACT;AAGA,MAAI,OAAO,gBAAgB,QAAW;AACpC,QAAI,CAAC,aAAa,KAAK,OAAO,YAAY,OAAO,GAAG;AAClD,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,mBAAmB,OAAO,oBAAoB;AACpD,QAAM,cAAc,OAAO,eAAe,CAAC;AAG3C,QAAM,UAAkC,CAAC;AACzC,aAAW,QAAQ,UAAU;AAC3B,QAAI,OAAO,OAAO,aAAa,IAAI,GAAG;AACpC,cAAQ,OAAO,IAAI,CAAC,IAAI,OAAO,YAAY,IAAI,CAAC;AAAA,IAClD,OAAO;AACL,cAAQ,OAAO,IAAI,CAAC,IAAI,OAAO,gBAAgB;AAAA,IACjD;AAAA,EACF;AAEA,QAAM,aAA8B;AAAA,IAClC,MAAM,OAAO,QAAQ;AAAA,IACrB,SAAS,OAAO,WAAW;AAAA,IAC3B,OAAO;AAAA,IACP,UAAU,OAAO,YAAY,CAAC;AAAA,IAC9B,aAAa,OAAO,eAAe,EAAE,MAAM,UAAU,YAAY,CAAC,EAAE;AAAA,IACpE;AAAA,EACF;AAEA,MAAI,OAAO,WAAW,QAAW;AAC/B,eAAW,SAAS,OAAO;AAAA,EAC7B;AAEA,MAAI,OAAO,eAAe,QAAW;AACnC,eAAW,aAAa,OAAO;AAAA,EACjC;AAEA,MAAI,OAAO,gBAAgB,QAAW;AACpC,eAAW,cAAc;AAAA,MACvB,SAAS,OAAO,YAAY;AAAA,MAC5B,kBAAkB,OAAO,YAAY;AAAA,IACvC;AAAA,EACF;AAEA,SAAO;AACT;;;AD5BA,IAAM,4BAA4B;AAuW3B,SAAS,WAAW,QAAiC;AAE1D,QAAM,eAAe,OAAO,cAAc;AAC1C,QAAM,kBAAkB,OAAO,iBAAiB;AAEhD,MAAI,gBAAgB,iBAAiB;AACnC,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MAAI,mBAAmB,OAAO,gBAAgB,QAAW;AACvD,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,sBAAsB,CAAC,gBAAgB,CAAC;AAC9C,QAAM,eAAe,gBAAgB;AAGrC,QAAM,cAAc,mBAAmB,OAAO,KAAK;AACnD,MAAI,0BAA0B,OAAO;AACrC,MAAI,CAAC,yBAAyB;AAC5B,UAAM,WAAW,YAAY,YAAY,OAAO;AAChD,UAAM,kBAAkB,CAAC,QAAQ;AACjC,UAAM,kBAA0C;AAAA,MAC9C,CAAC,QAAQ,GAAG,YAAY;AAAA,IAC1B;AACA,UAAM,gBACJ,YAAY,sBACR,EAAE,CAAC,QAAQ,GAAG,YAAY,oBAAoB,IAC9C;AAEN,8BAA0B;AAAA,MACxB;AAAA,MACA;AAAA,MACA,GAAI,iBAAiB,EAAE,cAAc;AAAA,IACvC;AAAA,EACF;AAGA,MAAI;AACJ,MAAI;AACF,eAAW,cAAc,OAAO,SAAS;AAAA,EAC3C,SAAS,OAAgB;AACvB,UAAM,IAAI;AAAA,MACR,sBAAsB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,MAC5E,iBAAiB,QAAQ,QAAQ;AAAA,IACnC;AAAA,EACF;AACA,QAAM,EAAE,QAAQ,WAAW,IAAI;AAG/B,QAAM,WAAW,IAAI,gBAAgB;AAGrC,MAAI,OAAO,UAAU;AACnB,eAAW,CAAC,MAAM,OAAO,KAAK,OAAO,QAAQ,OAAO,QAAQ,GAAG;AAC7D,YAAM,UAAU,OAAO,IAAI;AAC3B,UAAI,CAAC,OAAO,UAAU,OAAO,KAAK,UAAU,GAAG;AAC7C,cAAM,IAAI;AAAA,UACR,gFAAgF,IAAI;AAAA,QACtF;AAAA,MACF;AACA,eAAS,GAAG,SAAS,OAAO;AAAA,IAC9B;AAAA,EACF;AAGA,MAAI,OAAO,gBAAgB;AACzB,aAAS,UAAU,OAAO,cAAc;AAAA,EAC1C;AAGA,QAAM,WAAW,2BAA2B;AAAA,IAC1C,SAAS,OAAO,WAAW;AAAA,EAC7B,CAAC;AAGD,QAAM,SAAS,uBAAuB;AAAA,IACpC,kBAAkB,OAAO,oBAAoB;AAAA,IAC7C,WAAW;AAAA,IACX,aAAa,OAAO;AAAA,EACtB,CAAC;AAGD,QAAM,UAAU,OAAO,eAAe;AACtC,QAAM,UAAU,OAAO,eAAe;AAGtC,QAAM,iBAAiB,CAAC,SAA6B;AACnD,UAAM,QAAQ,OAAO,KAAK,MAAM,QAAQ;AACxC,WAAO,QAAQ,KAAK;AAAA,EACtB;AAGA,QAAM,aACJ,CAAC;AAGH,QAAM,mBAAmB,OACvB,YACkC;AAElC,QAAI,QAAQ,KAAK,SAAS,2BAA2B;AACnD,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,SAAS,sBAAsB,QAAQ,KAAK,MAAM,0BAA0B,yBAAyB;AAAA,MACvG;AAAA,IACF;AAGA,UAAM,YAAY,OAAO,KAAK,QAAQ,MAAM,QAAQ;AACpD,QAAI;AACJ,QAAI;AACF,aAAO,iBAAiB,SAAS;AAAA,IACnC,QAAQ;AAEN,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,SAAS;AAAA,MACX;AAAA,IACF;AAMA,QAAI,OAAO,WAAW,OAAO;AAC3B,YAAM,cACJ,QAAQ,KAAK,SAAS,KAClB,QAAQ,KAAK,UAAU,GAAG,EAAE,IAAI,QAChC,QAAQ;AAEd,YAAM,WAAW,CAAC,MAAc,EAAE,QAAQ,oBAAoB,EAAE;AAChE,cAAQ;AAAA,QACN;AAAA,QACA,QAAQ,KAAK,IAAI;AAAA,QACjB,UAAU,KAAK,OAAO,UAAU,GAAG,EAAE,CAAC;AAAA,QACtC,UAAU,SAAS,QAAQ,MAAM,CAAC;AAAA,QAClC,QAAQ,SAAS,QAAQ,WAAW,CAAC;AAAA,QACrC,QAAQ,WAAW;AAAA,MACrB;AAAA,IACF;AAGA,UAAM,eAAe,MAAM,SAAS,OAAO,MAAM,QAAQ,IAAI;AAC7D,QAAI,CAAC,aAAa,UAAU;AAG1B,UAAI,aAAa,WAAW;AAC1B,eAAO,aAAa;AAAA,MACtB;AACA,aAAO,EAAE,QAAQ,OAAO,MAAM,OAAO,SAAS,sBAAsB;AAAA,IACtE;AAGA,QAAI;AACJ,QAAI;AACF,eAAS,OAAO,QAAQ,MAAM;AAAA,IAChC,QAAQ;AACN,UAAI,OAAO,WAAW,OAAO;AAC3B,iBAAS;AAAA,MACX,OAAO;AACL,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AACA,QAAI,EAAE,OAAO,WAAW,QAAQ;AAC9B,YAAM,cAAc,OAAO,SAAS,MAAM,MAAM;AAChD,UAAI,CAAC,YAAY,UAAU;AAGzB,YAAI,YAAY,WAAW;AACzB,iBAAO,YAAY;AAAA,QACrB;AACA,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAGA,UAAM,MAAM,qBAAqB;AAAA,MAC/B,MAAM,QAAQ;AAAA,MACd;AAAA,MACA;AAAA,MACA,aAAa,QAAQ;AAAA,MACrB,aAAa;AAAA,IACf,CAAC;AAGD,QAAI;AACF,YAAM,SAAS,MAAM,SAAS,SAAS,GAAG;AAI1C,UAAI,OAAO,UAAU,KAAK,SAAS,SAAS,WAAW,SAAS;AAC9D,cAAM,UAAU,IAAI,OAAO;AAC3B,YAAI,SAAS;AACX,qBAAW,QAAQ,aAAa,OAAO;AAAA,QACzC;AAAA,MACF;AASA,UAAI,OAAO,UAAU,OAAO,YAAY,CAAC,OAAO,MAAM;AACpD,YAAI;AACF,gBAAM,eAAe,KAAK,UAAU,OAAO,QAAQ;AACnD,UAAC,OAA6B,OAAO,OAAO;AAAA,YAC1C;AAAA,YACA;AAAA,UACF,EAAE,SAAS,QAAQ;AAAA,QACrB,SAAS,KAAK;AACZ,kBAAQ;AAAA,YACN;AAAA,YACA,eAAe,QAAQ,IAAI,UAAU;AAAA,UACvC;AAAA,QACF;AAAA,MACF;AAYA,UACE,CAAC,OAAO,UACR,CAAE,OAAsC,gBACvC,OAA6B,MAC9B;AACA,cAAM,UAAW,OAA4B;AAC7C,QACE,OAGA,eAAe;AAAA,UACf,MAAM,kBAAkB,OAAO;AAAA,UAC/B,SACG,OAAgC,WAAW;AAAA,QAChD;AAAA,MACF;AAEA,aAAO;AAAA,IACT,SAAS,KAAc;AAErB,YAAM,SAAS,eAAe,QAAQ,IAAI,UAAU;AACpD,cAAQ,MAAM,4BAA4B,MAAM;AAChD,aAAO,EAAE,QAAQ,OAAO,MAAM,OAAO,SAAS,iBAAiB;AAAA,IACjE;AAAA,EACF;AAQA,MAAI;AACJ,MAAI;AAEJ,MACE,OAAO,qBAAqB,UAC5B,OAAO,iBAAiB,SAAS,GACjC;AAEA,QAAI,OAAO,mBAAmB,QAAW;AACvC,cAAQ;AAAA,QACN;AAAA,MACF;AAAA,IACF;AACA,2BAAuB,OAAO,iBAAiB;AAAA,MAAI,CAAC,MAClD,mBAAmB,GAAG,MAAM;AAAA,IAC9B;AAEA,aAAS,IAAI,GAAG,IAAI,qBAAqB,QAAQ,KAAK;AACpD,YAAM,SAAS,qBAAqB,OAAO,CAAC,GAAG,MAAM,MAAM,CAAC;AAC5D,4BAAsB,qBAAqB,CAAC,GAAa,MAAM;AAAA,IACjE;AACA,yBAAqB,qBAAqB,CAAC;AAAA,EAC7C,WAAW,OAAO,mBAAmB,QAAW;AAC9C,yBAAqB,mBAAmB,OAAO,gBAAgB,MAAM;AACrE,2BAAuB,CAAC,kBAAkB;AAAA,EAC5C,WAAW,OAAO,eAAe,QAAW;AAC1C,yBAAqB,OAAO;AAC5B,2BAAuB,CAAC,kBAAkB;AAAA,EAC5C,OAAO;AACL,yBAAqB,mBAAmB,iBAAiB,MAAM;AAC/D,2BAAuB,CAAC,kBAAkB;AAAA,EAC5C;AAGA,QAAM,kBAAkB,IAAI,gBAAgB;AAC5C,MACE,OAAO,qBAAqB,UAC5B,OAAO,iBAAiB,SAAS,GACjC;AACA,aAAS,IAAI,GAAG,IAAI,OAAO,iBAAiB,QAAQ,KAAK;AACvD,sBAAgB;AAAA,QACd,OAAO,iBAAiB,CAAC;AAAA,QACzB,qBAAqB,CAAC;AAAA,MACxB;AAAA,IACF;AAAA,EACF,WAAW,OAAO,mBAAmB,QAAW;AAC9C,oBAAgB,WAAW,OAAO,gBAAgB,kBAAkB;AAAA,EACtE,WAAW,OAAO,eAAe,QAAW;AAC1C,oBAAgB,WAAW,YAAY,kBAAkB;AAAA,EAC3D,OAAO;AACL,oBAAgB,WAAW,iBAAiB,kBAAkB;AAAA,EAChE;AAEA,QAAM,UAAU;AAAA,IACd,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,aAAa,OAAO,eAAe;AAAA,IACnC,GAAI,OAAO,iBAAiB,UAAa,EAAE,cAAc,OAAO,aAAa;AAAA,IAC7E,GAAI,OAAO,oBAAoB,UAAa,EAAE,iBAAiB,OAAO,gBAAgB;AAAA,IACtF,WAAW,OAAO,aAAa;AAAA,IAC/B,YAAY,OAAO,cAAc;AAAA,IACjC,YAAY,OAAO,OAAO,cAAc,EAAE;AAAA,EAC5C;AAGA,MAAI;AACJ,MAAI;AACJ,MAAI,gBAA+C;AACnD,MAAI;AACJ,MAAI;AACJ,MAAI,aAAgC;AAGpC,MAAI;AAKJ,MAAI;AAIJ,MAAI,uBAA4B;AAEhC,MAAI,gBAAgB,cAAc;AAEhC,UAAM,WAAW,eAAe;AAAA,MAC9B,WAAW,OAAO;AAAA,MAClB,cAAc;AAAA,MACd,WAAW,OAAO;AAAA,MAClB;AAAA,MACA,aAAa;AAAA,MACb,aAAa;AAAA,MACb,UAAU,OAAO;AAAA,MACjB,YAAY,OAAO;AAAA,MACnB,gBAAgB;AAAA,MAChB,kBAAkB,OAAO;AAAA,MACzB,gBAAgB,OAAO;AAAA,IACzB,CAAC;AAED,gBAAY,SAAS;AACrB,oBAAgB,SAAS;AACzB,+BAA2B,SAAS;AACpC,+BAA2B,SAAS;AACpC,kBAAc;AAAA,MACZ,SAAS,MAAM,QAAQ,QAAQ;AAAA,MAC/B,YAAY,MAAM,QAAQ,QAAQ;AAAA,IACpC;AAEA,eAAW,UAAU;AAErB,cAAU,YAAY,SAAS,MAAM;AACrC,aAAS,YAAY,SAAS,KAAK;AAAA,EACrC,WAAW,qBAAqB;AAI9B,+BAA2B,IAAI;AAAA,MAC7B;AAAA,QACE,YAAY,OAAO,cAAc,CAAC;AAAA,QAClC,gBAAgB,OAAO,kBAAkB;AAAA,QACzC,iBAAiB,OAAO,YAAY;AAAA,QACpC,gBAAgB;AAAA,QAChB,eAAe,QAAQ;AAAA,QACvB,aAAa;AAAA,QACb,aAAa;AAAA,QACb,kBAAkB,OAAO,oBAAoB;AAAA,MAC/C;AAAA,MACA,OAAO;AAAA,MACP;AAAA,IACF;AAEA,+BAA2B,uBAAuB;AAAA,MAChD,WAAW,OAAO;AAAA,MAClB,gBAAgB;AAAA,IAClB,CAAC;AAGD,gBAAY;AAAA,MACV,eAAe,MACb,QAAQ,OAAO,IAAI,UAAU,uCAAuC,CAAC;AAAA,IACzE;AACA,kBAAc;AAAA,MACZ,SAAS,MAAM,QAAQ,QAAQ;AAAA,MAC/B,YAAY,MAAM,QAAQ,QAAQ;AAAA,IACpC;AAEA,eAAW,UAAU;AAErB,cAAU,YAAY;AAGpB,UAAI;AAEJ,UAAI;AACJ,UAAI;AACF,cAAM,MAAM,MAAM,OAAO,0BAA0B;AACnD,6BAAqB,IAAI;AACzB,gCAAwB,IAAI;AAAA,MAC9B,QAAQ;AACN,cAAM,IAAI;AAAA,UACR;AAAA,QAEF;AAAA,MACF;AAEA,YAAM,SAAS,QAAQ,OAAO,MAAM,GAAG,EAAE,CAAC;AAC1C,YAAM,gBAAgB,OAAO,iBAAiB;AAC9C,YAAM,kBAAkB,sBAAsB,QAAQ,MAAM;AAG5D,UAAI,uBAAuB,OAAO;AAClC,UAAI,CAAC,sBAAsB;AACzB,cAAM,YAAY,OAAO,KAAK,OAAO,SAAS;AAC9C,+BAAuB,KAAK,UAAU,SAAS,KAAK,CAAC;AACrD,kBAAU,KAAK,CAAC;AAAA,MAClB;AAEA,YAAM,yBACJ,YAAY,mBAAmB,YAAY;AAG7C,YAAM,oBACJ,OAAO,mBAAmB,UAAa,OAAO,eAAe,SAAS;AAGxE,YAAM,0BAA0B,oBAC5B,OAAO,iBACP,yBACE;AAAA,QACE;AAAA,UACE,WAAW;AAAA,UACX,SAAS,OAAO,YAAY,WAAW,OAAO;AAAA,UAC9C,QAAQ,YAAY;AAAA,UACpB,iBAAiB,YAAY;AAAA,UAC7B,cAAc,YAAY;AAAA,UAC1B,YAAY;AAAA,QACd;AAAA,MACF,IACA;AAGN,YAAM,kBAAuB;AAAA,QAC3B;AAAA,QACA;AAAA,QACA,aAAa;AAAA,QACb,gBAAgB;AAAA,QAChB,OAAO,CAAC;AAAA,QACR,QAAQ,CAAC;AAAA,QACT,eAAe,EAAE,SAAS,MAAM;AAAA,MAClC;AACA,UAAI,yBAAyB;AAC3B,wBAAgB,iBAAiB;AAAA,MACnC;AACA,UAAI,OAAO,WAAW;AACpB,wBAAgB,YAAY,OAAO;AAAA,MACrC;AACA,UAAI,OAAO,OAAO;AAChB,wBAAgB,QAAQ,OAAO;AAAA,MACjC;AAEA,6BAAuB,IAAI;AAAA,QACzB;AAAA,QACA;AAAA,MACF;AAEA,YAAM,qBAAqB,MAAM;AAGjC,YAAM,YACJ;AACF,YAAM,WAAW,eAAe;AAAA,QAC9B;AAAA,QACA,cAAc;AAAA,QACd,WAAW,OAAO;AAAA,QAClB;AAAA,QACA,aAAa;AAAA,QACb,aAAa;AAAA,QACb,UAAU,OAAO;AAAA,QACjB,YAAY,OAAO;AAAA,QACnB,gBAAgB;AAAA,QAChB,kBAAkB,OAAO;AAAA,QACzB,gBAAgB,OAAO;AAAA,MACzB,CAAC;AAGD,kBAAY,SAAS;AACrB,sBAAgB,SAAS;AAGzB,+BAAyB,aAAa,SAAS,SAAS;AACxD,+BAAyB,kBAAkB;AAAA,QACzC,SAAS,MAAM,QAAQ,QAAQ;AAAA,QAC/B,YAAY,MAAM,QAAQ,QAAQ;AAAA,MACpC,CAAC;AACD,UAAI,SAAS,eAAe;AAC1B,iCAAyB,iBAAiB,SAAS,aAAa;AAAA,MAClE;AAEA,+BAAyB,kBAAkB;AAAA,QACzC,SAAS,MAAM,QAAQ,QAAQ;AAAA,QAC/B,YAAY,MAAM,QAAQ,QAAQ;AAAA,MACpC,CAAC;AACD,UAAI,SAAS,eAAe;AAC1B,iCAAyB,iBAAiB,SAAS,aAAa;AAAA,MAClE;AAEA,iBAAW,UAAU;AAGrB,iBAAW,QAAQ,QAAQ,cAAc;AACvC,6BAAqB,SAAS;AAAA,UAC5B,QAAQ;AAAA,UACR,SAAS;AAAA,UACT,UAAU;AAAA,QACZ,CAAC;AAAA,MACH;AAGA,YAAM,SAAS,MAAM,SAAS,MAAM;AACpC,aAAO;AAAA,IACT;AAEA,aAAS,YAAY;AACnB,UAAI,sBAAsB;AACxB,cAAM,qBAAqB,KAAK;AAChC,+BAAuB;AAAA,MACzB;AAAA,IACF;AAAA,EACF,OAAO;AAEL,UAAM,eAAe,OAAO;AAC5B,UAAM,cAAc,OAAO;AAE3B,gBAAY,oBAAoB,YAAY;AAC5C,kBAAc,yBAAyB,cAAc,EAAE;AAGvD,QAAI,yBAAyB;AAC3B,sBAAgB,wBAAwB,YAAY;AAAA,IACtD;AAGA,+BAA2B,IAAI;AAAA,MAC7B;AAAA,QACE,YAAY,OAAO,cAAc,CAAC;AAAA,QAClC,gBAAgB,OAAO,kBAAkB;AAAA,QACzC,iBAAiB,OAAO,YAAY;AAAA,QACpC,gBAAgB;AAAA,QAChB,eAAe,QAAQ;AAAA,QACvB,aAAa;AAAA,QACb,aAAa;AAAA,QACb,kBAAkB,OAAO,oBAAoB;AAAA,MAC/C;AAAA,MACA,OAAO;AAAA,MACP;AAAA,IACF;AAEA,6BAAyB,aAAa,SAAS;AAC/C,6BAAyB,kBAAkB,WAAW;AACtD,QAAI,eAAe;AACjB,+BAAyB,iBAAiB,aAAa;AAAA,IACzD;AAGA,+BAA2B,uBAAuB;AAAA,MAChD,WAAW,OAAO;AAAA,MAClB,gBAAgB;AAAA,IAClB,CAAC;AACD,6BAAyB,kBAAkB,WAAW;AACtD,QAAI,eAAe;AACjB,+BAAyB,iBAAiB,aAAa;AAAA,IACzD;AAEA,eAAW,UAAU;AAErB,cAAU,YAAY;AAEpB,mBAAa,aAAa,OAAO,KAAK,QAAQ;AAC5C,YAAI,IAAI,WAAW,SAAS,IAAI,QAAQ,WAAW;AACjD,cAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,cAAI,IAAI,KAAK,UAAU,EAAE,QAAQ,WAAW,OAAO,CAAC,CAAC;AACrD;AAAA,QACF;AAEA,YAAI,IAAI,WAAW,UAAU,IAAI,QAAQ,kBAAkB;AACzD,cAAI,OAAO;AACX,cAAI,GAAG,QAAQ,CAAC,UAAkB;AAChC,oBAAQ,MAAM,SAAS;AAAA,UACzB,CAAC;AACD,cAAI,GAAG,OAAO,YAAY;AACxB,gBAAI;AACF,oBAAM,UAAU,KAAK,MAAM,IAAI;AAC/B,kBACE,QAAQ,WAAW,UACnB,QAAQ,WAAW,QACnB,QAAQ,gBAAgB,UACxB,QAAQ,gBAAgB,QACxB,QAAQ,SAAS,UACjB,QAAQ,SAAS,MACjB;AACA,oBAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,oBAAI;AAAA,kBACF,KAAK,UAAU;AAAA,oBACb,QAAQ;AAAA,oBACR,MAAM;AAAA,oBACN,SAAS;AAAA,kBACX,CAAC;AAAA,gBACH;AACA;AAAA,cACF;AACA,oBAAM,SAAS,MAAM,iBAAiB,OAAO;AAC7C,kBAAI,UAAU,OAAO,SAAS,MAAM,KAAK;AAAA,gBACvC,gBAAgB;AAAA,cAClB,CAAC;AACD,kBAAI,IAAI,KAAK,UAAU,MAAM,CAAC;AAAA,YAChC,SAAS,KAAc;AACrB,sBAAQ,MAAM,8BAA8B,GAAG;AAC/C,kBAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,kBAAI;AAAA,gBACF,KAAK,UAAU;AAAA,kBACb,QAAQ;AAAA,kBACR,MAAM;AAAA,kBACN,SAAS;AAAA,gBACX,CAAC;AAAA,cACH;AAAA,YACF;AAAA,UACF,CAAC;AACD;AAAA,QACF;AAEA,YAAI,UAAU,GAAG;AACjB,YAAI,IAAI;AAAA,MACV,CAAC;AAED,YAAM,IAAI,QAAc,CAAC,YAAY;AACnC,oBAAY,OAAO,aAAa,MAAM,QAAQ,CAAC;AAAA,MACjD,CAAC;AAGD,YAAM,UAAU,MAAM,yBAAyB,UAAU;AACzD,YAAM,uBAAuB,QAAQ,IAAI,CAAC,MAAM,EAAE,UAAU,MAAM;AAClE,+BAAyB,mBAAmB,oBAAoB;AAEhE,aAAO;AAAA,QACL,kBAAkB;AAAA,QAClB,WAAW,QAAQ;AAAA,QACnB,cAAc,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE;AAAA,MACnD;AAAA,IACF;AAEA,aAAS,YAAY;AACnB,YAAM,SAAS;AACf,UAAI,QAAQ;AACV,cAAM,IAAI,QAAc,CAAC,YAAY;AACnC,iBAAO,MAAM,MAAM,QAAQ,CAAC;AAAA,QAC9B,CAAC;AACD,qBAAa;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAGA,MAAI,UAAU;AACd,MAAI,uBAA0C,CAAC;AAG/C,QAAM,OAAoB;AAAA,IACxB,IAAI,SAAS;AACX,aAAO;AAAA,IACT;AAAA,IACA,IAAI,aAAa;AACf,aAAO;AAAA,IACT;AAAA,IACA,IAAI,YAAY;AACd,aACE,OAAO,aACN,wBACD;AAAA,IAEJ;AAAA,IACA,IAAI,gBAAgB;AAClB,aAAO;AAAA,IACT;AAAA,IAEA,qBAAkD;AAChD,aAAO,qBAAqB,UAAU;AAAA,QACpC,kBAAkB,OAAO,oBAAoB;AAAA,QAC7C,aAAa,OAAO;AAAA,QACpB,GAAG,OAAO;AAAA,MACZ,CAAC;AAAA,IACH;AAAA,IAEA,GACE,aACA,mBACa;AACb,UAAI,OAAO,gBAAgB,UAAU;AAEnC,YAAI,CAAC,OAAO,UAAU,WAAW,KAAK,cAAc,GAAG;AACrD,gBAAM,IAAI;AAAA,YACR,4DAA4D,OAAO,WAAW,CAAC;AAAA,UACjF;AAAA,QACF;AACA,iBAAS,GAAG,aAAa,iBAA4B;AAAA,MACvD,WAAW,gBAAgB,aAAa;AAEtC,cAAM,WAAW;AACjB,iCAAyB,GAAG,QAAQ;AACpC,iCAAyB,GAAG,QAAQ;AAAA,MACtC,OAAO;AAGL,cAAM,YAAY,OAAO,WAAW,EAAE,QAAQ,oBAAoB,EAAE;AACpE,cAAM,IAAI;AAAA,UACR,6BAA6B,SAAS;AAAA,QACxC;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,IAEA,UAAU,SAA+B;AACvC,eAAS,UAAU,OAAO;AAC1B,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,QAA8B;AAClC,UAAI,SAAS;AACX,cAAM,IAAI,UAAU,sBAAsB;AAAA,MAC5C;AAEA,UAAI;AACF,cAAM,SAAS,MAAM,QAAQ;AAC7B,kBAAU;AACV,+BAAuB,OAAO;AAC9B,eAAO;AAAA,UACL,WAAW,OAAO;AAAA,UAClB,cAAc,OAAO;AAAA,UACrB,kBAAkB,OAAO;AAAA,QAC3B;AAAA,MACF,SAAS,OAAgB;AACvB,YAAI,iBAAiB,WAAW;AAC9B,gBAAM;AAAA,QACR;AACA,cAAM,IAAI;AAAA,UACR,yBAAyB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,UAC/E,iBAAiB,QAAQ,QAAQ;AAAA,QACnC;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,OAAsB;AAC1B,UAAI,CAAC,SAAS;AACZ;AAAA,MACF;AAEA,YAAM,OAAO;AACb,gBAAU;AAAA,IACZ;AAAA,IAEA,MAAM,SAAS,cAAqC;AAClD,UAAI,CAAC,SAAS;AACZ,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAEA,UACE,OAAO,iBAAiB,YACxB,aAAa,WAAW,MACxB,CAAC,iBAAiB,KAAK,YAAY,GACnC;AACA,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,aAAO,yBAAyB,SAAS,YAAY;AAAA,IACvD;AAAA,IAEA,MAAM,aACJ,OACA,SAC6B;AAE7B,UAAI,CAAC,SAAS;AACZ,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAGA,UAAI,CAAC,SAAS,aAAa;AACzB,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAEA,UAAI;AAEF,cAAM,WAAW,QAAQ,KAAK;AAG9B,cAAM,EAAE,SAAS,SAAS,IAAI,iBAAiB;AAAA,UAC7C,aAAa,QAAQ;AAAA,UACrB,eAAe,QAAQ;AAAA,UACvB,iBAAiB,yBAAyB,sBAAsB;AAAA,QAClE,CAAC;AAGD,mBAAW,WAAW,UAAU;AAC9B,kBAAQ,KAAK,kBAAkB,OAAO,EAAE;AAAA,QAC1C;AAGA,cAAM,oBACJ,QAAQ,WACP,OAAO,oBAAoB,OAAO,OAAO,SAAS,MAAM;AAG3D,YAAI,QAAQ,QAAQ,UAAa,oBAAoB,QAAQ,KAAK;AAChE,gBAAM,IAAI;AAAA,YACR,sCAAsC,iBAAiB,2BAA2B,QAAQ,GAAG;AAAA,UAC/F;AAAA,QACF;AAGA,YAAI;AACJ,YAAI,QAAQ,WAAW,QAAW;AAEhC,gBAAM,eAAe,qBAAqB;AAAA,YACxC,kBAAkB;AAAA,YAClB,kBAAkB,SAAS;AAAA,YAC3B;AAAA,UACF,CAAC;AACD,mBAAS,QAAQ,SAAS;AAAA,QAC5B,OAAO;AAEL,mBAAS,qBAAqB;AAAA,YAC5B,kBAAkB,OAAO,oBAAoB;AAAA,YAC7C,kBAAkB,SAAS;AAAA,YAC3B;AAAA,UACF,CAAC;AAAA,QACH;AAKA,cAAM,SAAS,gBAAgB;AAAA,UAC7B,aAAa,QAAQ;AAAA,UACrB;AAAA,UACA,MAAM;AAAA,QACR,CAAC;AAGD,cAAM,SAAS,MAAM,UAAU,cAAc,MAAM;AAGnD,YAAI,OAAO,UAAU;AACnB,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,MAAM;AAAA,YACf,GAAI,OAAO,SAAS,SAAY,EAAE,MAAM,OAAO,KAAK,IAAI,CAAC;AAAA,UAC3D;AAAA,QACF;AAEA,eAAO;AAAA,UACL,SAAS;AAAA,UACT,SAAS,MAAM;AAAA,UACf,MAAM,OAAO,QAAQ;AAAA,UACrB,SAAS,OAAO,WAAW;AAAA,QAC7B;AAAA,MACF,SAAS,OAAgB;AAEvB,YAAI,iBAAiB,WAAW;AAC9B,gBAAM;AAAA,QACR;AACA,cAAM,IAAI;AAAA,UACR,4BAA4B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,UAClF,iBAAiB,QAAQ,QAAQ;AAAA,QACnC;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,gBACJ,gBACA,gBACA,QACA,SACA,SAC6B;AAE7B,YAAM,gBAAgB;AAAA,QACpB,EAAE,gBAAgB,gBAAgB,QAAQ,QAAQ;AAAA,QAClD,OAAO;AAAA,MACT;AAGA,aAAO,KAAK,aAAa,eAAe,OAAO;AAAA,IACjD;AAAA,IAEA,MAAM,cACJ,gBACA,gBACA,QACA,SACA,SAC6B;AAE7B,YAAM,aAAa,SAAS,QAAQ;AAGpC,YAAM,cAAc;AAAA,QAClB,EAAE,MAAM,YAAY,gBAAgB,gBAAgB,QAAQ,QAAQ;AAAA,QACpE,OAAO;AAAA,MACT;AAGA,aAAO,KAAK,aAAa,aAAa,OAAO;AAAA,IAC/C;AAAA,IAEA,MAAM,cACJ,aACA,oBACA,SACwB;AAExB,cAAQ;AAAA,QACN;AAAA,MACF;AAGA,UAAI,CAAC,SAAS;AACZ,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAGA,UACE,CAAC,sBACD,OAAO,uBAAuB,YAC9B,mBAAmB,KAAK,MAAM,IAC9B;AACA,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAGA,YAAM,SAAS,eAAe,WAAW;AACzC,UAAI,CAAC,QAAQ;AACX,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAEA,YAAM,gBAAgB,OAAO;AAM7B,UAAI;AACJ,UAAI;AACF,uBAAe,OAAO,aAAa;AAAA,MACrC,QAAQ;AACN,cAAM,IAAI;AAAA,UACR,gDAAgD,aAAa;AAAA,QAC/D;AAAA,MACF;AACA,UAAI,eAAe,IAAI;AACrB,cAAM,IAAI;AAAA,UACR,gDAAgD,aAAa;AAAA,QAC/D;AAAA,MACF;AAGA,UAAI,SAAS,gBAAgB,QAAW;AACtC,YAAI;AACJ,YAAI;AACF,sBAAY,OAAO,QAAQ,WAAW;AAAA,QACxC,QAAQ;AACN,gBAAM,IAAI;AAAA,YACR,wCAAwC,QAAQ,WAAW;AAAA,UAC7D;AAAA,QACF;AACA,YAAI,eAAe,WAAW;AAC5B,gBAAM,IAAI;AAAA,YACR,yCAAyC,aAAa,2BAA2B,QAAQ,WAAW;AAAA,UACtG;AAAA,QACF;AAAA,MACF;AAIA,aAAO,UAAU,cAAc;AAAA,QAC7B,aAAa;AAAA,QACb,QAAQ;AAAA,QACR,MAAM;AAAA,MACR,CAAC;AAAA,IACH;AAAA;AAAA,IAGA,gBAAgB,gBAA8B;AAC5C,YAAM,iBAAiB,mBAAmB,gBAAgB,MAAM;AAGhE,4BAAsB,gBAAgB,gBAAgB,aAAa,CAAC;AAGpE,sBAAgB,WAAW,gBAAgB,cAAc;AAGzD,cAAQ,eAAe,gBAAgB,aAAa;AACpD,cAAQ,aACN,gBAAgB,kBAAkB,KAAK,QAAQ;AAGjD,UAAI,sBAAsB;AACxB,cAAM,SAAS,QAAQ,OAAO,MAAM,GAAG,EAAE,CAAC;AAC1C,6BAAqB,SAAS;AAAA,UAC5B,QAAQ;AAAA,UACR,SAAS;AAAA,UACT,UAAU;AAAA,QACZ,CAAC;AAAA,MACH;AAGA,UAAI,WAAW,qBAAqB,SAAS,GAAG;AAC9C,aAAK,yBAAyB,UAAU,oBAAoB;AAAA,MAC9D;AAAA,IACF;AAAA,IAEA,mBAAmB,gBAA8B;AAG/C,UACE,gBAAgB,UAAU,cAAc,KACxC,gBAAgB,QAAQ,GACxB;AACA,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAEA,YAAM,iBAAiB,gBAAgB,cAAc,cAAc;AACnE,UAAI,mBAAmB,QAAW;AAChC;AAAA,MACF;AAGA,cAAQ,eAAe,gBAAgB,aAAa;AACpD,YAAM,aAAa,gBAAgB,kBAAkB;AACrD,UAAI,eAAe,QAAW;AAC5B,gBAAQ,aAAa;AAAA,MACvB;AAGA,UAAI,wBAAwB,qBAAqB,aAAa;AAC5D,6BAAqB,YAAY,cAAc;AAAA,MACjD;AAGA,UAAI,WAAW,qBAAqB,SAAS,GAAG;AAC9C,aAAK,yBAAyB,UAAU,oBAAoB;AAAA,MAC9D;AAAA,IACF;AAAA,IAEA,MAAM,YACJ,QACA,qBACA,SAC6B;AAE7B,UAAI,CAAC,SAAS;AACZ,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAGA,YAAM,mBAAmB,eAAe,MAAM;AAC9C,UAAI,CAAC,iBAAiB,OAAO;AAC3B,cAAM,IAAI;AAAA,UACR,wBAAwB,iBAAiB,UAAU,gBAAgB;AAAA,QACrE;AAAA,MACF;AAGA,UAAI,cAAc,SAAS;AAC3B,UAAI,gBAAgB,QAAW;AAE7B,cAAM,QAAQ,yBAAyB,sBAAsB;AAC7D,cAAM,eAAe,MAAM;AAAA,UACzB,CAAC,MACC,EAAE,SAAS,eAAe,uBACzB,EAAE,SAAS,gBACV,EAAE,SAAS,aAAa,SAAS,mBAAmB;AAAA,QAC1D;AACA,YAAI,cAAc,SAAS,eAAe,WAAW;AACnD,wBAAc,OAAO,aAAa,SAAS,cAAc,SAAS;AAAA,QACpE,OAAO;AACL,gBAAM,IAAI;AAAA,YACR;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAGA,YAAM,aAAa;AAAA,QACjB,EAAE,iBAAiB,OAAO;AAAA,QAC1B,OAAO;AAAA,MACT;AAGA,aAAO,KAAK,aAAa,YAAY;AAAA,QACnC,aAAa;AAAA,QACb,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;;;AExiDA,SAAS,sBAAsB,wBAAwB;AAgFhD,IAAM,uBAAN,MAA2B;AAAA,EACf;AAAA,EACA;AAAA,EAKT,QAAuB;AAAA,EACvB,aAA8C;AAAA,EAC9C,kBAA0B,IAAI,OAAO,EAAE;AAAA,EACvC,iBAAyB,IAAI,OAAO,EAAE;AAAA,EACtC,mBAAmB;AAAA,EACnB,aAA0B,CAAC;AAAA,EAC3B,gBAAsD;AAAA,EACtD,qBAAqB,oBAAI,IAAY;AAAA,EAE7C,YAAY,MAAmB,SAAuC;AACpE,SAAK,OAAO;AACZ,SAAK,UAAU;AAAA,MACb,eAAe;AAAA;AAAA,MACf,GAAG;AAAA,IACL;AACA,QAAI,SAAS,iBAAiB;AAC5B,WAAK,kBAAkB,QAAQ;AAAA,IACjC;AACA,QAAI,SAAS,gBAAgB;AAC3B,WAAK,iBAAiB,QAAQ;AAAA,IAChC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,WAA0B;AACxB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgD;AAC9C,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,cAAc,YAAqD;AAGvE,QAAI,KAAK,UAAU,WAAW;AAC5B,YAAM,IAAI;AAAA,QACR;AAAA,MAEF;AAAA,IACF;AAKA,QAAI,CAAC,WAAW,SAAS,WAAW,MAAM,WAAW,GAAG;AACtD,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,SAAK,aAAa;AAGlB,SAAK,aAAa,WAAW,MAAM,IAAI,CAAC,GAAG,WAAW;AAAA,MACpD;AAAA,MACA,SAAS;AAAA,IACX,EAAE;AAGF,QAAI,KAAK,QAAQ,YAAY;AAI3B,YAAM,MAAM,KAAK,QAAQ,MAAM,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;AAC7D,YAAM,cAAc,KAAK,IAAI,SAAS,EAAE,EAAE,SAAS,IAAI,GAAG,CAAC,GAAG,KAAK,KAAK,OAAO,MAAM,GAAG,EAAE,CAAC;AAC3F,YAAM,aAAyB;AAAA,QAC7B,IAAI,YAAY,OAAO,IAAI,GAAG,EAAE,MAAM,GAAG,EAAE;AAAA,QAC3C,QAAQ,KAAK,KAAK;AAAA,QAClB,YAAY,KAAK,MAAM,MAAM,GAAI;AAAA,QACjC,MAAM;AAAA,QACN,SAAS,KAAK,UAAU;AAAA,UACtB,OAAO;AAAA,UACP;AAAA,QACF,CAAC;AAAA,QACD,MAAM,CAAC;AAAA,QACP,KAAK,IAAI,OAAO,GAAG;AAAA,MACrB;AACA,YAAM,KAAK,QAAQ,WAAW,MAAM,UAAU;AAAA,IAChD;AAGA,UAAM,KAAK;AAAA,MACT;AAAA,MACA,WAAW,aAAa;AAAA,MACxB,WAAW,aAAa;AAAA,IAC1B;AAGA,SAAK,QAAQ;AACb,SAAK,mBAAmB;AAGxB,SAAK,iBAAiB;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,iBAAiB,aAAwC;AAC7D,QAAI,CAAC,KAAK,WAAY;AAItB,QAAI,YAAY,OAAO,OAAQ,YAAY,OAAO,KAAM;AAGxD,QAAI,KAAK,mBAAmB,IAAI,YAAY,EAAE,EAAG;AAGjD,UAAM,cAAc,KAAK,WAAW,KAAK,gBAAgB;AACzD,QAAI,CAAC,YAAa;AAClB,QAAI,YAAY,YAAa;AAG7B,QAAI,KAAK,UAAU,eAAe,KAAK,MAAM,SAAS,SAAS,EAAG;AASlE,SAAK,mBAAmB,IAAI,YAAY,EAAE;AAG1C,SAAK,iBAAiB;AAGtB,gBAAY,cAAc;AAG1B,UAAM,KAAK,WAAW,KAAK,kBAAkB,WAAW;AAGxD,QAAI,KAAK,QAAQ,YAAY;AAC3B,YAAM,KAAK,QAAQ,WAAW,MAAM,WAAW;AAAA,IACjD;AAGA,UAAM,aACJ,KAAK,qBAAqB,KAAK,WAAW,MAAM,SAAS;AAE3D,QAAI,YAAY;AAEd,WAAK,QAAQ;AAGb,YAAM,KAAK;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,IACF,OAAO;AAEL,YAAM,YAAY,KAAK,mBAAmB;AAC1C,YAAM,gBAAgB,YAAY;AAElC,YAAM,KAAK,mBAAmB,WAAW,eAAe,MAAM;AAE9D,WAAK,mBAAmB;AACxB,WAAK,QAAQ,QAAQ,YAAY,CAAC;AAGlC,WAAK,iBAAiB;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,mBAAmB,eAA0C;AACjE,QAAI,CAAC,KAAK,WAAY;AAGtB,QAAI,KAAK,UAAU,eAAe,KAAK,MAAM,SAAS,SAAS,EAAG;AAElE,UAAM,SAAS,iBAAiB,aAAa;AAC7C,QAAI,CAAC,OAAQ;AAEb,QAAI,OAAO,WAAW,SAAS;AAE7B,WAAK,iBAAiB;AAGtB,WAAK,QAAQ,QAAQ,KAAK,mBAAmB,CAAC;AAG9C,UAAI,KAAK,QAAQ,YAAY;AAC3B,cAAM,KAAK,QAAQ,WAAW,MAAM,aAAa;AAAA,MACnD;AAGA,YAAM,KAAK;AAAA,QACT;AAAA,QACA,2BAA2B,KAAK,mBAAmB,CAAC,KAAK,OAAO,WAAW,eAAe;AAAA,MAC5F;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,UAAgB;AACd,SAAK,iBAAiB;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,mBACZ,WACA,WACA,WACe;AACf,QAAI,CAAC,KAAK,WAAY;AAEtB,UAAM,OAAO,KAAK,WAAW,MAAM,SAAS;AAC5C,QAAI,CAAC,KAAM;AAGX,UAAM,UAAU,KAAK,WAAW,SAAS;AAGzC,UAAM,aAAa;AAAA,MACjB;AAAA,QACE,MAAM,KAAK;AAAA,QACX,OAAO,EAAE,MAAM,WAAW,MAAM,UAAU;AAAA,QAC1C,KAAK;AAAA,QACL,QAAQ;AAAA,QACR,SAAS,KAAK;AAAA,QACd,gBAAgB,KAAK;AAAA,MACvB;AAAA,MACA,KAAK,aAAa;AAAA,IACpB;AAGA,UAAM,cAAc,KAAK,QAAQ,eAAe;AAChD,UAAM,KAAK,KAAK,aAAa,YAAY,EAAE,YAAY,CAAC;AAGxD,UAAM,YAAY,KAAK,WAAW,SAAS;AAC3C,QAAI,WAAW;AACb,gBAAU,iBAAiB,WAAW;AAAA,IACxC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,WACZ,WACA,aACe;AACf,QAAI,CAAC,KAAK,WAAY;AAEtB,UAAM,YAAY,KAAK,WAAW,SAAS;AAC3C,QAAI,CAAC,aAAa,UAAU,QAAS;AAErC,UAAM,OAAO,KAAK,WAAW,MAAM,SAAS;AAC5C,QAAI,CAAC,KAAM;AAKX,UAAM,qBAAqB,KAAK,QAAQ,eAAe;AAEvD,QAAI;AACF,YAAM,KAAK,KAAK,cAAc,aAAa,oBAAoB;AAAA,QAC7D,aAAa,KAAK,WAAW,SAAS;AAAA,MACxC,CAAC;AACD,gBAAU,UAAU;AAAA,IACtB,SAAS,MAAe;AAAA,IAKxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,WAAW,WAA2B;AAC5C,QAAI,CAAC,KAAK,WAAY,QAAO;AAE7B,UAAM,OAAO,KAAK,WAAW,MAAM,SAAS;AAC5C,QAAI,CAAC,KAAM,QAAO;AAGlB,QAAI,KAAK,kBAAkB,QAAW;AACpC,aAAO,KAAK;AAAA,IACd;AAKA,QAAI;AACF,YAAM,WAAW,OAAO,KAAK,WAAW,QAAQ;AAChD,YAAM,YAAY,OAAO,KAAK,WAAW,MAAM,MAAM;AACrD,YAAM,UAAU,WAAW;AAC3B,aAAO,QAAQ,SAAS;AAAA,IAC1B,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,4BACZ,QACA,SACe;AACf,UAAM,cAAc,KAAK,QAAQ,eAAe;AAChD,QAAI;AACF,YAAM,KAAK,KAAK;AAAA,QACd,KAAK;AAAA,QACL,KAAK;AAAA,QACL;AAAA,QACA;AAAA,QACA,EAAE,YAAY;AAAA,MAChB;AAAA,IACF,SAAS,MAAe;AAAA,IAIxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAyB;AAC/B,SAAK,iBAAiB;AAEtB,UAAM,YAAY,KAAK,QAAQ;AAC/B,UAAM,UAAU,KAAK,QAAQ,YAAY;AAEzC,SAAK,gBAAgB,QAAQ,MAAM;AAEjC,WAAK,QAAQ,QAAQ,KAAK,mBAAmB,CAAC;AAG9C,WAAK,KAAK;AAAA,QACR;AAAA,QACA,8BAA8B,KAAK,mBAAmB,CAAC,UAAU,SAAS;AAAA,MAC5E;AAAA,IACF,GAAG,SAAS;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,eAA2B;AACjC,QAAI,KAAK,QAAQ,WAAW;AAC1B,aAAO,KAAK,QAAQ;AAAA,IACtB;AAEA,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAyB;AAC/B,QAAI,KAAK,kBAAkB,MAAM;AAC/B,YAAM,WAAW,KAAK,QAAQ,cAAc;AAC5C,eAAS,KAAK,aAAa;AAC3B,WAAK,gBAAgB;AAAA,IACvB;AAAA,EACF;AACF;;;ACneA;AAAA,EACE,aAAAA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAkDA,IAAM,mBAAN,MAAuB;AAAA,EACX;AAAA,EACA;AAAA,EAKT,QAAoB;AAAA,EACpB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,eAAe;AAAA,EACf,cAA4B,CAAC;AAAA,EAC7B,gBAAsD;AAAA,EACtD,UAAU;AAAA,EACV,sBAAsB;AAAA,EACtB,gBAAgB,oBAAI,IAAY;AAAA,EAExC,YAAY,MAAmB,SAAmC;AAChE,SAAK,OAAO;AACZ,SAAK,UAAU;AAAA,MACb,WAAW;AAAA;AAAA,MACX,GAAG;AAAA,IACL;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,WAAuB;AACrB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,wBAAiC;AAC/B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAwC;AACtC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,WAAW,cAAyC;AACxD,UAAM,SAAS,kBAAkB,YAAY;AAC7C,QAAI,CAAC,QAAQ;AACX,YAAM,IAAIA;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,SAAK,iBAAiB,aAAa;AACnC,SAAK,iBAAiB,aAAa;AACnC,SAAK,eAAe,OAAO;AAC3B,SAAK,QAAQ;AACb,SAAK,cAAc,CAAC;AACpB,SAAK,gBAAgB,oBAAI,IAAY;AACrC,SAAK,UAAU;AAGf,QAAI,KAAK,QAAQ,YAAY;AAC3B,YAAM,KAAK,QAAQ,WAAW,MAAM,YAAY;AAAA,IAClD;AAGA,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,iBAAiB,aAAwC;AAC7D,QAAI,CAAC,KAAK,QAAS;AAGnB,QAAI,KAAK,UAAU,aAAc;AAGjC,QAAI,YAAY,OAAO,OAAQ,YAAY,OAAO,KAAM;AAGxD,UAAM,OAAO,YAAY,KAAK,KAAK,CAAC,MAAgB,EAAE,CAAC,MAAM,GAAG;AAChE,QAAI,CAAC,KAAM;AACX,UAAM,iBAAiB,KAAK,CAAC;AAC7B,QAAI,mBAAmB,UAAa,mBAAmB,KAAK;AAC1D;AAKF,QAAI,KAAK,cAAc,IAAI,YAAY,EAAE,EAAG;AAC5C,SAAK,cAAc,IAAI,YAAY,EAAE;AAGrC,SAAK,YAAY,KAAK,WAAW;AAGjC,QAAI,KAAK,QAAQ,YAAY;AAC3B,YAAM,KAAK,QAAQ,WAAW,MAAM,WAAW;AAAA,IACjD;AAGA,QAAI,KAAK,YAAY,UAAU,KAAK,cAAc;AAChD,WAAK,aAAa;AAClB,WAAK,QAAQ;AAAA,IACf;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,aAAa,gBAA2C;AAE5D,QAAI,KAAK,UAAU,WAAW;AAC5B,YAAM,IAAIA;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAGA,QAAI,KAAK,UAAU,WAAW;AAC5B,YAAM,IAAIA;AAAA,QACR,kCAAkC,KAAK,KAAK;AAAA,QAC5C;AAAA,MACF;AAAA,IACF;AAIA,QAAI,eAAe,WAAW,KAAK,gBAAgB;AACjD,YAAM,IAAIA;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,SAAS,oBAAoB,cAAc;AACjD,QAAI,CAAC,QAAQ;AACX,YAAM,IAAIA;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAGA,QAAI,OAAO,wBAAwB,KAAK,gBAAgB;AACtD,YAAM,IAAIA;AAAA,QACR,qCAAqC,OAAO,mBAAmB,wBAAwB,KAAK,cAAc;AAAA,QAC1G;AAAA,MACF;AAAA,IACF;AAGA,UAAM,mBAAmB,KAAK,YAAY;AAAA,MACxC,CAAC,MAAM,EAAE,OAAO,OAAO;AAAA,IACzB;AACA,QAAI,CAAC,kBAAkB;AACrB,YAAM,IAAIA;AAAA,QACR,2BAA2B,OAAO,mBAAmB;AAAA,QACrD;AAAA,MACF;AAAA,IACF;AAGA,UAAM,qBAAqB,KAAK,QAAQ,eAAe;AACvD,QAAI;AACF,YAAM,KAAK,KAAK,cAAc,kBAAkB,kBAAkB;AAClE,WAAK,sBAAsB;AAAA,IAC7B,SAAS,MAAe;AAItB,WAAK,sBAAsB;AAC3B,YAAM,IAAIA;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAGA,QAAI,KAAK,QAAQ,YAAY;AAC3B,YAAM,KAAK,QAAQ,WAAW,MAAM,cAAc;AAAA,IACpD;AAEA,SAAK,QAAQ;AAAA,EACf;AAAA;AAAA;AAAA;AAAA,EAKA,UAAgB;AACd,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,eAAqB;AAC3B,SAAK,aAAa;AAElB,UAAM,YAAY,KAAK,QAAQ;AAC/B,UAAM,UAAU,KAAK,QAAQ,YAAY;AAEzC,SAAK,gBAAgB,QAAQ,MAAM;AACjC,UAAI,KAAK,UAAU,aAAc;AAEjC,UAAI,KAAK,YAAY,WAAW,GAAG;AAEjC,aAAK,QAAQ;AAGb,aAAK,KAAK,6BAA6B;AAAA,MACzC,OAAO;AAEL,aAAK,QAAQ;AAAA,MACf;AAAA,IACF,GAAG,SAAS;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,+BAA8C;AAC1D,UAAM,cAAc,KAAK,QAAQ,eAAe;AAChD,QAAI;AACF,YAAM,KAAK,KAAK;AAAA,QACd,KAAK;AAAA,QACL,KAAK;AAAA,QACL;AAAA,QACA;AAAA,QACA,EAAE,YAAY;AAAA,MAChB;AAAA,IACF,SAAS,MAAe;AAAA,IAExB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAqB;AAC3B,QAAI,KAAK,kBAAkB,MAAM;AAC/B,YAAM,WAAW,KAAK,QAAQ,cAAc;AAC5C,eAAS,KAAK,aAAa;AAC3B,WAAK,gBAAgB;AAAA,IACvB;AAAA,EACF;AACF;;;ACzVA;AAAA,EACE;AAAA,EACA;AAAA,EACA,kBAAAC;AAAA,OACK;AAmCA,SAAS,yBACd,SACS;AACT,SAAO,OAAO,QAAkD;AAE9D,UAAM,QAAQ,IAAI,OAAO;AACzB,UAAM,QAAQ,sBAAsB,KAAK;AACzC,QAAI,CAAC,OAAO;AACV,aAAO,IAAI,OAAO,OAAO,+CAA+C;AAAA,IAC1E;AAGA,UAAM,aAAaA,gBAAe,MAAM,eAAe;AACvD,QAAI,CAAC,WAAW,OAAO;AACrB,aAAO,IAAI;AAAA,QACT;AAAA,QACA,mBAAmB,WAAW,UAAU,0BAA0B;AAAA,MACpE;AAAA,IACF;AAGA,QAAI,IAAI,SAAS,QAAQ,cAAc,WAAW;AAChD,aAAO,IAAI;AAAA,QACT;AAAA,QACA,kCAAkC,IAAI,MAAM,cAAc,QAAQ,cAAc,SAAS;AAAA,MAC3F;AAAA,IACF;AAGA,UAAM,kBAAkB,QAAQ,mBAAmB;AACnD,QAAI,gBAAgB,IAAI,MAAM,eAAe,GAAG;AAC9C,aAAO,IAAI;AAAA,QACT;AAAA,QACA,yBAAyB,MAAM,eAAe;AAAA,MAChD;AAAA,IACF;AAGA,UAAM,UAAU,QAAQ,YAAY,MAAM,iBAAiB,MAAM,MAAM;AACvE,QAAI,CAAC,SAAS;AACZ,aAAO,IAAI;AAAA,QACT;AAAA,QACA,yBAAyB,MAAM,eAAe;AAAA,MAChD;AAAA,IACF;AAGA,UAAM,SAAS,QAAQ,oBAAoB;AAC3C,UAAM,aAAa,GAAG,MAAM,IAAI,MAAM,eAAe;AACrD,UAAM,aAAa;AAAA,MACjB;AAAA,QACE,eAAe,MAAM;AAAA,QACrB,eAAe,MAAM;AAAA,QACrB;AAAA,MACF;AAAA,MACA,QAAQ;AAAA,IACV;AACA,UAAM,QAAQ,aAAa,UAAU;AAGrC,WAAO,IAAI,OAAO;AAAA,EACpB;AACF;;;AChGA,SAAS,+BAA+B;AAaxC,IAAM,aACJ;AACF,SAAS,oBAAoB,aAA6B;AAExD,QAAM,QAAQ,YAAY,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI,KAAK;AACpD,MAAI,KAAK,SAAS,KAAK,KAAK,UAAU,OAAO,WAAW,KAAK,IAAI,GAAG;AAClE,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAiBO,SAAS,wBACd,QACmD;AACnD,QAAM,EAAE,cAAc,cAAc,YAAY,IAAI;AAEpD,SAAO,OAAO,QAAkD;AAE9D,UAAM,QAAQ,IAAI,OAAO;AAGzB,UAAM,SAAS,wBAAwB,KAAK;AAC5C,QAAI,CAAC,QAAQ;AACX,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,SAAS;AAAA,MACX;AAAA,IACF;AAKA,UAAM,aAAqC;AAAA,MACzC,GAAG;AAAA,MACH,gBAAgB,oBAAoB,OAAO,WAAW;AAAA,IACxD;AAGA,QACE,OAAO,aAAa,UACpB,OAAO,eAAe,UACtB,OAAO,gBAAgB,QACvB;AACA,UAAI;AACF,cAAM,SAAS,aAAa;AAAA,UAC1B,OAAO;AAAA,UACP,OAAO;AAAA,UACP,OAAO;AAAA,UACP,OAAO;AAAA,QACT;AAEA,YAAI,CAAC,OAAO,UAAU;AAEpB,iBAAO;AAAA,YACL,QAAQ;AAAA,YACR,MAAM,OAAO,KAAK,OAAO,OAAO,UAAU,EAAE,EAAE,SAAS,QAAQ;AAAA,UACjE;AAAA,QACF;AAIA,cAAM,YAAY,OAAO,aAAa,OAAO,MAAM,CAAC;AACpD,cAAM,EAAE,KAAK,IAAI,MAAM,aAAa,OAAO,WAAW,UAAU;AAGhE,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,MAAM,OAAO,KAAK,IAAI,EAAE,SAAS,QAAQ;AAAA,QAC3C;AAAA,MACF,SAAS,OAAO;AAEd,gBAAQ;AAAA,UACN;AAAA,UACA,iBAAiB,QAAS,MAAM,SAAS,MAAM,UAAW;AAAA,QAC5D;AACA,cAAM,cACJ,iBAAiB,QAAQ,MAAM,UAAU;AAE3C,cAAM,UAAU,YAAY,SAAS,sBAAsB,IACvD,6BACA,YAAY,SAAS,oBAAoB,IACvC,6CACA,YAAY,SAAS,mBAAmB,IACtC,+BACA;AACR,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAGA,QAAI;AACF,YAAM,EAAE,KAAK,IAAI,MAAM,aAAa,OAAO,OAAO,UAAU,UAAU;AAEtE,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,MAAM,OAAO,KAAK,IAAI,EAAE,SAAS,QAAQ;AAAA,MAC3C;AAAA,IACF,SAAS,OAAO;AASd,YAAM,QACJ,iBAAiB,QAAS,MAAM,SAAS,MAAM,UAAW,OAAO,KAAK;AACxE,cAAQ;AAAA,QACN,8CAA8C,OAAO,SAAS,MAAM,mBACxD,KAAK;AAAA,MACnB;AACA,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,MAAM;AAAA,QACN,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACF;;;AChKA,SAAS,gBAAgB;AA2BlB,IAAM,qBAAN,MAAyD;AAAA,EACtD;AAAA,EACA,cAAc;AAAA,EAEtB,YAAY,aAAuB;AACjC,QAAI,aAAa;AACf,WAAK,cAAc;AACnB,WAAK,cAAc;AAAA,IACrB;AAAA,EACF;AAAA,EAEA,MAAc,YAA8B;AAC1C,QAAI,CAAC,KAAK,aAAa;AAErB,YAAM,EAAE,aAAa,IAAI,MAAM,OAAO,yBAAyB;AAC/D,YAAM,WAAW,MAAM,OAAO,oBAAS,GAAG;AAC1C,YAAM,UAAU,QAAQ,KAAK,CAAC,CAAC;AAC/B,YAAM,MAAM,MAAM,QAAQ,OAAO,YAAY;AAC7C,WAAK,cAAc,aAAa,cAAc,EAAE,YAAY,IAAI,CAAC;AACjE,WAAK,cAAc;AAAA,IACrB;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,OACJ,MACA,MAC2B;AAC3B,UAAM,SAAU,MAAM,KAAK,UAAU;AASrC,UAAM,WAA8C,CAAC;AACrD,QAAI,MAAM;AACR,iBAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAChD,iBAAS,KAAK,EAAE,MAAM,MAAM,CAAC;AAAA,MAC/B;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,OAAO,WAAW;AAAA,MACrC,mBAAmB,MAAM,SAAS,KAAK,IAAI;AAAA,MAC3C,iBAAiB,MAAM,KAAK;AAAA,MAC5B,GAAI,SAAS,SAAS,IAAI,EAAE,cAAc,EAAE,MAAM,SAAS,EAAE,IAAI,CAAC;AAAA,IACpE,CAAC;AAED,WAAO,EAAE,MAAM,OAAO,GAAG;AAAA,EAC3B;AACF;;;ACpDO,IAAM,eAAN,MAAmB;AAAA,EAChB,UAAU,oBAAI,IAAyB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,SAA6B,CAAC,GAAG;AAC3C,SAAK,YAAY,OAAO,aAAa;AACrC,SAAK,mBAAmB,OAAO,oBAAoB;AACnD,SAAK,oBAAoB,OAAO,qBAAqB,KAAK,OAAO;AAAA,EACnE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,SACE,UACA,YACA,aACA,MACgB;AAChB,QAAI,QAAQ,KAAK,QAAQ,IAAI,QAAQ;AAErC,QAAI,CAAC,OAAO;AAEV,UAAI,KAAK,QAAQ,QAAQ,KAAK,kBAAkB;AAC9C,cAAM,IAAI;AAAA,UACR,+BAA+B,KAAK,gBAAgB;AAAA,QACtD;AAAA,MACF;AAGA,YAAM,QAAQ,WAAW,MAAM;AAC7B,aAAK,QAAQ,QAAQ;AAAA,MACvB,GAAG,KAAK,SAAS;AAEjB,cAAQ;AAAA,QACN,QAAQ,oBAAI,IAAI;AAAA,QAChB;AAAA,QACA;AAAA,MACF;AACA,WAAK,QAAQ,IAAI,UAAU,KAAK;AAAA,IAClC;AAGA,QAAI,aAAa,KAAK,cAAc,MAAM,aAAa;AACrD,YAAM,IAAI;AAAA,QACR,cAAc,UAAU,kCAAkC,MAAM,WAAW,cAAc,QAAQ;AAAA,MACnG;AAAA,IACF;AAGA,QAAI,MAAM,OAAO,IAAI,UAAU,GAAG;AAChC,YAAM,IAAI;AAAA,QACR,wBAAwB,UAAU,iBAAiB,QAAQ;AAAA,MAC7D;AAAA,IACF;AAGA,QAAI,eAAe;AACnB,eAAW,SAAS,MAAM,OAAO,OAAO,GAAG;AACzC,sBAAgB,MAAM;AAAA,IACxB;AACA,QAAI,eAAe,KAAK,SAAS,KAAK,mBAAmB;AACvD,WAAK,QAAQ,QAAQ;AACrB,YAAM,IAAI;AAAA,QACR,UAAU,QAAQ,kCAAkC,KAAK,iBAAiB;AAAA,MAC5E;AAAA,IACF;AAGA,UAAM,OAAO,IAAI,YAAY,IAAI;AAGjC,QAAI,MAAM,OAAO,SAAS,MAAM,aAAa;AAE3C,YAAM,gBAA0B,CAAC;AACjC,eAAS,IAAI,GAAG,IAAI,MAAM,aAAa,KAAK;AAC1C,cAAM,QAAQ,MAAM,OAAO,IAAI,CAAC;AAChC,YAAI,CAAC,OAAO;AACV,gBAAM,IAAI,MAAM,iBAAiB,CAAC,aAAa;AAAA,QACjD;AACA,sBAAc,KAAK,KAAK;AAAA,MAC1B;AACA,YAAM,YAAY,OAAO,OAAO,aAAa;AAG7C,WAAK,QAAQ,QAAQ;AAErB,aAAO,EAAE,UAAU,MAAM,UAAU;AAAA,IACrC;AAEA,WAAO,EAAE,UAAU,MAAM;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,WAAW,UAA2B;AACpC,UAAM,QAAQ,KAAK,QAAQ,IAAI,QAAQ;AACvC,QAAI,CAAC,MAAO,QAAO;AACnB,WAAO,MAAM,OAAO,SAAS,MAAM;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ,UAAwB;AAC9B,UAAM,QAAQ,KAAK,QAAQ,IAAI,QAAQ;AACvC,QAAI,OAAO;AACT,mBAAa,MAAM,KAAK;AACxB,WAAK,QAAQ,OAAO,QAAQ;AAAA,IAC9B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,aAAmB;AACjB,eAAW,CAAC,EAAE,KAAK,KAAK,KAAK,SAAS;AACpC,mBAAa,MAAM,KAAK;AAAA,IAC1B;AACA,SAAK,QAAQ,MAAM;AAAA,EACrB;AACF;;;AC9JA,SAAS,kBAAkB;AAE3B,SAAS,+BAA+B;AAqDxC,eAAsB,WACpB,MACA,MACA,aACA,SACiB;AACjB,QAAM,cAAc,QAAQ,eAAe;AAC3C,QAAM,eAAe,QAAQ,gBAAgB;AAC7C,QAAM,SAAS,OAAO,KAAK,MAAM,IAAI;AAErC,QAAM,QAAQ;AAAA,IACZ;AAAA,MACE,UAAU;AAAA,MACV;AAAA,MACA,KAAK,OAAO,SAAS;AAAA,IACvB;AAAA,IACA,QAAQ;AAAA,EACV;AAEA,QAAM,SAAS,MAAM,KAAK,aAAa,OAAO,EAAE,aAAa,OAAO,CAAC;AAErE,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,IAAI,MAAM,uBAAuB,OAAO,WAAW,eAAe,EAAE;AAAA,EAC5E;AAIA,MAAI,OAAO,MAAM;AACf,WAAO,OAAO,KAAK,OAAO,MAAM,QAAQ,EAAE,SAAS,OAAO;AAAA,EAC5D;AACA,SAAO,OAAO;AAChB;AAeA,eAAsB,kBACpB,MACA,MACA,aACA,SACiB;AACjB,MAAI,KAAK,WAAW,GAAG;AACrB,UAAM,IAAI,MAAM,6CAA6C;AAAA,EAC/D;AAEA,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,cAAc,QAAQ,eAAe;AAC3C,QAAM,eAAe,QAAQ,gBAAgB;AAC7C,QAAM,WAAW,WAAW;AAG5B,QAAM,cAAc,KAAK,KAAK,KAAK,SAAS,SAAS;AACrD,MAAI;AAEJ,WAAS,IAAI,GAAG,IAAI,aAAa,KAAK;AACpC,UAAM,QAAQ,IAAI;AAClB,UAAM,MAAM,KAAK,IAAI,QAAQ,WAAW,KAAK,MAAM;AACnD,UAAM,YAAY,KAAK,SAAS,OAAO,GAAG;AAC1C,UAAM,SAAS,OAAO,UAAU,MAAM,IAAI;AAE1C,UAAM,QAAQ;AAAA,MACZ;AAAA,QACE,UAAU,OAAO,KAAK,SAAS;AAAA,QAC/B;AAAA,QACA,KAAK,OAAO,SAAS;AAAA,QACrB,QAAQ;AAAA,UACN,EAAE,KAAK,YAAY,OAAO,SAAS;AAAA,UACnC,EAAE,KAAK,cAAc,OAAO,OAAO,CAAC,EAAE;AAAA,UACtC,EAAE,KAAK,eAAe,OAAO,OAAO,WAAW,EAAE;AAAA,QACnD;AAAA,MACF;AAAA,MACA,QAAQ;AAAA,IACV;AAEA,iBAAa,MAAM,KAAK,aAAa,OAAO,EAAE,aAAa,OAAO,CAAC;AAEnE,QAAI,CAAC,WAAW,SAAS;AACvB,YAAM,IAAI;AAAA,QACR,SAAS,CAAC,IAAI,WAAW,mBAAmB,WAAW,WAAW,eAAe;AAAA,MACnF;AAAA,IACF;AAAA,EACF;AAIA,MAAI,CAAC,YAAY;AACf,UAAM,IAAI,MAAM,yBAAyB;AAAA,EAC3C;AACA,MAAI,WAAW,MAAM;AACnB,WAAO,OAAO,KAAK,WAAW,MAAM,QAAQ,EAAE,SAAS,OAAO;AAAA,EAChE;AACA,SAAO,WAAW;AACpB;;;ACtHA,SAAS,aAAAC,kBAAiB;AAYnB,IAAM,sBAAN,cAAkCC,WAAU;AAAA,EACjD,YAAY,SAAiB,OAAe;AAC1C,UAAM,SAAS,yBAAyB,KAAK;AAC7C,SAAK,OAAO;AAAA,EACd;AACF;AA0CA,IAAM,qBAAqB;AAGpB,SAAS,sBACd,OAC8B;AAC9B,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,QAAM,IAAI;AACV,SACE,EAAE,GAAG,MAAM,KACX,OAAO,EAAE,OAAO,MAAM,YACtB,mBAAmB,KAAK,EAAE,OAAO,CAAW,KAC5C,OAAO,EAAE,GAAG,MAAM,YAClB,OAAO,UAAU,EAAE,GAAG,CAAC,KACtB,EAAE,GAAG,KAAgB,KACtB,OAAO,EAAE,OAAO,MAAM,YACtB,OAAO,SAAS,EAAE,OAAO,CAAC,KACzB,EAAE,OAAO,KAAgB,KAC1B,OAAO,EAAE,SAAS,MAAM,YACxB,OAAO,SAAS,EAAE,SAAS,CAAC,KAC3B,EAAE,SAAS,KAAgB,KAC5B,OAAO,EAAE,aAAa,MAAM,YAC5B,OAAO,UAAU,EAAE,aAAa,CAAC,KAChC,EAAE,aAAa,KAAgB,KAChC,OAAO,EAAE,YAAY,MAAM,cAC1B,EAAE,aAAa,MAAM,WAAW,EAAE,aAAa,MAAM,aACtD,OAAO,EAAE,WAAW,MAAM;AAE9B;AAUO,SAAS,uBAAuB,QAG5B;AACT,QAAM,EAAE,aAAa,KAAK,IAAI;AAC9B,QAAM,OAAO,GAAG,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,KAAK;AACtD,QAAM,KAAK,GAAG,KAAK,GAAG,SAAS,IAAI,KAAK,GAAG,KAAK;AAChD,SAAO,GAAG,KAAK,KAAK,KAAK,IAAI,WAAW,IAAI,IAAI,IAAI,EAAE;AACxD;AAmBO,IAAM,mCAAN,MAA2E;AAAA,EAC/D,SAAS,oBAAI,IAAiC;AAAA,EAE/D,MAAM,KAAK,KAAuD;AAChE,UAAM,MAAM,KAAK,OAAO,IAAI,GAAG;AAC/B,WAAO,QAAQ,SAAY,SAAY,EAAE,GAAG,IAAI;AAAA,EAClD;AAAA,EAEA,MAAM,KAAK,KAAa,OAA2C;AACjE,SAAK,OAAO,IAAI,KAAK,EAAE,GAAG,MAAM,CAAC;AAAA,EACnC;AACF;AAUO,IAAM,mCAAN,MAA2E;AAAA,EAChF,YAA6B,UAAkB;AAAlB;AAC3B,QAAI,OAAO,aAAa,YAAY,SAAS,WAAW,GAAG;AACzD,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,UAA4C;AACxD,UAAM,KAAK,MAAM,OAAO,aAAkB;AAC1C,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,GAAG,SAAS,KAAK,UAAU,MAAM;AAAA,IAC/C,SAAS,KAAK;AACZ,UAAK,IAA8B,SAAS,SAAU,QAAO,CAAC;AAC9D,YAAM,IAAI;AAAA,QACR,wCAAwC,KAAK,QAAQ;AAAA,QACrD,eAAe,QAAQ,MAAM;AAAA,MAC/B;AAAA,IACF;AACA,QAAI;AACF,YAAM,SAAkB,KAAK,MAAM,GAAG;AACtC,UAAI,UAAU,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,GAAG;AAClE,eAAO;AAAA,MACT;AAAA,IACF,QAAQ;AAAA,IAGR;AACA,WAAO,CAAC;AAAA,EACV;AAAA,EAEA,MAAM,KAAK,KAAuD;AAChE,UAAM,MAAM,MAAM,KAAK,QAAQ;AAC/B,UAAM,YAAY,IAAI,GAAG;AACzB,WAAO,sBAAsB,SAAS,IAAI,EAAE,GAAG,UAAU,IAAI;AAAA,EAC/D;AAAA,EAEA,MAAM,KAAK,KAAa,OAA2C;AACjE,UAAM,KAAK,MAAM,OAAO,aAAkB;AAC1C,UAAM,OAAO,MAAM,OAAO,MAAW;AACrC,UAAM,MAAM,MAAM,KAAK,QAAQ;AAC/B,QAAI,GAAG,IAAI,EAAE,GAAG,MAAM;AACtB,UAAM,MAAM,KAAK,QAAQ,KAAK,QAAQ;AACtC,UAAM,GAAG,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACvC,UAAM,MAAM,GAAG,KAAK,QAAQ,IAAI,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC;AACzD,UAAM,GAAG,UAAU,KAAK,KAAK,UAAU,KAAK,MAAM,CAAC,GAAG,MAAM;AAC5D,UAAM,GAAG,OAAO,KAAK,KAAK,QAAQ;AAAA,EACpC;AACF;AAyHA,IAAM,uCAAuC;AAC7C,IAAM,qBAAqB;AAC3B,IAAM,8BAA8B;AACpC,IAAM,6BAA6B;AACnC,IAAM,qBAAqB;AAE3B,IAAM,YAAY;AAClB,IAAM,gBAAgB;AAGtB,SAAS,aAAa,MAAsB;AAC1C,QAAM,IAAI,OAAO,IAAI;AACrB,SAAO,OAAO,SAAS,CAAC,KAAK,IAAI,IAAI,IAAI;AAC3C;AAUO,IAAM,0BAAN,MAAM,yBAAgE;AAAA;AAAA,EAElE;AAAA,EAEQ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA;AAAA,EAET;AAAA;AAAA,EAEA,SAAwB;AAAA;AAAA,EAExB,WAA0B;AAAA,EAC1B,oBAAmC;AAAA,EAEnC,YACN,QACA,KACA,OACA,OACA;AACA,SAAK,MAAM;AACX,SAAK,QAAQ;AACb,SAAK,OAAO,OAAO;AACnB,UAAM,kBACJ,OAAO,6BAA6B;AACtC,SAAK,UAAU,mBAAmB,OAAO,mBAAmB;AAC5D,SAAK,kBAAkB,OAAO;AAC9B,SAAK,kBAAkB,OAAO,mBAAmB;AACjD,SAAK,YAAY,OAAO,aAAa;AACrC,SAAK,oBACH,OAAO,qBAAqB;AAC9B,SAAK,mBAAmB;AAAA,MACtB,OAAO,oBAAoB;AAAA,IAC7B;AACA,SAAK,QAAQ,OAAO,aAAa;AACjC,SAAK,MAAM,OAAO,OAAO,KAAK;AAC9B,SAAK,gBAAgB;AACrB,SAAK,QAAQ,OAAO,MAAM,KAAK;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,aAAa,OACX,QACkC;AAClC,mBAAe,MAAM;AACrB,UAAM,MAAM,uBAAuB;AAAA,MACjC,aAAa,OAAO;AAAA,MACpB,MAAM,OAAO;AAAA,IACf,CAAC;AACD,UAAM,QAAQ,OAAO,SAAS,IAAI,iCAAiC;AACnE,UAAM,MAAM,OAAO,OAAO,KAAK;AAC/B,UAAM,SAAS,MAAM,MAAM,KAAK,GAAG;AACnC,UAAM,QAA6B,sBAAsB,MAAM,IAC3D,SACA;AAAA,MACE,GAAG;AAAA,MACH,OAAO;AAAA,MACP,GAAG;AAAA,MACH,OAAO;AAAA,MACP,SAAS;AAAA,MACT,aAAa;AAAA,MACb,YAAY;AAAA;AAAA,MAEZ,aAAa;AAAA,MACb,WAAW,IAAI;AAAA,IACjB;AACJ,WAAO,IAAI,yBAAwB,QAAQ,KAAK,OAAO,KAAK;AAAA,EAC9D;AAAA;AAAA,EAGA,IAAI,QAA6B;AAC/B,WAAO,EAAE,GAAG,KAAK,eAAe,OAAO,KAAK,MAAM,SAAS,EAAE;AAAA,EAC/D;AAAA;AAAA,EAGA,IAAI,SAAiB;AACnB,WAAO,KAAK,cAAc;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,mBAA2B;AAC7B,UAAM,OAAO,KAAK,cAAc,QAAQ,KAAK,cAAc;AAC3D,QAAI,EAAE,OAAO,GAAI,QAAO;AACxB,WAAO,KAAK,UAAU;AAAA,EACxB;AAAA;AAAA,EAGQ,iBAAiB,WAA2B;AAClD,UAAM,OAAO,KAAK;AAClB,QAAI,CAAC,OAAO,SAAS,IAAI,KAAK,QAAQ,EAAG,QAAO;AAChD,QAAI,QAAQ,EAAG,QAAO,KAAK;AAC3B,UAAM,SAAS,OAAO,KAAK,MAAM,OAAO,aAAa,CAAC;AACtD,WAAQ,YAAY,SAAU;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,UAAU,WAA2B;AACnC,QAAI,OAAO,cAAc,YAAY,aAAa,GAAI,QAAO;AAE7D,QAAI,KAAK,WAAW,MAAM;AAGxB,UAAI,KAAK,YAAY,KAAK;AAC1B,YAAMC,UAAS,KAAK,iBAAiB,SAAS;AAC9C,UAAI,KAAKA,QAAQ,MAAKA;AACtB,UAAI,KAAK,oBAAoB,UAAa,KAAK,KAAK,iBAAiB;AACnE,aAAK,KAAK;AAAA,MACZ;AACA,UAAI,KAAK,KAAK,gBAAiB,MAAK,KAAK;AACzC,WAAK,SAAS;AACd,UAAI,KAAK,SAAS,IAAI;AAEpB,aAAK,QAAQ;AACb,aAAK,cAAc,QAAQ,GAAG,SAAS;AAAA,MACzC;AAAA,IACF;AAEA,QAAI,IAAI,KAAK;AACb,UAAM,SAAS,KAAK,iBAAiB,SAAS;AAC9C,QAAI,IAAI,OAAQ,KAAI;AACpB,QAAI,KAAK,oBAAoB,UAAa,IAAI,KAAK,iBAAiB;AAClE,UAAI,KAAK;AAAA,IACX;AACA,QAAI,IAAI,UAAW,KAAI;AACvB,UAAM,QACJ,KAAK,kBAAkB,YAAY,KAAK,kBAAkB;AAC5D,QAAI,IAAI,MAAO,KAAI;AACnB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,QAAQ,aAA+C;AAC3D,UAAM,IAAI,KAAK;AAGf,QACE,OAAO,YAAY,UAAU,YAC7B,OAAO,SAAS,YAAY,KAAK,KACjC,YAAY,QAAQ,GACpB;AACA,YAAM,SAAS,YAAY,QAAQ;AACnC,QAAE,UACA,EAAE,YAAY,IACV,SACA,KAAK,QAAQ,UAAU,IAAI,KAAK,SAAS,EAAE;AAAA,IACnD;AACA,QACE,OAAO,YAAY,SAAS,YAC5B,OAAO,YAAY,kBAAkB,YACrC,OAAO,SAAS,YAAY,aAAa,GACzC;AACA,YAAM,IAAI,aAAa,YAAY,IAAI;AACvC,UAAI,CAAC,OAAO,MAAM,CAAC,GAAG;AACpB,YACE,KAAK,aAAa,QAClB,KAAK,sBAAsB,QAC3B,YAAY,gBAAgB,KAAK,mBACjC;AACA,gBAAM,SACH,YAAY,gBAAgB,KAAK,qBAAqB;AACzD,gBAAM,OAAO,KAAK,IAAI,IAAI,KAAK,QAAQ,IAAI,KAAK,WAAW;AAC3D,cAAI,OAAO,SAAS,IAAI,GAAG;AACzB,cAAE,QACA,EAAE,UAAU,IACR,OACA,KAAK,QAAQ,QAAQ,IAAI,KAAK,SAAS,EAAE;AAAA,UACjD;AAAA,QACF;AACA,YACE,KAAK,sBAAsB,QAC3B,YAAY,iBAAiB,KAAK,mBAClC;AACA,eAAK,WAAW;AAChB,eAAK,oBAAoB,YAAY;AAAA,QACvC;AAAA,MACF;AAAA,IACF;AAGA,UAAM,OAAO,KAAK,aAAa,WAAW;AAC1C,UAAM,WACJ,YAAY,eAAe,aAAa,OAAO,KAAK;AAGtD,QAAI,UAAU;AACZ,UAAI,YAAY,eAAe,WAAW;AAExC,UAAE,IAAI,KAAK,IAAI,GAAG,KAAK,KAAK,EAAE,IAAI,CAAC,CAAC;AAAA,MACtC,OAAO;AAEL,YAAI,IAAI,KAAK,QAAQ;AACrB,YAAI,IAAI,KAAK,gBAAiB,KAAI,KAAK;AACvC,aAAK,QAAQ;AACb,UAAE,QAAQ,EAAE,SAAS;AAAA,MACvB;AACA,QAAE,cAAc;AAChB,QAAE,aAAa;AAAA,IACjB,OAAO;AACL,QAAE,eAAe;AACjB,UAAI,EAAE,eAAe,KAAK,mBAAmB;AAC3C,UAAE,cAAc;AAChB,cAAM,OACJ,EAAE,gBAAgB,UAAU,WAAW;AACzC,YAAI,SAAS,UAAU;AACrB,YAAE,IAAI,KAAK,IAAI,KAAK,WAAW,EAAE,IAAI,CAAC;AAAA,QACxC,OAAO;AAGL,gBAAM,YAAY,KAAK,UAAU,KAAK;AACtC,cAAI,IAAI,EAAE,aAAa,KAAK,QAAQ,YAAY,KAAK,QAAQ;AAC7D,cAAI,KAAK,oBAAoB,UAAa,IAAI,KAAK,iBAAiB;AAClE,gBAAI,KAAK;AAAA,UACX;AACA,cACE,YAAY,cAAc,UAC1B,YAAY,YAAY,IACxB;AACA,kBAAM,SAAS,KAAK,iBAAiB,YAAY,SAAS;AAC1D,gBAAI,IAAI,OAAQ,KAAI;AAAA,UACtB;AACA,cAAI,IAAI,KAAK,gBAAiB,KAAI,KAAK;AAEvC,cAAI,IAAI,KAAK,MAAO,KAAI,KAAK;AAC7B,eAAK,QAAQ;AACb,YAAE,QAAQ,EAAE,SAAS;AAAA,QACvB;AACA,UAAE,cAAc;AAAA,MAClB;AAAA,IACF;AAEA,MAAE,YAAY,KAAK,IAAI;AACvB,UAAM,KAAK,MAAM,KAAK,KAAK,KAAK,EAAE,GAAG,EAAE,CAAC;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,aAAa,aAAwC;AAC3D,QACE,YAAY,eAAe,aAC3B,YAAY,SAAS,UACrB,YAAY,iBAAiB,UAC7B,YAAY,iBAAiB,UAC7B,YAAY,gBAAgB,IAC5B;AACA,aAAO;AAAA,IACT;AACA,QAAI;AACJ,QAAI;AACF,iBAAW,UAAU;AAAA,QACnB,cAAc,YAAY;AAAA,QAC1B,WAAW,KAAK,KAAK,KAAK;AAAA,QAC1B,SAAS,KAAK,KAAK,GAAG;AAAA,QACtB,MAAM,YAAY;AAAA,MACpB,CAAC;AAAA,IACH,QAAQ;AACN,aAAO;AAAA,IACT;AACA,QAAI,YAAY,MAAM,YAAY,gBAAgB,SAAU,QAAO;AACnE,UAAM,YAAY,WAAW,YAAY;AACzC,WAAO,OAAQ,YAAY,WAAc,QAAQ,IAAI;AAAA,EACvD;AACF;AAEA,SAAS,eAAe,QAA6C;AACnE,MACE,OAAO,OAAO,gBAAgB,YAC9B,OAAO,YAAY,WAAW,GAC9B;AACA,UAAM,IAAI,oBAAoB,wCAAwC;AAAA,EACxE;AACA,MACE,CAAC,OAAO,QACR,OAAO,OAAO,SAAS,YACvB,CAAC,OAAO,KAAK,QACb,CAAC,OAAO,KAAK,MACb,OAAO,OAAO,KAAK,KAAK,UAAU,YAClC,OAAO,OAAO,KAAK,GAAG,UAAU,UAChC;AACA,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MACE,OAAO,OAAO,qBAAqB,YACnC,CAAC,OAAO,SAAS,OAAO,gBAAgB,KACxC,OAAO,oBAAoB,GAC3B;AACA,UAAM,IAAI;AAAA,MACR,4DAA4D;AAAA,QAC1D,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,EACF;AACA,MACE,OAAO,8BAA8B,WACpC,OAAO,OAAO,8BAA8B,YAC3C,CAAC,OAAO,SAAS,OAAO,yBAAyB,KACjD,OAAO,6BAA6B,IACtC;AACA,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MACE,OAAO,oBAAoB,WAC1B,OAAO,OAAO,oBAAoB,YAAY,OAAO,mBAAmB,KACzE;AACA,UAAM,IAAI,oBAAoB,2CAA2C;AAAA,EAC3E;AACA,MACE,OAAO,oBAAoB,WAC1B,OAAO,OAAO,oBAAoB,YAAY,OAAO,mBAAmB,KACzE;AACA,UAAM,IAAI,oBAAoB,2CAA2C;AAAA,EAC3E;AACA,MACE,OAAO,oBAAoB,UAC3B,OAAO,oBAAoB,UAC3B,OAAO,kBAAkB,OAAO,iBAChC;AACA,UAAM,IAAI,oBAAoB,4CAA4C;AAAA,EAC5E;AACA,MACE,OAAO,cAAc,WACpB,CAAC,OAAO,UAAU,OAAO,SAAS,KAAK,OAAO,YAAY,IAC3D;AACA,UAAM,IAAI,oBAAoB,mCAAmC;AAAA,EACnE;AACA,MACE,OAAO,sBAAsB,WAC5B,CAAC,OAAO,UAAU,OAAO,iBAAiB,KACzC,OAAO,oBAAoB,IAC7B;AACA,UAAM,IAAI,oBAAoB,2CAA2C;AAAA,EAC3E;AACA,MACE,OAAO,qBAAqB,WAC3B,CAAC,OAAO,UAAU,OAAO,gBAAgB,KAAK,OAAO,mBAAmB,IACzE;AACA,UAAM,IAAI,oBAAoB,0CAA0C;AAAA,EAC1E;AACA,MACE,OAAO,cAAc,WACpB,OAAO,OAAO,cAAc,YAC3B,EAAE,OAAO,YAAY,MACrB,OAAO,YAAY,IACrB;AACA,UAAM,IAAI,oBAAoB,6BAA6B;AAAA,EAC7D;AACF;;;ACzvBA,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB;;;ACO3B;AAAA,EACE,cAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;ADIP,IAAM,oCACJ;AACF,IAAM,iCACJ;AAGK,IAAM,mCAA+C;AAAA,EAC1D,IAAI,YAAY,EAAE,OAAO,iCAAiC;AAC5D,EAAE,MAAM,GAAG,CAAC;AAEZ,IAAM,6BACJ,OACA;AAAA,EACE,WAAW,IAAI,YAAY,EAAE,OAAO,8BAA8B,CAAC;AACrE;AAkBK,SAAS,wBAAwB,OAAiC;AACvE,MACE,MAAM,cAAc,UACpB,MAAM,qBAAqB,UAC3B,MAAM,UAAU,UAChB,MAAM,cAAc,QACpB;AACA,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,MAAI,MAAM,WAAW,WAAW,IAAI;AAClC,UAAM,IAAI;AAAA,MACR;AAAA,MACA,iDAAiD,MAAM,WAAW,MAAM;AAAA,IAC1E;AAAA,EACF;AACA,QAAM,IAAI,MAAM,WAAW,EAAE;AAC7B,MAAI,MAAM,MAAM,MAAM,IAAI;AACxB,UAAM,IAAI;AAAA,MACR;AAAA,MACA,yCAAyC,CAAC;AAAA,IAC5C;AAAA,EACF;AACA,QAAM,WAAW,IAAI;AACrB,QAAM,YAAY,MAAM,WAAW,MAAM,GAAG,EAAE;AAE9C,MAAI;AACJ,MAAI;AACJ,MAAI;AACF,UAAM,iBAAiBC,YAAW,MAAM,SAAS;AACjD,UAAM,iBAAiBA,YAAW,MAAM,SAAS;AACjD,QAAI,eAAe,WAAW,IAAI;AAChC,YAAM,IAAI;AAAA,QACR,mCAAmC,eAAe,MAAM;AAAA,MAC1D;AAAA,IACF;AACA,QAAI,eAAe,WAAW,IAAI;AAChC,YAAM,IAAI;AAAA,QACR,mCAAmC,eAAe,MAAM;AAAA,MAC1D;AAAA,IACF;AACA,cAAU;AAAA,MACR;AAAA,MACA,OAAO,MAAM,gBAAgB;AAAA,MAC7B,OAAO,MAAM,KAAK;AAAA,MAClB;AAAA,IACF;AACA,UAAM,MAAM,UAAU,UAAU;AAAA,MAC9B;AAAA,MACA;AAAA,IACF,EAAE,eAAe,QAAQ;AACzB,UAAM,QAAQ,IAAI,iBAAiB,OAAO;AAC1C,yBAAqB,MAAM,QAAQ,KAAK;AAAA,EAC1C,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,MACA,+BAA+B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MAC/E,EAAE,OAAO,IAAI;AAAA,IACf;AAAA,EACF;AAIA,QAAM,WAAW,WAAW,mBAAmB,MAAM,CAAC,CAAC;AACvD,SAAO,OAAO,WAAW,SAAS,MAAM,GAAG,CAAC,EAAE,YAAY;AAC5D;AAMA,SAAS,UAAU,OAA+B;AAChD,MAAI,MAAM,SAAS,IAAI;AACrB,UAAM,IAAI;AAAA,MACR;AAAA,MACA,8BAA8B,MAAM,MAAM;AAAA,IAC5C;AAAA,EACF;AACA,QAAM,MAAM,IAAI,WAAW,EAAE;AAC7B,MAAI,IAAI,OAAO,KAAK,MAAM,MAAM;AAChC,SAAO;AACT;AAEA,SAAS,WAAW,OAA+B;AACjD,QAAM,SAAS,KAAK,KAAK,MAAM,SAAS,EAAE,IAAI;AAC9C,QAAM,MAAM,IAAI,WAAW,MAAM;AACjC,MAAI,IAAI,OAAO,CAAC;AAChB,SAAO;AACT;AAEA,SAAS,4BACP,gBACA,kBACA,OACA,gBACA,WACY;AACZ,MAAI,eAAe,WAAW,IAAI;AAChC,UAAM,IAAI;AAAA,MACR;AAAA,MACA,mCAAmC,eAAe,MAAM;AAAA,IAC1D;AAAA,EACF;AACA,MAAI,eAAe,WAAW,IAAI;AAChC,UAAM,IAAI;AAAA,MACR;AAAA,MACA,mCAAmC,eAAe,MAAM;AAAA,IAC1D;AAAA,EACF;AAOA,QAAM,gBAAgB;AACtB,QAAM,iBAAiB,kBAAkB,gBAAgB;AACzD,QAAM,YAAY,kBAAkB,KAAK;AACzC,QAAM,gBAAgB,UAAU,cAAc;AAC9C,QAAM,aAAa,kBAAkB,IAAI;AACzC,QAAM,aAAa,kBAAkB,OAAO,UAAU,MAAM,CAAC;AAC7D,QAAM,YAAY,WAAW,SAAS;AAEtC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAMA,SAAS,eAAe,OAA+B;AACrD,MAAI,MAAM,WAAW,KAAK,MAAM,CAAC,MAAM,UAAa,MAAM,CAAC,IAAI,KAAM;AACnE,WAAO;AAAA,EACT;AACA,MAAI,MAAM,SAAS,IAAI;AACrB,WAAO,YAAY,IAAI,WAAW,CAAC,MAAO,MAAM,MAAM,CAAC,GAAG,KAAK;AAAA,EACjE;AACA,QAAM,WAAW,qBAAqB,OAAO,MAAM,MAAM,CAAC;AAC1D,SAAO,YAAY,IAAI,WAAW,CAAC,MAAO,SAAS,MAAM,CAAC,GAAG,UAAU,KAAK;AAC9E;AAEA,SAAS,cAAc,OAAiC;AACtD,QAAM,UAAU,YAAY,GAAG,KAAK;AACpC,MAAI,QAAQ,SAAS,IAAI;AACvB,WAAO,YAAY,IAAI,WAAW,CAAC,MAAO,QAAQ,MAAM,CAAC,GAAG,OAAO;AAAA,EACrE;AACA,QAAM,WAAW,qBAAqB,OAAO,QAAQ,MAAM,CAAC;AAC5D,SAAO;AAAA,IACL,IAAI,WAAW,CAAC,MAAO,SAAS,MAAM,CAAC;AAAA,IACvC;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,qBAAqB,GAAuB;AACnD,MAAI,IAAI,IAAI;AACV,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,MAAI,MAAM,GAAI,QAAO,IAAI,WAAW,CAAC;AACrC,MAAI,MAAM,EAAE,SAAS,EAAE;AACvB,MAAI,IAAI,SAAS,EAAG,OAAM,MAAM;AAChC,QAAM,MAAM,IAAI,WAAW,IAAI,SAAS,CAAC;AACzC,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,QAAI,CAAC,IAAI,SAAS,IAAI,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE;AAAA,EACnD;AACA,SAAO;AACT;AAEA,SAAS,cAAc,GAAuB;AAC5C,SAAO,eAAe,qBAAqB,CAAC,CAAC;AAC/C;AAMA,SAAS,oBAAoB,QAQd;AACb,SAAO,cAAc;AAAA,IACnB,cAAc,OAAO,KAAK;AAAA,IAC1B,cAAc,OAAO,QAAQ;AAAA,IAC7B,cAAc,OAAO,QAAQ;AAAA,IAC7B,eAAe,OAAO,EAAE;AAAA,IACxB,cAAc,OAAO,KAAK;AAAA,IAC1B,eAAe,OAAO,IAAI;AAAA,IAC1B,cAAc,OAAO,OAAO;AAAA,IAC5B,cAAc,EAAE;AAAA,IAChB,cAAc,EAAE;AAAA,EAClB,CAAC;AACH;AAiBO,SAAS,qBACd,QACA,QACA,WACA,oBACA,cACkB;AAClB,MACE,OAAO,cAAc,UACrB,OAAO,qBAAqB,UAC5B,OAAO,UAAU,UACjB,OAAO,cAAc,UACrB,OAAO,sBAAsB,QAC7B;AACA,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,OAAO,iBAAiB;AAC3B,UAAM,IAAI;AAAA,MACR;AAAA,MACA,8DAA8D,OAAO,KAAK,GAAG,KAAK;AAAA,IACpF;AAAA,EACF;AACA,MACE,OAAO,OAAO,YAAY,YAC1B,CAAC,OAAO,UAAU,OAAO,OAAO,KAChC,OAAO,WAAW,GAClB;AACA,UAAM,IAAI;AAAA,MACR;AAAA,MACA,gEAAgE,OAAO,OAAO;AAAA,IAChF;AAAA,EACF;AAEA,QAAM,iBAAiBA,YAAW,OAAO,SAAS;AAClD,QAAM,iBAAiBA,YAAW,SAAS;AAC3C,QAAM,gBAAgBA,YAAW,OAAO,eAAe;AAEvD,QAAM,WAAW;AAAA,IACf;AAAA,IACA,OAAO,OAAO,gBAAgB;AAAA,IAC9B,OAAO,OAAO,KAAK;AAAA,IACnB;AAAA,IACA,OAAO;AAAA,EACT;AAEA,QAAM,kBAAkB,oBAAoB;AAAA,IAC1C,OAAO;AAAA,IACP,UAAU;AAAA,IACV,UAAU;AAAA,IACV,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,MAAM;AAAA,IACN,SAAS,OAAO,OAAO,OAAO;AAAA,EAChC,CAAC;AAED,SAAO;AAAA,IACL,OAAO,OAAO,KAAK,GAAG;AAAA,IACtB,WAAW;AAAA,IACX,WAAW,OAAO;AAAA,IAClB,kBAAkB,OAAO;AAAA,IACzB,OAAO,OAAO;AAAA,IACd;AAAA,IACA,mBAAmB,OAAO;AAAA,IAC1B;AAAA,IACA,wBAAwB;AAAA,IACxB;AAAA,IACA;AAAA,IACA,aAAa,OAAO,KAAK,KAAK;AAAA,IAC9B,iBAAiB,OAAO,KAAK,KAAK;AAAA,EACpC;AACF;AAcO,SAAS,uBACd,QACA,KACA,QACY;AACZ,MAAI,OAAO,cAAc,OAAO;AAC9B,UAAM,IAAI;AAAA,MACR;AAAA,MACA,sDAAsD,OAAO,SAAS;AAAA,IACxE;AAAA,EACF;AACA,MAAI,CAAC,OAAO,iBAAiB;AAC3B,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,MAAI,OAAO,OAAO,YAAY,YAAY,OAAO,WAAW,GAAG;AAC7D,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAIA,QAAM,iBAAiBA,YAAW,OAAO,SAAS;AAClD,QAAM,iBAAiBA,YAAW,OAAO,SAAS;AAClD,QAAM,gBAAgBA,YAAW,OAAO,eAAe;AAOvD,QAAM,MAAM,2BAA2B,MAAM;AAC7C,QAAM,WAAW;AAAA,IACf;AAAA,IACA,OAAO,OAAO,gBAAgB;AAAA,IAC9B,OAAO,OAAO,KAAK;AAAA,IACnB;AAAA,IACA;AAAA,EACF;AACA,SAAO,oBAAoB;AAAA,IACzB,OAAO,IAAI;AAAA,IACX,UAAU,IAAI;AAAA,IACd,UAAU,IAAI;AAAA,IACd,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,MAAM;AAAA,IACN,SAAS,OAAO,OAAO,OAAO;AAAA,EAChC,CAAC;AACH;AAQA,SAAS,2BAA2B,QAAsC;AAGxE,QAAM,KAAK,OAAO;AAClB,QAAM,OAAO,cAAc,EAAE;AAC7B,MAAI,KAAK,SAAS,GAAG;AACnB,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,QAAM,OAAO,KAAK,CAAC;AACnB,MAAI,CAAC,MAAM;AACT,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,eAAe,IAAI,IAAI;AAC7B,MAAI,KAAK,SAAS,eAAe,IAAI;AACnC,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,QAAM,cAAc,KAAK,MAAM,cAAc,eAAe,EAAE;AAC9D,MAAI,SAAS;AACb,aAAW,KAAK,YAAa,UAAU,UAAU,KAAM,OAAO,CAAC;AAC/D,QAAM,WAAW,eAAe;AAChC,QAAM,SAAS,WAAW,OAAO,MAAM;AACvC,MAAI,SAAS,KAAK,QAAQ;AACxB,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,SAAO,KAAK,MAAM,UAAU,MAAM;AACpC;AAGA,SAAS,cAAc,KAA+B;AACpD,MAAI,IAAI,WAAW,GAAG;AACpB,UAAM,IAAI,kBAAkB,mBAAmB,iBAAiB;AAAA,EAClE;AACA,QAAM,QAAQ,IAAI,CAAC,KAAK;AACxB,MAAI;AACJ,MAAI;AACJ,MAAI,SAAS,OAAQ,SAAS,KAAM;AAClC,aAAS;AACT,cAAU,KAAK,QAAQ;AAAA,EACzB,WAAW,SAAS,OAAQ,SAAS,KAAM;AACzC,UAAM,WAAW,QAAQ;AACzB,QAAI,UAAU;AACd,aAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,gBAAW,WAAW,KAAM,IAAI,IAAI,CAAC,KAAK;AAAA,IAC5C;AACA,aAAS,IAAI;AACb,cAAU,SAAS;AAAA,EACrB,OAAO;AACL,UAAM,IAAI;AAAA,MACR;AAAA,MACA,oDAAoD,MAAM,SAAS,EAAE,CAAC;AAAA,IACxE;AAAA,EACF;AACA,QAAM,QAAsB,CAAC;AAC7B,MAAI,IAAI;AACR,SAAO,IAAI,SAAS;AAClB,UAAM,IAAI,IAAI,CAAC,KAAK;AACpB,QAAI,IAAI,KAAM;AACZ,YAAM,KAAK,IAAI,MAAM,GAAG,IAAI,CAAC,CAAC;AAC9B,WAAK;AAAA,IACP,WAAW,KAAK,KAAM;AACpB,YAAM,MAAM,IAAI;AAChB,YAAM,KAAK,IAAI,MAAM,IAAI,GAAG,IAAI,IAAI,GAAG,CAAC;AACxC,WAAK,IAAI;AAAA,IACX,WAAW,KAAK,KAAM;AACpB,YAAM,WAAW,IAAI;AACrB,UAAI,MAAM;AACV,eAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,cAAO,OAAO,KAAM,IAAI,IAAI,IAAI,CAAC,KAAK;AAAA,MACxC;AACA,YAAM,KAAK,IAAI,MAAM,IAAI,IAAI,UAAU,IAAI,IAAI,WAAW,GAAG,CAAC;AAC9D,WAAK,IAAI,WAAW;AAAA,IACtB,OAAO;AACL,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAQO,SAAS,wBACd,OACA,iBACuC;AACvC,QAAM,YAAY,wBAAwB,KAAK;AAC/C,QAAM,WAAW,gBAAgB,YAAY;AAC7C,SAAO;AAAA,IACL,OAAO,UAAU,YAAY,MAAM;AAAA,IACnC;AAAA,EACF;AACF;;;AEzeA,IAAM,eAAe;AAiCrB,eAAsB,uBAEpB;AACA,MAAI;AAIF,UAAM,MAAW,MAAM,OAAO,aAAa;AAC3C,UAAM,OAA6B,aAAa,MAAM,IAAI,UAAU;AACpE,WAAO,IAAI,KAAK,EAAE,SAAS,aAAa,CAAC;AAAA,EAC3C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAqBO,SAAS,oBACd,OACA,uBACA,QACS;AACT,MACE,MAAM,cAAc,UACpB,MAAM,qBAAqB,UAC3B,MAAM,UAAU,UAChB,MAAM,cAAc,QACpB;AACA,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,MAAI,MAAM,WAAW,WAAW,GAAG;AACjC,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAGA,QAAM,YAAY,IAAI,YAAY,EAAE,OAAO,MAAM,UAAU;AAE3D,QAAM,SAAS;AAAA,IACb,MAAM;AAAA,IACN,OAAO,MAAM,gBAAgB;AAAA,IAC7B,OAAO,MAAM,KAAK;AAAA,IAClB,MAAM;AAAA,EACR;AAEA,MAAI;AACF,WAAO,OAAO,aAAa;AAAA,MACzB,MAAM;AAAA,MACN;AAAA,MACA,WAAW;AAAA,IACb,CAAC;AAAA,EACH,QAAQ;AAGN,WAAO;AAAA,EACT;AACF;AAiCO,SAAS,sBACd,QACA,QACA,WACA,oBACA,cACkB;AAClB,MACE,OAAO,cAAc,UACrB,OAAO,qBAAqB,UAC5B,OAAO,UAAU,UACjB,OAAO,cAAc,UACrB,OAAO,sBAAsB,QAC7B;AACA,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,IAAI;AAAA,MACR;AAAA,MACA,uDAAuD,OAAO,KAAK,GAAG,KAAK;AAAA,IAC7E;AAAA,EACF;AACA,MAAI,OAAO,WAAW,WAAW,GAAG;AAClC,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAKA,QAAM,kBAAkB,OAAO;AAE/B,SAAO;AAAA,IACL,OAAO,OAAO,KAAK,GAAG;AAAA,IACtB,WAAW;AAAA,IACX,WAAW,OAAO;AAAA,IAClB,kBAAkB,OAAO;AAAA,IACzB,OAAO,OAAO;AAAA,IACd;AAAA,IACA,mBAAmB,OAAO;AAAA,IAC1B;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,OAAO,KAAK,KAAK;AAAA,IAC9B,iBAAiB,OAAO,KAAK,KAAK;AAAA,EACpC;AACF;;;ACnPA,SAAS,eAAe;AACxB,SAAS,cAAc;AAahB,SAAS,uBACd,OACA,uBACS;AACT,MACE,MAAM,cAAc,UACpB,MAAM,qBAAqB,UAC3B,MAAM,UAAU,UAChB,MAAM,cAAc,QACpB;AACA,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,MAAI,MAAM,WAAW,WAAW,IAAI;AAClC,UAAM,IAAI;AAAA,MACR;AAAA,MACA,0CAA0C,MAAM,WAAW,MAAM;AAAA,IACnE;AAAA,EACF;AACA,QAAM,UAAU;AAAA,IACd,MAAM;AAAA,IACN,OAAO,MAAM,gBAAgB;AAAA,IAC7B,OAAO,MAAM,KAAK;AAAA,IAClB,MAAM;AAAA,EACR;AACA,MAAI;AACJ,MAAI;AACF,kBAAc,aAAa,qBAAqB;AAAA,EAClD,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,MACA,uDAAuD,qBAAqB;AAAA,MAC5E,EAAE,OAAO,IAAI;AAAA,IACf;AAAA,EACF;AACA,MAAI,YAAY,WAAW,IAAI;AAC7B,UAAM,IAAI;AAAA,MACR;AAAA,MACA,uDAAuD,YAAY,MAAM;AAAA,IAC3E;AAAA,EACF;AACA,MAAI;AACF,WAAO,QAAQ,OAAO,MAAM,YAAY,SAAS,WAAW;AAAA,EAC9D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQA,IAAM,sCAAkD;AAAA,EACtD,IAAI,YAAY,EAAE,OAAO,uBAAuB;AAClD,EAAE,MAAM,GAAG,CAAC;AAMZ,SAAS,iBAAiB,GAAuB;AAC/C,MAAI,IAAI,IAAI;AACV,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,MAAI,IAAI,qBAAqB;AAC3B,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,QAAM,MAAM,IAAI,WAAW,CAAC;AAC5B,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,QAAI,CAAC,IAAI,OAAO,IAAI,KAAK;AACzB,UAAM;AAAA,EACR;AACA,SAAO;AACT;AAgBO,SAAS,wBACd,QACA,QACA,WACA,oBACA,cACkB;AAClB,MACE,OAAO,cAAc,UACrB,OAAO,qBAAqB,UAC5B,OAAO,UAAU,UACjB,OAAO,cAAc,UACrB,OAAO,sBAAsB,QAC7B;AACA,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,OAAO,WAAW;AACrB,UAAM,IAAI;AAAA,MACR;AAAA,MACA,2DAA2D,OAAO,KAAK,GAAG,KAAK;AAAA,IACjF;AAAA,EACF;AAEA,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AACF,qBAAiB,aAAa,OAAO,SAAS;AAC9C,qBAAiB,aAAa,SAAS;AACvC,gBAAY,aAAa,OAAO,iBAAiB;AACjD,qBAAiB,aAAa,OAAO,SAAS;AAAA,EAChD,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,MACA,+CAA+C,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MAC/F,EAAE,OAAO,IAAI;AAAA,IACf;AAAA,EACF;AACA,MAAI,eAAe,WAAW,IAAI;AAChC,UAAM,IAAI;AAAA,MACR;AAAA,MACA,0CAA0C,eAAe,MAAM;AAAA,IACjE;AAAA,EACF;AACA,MAAI,eAAe,WAAW,IAAI;AAChC,UAAM,IAAI;AAAA,MACR;AAAA,MACA,iDAAiD,eAAe,MAAM;AAAA,IACxE;AAAA,EACF;AACA,MAAI,UAAU,WAAW,IAAI;AAC3B,UAAM,IAAI;AAAA,MACR;AAAA,MACA,yDAAyD,UAAU,MAAM;AAAA,IAC3E;AAAA,EACF;AACA,MAAI,eAAe,WAAW,IAAI;AAChC,UAAM,IAAI;AAAA,MACR;AAAA,MACA,iDAAiD,eAAe,MAAM;AAAA,IACxE;AAAA,EACF;AAGA,QAAM,kBAAkB;AAAA,IACtB;AAAA,IACA,iBAAiB,OAAO,OAAO,gBAAgB,CAAC;AAAA,IAChD,iBAAiB,OAAO,OAAO,KAAK,CAAC;AAAA,IACrC,OAAO;AAAA,EACT;AAQA,QAAM,WAAW,CAAC,gBAAgB,WAAW,gBAAgB,cAAc;AAG3E,QAAM,SAAS,IAAI,WAAW,CAAC,GAAG,GAAG,CAAC,CAAC;AAGvC,QAAM,oBAAoB,IAAI,WAAW,CAAC,SAAS,MAAM,CAAC;AAC1D,QAAM,kBAAkB,IAAI,WAAW,EAAE;AACzC,QAAM,wBAAwB,IAAI,WAAW,CAAC,CAAC,CAAC;AAIhD,QAAM,iBAAiB,IAAI,WAAW,CAAC,CAAC,CAAC;AACzC,QAAM,mBAAmB,IAAI,WAAW,CAAC,CAAC,CAAC;AAC3C,QAAM,sBAAsB,IAAI,WAAW,CAAC,GAAG,GAAG,CAAC,CAAC;AAEpD,MAAI,gBAAgB,UAAU,KAAM;AAClC,UAAM,IAAI;AAAA,MACR;AAAA,MACA,sEAAsE,gBAAgB,MAAM;AAAA,IAC9F;AAAA,EACF;AACA,QAAM,eAAe,IAAI,WAAW,CAAC,gBAAgB,MAAM,CAAC;AAE5D,QAAM,cAAc;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,QAAM,kBAAkB;AAAA,IACtB;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAO;AAAA,IACL,OAAO,OAAO,KAAK,GAAG;AAAA,IACtB,WAAW;AAAA,IACX,WAAW,OAAO;AAAA,IAClB,kBAAkB,OAAO;AAAA,IACzB,OAAO,OAAO;AAAA,IACd;AAAA,IACA,mBAAmB,OAAO;AAAA,IAC1B;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,OAAO,KAAK,KAAK;AAAA,IAC9B,iBAAiB,OAAO,KAAK,KAAK;AAAA,EACpC;AACF;;;ACrOA,IAAM,oBAAoB;AAE1B,SAAS,YAAY,OAAsD;AACzE,MAAI,MAAM,WAAW,MAAM,EAAG,QAAO;AACrC,MAAI,MAAM,WAAW,SAAS,EAAG,QAAO;AACxC,MAAI,MAAM,WAAW,OAAO,EAAG,QAAO;AACtC,SAAO;AACT;AAsCO,SAAS,kBACd,QACyB;AAEzB,MAAI,CAAC,MAAM,QAAQ,OAAO,MAAM,KAAK,OAAO,OAAO,WAAW,GAAG;AAC/D,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,OAAO,WAAW,OAAO,OAAO,YAAY,UAAU;AACzD,UAAM,IAAI,kBAAkB,iBAAiB,yBAAyB;AAAA,EACxE;AACA,MAAI,CAAC,OAAO,cAAc,OAAO,OAAO,eAAe,UAAU;AAC/D,UAAM,IAAI,kBAAkB,iBAAiB,4BAA4B;AAAA,EAC3E;AAEA,QAAM,SAAS,OAAO;AACtB,QAAM,mBAAmB,OAAO,oBAAoB;AAGpD,WAAS,IAAI,GAAG,IAAI,OAAO,OAAO,QAAQ,KAAK;AAC7C,UAAM,IAAI,OAAO,OAAO,CAAC;AACzB,QAAI,MAAM,OAAW;AACrB,QACE,EAAE,cAAc,UAChB,EAAE,UAAU,UACZ,EAAE,qBAAqB,UACvB,EAAE,cAAc,UAChB,EAAE,sBAAsB,QACxB;AACA,YAAM,IAAI;AAAA,QACR;AAAA,QACA,UAAU,CAAC;AAAA,MACb;AAAA,IACF;AAAA,EACF;AAGA,QAAM,iBAAiB,oBAAI,IAAY;AACvC,aAAW,KAAK,OAAO,OAAQ,gBAAe,IAAI,EAAE,KAAK,GAAG,KAAK;AACjE,aAAW,SAAS,gBAAgB;AAClC,UAAM,SAAS,OAAO,QAAQ,KAAK;AACnC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR;AAAA,QACA,uCAAuC,KAAK;AAAA,MAC9C;AAAA,IACF;AACA,QAAI,EAAE,SAAS,OAAO,aAAa;AACjC,YAAM,IAAI;AAAA,QACR;AAAA,QACA,0CAA0C,KAAK;AAAA,MACjD;AAAA,IACF;AACA,UAAM,OAAO,YAAY,KAAK;AAC9B,QAAI,SAAS,OAAO;AAClB,UACE,CAAC,OAAO,mBACR,CAAC,kBAAkB,KAAK,OAAO,eAAe,GAC9C;AACA,cAAM,IAAI;AAAA,UACR;AAAA,UACA,eAAe,KAAK;AAAA,QACtB;AAAA,MACF;AACA,UACE,OAAO,OAAO,YAAY,YAC1B,CAAC,OAAO,UAAU,OAAO,OAAO,KAChC,OAAO,WAAW,GAClB;AACA,cAAM,IAAI;AAAA,UACR;AAAA,UACA,eAAe,KAAK;AAAA,QACtB;AAAA,MACF;AAAA,IACF,WAAW,SAAS,UAAU;AAC5B,UAAI,CAAC,OAAO,aAAa,OAAO,UAAU,WAAW,GAAG;AACtD,cAAM,IAAI;AAAA,UACR;AAAA,UACA,kBAAkB,KAAK;AAAA,QACzB;AAAA,MACF;AAEA,UAAI;AACJ,UAAI;AACF,uBAAe,aAAa,OAAO,SAAS,EAAE;AAAA,MAChD,SAAS,KAAK;AACZ,cAAM,IAAI;AAAA,UACR;AAAA,UACA,kBAAkB,KAAK;AAAA,UACvB,EAAE,OAAO,IAAI;AAAA,QACf;AAAA,MACF;AACA,UAAI,iBAAiB,IAAI;AACvB,cAAM,IAAI;AAAA,UACR;AAAA,UACA,kBAAkB,KAAK,4CAA4C,YAAY;AAAA,QACjF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,QAAM,WAAgD,CAAC;AACvD,QAAM,YAAgC,CAAC;AACvC,aAAW,SAAS,OAAO,QAAQ;AACjC,QAAI,CAAC,kBAAkB;AACrB,gBAAU,KAAK,KAAK;AACpB;AAAA,IACF;AACA,UAAM,QAAQ,MAAM,KAAK,GAAG;AAC5B,UAAM,SAAS,OAAO,QAAQ,KAAK;AACnC,QAAI,CAAC,QAAQ;AAEX,YAAM,IAAI;AAAA,QACR;AAAA,QACA,uCAAuC,KAAK;AAAA,MAC9C;AAAA,IACF;AACA,UAAM,OAAO,YAAY,KAAK;AAC9B,QAAI,SAAS,OAAO;AAClB,UAAI;AACF,cAAM,EAAE,OAAO,UAAU,IAAI;AAAA,UAC3B;AAAA,UACA,OAAO;AAAA,QACT;AACA,YAAI,CAAC,OAAO;AACV,mBAAS,KAAK;AAAA,YACZ;AAAA,YACA,QAAQ;AAAA,YACR,SAAS,aAAa,SAAS,aAAa,OAAO,QAAQ,YAAY,CAAC;AAAA,UAC1E,CAAC;AACD;AAAA,QACF;AACA,kBAAU,KAAK,KAAK;AAAA,MACtB,SAAS,KAAK;AACZ,iBAAS,KAAK;AAAA,UACZ;AAAA,UACA,QAAQ;AAAA,UACR,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,QAC1D,CAAC;AAAA,MACH;AAAA,IACF,WAAW,SAAS,UAAU;AAC5B,UAAI;AACF,cAAM,KAAK,uBAAuB,OAAO,OAAO,OAAO;AACvD,YAAI,CAAC,IAAI;AACP,mBAAS,KAAK;AAAA,YACZ;AAAA,YACA,QAAQ;AAAA,YACR,SAAS,yCAAyC,OAAO,OAAO;AAAA,UAClE,CAAC;AACD;AAAA,QACF;AACA,kBAAU,KAAK,KAAK;AAAA,MACtB,SAAS,KAAK;AACZ,iBAAS,KAAK;AAAA,UACZ;AAAA,UACA,QAAQ;AAAA,UACR,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,QAC1D,CAAC;AAAA,MACH;AAAA,IACF,WAAW,SAAS,QAAQ;AAC1B,UAAI,CAAC,OAAO,kBAAkB;AAK5B,iBAAS,KAAK;AAAA,UACZ;AAAA,UACA,QAAQ;AAAA,UACR,SACE;AAAA,QACJ,CAAC;AACD;AAAA,MACF;AACA,UAAI;AACF,cAAM,KAAK;AAAA,UACT;AAAA,UACA,OAAO;AAAA,UACP,OAAO;AAAA,QACT;AACA,YAAI,CAAC,IAAI;AACP,mBAAS,KAAK;AAAA,YACZ;AAAA,YACA,QAAQ;AAAA,YACR,SAAS,mDAAmD,OAAO,OAAO;AAAA,UAC5E,CAAC;AACD;AAAA,QACF;AACA,kBAAU,KAAK,KAAK;AAAA,MACtB,SAAS,KAAK;AACZ,iBAAS,KAAK;AAAA,UACZ;AAAA,UACA,QAAQ;AAAA,UACR,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,QAC1D,CAAC;AAAA,MACH;AAAA,IACF,OAAO;AACL,YAAM,IAAI;AAAA,QACR;AAAA,QACA,0BAA0B,KAAK;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAEA,UAAQ,QAAQ;AAAA,IACd,OAAO;AAAA,IACP,eAAe,UAAU;AAAA,IACzB,eAAe,SAAS;AAAA,EAC1B,CAAC;AAQD,QAAM,SAAS,oBAAI,IAAmB;AAEtC,QAAM,gBAAgB,oBAAI,IAA8B;AACxD,WAAS,IAAI,GAAG,IAAI,OAAO,OAAO,QAAQ,KAAK;AAC7C,UAAM,IAAI,OAAO,OAAO,CAAC;AACzB,QAAI,MAAM,OAAW,eAAc,IAAI,GAAG,CAAC;AAAA,EAC7C;AACA,aAAW,SAAS,WAAW;AAC7B,UAAM,QAAQ,MAAM,KAAK,GAAG;AAC5B,UAAM,YAAY,MAAM;AACxB,QAAI,cAAc,QAAW;AAE3B,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,UAAM,MAAM,GAAG,KAAK,KAAK,SAAS;AAClC,QAAI,IAAI,OAAO,IAAI,GAAG;AACtB,QAAI,CAAC,GAAG;AACN,UAAI,EAAE,OAAO,WAAW,QAAQ,CAAC,EAAE;AACnC,aAAO,IAAI,KAAK,CAAC;AAAA,IACnB;AACA,UAAM,MAAM,cAAc,IAAI,KAAK;AACnC,MAAE,OAAO,KAAK,EAAE,OAAO,eAAe,OAAO,GAAG,CAAC;AAAA,EACnD;AAGA,QAAM,aAAiC,CAAC;AACxC,QAAM,UAA8B,CAAC;AACrC,aAAW,KAAK,OAAO,OAAO,GAAG;AAC/B,QAAI,EAAE,OAAO,WAAW,EAAG;AAC3B,UAAM,QAAQ,EAAE,OAAO,CAAC;AACxB,QAAI,CAAC,MAAO;AACZ,UAAM,iBAAiB,MAAM,MAAM;AACnC,UAAM,kBAAkB,MAAM,MAAM;AACpC,QAAI,mBAAmB,UAAa,oBAAoB,QAAW;AACjE,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,aAAS,IAAI,GAAG,IAAI,EAAE,OAAO,QAAQ,KAAK;AACxC,YAAM,QAAQ,EAAE,OAAO,CAAC;AACxB,UAAI,CAAC,MAAO;AACZ,YAAM,IAAI,MAAM;AAChB,UAAI,EAAE,cAAc,gBAAgB;AAClC,cAAM,IAAI;AAAA,UACR;AAAA,UACA,qBAAqB,EAAE,SAAS,2BAA2B,cAAc,OAAO,OAAO,EAAE,SAAS,CAAC,mBAAmB,MAAM,aAAa,KAAK,MAAM,aAAa;AAAA,QACnK;AAAA,MACF;AACA,UAAI,EAAE,sBAAsB,iBAAiB;AAC3C,cAAM,IAAI;AAAA,UACR;AAAA,UACA,qBAAqB,EAAE,SAAS,mCAAmC,eAAe,OAAO,OAAO,EAAE,iBAAiB,CAAC;AAAA,QACtH;AAAA,MACF;AAAA,IACF;AAEA,UAAM,YAAY,oBAAI,IAAY;AAClC,eAAW,SAAS,EAAE,QAAQ;AAC5B,YAAM,WAAW,MAAM,MAAM;AAC7B,UAAI,aAAa,QAAW;AAC1B,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,UAAI,UAAU,IAAI,QAAQ,GAAG;AAC3B,cAAM,IAAI;AAAA,UACR;AAAA,UACA,WAAW,EAAE,SAAS,8BAA8B,QAAQ;AAAA,QAC9D;AAAA,MACF;AACA,gBAAU,IAAI,QAAQ;AAAA,IACxB;AAEA,UAAM,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM;AAC1C,YAAM,KAAK,OAAO,EAAE,MAAM,SAAS,GAAG;AACtC,YAAM,KAAK,OAAO,EAAE,MAAM,SAAS,GAAG;AACtC,aAAO,KAAK,KAAK,KAAK,KAAK,KAAK,IAAI;AAAA,IACtC,CAAC;AACD,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,YAAM,QAAQ,OAAO,IAAI,CAAC;AAC1B,YAAM,QAAQ,OAAO,CAAC;AACtB,UAAI,CAAC,SAAS,CAAC,MAAO;AACtB,YAAM,OAAO,OAAO,MAAM,MAAM,oBAAoB,GAAG;AACvD,YAAM,OAAO,OAAO,MAAM,MAAM,oBAAoB,GAAG;AACvD,UAAI,OAAO,MAAM;AACf,cAAM,IAAI;AAAA,UACR;AAAA,UACA,WAAW,EAAE,SAAS,UAAU,OAAO,MAAM,MAAM,KAAK,CAAC,yBAAyB,IAAI,qBAAqB,OAAO,MAAM,MAAM,KAAK,CAAC,qBAAqB,IAAI;AAAA,QAC/J;AAAA,MACF;AAAA,IACF;AAGA,UAAM,cAAc,OAAO,OAAO,SAAS,CAAC;AAC5C,QAAI,CAAC,YAAa;AAClB,UAAM,SAAS,YAAY;AAE3B,QAAI,OAAO,mBAAmB;AAC5B,eAAS,IAAI,GAAG,IAAI,OAAO,SAAS,GAAG,KAAK;AAC1C,cAAM,IAAI,OAAO,CAAC;AAClB,YAAI,EAAG,YAAW,KAAK,EAAE,KAAK;AAAA,MAChC;AAAA,IACF;AAGA,UAAM,SAAS,OAAO,QAAQ,EAAE,KAAK;AACrC,UAAM,YAAY,OAAO,WAAW,EAAE,KAAK;AAC3C,QAAI,CAAC,UAAU,cAAc,QAAW;AACtC,YAAM,IAAI;AAAA,QACR;AAAA,QACA,wCAAwC,EAAE,KAAK;AAAA,MACjD;AAAA,IACF;AACA,UAAM,OAAO,YAAY,EAAE,KAAK;AAChC,QAAI;AACJ,QAAI,SAAS,OAAO;AAKlB,UAAI,UAAU,YAAY,MAAM,eAAe,YAAY,GAAG;AAC5D,cAAM,IAAI;AAAA,UACR;AAAA,UACA,cAAc,EAAE,KAAK,MAAM,SAAS,6CAA6C,cAAc;AAAA,QACjG;AAAA,MACF;AACA,eAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA,UAAU,YAAY;AAAA,QACtB,YAAY;AAAA,QACZ,EAAE,OAAO;AAAA,MACX;AAAA,IACF,WAAW,SAAS,UAAU;AAC5B,UAAI,cAAc,gBAAgB;AAChC,cAAM,IAAI;AAAA,UACR;AAAA,UACA,cAAc,EAAE,KAAK,MAAM,SAAS,6CAA6C,cAAc;AAAA,QACjG;AAAA,MACF;AACA,eAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA,YAAY;AAAA,QACZ,EAAE,OAAO;AAAA,MACX;AAAA,IACF,WAAW,SAAS,QAAQ;AAE1B,UAAI,cAAc,gBAAgB;AAChC,cAAM,IAAI;AAAA,UACR;AAAA,UACA,cAAc,EAAE,KAAK,MAAM,SAAS,6CAA6C,cAAc;AAAA,QACjG;AAAA,MACF;AACA,eAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA,YAAY;AAAA,QACZ,EAAE,OAAO;AAAA,MACX;AAAA,IACF,OAAO;AACL,YAAM,IAAI;AAAA,QACR;AAAA,QACA,0BAA0B,EAAE,KAAK;AAAA,MACnC;AAAA,IACF;AACA,YAAQ,KAAK,MAAM;AAAA,EACrB;AAEA,UAAQ,OAAO;AAAA,IACb,OAAO;AAAA,IACP,aAAa,QAAQ;AAAA,IACrB,eAAe,SAAS;AAAA,IACxB,iBAAiB,WAAW;AAAA,EAC9B,CAAC;AAED,SAAO,EAAE,SAAS,UAAU,WAAW;AACzC;AAiBO,SAAS,uBACd,OACA,QACA,kBACoD;AACpD,QAAM,OAAO,YAAY,MAAM,KAAK,GAAG,KAAK;AAC5C,MAAI,SAAS,OAAO;AAClB,QAAI;AACF,YAAM,EAAE,OAAO,UAAU,IAAI;AAAA,QAC3B;AAAA,QACA,OAAO;AAAA,MACT;AACA,UAAI,MAAO,QAAO,EAAE,OAAO,KAAK;AAChC,aAAO;AAAA,QACL,OAAO;AAAA,QACP,QAAQ,8BAA8B,SAAS,aAAa,OAAO,QAAQ,YAAY,CAAC;AAAA,MAC1F;AAAA,IACF,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,OAAO;AAAA,QACP,QAAQ,sBAAsB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MAChF;AAAA,IACF;AAAA,EACF;AACA,MAAI,SAAS,UAAU;AACrB,QAAI;AACF,YAAM,KAAK,uBAAuB,OAAO,OAAO,OAAO;AACvD,aAAO,KACH,EAAE,OAAO,KAAK,IACd;AAAA,QACE,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA,IACN,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,OAAO;AAAA,QACP,QAAQ,sBAAsB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MAChF;AAAA,IACF;AAAA,EACF;AACA,MAAI,SAAS,QAAQ;AACnB,QAAI,CAAC,kBAAkB;AACrB,aAAO;AAAA,QACL,OAAO;AAAA,QACP,QACE;AAAA,MACJ;AAAA,IACF;AACA,QAAI;AACF,YAAM,KAAK,oBAAoB,OAAO,OAAO,SAAS,gBAAgB;AACtE,aAAO,KACH,EAAE,OAAO,KAAK,IACd;AAAA,QACE,OAAO;AAAA,QACP,QAAQ;AAAA,MACV;AAAA,IACN,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,OAAO;AAAA,QACP,QAAQ,sBAAsB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,MAChF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AAAA,IACL,OAAO;AAAA,IACP,QAAQ,sBAAsB,MAAM,KAAK,GAAG,KAAK;AAAA,EACnD;AACF;","names":["ToonError","validatePrefix","ToonError","ToonError","capAbs","hexToBytes","hexToBytes"]}
|