@unicitylabs/sphere-sdk 0.13.3-dev.1 → 0.13.3-dev.2

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/index.d.ts CHANGED
@@ -1048,605 +1048,628 @@ declare function keyFromTokenId(tokenId: string): string;
1048
1048
  declare function isValidTokenId(tokenId: string): boolean;
1049
1049
 
1050
1050
  /**
1051
- * Transport Provider Interface
1052
- * Platform-independent P2P messaging abstraction
1053
- */
1054
-
1055
- /**
1056
- * P2P messaging transport provider
1051
+ * transport/delivery-provider.ts — the `DeliveryProvider` port (sdk-changes S7,
1052
+ * covenant §3.1-6).
1053
+ *
1054
+ * The seam that keeps the delivery rail swappable. In Unicity, a transfer —
1055
+ * after certification — is just a file handoff, so the port is deliberately
1056
+ * tiny: hand a finished token blob to a recipient, pull incoming deliveries,
1057
+ * acknowledge them. `WalletApiMailboxProvider`
1058
+ * (impl/shared/wallet-api/WalletApiMailboxProvider.ts) is the reference
1059
+ * implementation; anything that can move a file can implement it (the port
1060
+ * shape must not preclude the old Nostr transport or a future federated
1061
+ * transport — neither is a deliverable here).
1062
+ *
1063
+ * Normative shapes (sdk-changes S7):
1064
+ * - `DeliveryReceipt = { deliveryId }`
1065
+ * - `IncomingDelivery = { deliveryId, transferId?, senderPubkey?, memo?,
1066
+ * fetchBlob(), cursor }`
1067
+ * - `deliveryId` is the **content-derived** entry id —
1068
+ * `hex(SHA-256(tokenId bytes ‖ stateHash bytes))` — NEVER a server-assigned
1069
+ * row id or seq (covenant §3.1-4; the contract suite asserts it). It is
1070
+ * computed client-side ({@link computeDeliveryId}) and must equal the
1071
+ * backend's `entry_id` (ARCHITECTURE §6).
1072
+ * - **Custody is a composition-time property, not a per-call flag**:
1073
+ * implementations take `custody: 'inventory' | 'external'` at construction
1074
+ * and every ack sends the corresponding `intoInventory` — delivery-only
1075
+ * safety must never depend on remembering an option at a call site.
1076
+ * - Implementations MUST keep a **persistent `(tokenId, stateHash)` seen-set**
1077
+ * for incoming deliveries: the recipient-side replay guard is part of the
1078
+ * port contract, not a server promise (the recipient never trusts the
1079
+ * backend — ARCHITECTURE §8.2). `deliveryId` is the canonical hash encoding
1080
+ * of exactly that pair, so a persistent deliveryId set satisfies this.
1057
1081
  */
1058
- interface TransportProvider extends BaseProvider {
1059
- /**
1060
- * Set identity for signing/encryption.
1061
- * If the transport is already connected, reconnects with the new identity.
1062
- */
1063
- setIdentity(identity: FullIdentity): void | Promise<void>;
1064
- /**
1065
- * Send encrypted direct message
1066
- * @param recipientTransportPubkey - Transport-specific pubkey for messaging
1067
- * @returns Event ID
1068
- */
1069
- sendMessage(recipientTransportPubkey: string, content: string): Promise<string>;
1070
- /**
1071
- * Subscribe to incoming direct messages
1072
- * @returns Unsubscribe function
1073
- */
1074
- onMessage(handler: MessageHandler): () => void;
1075
- /**
1076
- * Send token transfer payload
1077
- * @param recipientTransportPubkey - Transport-specific pubkey for messaging
1078
- * @returns Event ID
1079
- */
1080
- sendTokenTransfer(recipientTransportPubkey: string, payload: TokenTransferPayload): Promise<string>;
1081
- /**
1082
- * Subscribe to incoming token transfers
1083
- * @returns Unsubscribe function
1084
- */
1085
- onTokenTransfer(handler: TokenTransferHandler): () => void;
1082
+ /** Receipt for a delivered blob. `deliveryId` is content-derived — see module doc. */
1083
+ interface DeliveryReceipt {
1084
+ deliveryId: string;
1085
+ }
1086
+ /** Options for {@link DeliveryProvider.deliver}. */
1087
+ interface DeliverOptions {
1086
1088
  /**
1087
- * Resolve any identifier to full peer information.
1088
- * Accepts @nametag, bare nametag, DIRECT://, chain pubkey, or transport pubkey.
1089
- * @param identifier - Any supported identifier format
1090
- * @returns PeerInfo or null if not found
1089
+ * The send's transferId (the E.3 intent id / realization seed). Recorded
1090
+ * with the delivery so the recipient can group multi-token payments and the
1091
+ * backend can evidence-check the sender's removals (ARCHITECTURE §5.3/§6).
1091
1092
  */
1092
- resolve?(identifier: string): Promise<PeerInfo | null>;
1093
+ transferId: string;
1094
+ /** Optional human memo. Implementations encrypt it client-side (S6). */
1095
+ memo?: string;
1093
1096
  /**
1094
- * Resolve nametag to public key
1097
+ * The SENDER's own nametag (without a leading `@`), so the recipient can
1098
+ * render the human identity instead of a raw pubkey ("Someone"). Bundled
1099
+ * with the memo into ONE recipient-addressed (ECDH) `enc1.` envelope (S6) —
1100
+ * the operator never sees it. Attached whenever the sender has a nametag OR
1101
+ * a memo (so the nametag travels even on a memo-less transfer).
1095
1102
  */
1096
- resolveNametag?(nametag: string): Promise<string | null>;
1103
+ senderNametag?: string;
1104
+ }
1105
+ /** One incoming delivery pulled from the feed. */
1106
+ interface IncomingDelivery {
1107
+ /** Content-derived id — `hex(SHA-256(tokenId bytes ‖ stateHash bytes))`. */
1108
+ deliveryId: string;
1109
+ /** The sender's transferId, when the transport carries it. */
1110
+ transferId?: string;
1111
+ /** The sender's pubkey, when the transport carries it. */
1112
+ senderPubkey?: string;
1113
+ /** Decrypted memo (S6), when present and decryptable. */
1114
+ memo?: string;
1097
1115
  /**
1098
- * Resolve nametag to full peer information
1099
- * Returns transportPubkey, chainPubkey, directAddress
1116
+ * The sender's nametag (without a leading `@`), decrypted from the same
1117
+ * recipient-addressed delivery envelope as {@link memo} (S6). Lets the
1118
+ * receiver render the human identity instead of a raw pubkey, with no
1119
+ * Nostr/transport lookup. Absent when the envelope carried none or could
1120
+ * not be decrypted.
1100
1121
  */
1101
- resolveNametagInfo?(nametag: string): Promise<PeerInfo | null>;
1122
+ senderNametag?: string;
1123
+ /** Fetch the finished token blob bytes (the encoded TokenBlob). */
1124
+ fetchBlob(): Promise<Uint8Array>;
1125
+ /** Transport-local resume cursor (opaque to callers). */
1126
+ cursor: string;
1127
+ }
1128
+ type DeliveryDisposition = 'claimed' | 'rejected';
1129
+ /**
1130
+ * The §9 wake streams a backend may nudge: `mailbox` (incoming deliveries),
1131
+ * `inventory` (owned-token set changed — e.g. a top-up or a claim on another
1132
+ * device), and `payment_requests` (a request created/answered). A wake on any
1133
+ * of these is a NUDGE — the consumer pulls that stream's cursor; correctness
1134
+ * never depends on the wake arriving (the poll backstop is the source of
1135
+ * truth).
1136
+ */
1137
+ type WakeStream = 'inventory' | 'mailbox' | 'payment_requests';
1138
+ /**
1139
+ * True liveness of the realtime wake channel (§9), decoupled from sign-in
1140
+ * session state: `connecting`/`connected` — a socket is (being) established;
1141
+ * `reconnecting` — it dropped and is backing off to re-establish (the poll
1142
+ * backstop carries correctness meanwhile); `closed` — torn down intentionally.
1143
+ * The wake is a nudge, so this is informational for the frontend (a "live"
1144
+ * indicator) — never a correctness gate.
1145
+ */
1146
+ type WakeChannelStatus = 'connecting' | 'connected' | 'reconnecting' | 'closed';
1147
+ /**
1148
+ * Custody mode (composition-time): `'inventory'` — acknowledged deliveries
1149
+ * enter the wallet-api inventory (the full wallet-api preset); `'external'` —
1150
+ * the app's own storage keeps custody and acks perform ZERO inventory writes
1151
+ * (the delivery-only preset, ARCHITECTURE §6 "delivery-only claim").
1152
+ */
1153
+ type DeliveryCustody = 'inventory' | 'external';
1154
+ interface DeliveryProvider {
1155
+ /** Composition-time custody property — never a per-call flag (S7). */
1156
+ readonly custody: DeliveryCustody;
1102
1157
  /**
1103
- * Resolve a DIRECT:// or PROXY:// address to full peer info.
1104
- * Performs reverse lookup: address → binding event → PeerInfo.
1105
- * @param address - L3 address (DIRECT://... or PROXY://...)
1106
- * @returns PeerInfo or null if no binding found for this address
1158
+ * Bind the wallet identity (optional — implementations that authenticate or
1159
+ * encrypt per-wallet need it; mirrors `TokenStorageProvider.setIdentity`).
1107
1160
  */
1108
- resolveAddressInfo?(address: string): Promise<PeerInfo | null>;
1161
+ setIdentity?(identity: {
1162
+ privateKey: string;
1163
+ chainPubkey: string;
1164
+ }): void;
1109
1165
  /**
1110
- * Resolve transport pubkey to full peer info.
1111
- * Queries binding events authored by the given transport pubkey.
1112
- * @param transportPubkey - Transport-specific pubkey (e.g. 64-char hex string)
1113
- * @returns PeerInfo or null if no binding found
1166
+ * #583 per-address client isolation: mint an INDEPENDENT delivery provider for
1167
+ * a different HD address, backed by its OWN authenticated client + wake socket
1168
+ * (mirrors `TokenStorageProvider.createForAddress`). Implementations that hold
1169
+ * a single mutable identity+session per instance (e.g. the wallet-api mailbox
1170
+ * over one `WalletApiClient`) provide this so `Sphere.switchToAddress` can give
1171
+ * each address its OWN delivery instance — an orphaned previous-address pump
1172
+ * then re-auths as ITS OWN owner (harmless) instead of driving a client that
1173
+ * was re-bound to the new owner. Stateless transports may omit it (the same
1174
+ * instance serves every address).
1114
1175
  */
1115
- resolveTransportPubkeyInfo?(transportPubkey: string): Promise<PeerInfo | null>;
1176
+ createForAddress?(): DeliveryProvider;
1116
1177
  /**
1117
- * Batch-resolve multiple transport pubkeys to peer info.
1118
- * Used for HD address discovery: derives transport pubkeys for indices 0..N
1119
- * and queries binding events in a single batch.
1120
- * @param transportPubkeys - Array of transport-specific pubkeys to look up
1121
- * @returns Array of PeerInfo for pubkeys that have binding events (may be shorter than input)
1178
+ * Hand a finished token blob to a recipient. `recipientPubkey` is the
1179
+ * recipient's CHAIN pubkey (33-byte compressed secp256k1, hex) — the
1180
+ * canonical Unicity identity (ARCHITECTURE §4); transports that address
1181
+ * recipients differently resolve it themselves.
1182
+ *
1183
+ * MUST be idempotent per (token, state): re-delivering the same finished
1184
+ * blob — including after the recipient claimed — succeeds and returns the
1185
+ * same content-derived `deliveryId` (ARCHITECTURE §6 deposit idempotency).
1122
1186
  */
1123
- discoverAddresses?(transportPubkeys: string[]): Promise<PeerInfo[]>;
1187
+ deliver(recipientPubkey: string, blob: Uint8Array, options: DeliverOptions): Promise<DeliveryReceipt>;
1124
1188
  /**
1125
- * Recover nametag for current identity by decrypting stored encrypted nametag
1126
- * Used after wallet import to recover associated nametag
1127
- * @returns Decrypted nametag or null if none found
1189
+ * Pull-based feed of incoming deliveries since the given transport-local
1190
+ * cursor (or the provider's persisted cursor when omitted). Yields only
1191
+ * deliveries not yet in the persistent seen-set; completes when the feed is
1192
+ * drained — callers re-invoke on poll/wake. Feeds the existing
1193
+ * transport-agnostic `handleV2Transfer` (sdk-changes S3).
1128
1194
  */
1129
- recoverNametag?(): Promise<string | null>;
1195
+ incoming(sinceCursor?: string): AsyncIterable<IncomingDelivery>;
1130
1196
  /**
1131
- * Publish identity binding event.
1132
- * Without nametag: publishes base binding (chainPubkey, directAddress).
1133
- * With nametag: adds nametag hash, proxy address, encrypted nametag for recovery.
1134
- * Uses parameterized replaceable event (kind 30078, d=hash(nostrPubkey)).
1135
- * @returns true if successful, false if nametag is taken by another pubkey
1197
+ * Acknowledge a delivery: `'claimed'` accepts it (with the provider's
1198
+ * composition-time custody), `'rejected'` marks it locally-unverifiable —
1199
+ * terminal for discovery only (the entry stays claimable server-side and
1200
+ * its blob is retained — ARCHITECTURE §6). Both record the delivery in the
1201
+ * persistent seen-set.
1136
1202
  */
1137
- publishIdentityBinding?(chainPubkey: string, directAddress: string, nametag?: string): Promise<boolean>;
1203
+ ack(deliveryId: string, disposition: DeliveryDisposition): Promise<void>;
1138
1204
  /**
1139
- * Subscribe to broadcast messages (global/channel)
1205
+ * Optional batch acknowledge (#623): claim and reject whole pages of incoming deliveries in a
1206
+ * single request each, instead of one per entry — so draining a large inbox (a long-offline or
1207
+ * service wallet) doesn't fire thousands of writes and trip the per-owner rate limit. Same
1208
+ * semantics as {@link ack}: the seen-set records only entries that were acked successfully, so a
1209
+ * partial/failed batch is re-listed and re-processed (idempotent claim, §6). A provider that does
1210
+ * not implement it (e.g. the relay no-op) is driven via per-entry {@link ack}.
1140
1211
  */
1141
- subscribeToBroadcast?(tags: string[], handler: BroadcastHandler): () => void;
1212
+ ackBatch?(claimed: string[], rejected: string[]): Promise<void>;
1142
1213
  /**
1143
- * Publish broadcast message
1214
+ * Optional batch deliver (#699): hand N finished blobs to ONE recipient with a single deposit
1215
+ * request (and one upload-urls request) instead of N — a multi-source send then costs O(1)
1216
+ * against the backend's deposit rate limit regardless of fragmentation. Optional like
1217
+ * {@link ackBatch}: the port must not preclude the relay transport or a future federated one,
1218
+ * and neither has a batch primitive — callers probe and fall back to per-blob {@link deliver}.
1219
+ *
1220
+ * Semantically equivalent to awaiting {@link deliver} once per blob, in order:
1221
+ * - receipts return in REQUEST order; each `deliveryId` is the content-derived entry id
1222
+ * (covenant §3.1-4 — NEVER the batch endpoint's server-assigned seq);
1223
+ * - idempotent per (token, state) exactly like {@link deliver};
1224
+ * - `options` apply to every blob (one send = one transferId/memo/senderNametag);
1225
+ * - throws when ANY blob could not be deposited — blobs that DID land are absorbed
1226
+ * idempotently when the caller retries, batched or per-blob.
1144
1227
  */
1145
- publishBroadcast?(content: string, tags?: string[]): Promise<string>;
1228
+ deliverBatch?(recipientPubkey: string, blobs: Uint8Array[], options: DeliverOptions): Promise<DeliveryReceipt[]>;
1146
1229
  /**
1147
- * Send payment request to a recipient
1148
- * @param recipientTransportPubkey - Transport-specific pubkey for messaging
1149
- * @returns Event ID
1230
+ * Optional wake hook: `callback` fires with the {@link WakeStream} that was
1231
+ * nudged when new data may be available on it (e.g. a WS nudge — never a
1232
+ * correctness dependency, ARCHITECTURE §9). The wallet-api wake socket
1233
+ * multiplexes all three owner streams (`mailbox` | `inventory` |
1234
+ * `payment_requests`); the consumer routes each to that stream's pull.
1235
+ *
1236
+ * The underlying socket SELF-HEALS (§9): it reconnects with backoff on any
1237
+ * drop and a liveness watchdog force-reconnects a half-open socket. On every
1238
+ * (re)connect the consumer MUST run a full catch-up pull of every stream —
1239
+ * wakes missed while the socket was dead are not replayed — so `callback`
1240
+ * fires once for EACH stream on (re)connect (a synthetic catch-up nudge).
1241
+ * `onStatus` (optional) surfaces true socket liveness for the frontend,
1242
+ * decoupled from sign-in state. Returns an unsubscribe function.
1150
1243
  */
1151
- sendPaymentRequest?(recipientTransportPubkey: string, request: PaymentRequestPayload): Promise<string>;
1244
+ onWake?(callback: (stream: WakeStream) => void, onStatus?: (status: WakeChannelStatus) => void): () => void;
1152
1245
  /**
1153
- * Subscribe to incoming payment requests
1154
- * @returns Unsubscribe function
1246
+ * Late-bind the backend-true (tokenId, stateHash) derivation —
1247
+ * `ITokenEngine.deliveryKeys`. Compositions are engine-less (the engine is
1248
+ * built later); the module that owns both (PaymentsModule) binds this at
1249
+ * init. Implementations that derive ids (S7) MUST use it and fail loudly if
1250
+ * unbound; transports that don't derive may omit the method.
1155
1251
  */
1156
- onPaymentRequest?(handler: PaymentRequestHandler$1): () => void;
1157
- /**
1158
- * Send response to a payment request
1159
- * @param recipientTransportPubkey - Transport-specific pubkey for messaging
1160
- * @returns Event ID
1161
- */
1162
- sendPaymentRequestResponse?(recipientTransportPubkey: string, response: PaymentRequestResponsePayload): Promise<string>;
1163
- /**
1164
- * Subscribe to incoming payment request responses
1165
- * @returns Unsubscribe function
1166
- */
1167
- onPaymentRequestResponse?(handler: PaymentRequestResponseHandler$1): () => void;
1168
- /**
1169
- * Send a read receipt for a message
1170
- * @param recipientTransportPubkey - Transport pubkey of the message sender
1171
- * @param messageEventId - Event ID of the message being acknowledged
1172
- */
1173
- sendReadReceipt?(recipientTransportPubkey: string, messageEventId: string): Promise<void>;
1252
+ bindDeliveryKeys?(derive: (blobBytes: Uint8Array) => Promise<{
1253
+ tokenId: string;
1254
+ stateHash: string;
1255
+ }>): void;
1256
+ }
1257
+ /**
1258
+ * The content-derived delivery id:
1259
+ * `hex(SHA-256(tokenId bytes ‖ stateHash bytes))` — identical to the
1260
+ * backend's `entry_id` (ARCHITECTURE §6), computed client-side so no
1261
+ * implementation can substitute a server row id (covenant §3.1-4).
1262
+ *
1263
+ * @param tokenIdHex - genesis-stable 64-hex token id
1264
+ * @param stateHashHex - the SDK's per-state hash (DataHash imprint, hex) — the
1265
+ * backend's `state_hash` (wallet-api validation/chain.ts); NEVER a plain
1266
+ * sha256 over the token bytes (that variant 422s on deposit).
1267
+ */
1268
+ declare function computeDeliveryId(tokenIdHex: string, stateHashHex: string): string;
1269
+ /** Full delivery keys: the engine-derived pair + the composed deliveryId. */
1270
+ interface DeliveryBlobKeys {
1271
+ /** Genesis-stable 64-hex token id (from the TokenBlob envelope). */
1272
+ tokenId: string;
1273
+ /** Per-state hash: `hex(SHA-256(inner token bytes))` — changes every transfer. */
1274
+ stateHash: string;
1275
+ /** {@link computeDeliveryId} of the two above. */
1276
+ deliveryId: string;
1277
+ }
1278
+ /**
1279
+ * Derive (tokenId, stateHash, deliveryId) from encoded TokenBlob bytes.
1280
+ * Throws when the bytes are not a decodable TokenBlob.
1281
+ */
1282
+ /**
1283
+ * Compose full delivery keys from an engine-derived (tokenId, stateHash) pair.
1284
+ * The pair MUST come from `ITokenEngine.deliveryKeys` (the backend-true SDK
1285
+ * derivation) — the port module deliberately cannot decode tokens itself.
1286
+ */
1287
+ declare function composeDeliveryKeys(keys: {
1288
+ tokenId: string;
1289
+ stateHash: string;
1290
+ }): DeliveryBlobKeys;
1291
+
1292
+ /**
1293
+ * Receive + delivery: `receive()`, the `handleV2Transfer` receiver, the
1294
+ * PENDING_V2_DELIVERIES journal, the hoisted send delivery pass and the bounded
1295
+ * replay + poison budget. The token map and `save()` stay in PaymentsModule.
1296
+ */
1297
+
1298
+ /**
1299
+ * @deprecated v2 transfers arrive as finished tokens — there is no finalization
1300
+ * phase. The options are accepted for backwards compatibility and ignored.
1301
+ */
1302
+ interface ReceiveOptions {
1303
+ /** @deprecated Ignored — v2 tokens are stored confirmed on receipt. */
1304
+ finalize?: boolean;
1305
+ /** @deprecated Ignored. */
1306
+ timeout?: number;
1307
+ /** @deprecated Ignored. */
1308
+ pollInterval?: number;
1309
+ }
1310
+ interface ReceiveResult {
1311
+ /** Newly received incoming transfers. */
1312
+ transfers: IncomingTransfer[];
1313
+ }
1314
+
1315
+ /**
1316
+ * Transport Provider Interface
1317
+ * Platform-independent P2P messaging abstraction
1318
+ */
1319
+
1320
+ /**
1321
+ * P2P messaging transport provider
1322
+ */
1323
+ interface TransportProvider extends BaseProvider {
1174
1324
  /**
1175
- * Subscribe to incoming read receipts
1176
- * @returns Unsubscribe function
1325
+ * Set identity for signing/encryption.
1326
+ * If the transport is already connected, reconnects with the new identity.
1177
1327
  */
1178
- onReadReceipt?(handler: ReadReceiptHandler): () => void;
1328
+ setIdentity(identity: FullIdentity): void | Promise<void>;
1179
1329
  /**
1180
- * Send typing indicator to a recipient
1181
- * @param recipientTransportPubkey - Transport pubkey of the conversation partner
1330
+ * Send encrypted direct message
1331
+ * @param recipientTransportPubkey - Transport-specific pubkey for messaging
1332
+ * @returns Event ID
1182
1333
  */
1183
- sendTypingIndicator?(recipientTransportPubkey: string): Promise<void>;
1334
+ sendMessage(recipientTransportPubkey: string, content: string): Promise<string>;
1184
1335
  /**
1185
- * Subscribe to incoming typing indicators
1336
+ * Subscribe to incoming direct messages
1186
1337
  * @returns Unsubscribe function
1187
1338
  */
1188
- onTypingIndicator?(handler: TypingIndicatorHandler): () => void;
1339
+ onMessage(handler: MessageHandler): () => void;
1189
1340
  /**
1190
- * Send composing indicator to a recipient using NIP-44 encrypted gift wrap
1191
- * @param recipientTransportPubkey - Transport pubkey of the conversation partner
1192
- * @param content - JSON payload with senderNametag and expiresIn
1341
+ * Send token transfer payload
1342
+ * @param recipientTransportPubkey - Transport-specific pubkey for messaging
1343
+ * @returns Event ID
1193
1344
  */
1194
- sendComposingIndicator?(recipientTransportPubkey: string, content: string): Promise<void>;
1345
+ sendTokenTransfer(recipientTransportPubkey: string, payload: TokenTransferPayload): Promise<string>;
1195
1346
  /**
1196
- * Subscribe to incoming composing indicators
1347
+ * Subscribe to incoming token transfers
1197
1348
  * @returns Unsubscribe function
1198
1349
  */
1199
- onComposing?(handler: ComposingHandler): () => void;
1350
+ onTokenTransfer(handler: TokenTransferHandler): () => void;
1200
1351
  /**
1201
- * Get list of configured relay URLs
1352
+ * Resolve any identifier to full peer information.
1353
+ * Accepts @nametag, bare nametag, DIRECT://, chain pubkey, or transport pubkey.
1354
+ * @param identifier - Any supported identifier format
1355
+ * @returns PeerInfo or null if not found
1202
1356
  */
1203
- getRelays?(): string[];
1357
+ resolve?(identifier: string): Promise<PeerInfo | null>;
1204
1358
  /**
1205
- * Get list of currently connected relay URLs
1359
+ * Resolve nametag to public key
1206
1360
  */
1207
- getConnectedRelays?(): string[];
1361
+ resolveNametag?(nametag: string): Promise<string | null>;
1208
1362
  /**
1209
- * Add a relay dynamically
1210
- * @returns true if added successfully
1363
+ * Resolve nametag to full peer information
1364
+ * Returns transportPubkey, chainPubkey, directAddress
1211
1365
  */
1212
- addRelay?(relayUrl: string): Promise<boolean>;
1366
+ resolveNametagInfo?(nametag: string): Promise<PeerInfo | null>;
1213
1367
  /**
1214
- * Remove a relay dynamically
1215
- * @returns true if removed successfully
1368
+ * Resolve a DIRECT:// or PROXY:// address to full peer info.
1369
+ * Performs reverse lookup: address → binding event → PeerInfo.
1370
+ * @param address - L3 address (DIRECT://... or PROXY://...)
1371
+ * @returns PeerInfo or null if no binding found for this address
1216
1372
  */
1217
- removeRelay?(relayUrl: string): Promise<boolean>;
1373
+ resolveAddressInfo?(address: string): Promise<PeerInfo | null>;
1218
1374
  /**
1219
- * Check if a relay is configured
1375
+ * Resolve transport pubkey to full peer info.
1376
+ * Queries binding events authored by the given transport pubkey.
1377
+ * @param transportPubkey - Transport-specific pubkey (e.g. 64-char hex string)
1378
+ * @returns PeerInfo or null if no binding found
1220
1379
  */
1221
- hasRelay?(relayUrl: string): boolean;
1380
+ resolveTransportPubkeyInfo?(transportPubkey: string): Promise<PeerInfo | null>;
1222
1381
  /**
1223
- * Check if a relay is currently connected
1382
+ * Batch-resolve multiple transport pubkeys to peer info.
1383
+ * Used for HD address discovery: derives transport pubkeys for indices 0..N
1384
+ * and queries binding events in a single batch.
1385
+ * @param transportPubkeys - Array of transport-specific pubkeys to look up
1386
+ * @returns Array of PeerInfo for pubkeys that have binding events (may be shorter than input)
1224
1387
  */
1225
- isRelayConnected?(relayUrl: string): boolean;
1388
+ discoverAddresses?(transportPubkeys: string[]): Promise<PeerInfo[]>;
1226
1389
  /**
1227
- * Set fallback 'since' timestamp for event subscriptions.
1228
- * Used when switching to an address that has never subscribed before.
1229
- * The transport uses this instead of 'now' as the initial since filter,
1230
- * ensuring events sent while the address was inactive are not missed.
1231
- * Consumed once by the next subscription setup, then cleared.
1232
- *
1233
- * @param sinceSeconds - Unix timestamp in seconds
1390
+ * Recover nametag for current identity by decrypting stored encrypted nametag
1391
+ * Used after wallet import to recover associated nametag
1392
+ * @returns Decrypted nametag or null if none found
1234
1393
  */
1235
- setFallbackSince?(sinceSeconds: number): void;
1394
+ recoverNametag?(): Promise<string | null>;
1236
1395
  /**
1237
- * Set fallback 'since' timestamp for DM (gift-wrap) subscriptions.
1238
- * Used when no persisted DM timestamp exists in storage (e.g. first connect).
1239
- * Consumed once by the next subscription setup, then cleared.
1240
- *
1241
- * @param sinceSeconds - Unix timestamp in seconds
1396
+ * Publish identity binding event.
1397
+ * Without nametag: publishes base binding (chainPubkey, directAddress).
1398
+ * With nametag: adds nametag hash, proxy address, encrypted nametag for recovery.
1399
+ * Uses parameterized replaceable event (kind 30078, d=hash(nostrPubkey)).
1400
+ * @returns true if successful, false if nametag is taken by another pubkey
1242
1401
  */
1243
- setFallbackDmSince?(sinceSeconds: number): void;
1402
+ publishIdentityBinding?(chainPubkey: string, directAddress: string, nametag?: string): Promise<boolean>;
1244
1403
  /**
1245
- * Fetch pending events from transport (one-shot query).
1246
- * Creates a temporary subscription, processes events through normal handlers,
1247
- * and resolves after EOSE (End Of Stored Events).
1404
+ * Subscribe to broadcast messages (global/channel)
1248
1405
  */
1249
- fetchPendingEvents?(): Promise<void>;
1406
+ subscribeToBroadcast?(tags: string[], handler: BroadcastHandler): () => void;
1250
1407
  /**
1251
- * Register a handler to be called when the chat subscription receives EOSE
1252
- * (End Of Stored Events), indicating that historical DMs have been delivered.
1253
- * The handler fires at most once per subscription lifecycle.
1254
- *
1255
- * @returns Unsubscribe function
1408
+ * Publish broadcast message
1256
1409
  */
1257
- onChatReady?(handler: () => void): () => void;
1258
- }
1259
- interface IncomingMessage {
1260
- id: string;
1261
- /** Transport-specific pubkey of sender */
1262
- senderTransportPubkey: string;
1263
- /** Sender's nametag (if known from NIP-17 unwrap) */
1264
- senderNametag?: string;
1265
- content: string;
1266
- timestamp: number;
1267
- encrypted: boolean;
1268
- /** Set when this is a self-wrap replay (sent message recovered from relay) */
1269
- isSelfWrap?: boolean;
1270
- /** Recipient pubkey — only present on self-wrap replays */
1271
- recipientTransportPubkey?: string;
1272
- }
1273
- type MessageHandler = (message: IncomingMessage) => void;
1274
- interface TokenTransferPayload {
1275
- /** Serialized token data */
1276
- token: string;
1277
- /** Inclusion proof */
1278
- proof: unknown;
1279
- /** Optional memo */
1280
- memo?: string;
1281
- /** Sender info */
1282
- sender?: {
1283
- /** Transport-specific pubkey */
1284
- transportPubkey: string;
1285
- nametag?: string;
1286
- };
1287
- }
1288
- interface IncomingTokenTransfer {
1289
- id: string;
1290
- /** Transport-specific pubkey of sender */
1291
- senderTransportPubkey: string;
1292
- payload: TokenTransferPayload;
1293
- timestamp: number;
1294
- }
1295
- type TokenTransferHandler = (transfer: IncomingTokenTransfer) => void | Promise<void>;
1296
- interface PaymentRequestPayload {
1297
- /** Amount requested (in smallest units) */
1298
- amount: string | bigint;
1299
- /** Coin/token type ID */
1300
- coinId: string;
1301
- /** Message/memo for recipient */
1302
- message?: string;
1303
- /** Recipient's nametag (who should pay) */
1304
- recipientNametag?: string;
1305
- /** Custom metadata */
1306
- metadata?: Record<string, unknown>;
1307
- }
1308
- interface IncomingPaymentRequest$1 {
1309
- /** Event ID */
1310
- id: string;
1311
- /** Transport-specific pubkey of sender */
1312
- senderTransportPubkey: string;
1313
- /** Sender's nametag (if included in encrypted content) */
1314
- senderNametag?: string;
1315
- /** Parsed request data */
1316
- request: {
1317
- requestId: string;
1318
- amount: string;
1319
- coinId: string;
1320
- message?: string;
1321
- recipientNametag?: string;
1322
- metadata?: Record<string, unknown>;
1323
- };
1324
- /** Timestamp */
1325
- timestamp: number;
1326
- }
1327
- type PaymentRequestHandler$1 = (request: IncomingPaymentRequest$1) => void;
1328
- type PaymentRequestResponseType$1 = 'accepted' | 'rejected' | 'paid';
1329
- interface PaymentRequestResponsePayload {
1330
- /** Original request ID */
1331
- requestId: string;
1332
- /** Response type */
1333
- responseType: PaymentRequestResponseType$1;
1334
- /** Optional message */
1335
- message?: string;
1336
- /** Transfer ID (if paid) */
1337
- transferId?: string;
1338
- }
1339
- interface IncomingPaymentRequestResponse {
1340
- /** Event ID */
1341
- id: string;
1342
- /** Transport-specific pubkey of responder */
1343
- responderTransportPubkey: string;
1344
- /** Parsed response data */
1345
- response: {
1346
- requestId: string;
1347
- responseType: PaymentRequestResponseType$1;
1348
- message?: string;
1349
- transferId?: string;
1350
- };
1351
- /** Timestamp */
1352
- timestamp: number;
1353
- }
1354
- type PaymentRequestResponseHandler$1 = (response: IncomingPaymentRequestResponse) => void;
1355
- interface IncomingBroadcast {
1356
- id: string;
1357
- /** Transport-specific pubkey of author */
1358
- authorTransportPubkey: string;
1359
- content: string;
1360
- tags: string[];
1361
- timestamp: number;
1362
- }
1363
- type BroadcastHandler = (broadcast: IncomingBroadcast) => void;
1364
- type TransportEventType = 'transport:connected' | 'transport:disconnected' | 'transport:reconnecting' | 'transport:error' | 'transport:relay_added' | 'transport:relay_removed' | 'message:received' | 'message:sent' | 'transfer:received' | 'transfer:sent';
1365
- interface TransportEvent {
1366
- type: TransportEventType;
1367
- timestamp: number;
1368
- data?: unknown;
1369
- error?: string;
1370
- }
1371
- type TransportEventCallback = (event: TransportEvent) => void;
1372
- /**
1373
- * Resolved peer identity information.
1374
- * Returned by resolve methods — contains all public address formats for a peer.
1375
- * The nametag field is optional (only present if a nametag is registered).
1376
- */
1377
- interface PeerInfo {
1378
- /** Nametag name (without @), if registered */
1379
- nametag?: string;
1380
- /** Transport-specific pubkey (for messaging/encryption) */
1381
- transportPubkey: string;
1382
- /** 33-byte compressed secp256k1 public key (for L3 chain) */
1383
- chainPubkey: string;
1384
- /** L3 DIRECT address (DIRECT://...) */
1385
- directAddress: string;
1386
- /** Event timestamp */
1387
- timestamp: number;
1388
- }
1389
- interface IncomingReadReceipt {
1390
- /** Transport-specific pubkey of the sender who read the message */
1391
- senderTransportPubkey: string;
1392
- /** Event ID of the message that was read */
1393
- messageEventId: string;
1394
- /** Timestamp */
1395
- timestamp: number;
1396
- }
1397
- type ReadReceiptHandler = (receipt: IncomingReadReceipt) => void;
1398
- interface IncomingTypingIndicator {
1399
- /** Transport-specific pubkey of the sender who is typing */
1400
- senderTransportPubkey: string;
1401
- /** Sender's nametag (if known) */
1402
- senderNametag?: string;
1403
- /** Timestamp */
1404
- timestamp: number;
1405
- }
1406
- type TypingIndicatorHandler = (indicator: IncomingTypingIndicator) => void;
1407
- type ComposingHandler = (indicator: ComposingIndicator) => void;
1408
-
1409
- /**
1410
- * transport/delivery-provider.ts — the `DeliveryProvider` port (sdk-changes S7,
1411
- * covenant §3.1-6).
1412
- *
1413
- * The seam that keeps the delivery rail swappable. In Unicity, a transfer —
1414
- * after certification — is just a file handoff, so the port is deliberately
1415
- * tiny: hand a finished token blob to a recipient, pull incoming deliveries,
1416
- * acknowledge them. `WalletApiMailboxProvider`
1417
- * (impl/shared/wallet-api/WalletApiMailboxProvider.ts) is the reference
1418
- * implementation; anything that can move a file can implement it (the port
1419
- * shape must not preclude the old Nostr transport or a future federated
1420
- * transport — neither is a deliverable here).
1421
- *
1422
- * Normative shapes (sdk-changes S7):
1423
- * - `DeliveryReceipt = { deliveryId }`
1424
- * - `IncomingDelivery = { deliveryId, transferId?, senderPubkey?, memo?,
1425
- * fetchBlob(), cursor }`
1426
- * - `deliveryId` is the **content-derived** entry id —
1427
- * `hex(SHA-256(tokenId bytes ‖ stateHash bytes))` — NEVER a server-assigned
1428
- * row id or seq (covenant §3.1-4; the contract suite asserts it). It is
1429
- * computed client-side ({@link computeDeliveryId}) and must equal the
1430
- * backend's `entry_id` (ARCHITECTURE §6).
1431
- * - **Custody is a composition-time property, not a per-call flag**:
1432
- * implementations take `custody: 'inventory' | 'external'` at construction
1433
- * and every ack sends the corresponding `intoInventory` — delivery-only
1434
- * safety must never depend on remembering an option at a call site.
1435
- * - Implementations MUST keep a **persistent `(tokenId, stateHash)` seen-set**
1436
- * for incoming deliveries: the recipient-side replay guard is part of the
1437
- * port contract, not a server promise (the recipient never trusts the
1438
- * backend — ARCHITECTURE §8.2). `deliveryId` is the canonical hash encoding
1439
- * of exactly that pair, so a persistent deliveryId set satisfies this.
1440
- */
1441
- /** Receipt for a delivered blob. `deliveryId` is content-derived — see module doc. */
1442
- interface DeliveryReceipt {
1443
- deliveryId: string;
1444
- }
1445
- /** Options for {@link DeliveryProvider.deliver}. */
1446
- interface DeliverOptions {
1410
+ publishBroadcast?(content: string, tags?: string[]): Promise<string>;
1447
1411
  /**
1448
- * The send's transferId (the E.3 intent id / realization seed). Recorded
1449
- * with the delivery so the recipient can group multi-token payments and the
1450
- * backend can evidence-check the sender's removals (ARCHITECTURE §5.3/§6).
1412
+ * Send payment request to a recipient
1413
+ * @param recipientTransportPubkey - Transport-specific pubkey for messaging
1414
+ * @returns Event ID
1451
1415
  */
1452
- transferId: string;
1453
- /** Optional human memo. Implementations encrypt it client-side (S6). */
1454
- memo?: string;
1416
+ sendPaymentRequest?(recipientTransportPubkey: string, request: PaymentRequestPayload): Promise<string>;
1455
1417
  /**
1456
- * The SENDER's own nametag (without a leading `@`), so the recipient can
1457
- * render the human identity instead of a raw pubkey ("Someone"). Bundled
1458
- * with the memo into ONE recipient-addressed (ECDH) `enc1.` envelope (S6) —
1459
- * the operator never sees it. Attached whenever the sender has a nametag OR
1460
- * a memo (so the nametag travels even on a memo-less transfer).
1418
+ * Subscribe to incoming payment requests
1419
+ * @returns Unsubscribe function
1461
1420
  */
1462
- senderNametag?: string;
1463
- }
1464
- /** One incoming delivery pulled from the feed. */
1465
- interface IncomingDelivery {
1466
- /** Content-derived id — `hex(SHA-256(tokenId bytes ‖ stateHash bytes))`. */
1467
- deliveryId: string;
1468
- /** The sender's transferId, when the transport carries it. */
1469
- transferId?: string;
1470
- /** The sender's pubkey, when the transport carries it. */
1471
- senderPubkey?: string;
1472
- /** Decrypted memo (S6), when present and decryptable. */
1473
- memo?: string;
1421
+ onPaymentRequest?(handler: PaymentRequestHandler$1): () => void;
1474
1422
  /**
1475
- * The sender's nametag (without a leading `@`), decrypted from the same
1476
- * recipient-addressed delivery envelope as {@link memo} (S6). Lets the
1477
- * receiver render the human identity instead of a raw pubkey, with no
1478
- * Nostr/transport lookup. Absent when the envelope carried none or could
1479
- * not be decrypted.
1423
+ * Send response to a payment request
1424
+ * @param recipientTransportPubkey - Transport-specific pubkey for messaging
1425
+ * @returns Event ID
1480
1426
  */
1481
- senderNametag?: string;
1482
- /** Fetch the finished token blob bytes (the encoded TokenBlob). */
1483
- fetchBlob(): Promise<Uint8Array>;
1484
- /** Transport-local resume cursor (opaque to callers). */
1485
- cursor: string;
1486
- }
1487
- type DeliveryDisposition = 'claimed' | 'rejected';
1488
- /**
1489
- * The §9 wake streams a backend may nudge: `mailbox` (incoming deliveries),
1490
- * `inventory` (owned-token set changed — e.g. a top-up or a claim on another
1491
- * device), and `payment_requests` (a request created/answered). A wake on any
1492
- * of these is a NUDGE — the consumer pulls that stream's cursor; correctness
1493
- * never depends on the wake arriving (the poll backstop is the source of
1494
- * truth).
1495
- */
1496
- type WakeStream = 'inventory' | 'mailbox' | 'payment_requests';
1497
- /**
1498
- * True liveness of the realtime wake channel (§9), decoupled from sign-in
1499
- * session state: `connecting`/`connected` — a socket is (being) established;
1500
- * `reconnecting` — it dropped and is backing off to re-establish (the poll
1501
- * backstop carries correctness meanwhile); `closed` — torn down intentionally.
1502
- * The wake is a nudge, so this is informational for the frontend (a "live"
1503
- * indicator) — never a correctness gate.
1504
- */
1505
- type WakeChannelStatus = 'connecting' | 'connected' | 'reconnecting' | 'closed';
1506
- /**
1507
- * Custody mode (composition-time): `'inventory'` — acknowledged deliveries
1508
- * enter the wallet-api inventory (the full wallet-api preset); `'external'` —
1509
- * the app's own storage keeps custody and acks perform ZERO inventory writes
1510
- * (the delivery-only preset, ARCHITECTURE §6 "delivery-only claim").
1511
- */
1512
- type DeliveryCustody = 'inventory' | 'external';
1513
- interface DeliveryProvider {
1514
- /** Composition-time custody property — never a per-call flag (S7). */
1515
- readonly custody: DeliveryCustody;
1427
+ sendPaymentRequestResponse?(recipientTransportPubkey: string, response: PaymentRequestResponsePayload): Promise<string>;
1516
1428
  /**
1517
- * Bind the wallet identity (optional — implementations that authenticate or
1518
- * encrypt per-wallet need it; mirrors `TokenStorageProvider.setIdentity`).
1429
+ * Subscribe to incoming payment request responses
1430
+ * @returns Unsubscribe function
1519
1431
  */
1520
- setIdentity?(identity: {
1521
- privateKey: string;
1522
- chainPubkey: string;
1523
- }): void;
1432
+ onPaymentRequestResponse?(handler: PaymentRequestResponseHandler$1): () => void;
1524
1433
  /**
1525
- * #583 per-address client isolation: mint an INDEPENDENT delivery provider for
1526
- * a different HD address, backed by its OWN authenticated client + wake socket
1527
- * (mirrors `TokenStorageProvider.createForAddress`). Implementations that hold
1528
- * a single mutable identity+session per instance (e.g. the wallet-api mailbox
1529
- * over one `WalletApiClient`) provide this so `Sphere.switchToAddress` can give
1530
- * each address its OWN delivery instance — an orphaned previous-address pump
1531
- * then re-auths as ITS OWN owner (harmless) instead of driving a client that
1532
- * was re-bound to the new owner. Stateless transports may omit it (the same
1533
- * instance serves every address).
1434
+ * Send a read receipt for a message
1435
+ * @param recipientTransportPubkey - Transport pubkey of the message sender
1436
+ * @param messageEventId - Event ID of the message being acknowledged
1534
1437
  */
1535
- createForAddress?(): DeliveryProvider;
1438
+ sendReadReceipt?(recipientTransportPubkey: string, messageEventId: string): Promise<void>;
1536
1439
  /**
1537
- * Hand a finished token blob to a recipient. `recipientPubkey` is the
1538
- * recipient's CHAIN pubkey (33-byte compressed secp256k1, hex) — the
1539
- * canonical Unicity identity (ARCHITECTURE §4); transports that address
1540
- * recipients differently resolve it themselves.
1541
- *
1542
- * MUST be idempotent per (token, state): re-delivering the same finished
1543
- * blob — including after the recipient claimed — succeeds and returns the
1544
- * same content-derived `deliveryId` (ARCHITECTURE §6 deposit idempotency).
1440
+ * Subscribe to incoming read receipts
1441
+ * @returns Unsubscribe function
1545
1442
  */
1546
- deliver(recipientPubkey: string, blob: Uint8Array, options: DeliverOptions): Promise<DeliveryReceipt>;
1443
+ onReadReceipt?(handler: ReadReceiptHandler): () => void;
1547
1444
  /**
1548
- * Pull-based feed of incoming deliveries since the given transport-local
1549
- * cursor (or the provider's persisted cursor when omitted). Yields only
1550
- * deliveries not yet in the persistent seen-set; completes when the feed is
1551
- * drained — callers re-invoke on poll/wake. Feeds the existing
1552
- * transport-agnostic `handleV2Transfer` (sdk-changes S3).
1445
+ * Send typing indicator to a recipient
1446
+ * @param recipientTransportPubkey - Transport pubkey of the conversation partner
1553
1447
  */
1554
- incoming(sinceCursor?: string): AsyncIterable<IncomingDelivery>;
1448
+ sendTypingIndicator?(recipientTransportPubkey: string): Promise<void>;
1555
1449
  /**
1556
- * Acknowledge a delivery: `'claimed'` accepts it (with the provider's
1557
- * composition-time custody), `'rejected'` marks it locally-unverifiable —
1558
- * terminal for discovery only (the entry stays claimable server-side and
1559
- * its blob is retained — ARCHITECTURE §6). Both record the delivery in the
1560
- * persistent seen-set.
1450
+ * Subscribe to incoming typing indicators
1451
+ * @returns Unsubscribe function
1561
1452
  */
1562
- ack(deliveryId: string, disposition: DeliveryDisposition): Promise<void>;
1453
+ onTypingIndicator?(handler: TypingIndicatorHandler): () => void;
1563
1454
  /**
1564
- * Optional batch acknowledge (#623): claim and reject whole pages of incoming deliveries in a
1565
- * single request each, instead of one per entry — so draining a large inbox (a long-offline or
1566
- * service wallet) doesn't fire thousands of writes and trip the per-owner rate limit. Same
1567
- * semantics as {@link ack}: the seen-set records only entries that were acked successfully, so a
1568
- * partial/failed batch is re-listed and re-processed (idempotent claim, §6). A provider that does
1569
- * not implement it (e.g. the relay no-op) is driven via per-entry {@link ack}.
1455
+ * Send composing indicator to a recipient using NIP-44 encrypted gift wrap
1456
+ * @param recipientTransportPubkey - Transport pubkey of the conversation partner
1457
+ * @param content - JSON payload with senderNametag and expiresIn
1570
1458
  */
1571
- ackBatch?(claimed: string[], rejected: string[]): Promise<void>;
1459
+ sendComposingIndicator?(recipientTransportPubkey: string, content: string): Promise<void>;
1572
1460
  /**
1573
- * Optional batch deliver (#699): hand N finished blobs to ONE recipient with a single deposit
1574
- * request (and one upload-urls request) instead of N — a multi-source send then costs O(1)
1575
- * against the backend's deposit rate limit regardless of fragmentation. Optional like
1576
- * {@link ackBatch}: the port must not preclude the relay transport or a future federated one,
1577
- * and neither has a batch primitive — callers probe and fall back to per-blob {@link deliver}.
1461
+ * Subscribe to incoming composing indicators
1462
+ * @returns Unsubscribe function
1463
+ */
1464
+ onComposing?(handler: ComposingHandler): () => void;
1465
+ /**
1466
+ * Get list of configured relay URLs
1467
+ */
1468
+ getRelays?(): string[];
1469
+ /**
1470
+ * Get list of currently connected relay URLs
1471
+ */
1472
+ getConnectedRelays?(): string[];
1473
+ /**
1474
+ * Add a relay dynamically
1475
+ * @returns true if added successfully
1476
+ */
1477
+ addRelay?(relayUrl: string): Promise<boolean>;
1478
+ /**
1479
+ * Remove a relay dynamically
1480
+ * @returns true if removed successfully
1481
+ */
1482
+ removeRelay?(relayUrl: string): Promise<boolean>;
1483
+ /**
1484
+ * Check if a relay is configured
1485
+ */
1486
+ hasRelay?(relayUrl: string): boolean;
1487
+ /**
1488
+ * Check if a relay is currently connected
1489
+ */
1490
+ isRelayConnected?(relayUrl: string): boolean;
1491
+ /**
1492
+ * Set fallback 'since' timestamp for event subscriptions.
1493
+ * Used when switching to an address that has never subscribed before.
1494
+ * The transport uses this instead of 'now' as the initial since filter,
1495
+ * ensuring events sent while the address was inactive are not missed.
1496
+ * Consumed once by the next subscription setup, then cleared.
1578
1497
  *
1579
- * Semantically equivalent to awaiting {@link deliver} once per blob, in order:
1580
- * - receipts return in REQUEST order; each `deliveryId` is the content-derived entry id
1581
- * (covenant §3.1-4 — NEVER the batch endpoint's server-assigned seq);
1582
- * - idempotent per (token, state) exactly like {@link deliver};
1583
- * - `options` apply to every blob (one send = one transferId/memo/senderNametag);
1584
- * - throws when ANY blob could not be deposited — blobs that DID land are absorbed
1585
- * idempotently when the caller retries, batched or per-blob.
1498
+ * @param sinceSeconds - Unix timestamp in seconds
1586
1499
  */
1587
- deliverBatch?(recipientPubkey: string, blobs: Uint8Array[], options: DeliverOptions): Promise<DeliveryReceipt[]>;
1500
+ setFallbackSince?(sinceSeconds: number): void;
1588
1501
  /**
1589
- * Optional wake hook: `callback` fires with the {@link WakeStream} that was
1590
- * nudged when new data may be available on it (e.g. a WS nudge — never a
1591
- * correctness dependency, ARCHITECTURE §9). The wallet-api wake socket
1592
- * multiplexes all three owner streams (`mailbox` | `inventory` |
1593
- * `payment_requests`); the consumer routes each to that stream's pull.
1502
+ * Set fallback 'since' timestamp for DM (gift-wrap) subscriptions.
1503
+ * Used when no persisted DM timestamp exists in storage (e.g. first connect).
1504
+ * Consumed once by the next subscription setup, then cleared.
1594
1505
  *
1595
- * The underlying socket SELF-HEALS (§9): it reconnects with backoff on any
1596
- * drop and a liveness watchdog force-reconnects a half-open socket. On every
1597
- * (re)connect the consumer MUST run a full catch-up pull of every stream —
1598
- * wakes missed while the socket was dead are not replayed — so `callback`
1599
- * fires once for EACH stream on (re)connect (a synthetic catch-up nudge).
1600
- * `onStatus` (optional) surfaces true socket liveness for the frontend,
1601
- * decoupled from sign-in state. Returns an unsubscribe function.
1506
+ * @param sinceSeconds - Unix timestamp in seconds
1602
1507
  */
1603
- onWake?(callback: (stream: WakeStream) => void, onStatus?: (status: WakeChannelStatus) => void): () => void;
1508
+ setFallbackDmSince?(sinceSeconds: number): void;
1604
1509
  /**
1605
- * Late-bind the backend-true (tokenId, stateHash) derivation —
1606
- * `ITokenEngine.deliveryKeys`. Compositions are engine-less (the engine is
1607
- * built later); the module that owns both (PaymentsModule) binds this at
1608
- * init. Implementations that derive ids (S7) MUST use it and fail loudly if
1609
- * unbound; transports that don't derive may omit the method.
1510
+ * Fetch pending events from transport (one-shot query).
1511
+ * Creates a temporary subscription, processes events through normal handlers,
1512
+ * and resolves after EOSE (End Of Stored Events).
1610
1513
  */
1611
- bindDeliveryKeys?(derive: (blobBytes: Uint8Array) => Promise<{
1612
- tokenId: string;
1613
- stateHash: string;
1614
- }>): void;
1514
+ fetchPendingEvents?(): Promise<void>;
1515
+ /**
1516
+ * Register a handler to be called when the chat subscription receives EOSE
1517
+ * (End Of Stored Events), indicating that historical DMs have been delivered.
1518
+ * The handler fires at most once per subscription lifecycle.
1519
+ *
1520
+ * @returns Unsubscribe function
1521
+ */
1522
+ onChatReady?(handler: () => void): () => void;
1523
+ }
1524
+ interface IncomingMessage {
1525
+ id: string;
1526
+ /** Transport-specific pubkey of sender */
1527
+ senderTransportPubkey: string;
1528
+ /** Sender's nametag (if known from NIP-17 unwrap) */
1529
+ senderNametag?: string;
1530
+ content: string;
1531
+ timestamp: number;
1532
+ encrypted: boolean;
1533
+ /** Set when this is a self-wrap replay (sent message recovered from relay) */
1534
+ isSelfWrap?: boolean;
1535
+ /** Recipient pubkey — only present on self-wrap replays */
1536
+ recipientTransportPubkey?: string;
1537
+ }
1538
+ type MessageHandler = (message: IncomingMessage) => void;
1539
+ interface TokenTransferPayload {
1540
+ /** Serialized token data */
1541
+ token: string;
1542
+ /** Inclusion proof */
1543
+ proof: unknown;
1544
+ /** Optional memo */
1545
+ memo?: string;
1546
+ /** Sender info */
1547
+ sender?: {
1548
+ /** Transport-specific pubkey */
1549
+ transportPubkey: string;
1550
+ nametag?: string;
1551
+ };
1615
1552
  }
1616
- /**
1617
- * The content-derived delivery id:
1618
- * `hex(SHA-256(tokenId bytes ‖ stateHash bytes))` — identical to the
1619
- * backend's `entry_id` (ARCHITECTURE §6), computed client-side so no
1620
- * implementation can substitute a server row id (covenant §3.1-4).
1621
- *
1622
- * @param tokenIdHex - genesis-stable 64-hex token id
1623
- * @param stateHashHex - the SDK's per-state hash (DataHash imprint, hex) — the
1624
- * backend's `state_hash` (wallet-api validation/chain.ts); NEVER a plain
1625
- * sha256 over the token bytes (that variant 422s on deposit).
1626
- */
1627
- declare function computeDeliveryId(tokenIdHex: string, stateHashHex: string): string;
1628
- /** Full delivery keys: the engine-derived pair + the composed deliveryId. */
1629
- interface DeliveryBlobKeys {
1630
- /** Genesis-stable 64-hex token id (from the TokenBlob envelope). */
1631
- tokenId: string;
1632
- /** Per-state hash: `hex(SHA-256(inner token bytes))` — changes every transfer. */
1633
- stateHash: string;
1634
- /** {@link computeDeliveryId} of the two above. */
1635
- deliveryId: string;
1553
+ interface IncomingTokenTransfer {
1554
+ id: string;
1555
+ /** Transport-specific pubkey of sender */
1556
+ senderTransportPubkey: string;
1557
+ payload: TokenTransferPayload;
1558
+ timestamp: number;
1636
1559
  }
1560
+ type TokenTransferHandler = (transfer: IncomingTokenTransfer) => void | Promise<void>;
1561
+ interface PaymentRequestPayload {
1562
+ /** Amount requested (in smallest units) */
1563
+ amount: string | bigint;
1564
+ /** Coin/token type ID */
1565
+ coinId: string;
1566
+ /** Message/memo for recipient */
1567
+ message?: string;
1568
+ /** Recipient's nametag (who should pay) */
1569
+ recipientNametag?: string;
1570
+ /** Custom metadata */
1571
+ metadata?: Record<string, unknown>;
1572
+ }
1573
+ interface IncomingPaymentRequest$1 {
1574
+ /** Event ID */
1575
+ id: string;
1576
+ /** Transport-specific pubkey of sender */
1577
+ senderTransportPubkey: string;
1578
+ /** Sender's nametag (if included in encrypted content) */
1579
+ senderNametag?: string;
1580
+ /** Parsed request data */
1581
+ request: {
1582
+ requestId: string;
1583
+ amount: string;
1584
+ coinId: string;
1585
+ message?: string;
1586
+ recipientNametag?: string;
1587
+ metadata?: Record<string, unknown>;
1588
+ };
1589
+ /** Timestamp */
1590
+ timestamp: number;
1591
+ }
1592
+ type PaymentRequestHandler$1 = (request: IncomingPaymentRequest$1) => void;
1593
+ type PaymentRequestResponseType$1 = 'accepted' | 'rejected' | 'paid';
1594
+ interface PaymentRequestResponsePayload {
1595
+ /** Original request ID */
1596
+ requestId: string;
1597
+ /** Response type */
1598
+ responseType: PaymentRequestResponseType$1;
1599
+ /** Optional message */
1600
+ message?: string;
1601
+ /** Transfer ID (if paid) */
1602
+ transferId?: string;
1603
+ }
1604
+ interface IncomingPaymentRequestResponse {
1605
+ /** Event ID */
1606
+ id: string;
1607
+ /** Transport-specific pubkey of responder */
1608
+ responderTransportPubkey: string;
1609
+ /** Parsed response data */
1610
+ response: {
1611
+ requestId: string;
1612
+ responseType: PaymentRequestResponseType$1;
1613
+ message?: string;
1614
+ transferId?: string;
1615
+ };
1616
+ /** Timestamp */
1617
+ timestamp: number;
1618
+ }
1619
+ type PaymentRequestResponseHandler$1 = (response: IncomingPaymentRequestResponse) => void;
1620
+ interface IncomingBroadcast {
1621
+ id: string;
1622
+ /** Transport-specific pubkey of author */
1623
+ authorTransportPubkey: string;
1624
+ content: string;
1625
+ tags: string[];
1626
+ timestamp: number;
1627
+ }
1628
+ type BroadcastHandler = (broadcast: IncomingBroadcast) => void;
1629
+ type TransportEventType = 'transport:connected' | 'transport:disconnected' | 'transport:reconnecting' | 'transport:error' | 'transport:relay_added' | 'transport:relay_removed' | 'message:received' | 'message:sent' | 'transfer:received' | 'transfer:sent';
1630
+ interface TransportEvent {
1631
+ type: TransportEventType;
1632
+ timestamp: number;
1633
+ data?: unknown;
1634
+ error?: string;
1635
+ }
1636
+ type TransportEventCallback = (event: TransportEvent) => void;
1637
1637
  /**
1638
- * Derive (tokenId, stateHash, deliveryId) from encoded TokenBlob bytes.
1639
- * Throws when the bytes are not a decodable TokenBlob.
1640
- */
1641
- /**
1642
- * Compose full delivery keys from an engine-derived (tokenId, stateHash) pair.
1643
- * The pair MUST come from `ITokenEngine.deliveryKeys` (the backend-true SDK
1644
- * derivation) — the port module deliberately cannot decode tokens itself.
1638
+ * Resolved peer identity information.
1639
+ * Returned by resolve methods — contains all public address formats for a peer.
1640
+ * The nametag field is optional (only present if a nametag is registered).
1645
1641
  */
1646
- declare function composeDeliveryKeys(keys: {
1647
- tokenId: string;
1648
- stateHash: string;
1649
- }): DeliveryBlobKeys;
1642
+ interface PeerInfo {
1643
+ /** Nametag name (without @), if registered */
1644
+ nametag?: string;
1645
+ /** Transport-specific pubkey (for messaging/encryption) */
1646
+ transportPubkey: string;
1647
+ /** 33-byte compressed secp256k1 public key (for L3 chain) */
1648
+ chainPubkey: string;
1649
+ /** L3 DIRECT address (DIRECT://...) */
1650
+ directAddress: string;
1651
+ /** Event timestamp */
1652
+ timestamp: number;
1653
+ }
1654
+ interface IncomingReadReceipt {
1655
+ /** Transport-specific pubkey of the sender who read the message */
1656
+ senderTransportPubkey: string;
1657
+ /** Event ID of the message that was read */
1658
+ messageEventId: string;
1659
+ /** Timestamp */
1660
+ timestamp: number;
1661
+ }
1662
+ type ReadReceiptHandler = (receipt: IncomingReadReceipt) => void;
1663
+ interface IncomingTypingIndicator {
1664
+ /** Transport-specific pubkey of the sender who is typing */
1665
+ senderTransportPubkey: string;
1666
+ /** Sender's nametag (if known) */
1667
+ senderNametag?: string;
1668
+ /** Timestamp */
1669
+ timestamp: number;
1670
+ }
1671
+ type TypingIndicatorHandler = (indicator: IncomingTypingIndicator) => void;
1672
+ type ComposingHandler = (indicator: ComposingIndicator) => void;
1650
1673
 
1651
1674
  /**
1652
1675
  * Price Provider Interface
@@ -1813,22 +1836,6 @@ declare function createPriceProvider(config: PriceProviderConfig): PriceProvider
1813
1836
  * Single source of truth: {@link HistoryRecord} in `storage/storage-provider.ts`.
1814
1837
  */
1815
1838
  type TransactionHistoryEntry = HistoryRecord;
1816
- /**
1817
- * @deprecated v2 transfers arrive as finished tokens — there is no finalization
1818
- * phase. The options are accepted for backwards compatibility and ignored.
1819
- */
1820
- interface ReceiveOptions {
1821
- /** @deprecated Ignored — v2 tokens are stored confirmed on receipt. */
1822
- finalize?: boolean;
1823
- /** @deprecated Ignored. */
1824
- timeout?: number;
1825
- /** @deprecated Ignored. */
1826
- pollInterval?: number;
1827
- }
1828
- interface ReceiveResult {
1829
- /** Newly received incoming transfers. */
1830
- transfers: IncomingTransfer[];
1831
- }
1832
1839
  interface PaymentsModuleConfig {
1833
1840
  /** Auto-sync after operations */
1834
1841
  autoSync?: boolean;
@@ -2062,15 +2069,17 @@ declare class PaymentsModule {
2062
2069
  private readonly moduleConfig;
2063
2070
  private deps;
2064
2071
  private tokens;
2065
- private tombstones;
2066
- private tombstoneKeySet;
2067
- private _historyCache;
2068
2072
  private nametags;
2069
- private paymentRequests;
2070
- private paymentRequestHandlers;
2071
- private outgoingPaymentRequests;
2072
- private paymentRequestResponseHandlers;
2073
- private pendingResponseResolvers;
2073
+ /** Inventory reads: balances/assets, the token accessors, tombstones, validate(). */
2074
+ private readonly inventory;
2075
+ /** Payment requests (incoming + outgoing, the S4 pump, the #441 journal). */
2076
+ private readonly requests;
2077
+ /** Transaction history (the cache, the dedupKey rules, the §10 server log). */
2078
+ private readonly history;
2079
+ /** E.3/E.4 intent resume (the sign-in replay of OPEN send intents). */
2080
+ private readonly intents;
2081
+ /** Receive + delivery (receive(), the v2 receiver, the journal, the replay budget). */
2082
+ private readonly deliveries;
2074
2083
  /** The single delivery seam — an injected provider or the transport adapter. */
2075
2084
  private delivery;
2076
2085
  /** Set only when a provider was INJECTED — gates the incoming pump (S3). */
@@ -2087,35 +2096,6 @@ declare class PaymentsModule {
2087
2096
  /** S6 field-encryption key (intent payloads, history memos) — per identity. */
2088
2097
  private fieldEncryptionKey;
2089
2098
  private checkpointStore;
2090
- private prPollTimer;
2091
- /** Coalesces concurrent payment-request pump runs. */
2092
- private prPumpInFlight;
2093
- /**
2094
- * Set after the once-per-session full incoming hydration (#556): the surfaced
2095
- * incoming list is in-memory only, so on a fresh engine the CURRENT state of
2096
- * ALL incoming requests — open AND resolved (paid/declined/expired) — must be
2097
- * rebuilt from a `role=incoming&since=0` pull, not just the still-open ones.
2098
- * A status-filtered bootstrap (the pre-#556 `status=open` scan) dropped
2099
- * requests resolved in a PRIOR session, so the payer reopened and the
2100
- * 'Paid Successfully' request was gone (twin of #521/#549).
2101
- */
2102
- private prBootstrapped;
2103
- /**
2104
- * #441 deferred-paid journal (durable, per network+identity): links a payment
2105
- * request to the in-flight transfer of a possibly-committed pay so the request
2106
- * is held NON-payable ('settling') until that transfer completes (→ 'paid',
2107
- * server told) or aborts (→ payable, journal cleared). Keyed by request wire id
2108
- * (== requestId for wallet-api-surfaced requests). The in-memory Map is the
2109
- * synchronous source of truth used by the reload re-apply seam; it is
2110
- * single-flight loaded and every read-modify-write is serialized through a tail
2111
- * promise (the #679/#680 lesson: an unserialized RMW drops entries under
2112
- * concurrency = a re-payable request = the double-pay this fix prevents).
2113
- */
2114
- private settlingJournal;
2115
- private settlingJournalLoad;
2116
- private settlingJournalWrite;
2117
- /** #441: guard overlapping resume reconciles (resumeOpenIntents has 2 fire-and-forget call sites). */
2118
- private reconcileInFlight;
2119
2099
  private loadedPromise;
2120
2100
  private loaded;
2121
2101
  /**
@@ -2132,20 +2112,6 @@ declare class PaymentsModule {
2132
2112
  private loadInFlightOwner;
2133
2113
  private loadRerunRequested;
2134
2114
  private loadRerunTimer;
2135
- /**
2136
- * Owner (chainPubkey) whose server history hydration last completed —
2137
- * enables the #642 incremental fast path in {@link hydrateHistoryFromServer}.
2138
- * Reset on re-init (address switch). `serverSeenHistoryKeys` holds only
2139
- * dedupKeys actually PULLED from the server (never locally-POSTed ones), so
2140
- * the incremental stop condition can't be masked by our own fresh POSTs;
2141
- * `incrementalHistoryPulls` forces a periodic full re-pull to bound the
2142
- * staleness window if server keyset order ever diverges from arrival order.
2143
- * `hydrationEpoch` guards a pull racing a same-owner re-init.
2144
- */
2145
- private historyHydratedFor;
2146
- private serverSeenHistoryKeys;
2147
- private incrementalHistoryPulls;
2148
- private hydrationEpoch;
2149
2115
  private inventoryDebounceTimer;
2150
2116
  private static readonly SYNC_DEBOUNCE_MS;
2151
2117
  /** Quiet-then-escalate logging for the background wallet-api pumps (#630). */
@@ -2156,43 +2122,45 @@ declare class PaymentsModule {
2156
2122
  private tokenChangeCallbacks;
2157
2123
  private readonly reservationLedger;
2158
2124
  private readonly spendPlanner;
2159
- /**
2160
- * Per-request single-flight for {@link payPaymentRequest}. The pay flow flips the request to
2161
- * 'accepted' before it awaits send(), and the status guard re-admits 'accepted' (intended for a
2162
- * sequential retry-after-failure) — so a concurrent double-tap / second session would otherwise
2163
- * enter send() a second time and DOUBLE-PAY (each send picks a different token; the reservation
2164
- * ledger is coin-scoped, not request-scoped). Concurrent calls for the same requestId coalesce
2165
- * onto the first in-flight pay; the entry is cleared when it settles, so a later retry is unaffected.
2166
- */
2167
- private readonly payInFlight;
2168
2125
  private spendQueue;
2169
2126
  /** Cache of parsed SdkToken data for synchronous queue re-evaluation */
2170
2127
  private readonly parsedTokenCache;
2128
+ constructor(config?: PaymentsModuleConfig);
2171
2129
  /**
2172
- * Base delay (ms) for the journaled-delivery replay backoff (#517 item 1).
2173
- * A field, not a const, so tests can drive the bounded retry loop without
2174
- * waiting real time — never mutated in production.
2130
+ * The narrow seam {@link TokenView} reaches back through. Live getters for
2131
+ * `deps` and `priceProvider` (both swapped on {@link initialize}); the token
2132
+ * map is handed over as a READ-ONLY view, so the inventory view never becomes
2133
+ * a second writer of it.
2175
2134
  */
2176
- private replayBackoffBaseMs;
2177
- /** Deferral window for recipient-quota (429) deliveries (#621). Overridable in tests. */
2178
- private deliveryDeferralMs;
2135
+ private tokenViewHost;
2179
2136
  /**
2180
- * #517: serializes {@link replayPendingV2Deliveries}. `load()` kicks replay
2181
- * off fire-and-forget and `receive()` calls `load()`, so two passes can
2182
- * overlap — duplicating delivery attempts and clobbering each other's
2183
- * `attempts` increments (both read the same stale value, both write N+1,
2184
- * delaying poison surfacing). Only one pass runs per module instance at a time.
2137
+ * The narrow seam {@link Delivery} reaches back through. Live getters for
2138
+ * `deps` and `delivery` (both swapped on every {@link initialize}); the token
2139
+ * map is handed over as a READ-ONLY view and written only by the module's own
2140
+ * `storeEngineToken`, so delivery never becomes a second writer.
2185
2141
  */
2186
- private replayInFlight;
2142
+ private deliveryHost;
2187
2143
  /**
2188
- * #517: serializes every read-modify-write of the PENDING_V2_DELIVERIES journal.
2189
- * save/remove/update each load → mutate → store the WHOLE blob, so a replay
2190
- * (fire-and-forget from load()) overlapping a send()'s journal write could
2191
- * clobber a newly-saved undelivered entry and lose it — defeating the crash-
2192
- * safety guarantee. A promise-chain mutex makes each whole RMW atomic.
2144
+ * The narrow seam {@link PaymentRequests} reaches back through. Live getters,
2145
+ * not a snapshot: `deps` is swapped on every {@link initialize} (address
2146
+ * switch) and the feature must always observe the CURRENT one.
2193
2147
  */
2194
- private journalMutation;
2195
- constructor(config?: PaymentsModuleConfig);
2148
+ private paymentRequestsHost;
2149
+ /**
2150
+ * The narrow seam {@link TransferHistory} reaches back through. Live getter
2151
+ * for `deps` (swapped on every {@link initialize}); the rest are thunks onto
2152
+ * the module's own private helpers — history never touches the token map or
2153
+ * `save()`.
2154
+ */
2155
+ private transferHistoryHost;
2156
+ /**
2157
+ * The narrow seam {@link IntentResume} reaches back through. Live getters for
2158
+ * `deps` and `delivery` (both swapped on every {@link initialize}); the rest
2159
+ * are thunks onto the module's own helpers. The token map is READ through
2160
+ * `getHeldToken` and written only by the module's `removeToken` /
2161
+ * `storeEngineToken` — resume never becomes a second writer.
2162
+ */
2163
+ private intentResumeHost;
2196
2164
  /**
2197
2165
  * Get the current module configuration.
2198
2166
  *
@@ -2515,141 +2483,40 @@ declare class PaymentsModule {
2515
2483
  * fetched only when a token is selected to be spent.
2516
2484
  */
2517
2485
  private mergeLazyInventory;
2518
- /**
2519
- * Send a payment request to someone
2520
- * @param recipientPubkeyOrNametag - Recipient's pubkey or @nametag
2521
- * @param request - Payment request details
2522
- * @returns Result with event ID
2523
- */
2486
+ /** Send a payment request to someone. @see PaymentRequests.sendPaymentRequest */
2524
2487
  sendPaymentRequest(recipientPubkeyOrNametag: string, request: Omit<PaymentRequest, 'id' | 'createdAt'>): Promise<PaymentRequestResult>;
2525
- /**
2526
- * S4: create the request via wallet-api (§16). The payer is addressed by
2527
- * CHAIN pubkey (the canonical identity); the memo is S6-encrypted client-
2528
- * side BEFORE it leaves the device (§8.3) — the operator stores ciphertext.
2529
- * Mirrors the transport path's no-throw contract: failures (including the
2530
- * §5.5 per-payer cap → 429) come back as `{ success: false, error }`.
2531
- */
2532
- private sendWalletApiPaymentRequest;
2533
- /**
2534
- * Subscribe to incoming payment requests
2535
- * @param handler - Handler function for incoming requests
2536
- * @returns Unsubscribe function
2537
- */
2488
+ /** Subscribe to incoming payment requests. @see PaymentRequests.onPaymentRequest */
2538
2489
  onPaymentRequest(handler: PaymentRequestHandler): () => void;
2539
- /**
2540
- * Get all payment requests
2541
- * @param filter - Optional status filter
2542
- */
2490
+ /** Get all payment requests. @see PaymentRequests.getPaymentRequests */
2543
2491
  getPaymentRequests(filter?: {
2544
2492
  status?: PaymentRequestStatus;
2545
2493
  }): IncomingPaymentRequest[];
2546
- /**
2547
- * Get the count of payment requests with status `'pending'`.
2548
- *
2549
- * @returns Number of pending incoming payment requests.
2550
- */
2494
+ /** Count of incoming payment requests with status `'pending'`. @see PaymentRequests.getPendingPaymentRequestsCount */
2551
2495
  getPendingPaymentRequestsCount(): number;
2552
- /**
2553
- * Accept a payment request and notify the requester.
2554
- *
2555
- * Marks the request as `'accepted'` and sends a response via transport.
2556
- * The caller should subsequently call {@link send} to fulfill the payment.
2557
- *
2558
- * @param requestId - ID of the incoming payment request to accept.
2559
- */
2560
- acceptPaymentRequest(requestId: string): Promise<void>;
2561
- /**
2562
- * Reject a payment request and notify the requester.
2563
- *
2564
- * On the wallet-api path (S4) the respond IS the state change — it is
2565
- * confirmed server-side (`action: 'declined'`, §16) before the local status
2566
- * flips, and a server rejection (403/409) propagates to the caller. The
2567
- * transport path is best-effort and never throws.
2568
- *
2569
- * @param requestId - ID of the incoming payment request to reject.
2570
- */
2496
+ /** Reject a payment request and notify the requester. @see PaymentRequests.rejectPaymentRequest */
2571
2497
  rejectPaymentRequest(requestId: string): Promise<void>;
2572
- /**
2573
- * Mark a payment request as paid (local status update only).
2574
- *
2575
- * Typically called after a successful {@link send} to record that the
2576
- * request has been fulfilled.
2577
- *
2578
- * @param requestId - ID of the incoming payment request to mark as paid.
2579
- */
2580
- markPaymentRequestPaid(requestId: string): void;
2581
- /**
2582
- * Remove resolved incoming payment requests from memory.
2583
- *
2584
- * Keeps requests with status `'pending'` OR `'settling'` (#441). A `'settling'`
2585
- * request is UNRESOLVED — its linked transfer is still in-flight — so evicting
2586
- * it would drop the in-memory hold that keeps it non-payable until the journal
2587
- * re-surfaces it. Only terminal statuses (`'paid'`/`'rejected'`/`'expired'`)
2588
- * are removed.
2589
- */
2498
+ /** Remove resolved incoming payment requests from memory. @see PaymentRequests.clearProcessedPaymentRequests */
2590
2499
  clearProcessedPaymentRequests(): void;
2591
- /**
2592
- * Remove a specific incoming payment request by ID.
2593
- *
2594
- * @param requestId - ID of the payment request to remove.
2595
- */
2500
+ /** Remove a specific incoming payment request by ID. @see PaymentRequests.removePaymentRequest */
2596
2501
  removePaymentRequest(requestId: string): void;
2597
- /**
2598
- * Pay a payment request directly
2599
- * Convenience method that accepts, sends, and marks as paid
2600
- */
2502
+ /** Pay a payment request directly. @see PaymentRequests.payPaymentRequest */
2601
2503
  payPaymentRequest(requestId: string, memo?: string): Promise<TransferResult>;
2602
- private payPaymentRequestInner;
2603
- private updatePaymentRequestStatus;
2604
- /**
2605
- * Get outgoing payment requests
2606
- * @param filter - Optional status filter
2607
- */
2504
+ /** Get outgoing payment requests. @see PaymentRequests.getOutgoingPaymentRequests */
2608
2505
  getOutgoingPaymentRequests(filter?: {
2609
2506
  status?: PaymentRequestStatus;
2610
2507
  }): OutgoingPaymentRequest[];
2611
- /**
2612
- * Subscribe to payment request responses (for outgoing requests)
2613
- * @param handler - Handler function for incoming responses
2614
- * @returns Unsubscribe function
2615
- */
2508
+ /** Subscribe to payment request responses. @see PaymentRequests.onPaymentRequestResponse */
2616
2509
  onPaymentRequestResponse(handler: PaymentRequestResponseHandler): () => void;
2617
- /**
2618
- * Wait for a response to a payment request
2619
- * @param requestId - The outgoing request ID to wait for
2620
- * @param timeoutMs - Timeout in milliseconds (default: 60000)
2621
- * @returns Promise that resolves with the response or rejects on timeout
2622
- */
2510
+ /** Wait for a response to a payment request. @see PaymentRequests.waitForPaymentResponse */
2623
2511
  waitForPaymentResponse(requestId: string, timeoutMs?: number): Promise<PaymentRequestResponse>;
2624
- /**
2625
- * Cancel an active {@link waitForPaymentResponse} call.
2626
- *
2627
- * The pending promise is rejected with a `'Cancelled'` error.
2628
- *
2629
- * @param requestId - The outgoing request ID whose wait should be cancelled.
2630
- */
2512
+ /** Cancel an active {@link waitForPaymentResponse} call. @see PaymentRequests.cancelWaitForPaymentResponse */
2631
2513
  cancelWaitForPaymentResponse(requestId: string): void;
2632
- /**
2633
- * Remove an outgoing payment request and cancel any pending wait.
2634
- *
2635
- * @param requestId - ID of the outgoing request to remove.
2636
- */
2514
+ /** Remove an outgoing payment request and cancel any pending wait. @see PaymentRequests.removeOutgoingPaymentRequest */
2637
2515
  removeOutgoingPaymentRequest(requestId: string): void;
2638
- /**
2639
- * Remove all outgoing payment requests that are `'paid'`, `'rejected'`, or `'expired'`.
2640
- */
2516
+ /** Remove all `'paid'`/`'rejected'`/`'expired'` outgoing requests. @see PaymentRequests.clearCompletedOutgoingPaymentRequests */
2641
2517
  clearCompletedOutgoingPaymentRequests(): void;
2642
- /**
2643
- * Fold a payment-request response into the outgoing surface and notify —
2644
- * shared by the transport subscription and the wallet-api outgoing refresh
2645
- * (S4): update the matched outgoing request, resolve any
2646
- * {@link waitForPaymentResponse} waiter, emit the event, run the handlers.
2647
- */
2648
- private dispatchPaymentRequestResponse;
2649
- /**
2650
- * Send a response to a payment request (used internally by accept/reject/pay methods)
2651
- */
2652
- private sendPaymentRequestResponse;
2518
+ /** Pull the wallet-api payment-request streams now (S4). @see PaymentRequests.syncPaymentRequests */
2519
+ syncPaymentRequests(): Promise<void>;
2653
2520
  /**
2654
2521
  * Fetch and process pending incoming transfers from the transport layer.
2655
2522
  *
@@ -2663,6 +2530,7 @@ declare class PaymentsModule {
2663
2530
  * @param _options - Deprecated; the v1 finalization options are ignored.
2664
2531
  * @param callback - Optional callback invoked for each newly received transfer
2665
2532
  * @returns ReceiveResult with the newly received transfers
2533
+ * @see Delivery.receive
2666
2534
  */
2667
2535
  receive(_options?: ReceiveOptions, callback?: (transfer: IncomingTransfer) => void): Promise<ReceiveResult>;
2668
2536
  /**
@@ -2672,6 +2540,8 @@ declare class PaymentsModule {
2672
2540
  /**
2673
2541
  * Get total portfolio value in USD.
2674
2542
  * Returns null if PriceProvider is not configured.
2543
+ *
2544
+ * @see TokenView.getFiatBalance
2675
2545
  */
2676
2546
  getFiatBalance(): Promise<number | null>;
2677
2547
  /**
@@ -2689,6 +2559,7 @@ declare class PaymentsModule {
2689
2559
  *
2690
2560
  * @param coinId - Optional coin ID to filter by (e.g. hex string). When omitted, all coin types are returned.
2691
2561
  * @returns Array of balance summaries (synchronous — no await needed).
2562
+ * @see TokenView.getBalance
2692
2563
  */
2693
2564
  getBalance(coinId?: string): Asset[];
2694
2565
  /**
@@ -2697,22 +2568,10 @@ declare class PaymentsModule {
2697
2568
  * (`'transferring'`) tokens are reported only in the `transferring*` fields
2698
2569
  * and excluded from `totalAmount` (#517 item 3). Fiat value derives from
2699
2570
  * `totalAmount`, so it likewise excludes in-flight value.
2700
- */
2701
- getAssets(coinId?: string): Promise<Asset[]>;
2702
- /**
2703
- * Aggregate tokens by coinId with confirmed/unconfirmed/transferring breakdown.
2704
- * Excludes tokens with status 'spent' or 'invalid'.
2705
2571
  *
2706
- * In-flight (`'transferring'`) tokens are LEAVING the wallet during an active
2707
- * send, so they are NOT counted as spendable: they are tracked in their own
2708
- * `transferring*` fields and excluded from `totalAmount`/`unconfirmedAmount`.
2709
- * Counting them as balance would show the user value they cannot spend (the
2710
- * #517 incident follow-up).
2572
+ * @see TokenView.getAssets
2711
2573
  */
2712
- private aggregateTokens;
2713
- /** Fold one token into its coin's running totals (see {@link aggregateTokens}). */
2714
- private accumulateToken;
2715
- private newAssetAccumulator;
2574
+ getAssets(coinId?: string): Promise<Asset[]>;
2716
2575
  /**
2717
2576
  * Get all tokens, optionally filtered by coin type and/or status.
2718
2577
  *
@@ -2720,6 +2579,7 @@ declare class PaymentsModule {
2720
2579
  * @param filter.coinId - Return only tokens of this coin type.
2721
2580
  * @param filter.status - Return only tokens with this status (e.g. `'submitted'` for unconfirmed).
2722
2581
  * @returns Array of matching {@link Token} objects (synchronous).
2582
+ * @see TokenView.getTokens
2723
2583
  */
2724
2584
  getTokens(filter?: {
2725
2585
  coinId?: string;
@@ -2730,6 +2590,7 @@ declare class PaymentsModule {
2730
2590
  *
2731
2591
  * @param id - The local UUID assigned when the token was added.
2732
2592
  * @returns The token, or `undefined` if not found.
2593
+ * @see TokenView.getToken
2733
2594
  */
2734
2595
  getToken(id: string): Token | undefined;
2735
2596
  /**
@@ -2787,6 +2648,7 @@ declare class PaymentsModule {
2787
2648
  * token state from being re-added (e.g. via Nostr re-delivery).
2788
2649
  *
2789
2650
  * @returns A shallow copy of the tombstone array.
2651
+ * @see TokenView.getTombstones
2790
2652
  */
2791
2653
  getTombstones(): TombstoneEntry[];
2792
2654
  /**
@@ -2796,36 +2658,23 @@ declare class PaymentsModule {
2796
2658
  * @param tokenId - The genesis token ID.
2797
2659
  * @param stateHash - The state hash of the token version to check.
2798
2660
  * @returns `true` if the exact combination has been tombstoned.
2661
+ * @see TokenView.isStateTombstoned
2799
2662
  */
2800
2663
  isStateTombstoned(tokenId: string, stateHash: string): boolean;
2801
- private rebuildTombstoneKeySet;
2802
- /**
2803
- * Merge tombstones received from a remote sync source.
2804
- *
2805
- * Any local token whose `(tokenId, stateHash)` matches a remote tombstone is
2806
- * removed. The remote tombstones are then added to the local set (union merge).
2807
- *
2808
- * @param remoteTombstones - Tombstone entries from the remote source.
2809
- * @returns Number of local tokens that were removed.
2810
- */
2811
- mergeTombstones(remoteTombstones: TombstoneEntry[]): Promise<number>;
2812
2664
  /**
2813
2665
  * Remove tombstones older than `maxAge` and cap the list at 100 entries.
2814
2666
  *
2815
2667
  * @param maxAge - Maximum age in milliseconds (default: 30 days).
2668
+ * @see TokenView.pruneTombstones
2816
2669
  */
2817
2670
  pruneTombstones(maxAge?: number): Promise<void>;
2818
2671
  /**
2819
2672
  * Get the transaction history sorted newest-first.
2820
2673
  *
2821
2674
  * @returns Array of {@link TransactionHistoryEntry} objects in descending timestamp order.
2675
+ * @see TransferHistory.getHistory
2822
2676
  */
2823
2677
  getHistory(): TransactionHistoryEntry[];
2824
- /**
2825
- * Best-effort resolve sender's DIRECT address and nametag from their transport pubkey.
2826
- * Returns empty object if transport doesn't support resolution or lookup fails.
2827
- */
2828
- private resolveSenderInfo;
2829
2678
  /**
2830
2679
  * Append an entry to the transaction history.
2831
2680
  *
@@ -2834,56 +2683,15 @@ declare class PaymentsModule {
2834
2683
  * Duplicate entries with the same `dedupKey` are silently ignored (upsert).
2835
2684
  *
2836
2685
  * @param entry - History entry fields (without `id` and `dedupKey`).
2686
+ * @see TransferHistory.addToHistory
2837
2687
  */
2838
2688
  addToHistory(entry: Omit<TransactionHistoryEntry, 'id' | 'dedupKey'>): Promise<void>;
2839
2689
  /**
2840
2690
  * Load history into the in-memory cache.
2841
2691
  *
2842
- * In the wallet-api composition (the `walletApi` client is present) the
2843
- * durable §10 history log lives on the SERVER — the thin storage provider
2844
- * keeps none — so the cache is rebuilt from `walletApi.listHistory()`. The
2845
- * twin of the #521 inventory reload bug: `_historyCache` is process-lifetime,
2846
- * so a reload (tab refresh) must re-pull it or render an empty history.
2847
- * Compositions WITHOUT `walletApi` keep the legacy local path below.
2692
+ * @see TransferHistory.loadHistory
2848
2693
  */
2849
2694
  loadHistory(): Promise<void>;
2850
- /**
2851
- * Rebuild `_historyCache` from the server's §10 history log (the wallet-api
2852
- * composition). Pages newest-first via the keyset cursor until `more:false`
2853
- * or the page cap; dedups by `dedupKey` (a hydrate-then-receive in the same
2854
- * session must not double-list). The S6 `memo` / `counterpartyNametag`
2855
- * envelopes are decrypted with the owner's own field key on the way in.
2856
- *
2857
- * #642 incremental fast path: after one completed hydration for this owner,
2858
- * later pulls stop at the first non-empty page holding nothing new — the
2859
- * §10 log is append-only (newest-first keyset), so everything past a fully
2860
- * known page is already cached. The steady-state 30s inventory resync then
2861
- * costs ONE history page instead of a full re-pagination (which on a wallet
2862
- * whose history overflows the page cap was 100 pages, every tick, forever).
2863
- *
2864
- * Best-effort, like the §10 history POST: history is untrusted DISPLAY data,
2865
- * so a backend outage during hydration must NEVER fail `load()` (the money
2866
- * path) — the in-session cache is left intact and the pull retries next load.
2867
- */
2868
- private hydrateHistoryFromServer;
2869
- /**
2870
- * Map one §16 history wire record onto the display
2871
- * {@link TransactionHistoryEntry}. `counterpartyNametag` lands on the role-
2872
- * appropriate field (sender for RECEIVED, recipient otherwise); the S6 memo +
2873
- * nametag envelopes decrypt under THIS wallet's field key (self-scoped at
2874
- * rest — §8.3), surfaced as absent if they don't decrypt rather than as
2875
- * ciphertext (same rule as mailbox/payment-request memos).
2876
- */
2877
- private historyEntryFromWire;
2878
- /** S6 field decrypt that surfaces an undecryptable envelope as absent (§8.3). */
2879
- private tryDecryptField;
2880
- /**
2881
- * Import history entries from remote TXF data into local store.
2882
- * Delegates to the local TokenStorageProvider's importHistoryEntries() for
2883
- * persistent storage, with in-memory fallback.
2884
- * Reused by both load() (initial IPFS fetch) and _doSync() (merge result).
2885
- */
2886
- private importRemoteHistoryEntries;
2887
2695
  /**
2888
2696
  * Get the first local token storage provider (for history operations).
2889
2697
  */
@@ -3006,6 +2814,7 @@ declare class PaymentsModule {
3006
2814
  * Tokens that fail validation or are detected as spent are marked `'invalid'`.
3007
2815
  *
3008
2816
  * @returns Object with arrays of valid and invalid tokens.
2817
+ * @see TokenView.validate
3009
2818
  */
3010
2819
  validate(): Promise<{
3011
2820
  valid: Token[];
@@ -3020,17 +2829,6 @@ declare class PaymentsModule {
3020
2829
  * Uses pre-resolved PeerInfo if available, otherwise resolves via transport.
3021
2830
  */
3022
2831
  private resolveTransportPubkey;
3023
- /**
3024
- * v2 engine transfer (sender-driven): the sender handed us a FINISHED token.
3025
- * Decode the blob, dedup by the genesis-stable token id, store it as a
3026
- * confirmed token, and emit/record the receipt. No commitment / inclusion-proof
3027
- * / finalization round-trip (contrast the v1 sourceToken+transferTx path).
3028
- *
3029
- * Transport-agnostic (sdk-changes S3): fed by the relay push subscription
3030
- * AND by the delivery port's incoming pump — the returned verdict lets the
3031
- * pump map outcomes onto `ack('claimed' | 'rejected')`.
3032
- */
3033
- private handleV2Transfer;
3034
2832
  private teardownDeliveryPump;
3035
2833
  /**
3036
2834
  * Route a §9 wake nudge to the matching stream's pull. The wake is
@@ -3094,158 +2892,16 @@ declare class PaymentsModule {
3094
2892
  * ack for a provider without it (e.g. the relay no-op).
3095
2893
  */
3096
2894
  private flushIncomingAcks;
3097
- /** The S4 capability slice — null unless the composed wallet-api port carries it. */
3098
- private paymentRequestsApi;
3099
- private teardownPaymentRequestPump;
3100
- private prCursorKey;
3101
- private readPrCursorState;
3102
- private persistPrCursorState;
3103
- private prSettlingKey;
3104
- /**
3105
- * Single-flight load: concurrent callers (the pump's reload re-apply, resume's
3106
- * reconcile, a live pay catch) share ONE storage.get + ONE Map instance so a
3107
- * lazy read never overwrites another context's in-memory mutation.
3108
- */
3109
- private ensureSettlingJournalLoaded;
3110
- /**
3111
- * Serialize every read-modify-write through a tail-promise chain so two
3112
- * concurrent mutations can't lose an entry (the #679/#680 mutex lesson — a
3113
- * dropped entry is a re-payable request is a double-pay). `fn` returns true
3114
- * when the Map changed and must be persisted.
3115
- */
3116
- private mutateSettlingJournal;
3117
- /**
3118
- * #441: link a request to its in-flight transfer. `committed` marks a
3119
- * DEFINITE spend whose anchor intent is soft-aborted and will NOT resume-
3120
- * complete — a `PartialSendConflictError` (≥1 leg already delivered). The
3121
- * reconcile must resolve such a link 'paid' and NEVER revert it to payable
3122
- * (re-paying the full amount would double-pay the delivered leg). A
3123
- * non-`committed` link is a keep-open outcome that resume may still complete
3124
- * OR abort (e.g. CERTIFICATION_UNCONFIRMED losing to a foreign tx delivers
3125
- * nothing) — those DO revert to payable on abort.
3126
- */
3127
- private journalSettling;
3128
- private clearSettling;
3129
- /**
3130
- * Pull the wallet-api payment-request streams now (S4): drains the payer's
3131
- * incoming `?since=<seq>` stream (gap-free — §9/§16) into the existing
3132
- * handler/event surface and refreshes outgoing requests still awaiting a
3133
- * response. The poll interval and `load()` call this automatically; it is
3134
- * public for explicit fetch-now flows (mirrors {@link receive}). A no-op in
3135
- * compositions without the wallet-api payment-request capability — there
3136
- * the Nostr subscription is push-based.
3137
- */
3138
- syncPaymentRequests(): Promise<void>;
3139
- /** Coalesces concurrent pump runs (poll + wake + load can overlap). */
3140
- private pumpPaymentRequests;
3141
- private doPumpPaymentRequests;
3142
- /**
3143
- * Drain the payer's gap-free `?since=<seq>` stream (§9/§16), mirroring the
3144
- * mailbox-cursor pattern: the persisted `{cursor, syncEpoch}` is the resume
3145
- * point; a `syncEpoch` change (server restore — §5.4) voids cursor
3146
- * continuity, so the tail re-pulls from 0 and the id-dedup in
3147
- * {@link surfaceIncomingPaymentRequest} absorbs the replays.
3148
- *
3149
- * Because the surfaced list is in-memory only, each session FIRST runs one
3150
- * full incoming hydration from `since=0` with NO status filter (#556 —
3151
- * mirrors {@link hydrateHistoryFromServer}): a fresh engine rebuilds the
3152
- * CURRENT state of ALL incoming requests — open AND resolved — so a request
3153
- * paid/declined/expired in a PRIOR session is still present (with its
3154
- * resolved status) instead of vanishing once the cursor advanced past it.
3155
- * Hydration NEVER fires the new-incoming handlers/events for resolved
3156
- * requests — only `open` ones notify (the status-aware
3157
- * {@link surfaceIncomingPaymentRequest}) — so reopening can't spam stale
3158
- * 'new request' notifications. After hydration the `since`-cursor delta poll
3159
- * picks up live updates from the resume point.
3160
- */
3161
- private pumpIncomingPaymentRequests;
3162
- /**
3163
- * Decrypt a payment-request's recipient-addressed memo envelope into
3164
- * `{ memo, senderNametag }` (the requester's message + nametag). The key is
3165
- * the ECDH shared secret between THIS wallet (the payer) and the requester's
3166
- * chain pubkey (`wire.fromPubkey`) — symmetric with the requester's
3167
- * create-time derivation. Returns an empty bundle (and logs at debug) on any
3168
- * absence/failure so the incoming view never wedges on an unreadable memo
3169
- * (PR twin of #546/#547).
3170
- */
3171
- private decryptPaymentRequestMemo;
3172
- /** §16 wire status → the public {@link PaymentRequestStatus} display status. */
3173
- private static readonly PR_WIRE_STATUS;
3174
- /** Terminal local statuses: a re-surfaced wire may advance a request INTO one, never out of it. */
3175
- private static readonly PR_TERMINAL;
3176
- /**
3177
- * Map a §16 wire request onto the public {@link IncomingPaymentRequest}
3178
- * surface, deduped by id (the in-memory id-dedup doubles as the replay guard
3179
- * for cursor resets). Requests of EVERY status are surfaced so a reloaded
3180
- * thin wallet rebuilds the CURRENT state of its incoming view (#556) — open
3181
- * ones land as actionable `pending`, resolved ones carry their paid/declined
3182
- * (→ `rejected`)/expired status. Only `open` requests fire the new-incoming
3183
- * event + handlers; resolved requests are folded into the list silently, so a
3184
- * reload (or a `syncEpoch` re-pull) never re-notifies for already-resolved
3185
- * requests. Multi-asset requests surface their first asset (the module's
3186
- * request surface is single-asset; module-created requests always are).
3187
- */
3188
- private surfaceIncomingPaymentRequest;
3189
- /**
3190
- * Outgoing requests are a `?before=` backfill view (§16 — newest-first, no
3191
- * gap-free tail): refresh only while something local still awaits a
3192
- * response, paging until every pending id is resolved or the view drains.
3193
- */
3194
- private refreshOutgoingPaymentRequests;
3195
- /** Fold a server-side status change into the outgoing surface (responses + expiry). */
3196
- private applyOutgoingPaymentRequestState;
3197
- /**
3198
- * E.3 resume: list this wallet's OPEN intents (server-side — any device),
3199
- * decrypt each payload, and re-run the engine with the SAME transferId and
3200
- * inputs — deterministic realization yields byte-identical transactions, so
3201
- * an interrupted transfer completes instead of failing (proof fetch →
3202
- * match-verify → apply, Part E). Called at sign-in (S4).
2895
+ /**
2896
+ * E.3 resume of this wallet's OPEN send intents.
2897
+ *
2898
+ * @see IntentResume.resumeOpenIntents
3203
2899
  */
3204
2900
  resumeOpenIntents(): Promise<{
3205
2901
  resumed: string[];
3206
2902
  conflicted: string[];
3207
2903
  failed: string[];
3208
2904
  }>;
3209
- /**
3210
- * #441: resolve every journaled settling payment request against a resume
3211
- * outcome. Completed → send the deferred 'paid' response (server now told) and
3212
- * resolve 'paid'. Aborted (nothing delivered) → clear journal, return to
3213
- * payable. Still open → leave it. For an id accounted for by NONE of those
3214
- * (a crash between resume-complete and the local write), consult the server
3215
- * `listIntents('aborted')` authority: aborted → payable; else → paid (a
3216
- * completed row the server GC'd). Direction-of-error is deliberate: the only
3217
- * residual false-paid is a transfer that aborted server-side AND whose local
3218
- * 'aborted' write was lost AND whose server aborted-row was later GC'd — it is
3219
- * treated as paid, erring toward PAID-NEVER-RE-PAYABLE (no double-pay), the
3220
- * invariant this fix exists to protect.
3221
- */
3222
- private reconcileSettlingPaymentRequests;
3223
- /** #441: the linked transfer completed — tell the server 'paid' and resolve 'paid'. */
3224
- private resolveSettledPaid;
3225
- /** #441: the linked transfer aborted — nothing was paid; clear the link and return to payable. */
3226
- private revertSettlingToPayable;
3227
- /**
3228
- * Re-run one intent end-to-end: getToken (works for tombstoned rows — §5.3
3229
- * recovery surface), engine re-run under the original transferId, journaled
3230
- * delivery, the single §7 apply (inventory custody only), uniform close,
3231
- * and the SENT history record (dedupKey'd by transferId — idempotent).
3232
- */
3233
- private resumeIntent;
3234
- /**
3235
- * A resumed split that produced no output: a lost source means the leg delivered
3236
- * nothing (returns false), and every checkpoint failure keeps the intent open by
3237
- * throwing. There is no path here that records the spend.
3238
- */
3239
- private classifyResumeSplitFailure;
3240
- /**
3241
- * Drop the local records of the sources a resume consumed (present when
3242
- * resuming on the originating device). M7: each removal carries the LOCAL state
3243
- * we spent, so a source a concurrent claim reactivated to a NEW state (a
3244
- * self-send round-trip) is KEPT, not destroyed.
3245
- */
3246
- private dropResumedSources;
3247
- /** §10: the SENT record — same dedupKey as the original attempt, so a resume after the history POST is a server-side no-op. */
3248
- private recordResumedSentHistory;
3249
2905
  /**
3250
2906
  * Persist the token state to every (non-disabled) token storage provider.
3251
2907
  *
@@ -3261,77 +2917,6 @@ declare class PaymentsModule {
3261
2917
  * behavior.
3262
2918
  */
3263
2919
  private save;
3264
- private loadPendingV2Deliveries;
3265
- /**
3266
- * #621: journaled finished blobs for an intent, keyed by op position. opIndex when present,
3267
- * else positional among the intent's entries (legacy entries journaled in op order). Resume
3268
- * uses this to re-deliver an already-certified op instead of re-running the engine on its spent source.
3269
- */
3270
- private journaledByOp;
3271
- /** #517: run a journal read-modify-write atomically against all other journal mutations. */
3272
- private withJournalLock;
3273
- private savePendingV2Delivery;
3274
- private removePendingV2Delivery;
3275
- /**
3276
- * The hoisted send delivery pass (#699): hand a send's journaled committed
3277
- * blobs to ONE recipient — a single deliverBatch call when the port offers
3278
- * it and there is more than one blob, else per-blob deliver at the
3279
- * certification fan-out width. Returns true iff every blob was delivered
3280
- * (deferred blobs stay journaled). Never throws (§3.1).
3281
- */
3282
- private deliverCommittedBlobs;
3283
- /**
3284
- * Batch sibling of {@link tryDeliver}: success clears every journal entry;
3285
- * ANY failure keeps them ALL journaled (the deposit is idempotent by
3286
- * content-derived entry_id, so a replay absorbs entries that already
3287
- * landed). Never throws.
3288
- */
3289
- private tryDeliverBatch;
3290
- /**
3291
- * Per-blob fallback (a batch-less port, or a single blob), chunked at
3292
- * MAX_SEND_OPERATION_CONCURRENCY so the pass keeps the certification
3293
- * fan-out era's delivery width. Never throws.
3294
- */
3295
- private deliverPerBlob;
3296
- /**
3297
- * Covenant §3.1 (#621): once the source is certified on-chain, a delivery failure must NEVER
3298
- * fail the sender. Recipient-side conditions (a full mailbox / 429 from a never-claiming recipient)
3299
- * and transient delivery-infra failures (5xx/network) both leave the finished blob JOURNALED for
3300
- * (re-)delivery — the send resolves as delivery-pending and the sender moves on. The blob is already
3301
- * journaled (savePendingV2Delivery ran before this); on success we clear the journal, on any failure
3302
- * we keep it. Returns true if delivered, false if deferred. Never throws.
3303
- */
3304
- private tryDeliver;
3305
- /**
3306
- * Replay journaled finished-but-undelivered v2 blobs (kicked from load())
3307
- * through the delivery port (sdk-changes S3). Idempotent end to end: the
3308
- * mailbox deposit is idempotent by the content-derived entry_id — it succeeds
3309
- * even after the recipient claimed (§6) — and the relay path's recipient
3310
- * dedups by the genesis-stable token id.
3311
- *
3312
- * #517 item 1: instead of a silent unbounded fire-and-forget, each entry is
3313
- * retried with bounded exponential backoff, its cumulative failed-attempt
3314
- * count is journaled across loads, and an entry that exhausts
3315
- * {@link MAX_DELIVERY_REPLAY_ATTEMPTS} is SURFACED as poison
3316
- * (`delivery:undeliverable`) and left journaled but no longer auto-retried —
3317
- * so a journaled blob never sits undelivered invisibly.
3318
- */
3319
- private replayPendingV2Deliveries;
3320
- /** Replay a single journaled entry: backoff retries → success/removal, or → bounded poison. */
3321
- private replayOneDelivery;
3322
- /**
3323
- * §3.1 (#621): defer a recipient-quota (429) delivery — keep it journaled, never poison it (it
3324
- * self-heals when the recipient claims), and retry no sooner than the deferral window. Surfaced
3325
- * distinctly (delivery:deferred) so a UI can show "recipient's mailbox is full — will retry".
3326
- */
3327
- private deferDelivery;
3328
- /** One delivery with in-pass exponential backoff; throws the last error if all attempts fail. */
3329
- private attemptDeliveryWithBackoff;
3330
- /** Mark a journal entry poison (keep it, stop retrying) and surface it (#517 item 1). */
3331
- private markDeliveryPoison;
3332
- private bumpDeliveryAttempts;
3333
- /** Mutate one journaled entry in place (keyed by tokenBlob) and persist. No-op if gone. */
3334
- private updatePendingV2Delivery;
3335
2920
  private createStorageData;
3336
2921
  private loadFromStorageData;
3337
2922
  /**
@@ -4721,7 +4306,7 @@ interface IncomingTransfer {
4721
4306
  readonly memo?: string;
4722
4307
  readonly receivedAt: number;
4723
4308
  }
4724
- type PaymentRequestStatus = 'pending' | 'accepted' | 'rejected' | 'paid' | 'expired' | 'settling';
4309
+ type PaymentRequestStatus = 'pending' | 'rejected' | 'paid' | 'expired' | 'settling';
4725
4310
  /**
4726
4311
  * Outgoing payment request (requesting payment from someone)
4727
4312
  */
@@ -4780,7 +4365,7 @@ type PaymentRequestHandler = (request: IncomingPaymentRequest) => void;
4780
4365
  /**
4781
4366
  * Response type for payment requests
4782
4367
  */
4783
- type PaymentRequestResponseType = 'accepted' | 'rejected' | 'paid';
4368
+ type PaymentRequestResponseType = 'rejected' | 'paid';
4784
4369
  /**
4785
4370
  * Outgoing payment request (we sent to someone)
4786
4371
  */
@@ -4882,7 +4467,7 @@ interface TrackedAddress extends TrackedAddressEntry {
4882
4467
  /** Primary nametag (from nametag cache, without @ prefix) */
4883
4468
  readonly nametag?: string;
4884
4469
  }
4885
- type SphereEventType = 'transfer:incoming' | 'transfer:confirmed' | 'transfer:failed' | 'transfer:delivery_pending' | 'transfer:invalid' | 'payment_request:incoming' | 'payment_request:accepted' | 'payment_request:rejected' | 'payment_request:paid' | 'payment_request:expired' | 'payment_request:settling' | 'payment_request:response' | 'message:dm' | 'message:read' | 'message:typing' | 'composing:started' | 'message:broadcast' | 'sync:started' | 'sync:completed' | 'sync:error' | 'storage:degraded' | 'inventory:conflict' | 'split:checkpoint-stuck' | 'send:partial-remainder' | 'delivery:undeliverable' | 'delivery:deferred' | 'walletapi:session' | 'realtime:status' | 'connection:changed' | 'nametag:registered' | 'nametag:recovered' | 'identity:changed' | 'address:activated' | 'address:hidden' | 'address:unhidden' | 'sync:remote-update' | 'groupchat:message' | 'groupchat:joined' | 'groupchat:left' | 'groupchat:kicked' | 'groupchat:group_deleted' | 'groupchat:updated' | 'groupchat:connection' | 'groupchat:ready' | 'communications:ready' | 'history:updated' | 'invoice:created' | 'invoice:payment' | 'invoice:asset_covered' | 'invoice:target_covered' | 'invoice:covered' | 'invoice:closed' | 'invoice:cancelled' | 'invoice:expired' | 'invoice:unknown_reference' | 'invoice:overpayment' | 'invoice:irrelevant' | 'invoice:auto_returned' | 'invoice:auto_return_failed' | 'invoice:return_received' | 'invoice:over_refund_warning' | 'invoice:receipt_sent' | 'invoice:receipt_received' | 'invoice:cancellation_sent' | 'invoice:cancellation_received' | 'swap:proposal_received' | 'swap:proposed' | 'swap:accepted' | 'swap:rejected' | 'swap:announced' | 'swap:deposit_sent' | 'swap:deposit_confirmed' | 'swap:deposits_covered' | 'swap:concluding' | 'swap:payout_received' | 'swap:completed' | 'swap:cancelled' | 'swap:failed' | 'swap:deposit_returned' | 'swap:bounce_received';
4470
+ type SphereEventType = 'transfer:incoming' | 'transfer:confirmed' | 'transfer:failed' | 'transfer:delivery_pending' | 'transfer:invalid' | 'payment_request:incoming' | 'payment_request:rejected' | 'payment_request:paid' | 'payment_request:expired' | 'payment_request:settling' | 'payment_request:response' | 'message:dm' | 'message:read' | 'message:typing' | 'composing:started' | 'message:broadcast' | 'sync:started' | 'sync:completed' | 'sync:error' | 'storage:degraded' | 'inventory:conflict' | 'split:checkpoint-stuck' | 'send:partial-remainder' | 'delivery:undeliverable' | 'delivery:deferred' | 'walletapi:session' | 'realtime:status' | 'connection:changed' | 'nametag:registered' | 'nametag:recovered' | 'identity:changed' | 'address:activated' | 'address:hidden' | 'address:unhidden' | 'sync:remote-update' | 'groupchat:message' | 'groupchat:joined' | 'groupchat:left' | 'groupchat:kicked' | 'groupchat:group_deleted' | 'groupchat:updated' | 'groupchat:connection' | 'groupchat:ready' | 'communications:ready' | 'history:updated' | 'invoice:created' | 'invoice:payment' | 'invoice:asset_covered' | 'invoice:target_covered' | 'invoice:covered' | 'invoice:closed' | 'invoice:cancelled' | 'invoice:expired' | 'invoice:unknown_reference' | 'invoice:overpayment' | 'invoice:irrelevant' | 'invoice:auto_returned' | 'invoice:auto_return_failed' | 'invoice:return_received' | 'invoice:over_refund_warning' | 'invoice:receipt_sent' | 'invoice:receipt_received' | 'invoice:cancellation_sent' | 'invoice:cancellation_received' | 'swap:proposal_received' | 'swap:proposed' | 'swap:accepted' | 'swap:rejected' | 'swap:announced' | 'swap:deposit_sent' | 'swap:deposit_confirmed' | 'swap:deposits_covered' | 'swap:concluding' | 'swap:payout_received' | 'swap:completed' | 'swap:cancelled' | 'swap:failed' | 'swap:deposit_returned' | 'swap:bounce_received';
4886
4471
  interface SphereEventMap {
4887
4472
  'transfer:incoming': IncomingTransfer;
4888
4473
  'transfer:confirmed': TransferResult;
@@ -4905,7 +4490,6 @@ interface SphereEventMap {
4905
4490
  reason: string;
4906
4491
  };
4907
4492
  'payment_request:incoming': IncomingPaymentRequest;
4908
- 'payment_request:accepted': IncomingPaymentRequest;
4909
4493
  'payment_request:rejected': IncomingPaymentRequest;
4910
4494
  'payment_request:paid': IncomingPaymentRequest;
4911
4495
  'payment_request:expired': IncomingPaymentRequest;