@toon-protocol/sdk 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -19
- package/dist/chunk-XTHUXP63.js +1637 -0
- package/dist/chunk-XTHUXP63.js.map +1 -0
- package/dist/index.d.ts +1960 -0
- package/dist/index.js +1187 -301
- package/dist/index.js.map +1 -1
- package/dist/swap-D4Ozr_BM.d.ts +484 -0
- package/dist/swap.d.ts +3 -0
- package/dist/swap.js +14 -0
- package/dist/swap.js.map +1 -0
- package/package.json +23 -18
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
import { NostrEvent, UnsignedEvent } from 'nostr-tools/pure';
|
|
2
|
+
import { IlpPreparePacket, SwapPair } from '@toon-protocol/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* NIP-59 Gift Wrap Integration for ILP Packets (Story 12.2)
|
|
6
|
+
*
|
|
7
|
+
* Provides privacy-preserving encoding/decoding of swap packets using the
|
|
8
|
+
* NIP-59 three-layer gift wrap construction (rumor -> seal -> gift wrap).
|
|
9
|
+
* Intermediary peers routing ILP packets see only opaque TOON-encoded binary
|
|
10
|
+
* in the data field -- they cannot determine the event kind, sender identity,
|
|
11
|
+
* or swap intent. Only the destination Mill can unwrap and process.
|
|
12
|
+
*
|
|
13
|
+
* Also provides NIP-44 encryption/decryption for FULFILL claim return path
|
|
14
|
+
* (D12-008), using ephemeral keys so intermediaries on the return path see
|
|
15
|
+
* only opaque ciphertext.
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** Parameters for {@link wrapSwapPacket}. */
|
|
21
|
+
interface WrapSwapPacketParams {
|
|
22
|
+
/** Unsigned inner event containing swap metadata. */
|
|
23
|
+
rumor: UnsignedEvent;
|
|
24
|
+
/** Sender's secp256k1 secret key. */
|
|
25
|
+
senderSecretKey: Uint8Array;
|
|
26
|
+
/** Mill's compressed hex pubkey (64 chars). */
|
|
27
|
+
recipientPubkey: string;
|
|
28
|
+
}
|
|
29
|
+
/** Result of {@link wrapSwapPacket}. */
|
|
30
|
+
interface WrapSwapPacketResult {
|
|
31
|
+
/** Fully-formed kind:1059 gift wrap event signed by a fresh ephemeral key. */
|
|
32
|
+
giftWrap: NostrEvent;
|
|
33
|
+
/** The ephemeral pubkey used for the outer gift wrap layer. */
|
|
34
|
+
ephemeralPubkey: string;
|
|
35
|
+
}
|
|
36
|
+
/** Parameters for {@link unwrapSwapPacket}. */
|
|
37
|
+
interface UnwrapSwapPacketParams {
|
|
38
|
+
/** A kind:1059 gift wrap event. */
|
|
39
|
+
giftWrap: NostrEvent;
|
|
40
|
+
/** Recipient's (Mill's) secret key. */
|
|
41
|
+
recipientSecretKey: Uint8Array;
|
|
42
|
+
}
|
|
43
|
+
/** Result of {@link unwrapSwapPacket}. */
|
|
44
|
+
interface UnwrapSwapPacketResult {
|
|
45
|
+
/** The decrypted inner rumor (unsigned). */
|
|
46
|
+
rumor: UnsignedEvent;
|
|
47
|
+
/** The sender's real pubkey (extracted from the seal layer). */
|
|
48
|
+
senderPubkey: string;
|
|
49
|
+
}
|
|
50
|
+
/** Parameters for {@link wrapSwapPacketToToon}. */
|
|
51
|
+
interface WrapSwapPacketToToonParams {
|
|
52
|
+
/** Unsigned inner event containing swap metadata. */
|
|
53
|
+
rumor: UnsignedEvent;
|
|
54
|
+
/** Sender's secp256k1 secret key. */
|
|
55
|
+
senderSecretKey: Uint8Array;
|
|
56
|
+
/** Mill's compressed hex pubkey (64 chars). */
|
|
57
|
+
recipientPubkey: string;
|
|
58
|
+
/** ILP destination address. */
|
|
59
|
+
destination: string;
|
|
60
|
+
/** Payment amount in ILP units (bigint). */
|
|
61
|
+
amount: bigint;
|
|
62
|
+
/** Packet expiry. Default: 30s from now (inside buildIlpPrepare). */
|
|
63
|
+
expiresAt?: Date;
|
|
64
|
+
}
|
|
65
|
+
/** Result of {@link wrapSwapPacketToToon}. */
|
|
66
|
+
interface WrapSwapPacketToToonResult {
|
|
67
|
+
/** Ready-to-send ILP PREPARE packet with TOON-encoded gift wrap as data. */
|
|
68
|
+
ilpPrepare: IlpPreparePacket;
|
|
69
|
+
/** The ephemeral pubkey used for the outer gift wrap layer. */
|
|
70
|
+
ephemeralPubkey: string;
|
|
71
|
+
}
|
|
72
|
+
/** Parameters for {@link unwrapSwapPacketFromToon}. */
|
|
73
|
+
interface UnwrapSwapPacketFromToonParams {
|
|
74
|
+
/** The data field from an incoming ILP PREPARE (TOON-encoded gift wrap). */
|
|
75
|
+
toonData: Uint8Array;
|
|
76
|
+
/** Recipient's (Mill's) secret key. */
|
|
77
|
+
recipientSecretKey: Uint8Array;
|
|
78
|
+
}
|
|
79
|
+
/** Parameters for {@link encryptFulfillClaim}. */
|
|
80
|
+
interface EncryptFulfillClaimParams {
|
|
81
|
+
/** The signed claim bytes to encrypt. */
|
|
82
|
+
claimData: Uint8Array;
|
|
83
|
+
/** The original sender's pubkey (recovered from unwrap). */
|
|
84
|
+
senderPubkey: string;
|
|
85
|
+
}
|
|
86
|
+
/** Result of {@link encryptFulfillClaim}. */
|
|
87
|
+
interface EncryptFulfillClaimResult {
|
|
88
|
+
/** NIP-44 encrypted claim bytes. */
|
|
89
|
+
ciphertext: Uint8Array;
|
|
90
|
+
/** The Mill's ephemeral pubkey (included in FULFILL so sender can decrypt). */
|
|
91
|
+
ephemeralPubkey: string;
|
|
92
|
+
}
|
|
93
|
+
/** Parameters for {@link decryptFulfillClaim}. */
|
|
94
|
+
interface DecryptFulfillClaimParams {
|
|
95
|
+
/** The encrypted claim bytes from the FULFILL data field. */
|
|
96
|
+
ciphertext: Uint8Array;
|
|
97
|
+
/** The Mill's ephemeral pubkey from the FULFILL data field. */
|
|
98
|
+
ephemeralPubkey: string;
|
|
99
|
+
/** The sender's (recipient of FULFILL) secret key. */
|
|
100
|
+
recipientSecretKey: Uint8Array;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* NIP-59 three-layer gift wrap a swap packet.
|
|
104
|
+
*
|
|
105
|
+
* Constructs a kind:1059 gift wrap event using a fresh ephemeral keypair.
|
|
106
|
+
* The rumor is sealed with the sender's real key, then wrapped with the
|
|
107
|
+
* ephemeral key. Intermediaries see only the ephemeral pubkey.
|
|
108
|
+
*
|
|
109
|
+
* Each invocation generates a fresh ephemeral keypair for forward secrecy
|
|
110
|
+
* and message unlinkability (risk R-006).
|
|
111
|
+
*
|
|
112
|
+
* @throws {GiftWrapError} If the wrap operation fails (invalid keys, crypto failure).
|
|
113
|
+
*/
|
|
114
|
+
declare function wrapSwapPacket(params: WrapSwapPacketParams): WrapSwapPacketResult;
|
|
115
|
+
/**
|
|
116
|
+
* Unwrap a NIP-59 gift-wrapped swap packet, recovering the inner rumor
|
|
117
|
+
* and the sender's real pubkey.
|
|
118
|
+
*
|
|
119
|
+
* Performs two-layer decryption: gift wrap -> seal -> rumor.
|
|
120
|
+
* The sender pubkey is extracted from the seal's pubkey field.
|
|
121
|
+
*
|
|
122
|
+
* @throws {GiftWrapError} If kind is not 1059, decryption fails, or structure is malformed.
|
|
123
|
+
*/
|
|
124
|
+
declare function unwrapSwapPacket(params: UnwrapSwapPacketParams): UnwrapSwapPacketResult;
|
|
125
|
+
/**
|
|
126
|
+
* Convenience: gift wrap a swap packet, encode to TOON binary, and build
|
|
127
|
+
* an ILP PREPARE packet.
|
|
128
|
+
*
|
|
129
|
+
* This is the path that `streamSwap()` (Story 12.5) will use in its
|
|
130
|
+
* per-packet loop.
|
|
131
|
+
*/
|
|
132
|
+
declare function wrapSwapPacketToToon(params: WrapSwapPacketToToonParams): WrapSwapPacketToToonResult;
|
|
133
|
+
/**
|
|
134
|
+
* Convenience: decode TOON binary from an incoming ILP PREPARE data field
|
|
135
|
+
* and unwrap the gift-wrapped swap packet.
|
|
136
|
+
*
|
|
137
|
+
* This is the path that the Mill handler (Story 12.3) will use to process
|
|
138
|
+
* incoming swap packets.
|
|
139
|
+
*/
|
|
140
|
+
declare function unwrapSwapPacketFromToon(params: UnwrapSwapPacketFromToonParams): UnwrapSwapPacketResult;
|
|
141
|
+
/**
|
|
142
|
+
* Encrypt a FULFILL claim for the return path (D12-008).
|
|
143
|
+
*
|
|
144
|
+
* Generates a fresh ephemeral keypair and NIP-44 encrypts the claim data.
|
|
145
|
+
* The ephemeral pubkey is included alongside the ciphertext so the sender
|
|
146
|
+
* can decrypt. The ephemeral privkey is discarded after encryption.
|
|
147
|
+
*
|
|
148
|
+
* @throws {GiftWrapError} If claimData is empty or encryption fails.
|
|
149
|
+
*/
|
|
150
|
+
declare function encryptFulfillClaim(params: EncryptFulfillClaimParams): EncryptFulfillClaimResult;
|
|
151
|
+
/**
|
|
152
|
+
* Decrypt a FULFILL claim from the return path.
|
|
153
|
+
*
|
|
154
|
+
* Uses the sender's secret key and the Mill's ephemeral pubkey (from the
|
|
155
|
+
* FULFILL data field) to NIP-44 decrypt the claim bytes.
|
|
156
|
+
*
|
|
157
|
+
* @throws {GiftWrapError} If decryption fails (wrong key, malformed ciphertext).
|
|
158
|
+
*/
|
|
159
|
+
declare function decryptFulfillClaim(params: DecryptFulfillClaimParams): Uint8Array;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Sender-side `streamSwap()` API (Story 12.5).
|
|
163
|
+
*
|
|
164
|
+
* Drives the sender side of the Token Swap Primitive: chunks a total source
|
|
165
|
+
* amount into N sender-chosen packets, wraps each with `wrapSwapPacketToToon()`
|
|
166
|
+
* using a fresh ephemeral gift-wrap key per packet (D12-003 / risk R-006),
|
|
167
|
+
* sends each via `ToonClient.sendSwapPacket(...)`, decrypts each FULFILL's
|
|
168
|
+
* NIP-44-encrypted claim with `decryptFulfillClaim()`, accumulates the
|
|
169
|
+
* decrypted claims into an ordered array, invokes a per-packet rate monitoring
|
|
170
|
+
* callback, and supports pause / resume / stop / AbortSignal so the sender
|
|
171
|
+
* can abort when rate drifts past tolerance.
|
|
172
|
+
*
|
|
173
|
+
* Composition story: consumes Stories 12.1 (SwapPair type), 12.2 (gift wrap
|
|
174
|
+
* primitives), 12.3 (handler wire contract). Produces `AccumulatedClaim[]`
|
|
175
|
+
* consumed by Story 12.6 (`buildSettlementTx()`).
|
|
176
|
+
*
|
|
177
|
+
* Design decisions:
|
|
178
|
+
* - `streamSwap()` does NOT throw on mid-stream failure. Caller gets
|
|
179
|
+
* `StreamSwapResult` with `state` and `abortReason`. Only construction-time
|
|
180
|
+
* validation throws synchronously.
|
|
181
|
+
* - `streamSwap()` does NOT retry individual packets. BTP-level connection
|
|
182
|
+
* retries are handled inside `BtpRuntimeClient`; application-layer retry
|
|
183
|
+
* (e.g., T04 insufficient inventory) is a future-story concern.
|
|
184
|
+
* - Rate deviation math stays BigInt throughout. The `effectiveRate` on
|
|
185
|
+
* `PacketProgress` is a display-only `number` (Epic 11 retro MAX_SAFE_INTEGER
|
|
186
|
+
* guard).
|
|
187
|
+
*
|
|
188
|
+
* @module
|
|
189
|
+
*/
|
|
190
|
+
|
|
191
|
+
/** Minimal `IlpSendResult` shape — mirrors `packages/client/src/types.ts`. */
|
|
192
|
+
interface IlpSendResultLike {
|
|
193
|
+
accepted: boolean;
|
|
194
|
+
data?: string;
|
|
195
|
+
code?: string;
|
|
196
|
+
message?: string;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Minimal `SignedBalanceProof` structural type.
|
|
200
|
+
*
|
|
201
|
+
* We avoid importing from `@toon-protocol/client` so the SDK package does not
|
|
202
|
+
* gain a runtime dep on the client package. The sender layer only forwards
|
|
203
|
+
* this opaquely to `ToonClient.sendSwapPacket`.
|
|
204
|
+
*/
|
|
205
|
+
type SignedBalanceProofLike = any;
|
|
206
|
+
/**
|
|
207
|
+
* The subset of `ToonClient` that `streamSwap()` requires. This narrow shape
|
|
208
|
+
* exists so that consumers can pass a real `ToonClient` OR a mock without
|
|
209
|
+
* importing the whole class.
|
|
210
|
+
*/
|
|
211
|
+
interface StreamSwapClient {
|
|
212
|
+
sendSwapPacket(params: {
|
|
213
|
+
destination: string;
|
|
214
|
+
amount: bigint;
|
|
215
|
+
toonData: Uint8Array;
|
|
216
|
+
timeout?: number;
|
|
217
|
+
claim?: SignedBalanceProofLike;
|
|
218
|
+
}): Promise<IlpSendResultLike>;
|
|
219
|
+
getPublicKey(): string;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Parameters for {@link streamSwap} / {@link streamSwapControlled}.
|
|
223
|
+
*
|
|
224
|
+
* @stable — downstream Stories 12.6 and 12.8 depend on this shape.
|
|
225
|
+
*/
|
|
226
|
+
interface StreamSwapParams {
|
|
227
|
+
/** SDK client with BTP wiring (see {@link StreamSwapClient}). */
|
|
228
|
+
client: StreamSwapClient;
|
|
229
|
+
/** Mill's 64-char lowercase hex pubkey (recipient of gift wrap). */
|
|
230
|
+
millPubkey: string;
|
|
231
|
+
/** Mill's ILP destination address (e.g., 'g.toon.mill1'). */
|
|
232
|
+
millIlpAddress: string;
|
|
233
|
+
/** The `SwapPair` being executed (from kind:10032 discovery, Story 12.1). */
|
|
234
|
+
pair: SwapPair;
|
|
235
|
+
/** Sender's 32-byte secp256k1 secret key. Used for seal signing AND FULFILL decryption. */
|
|
236
|
+
senderSecretKey: Uint8Array;
|
|
237
|
+
/**
|
|
238
|
+
* Sender's chain-specific payout address for `pair.to.chain` (Story 12.9 AC-4).
|
|
239
|
+
*
|
|
240
|
+
* REQUIRED. The Nostr `senderPubkey` (identity layer) cannot be used as the
|
|
241
|
+
* on-chain `recipient` in a balance-proof because they are cryptographically
|
|
242
|
+
* independent keys (D12-011). For `evm:*` chains this must be a 20-byte
|
|
243
|
+
* lowercased `0x`-prefixed hex address; for `solana:*` a base58 pubkey that
|
|
244
|
+
* decodes to 32 bytes; for `mina:*` a base58 public-key string. The value
|
|
245
|
+
* is validated at `streamSwapControlled()` entry via `validateChainAddress`
|
|
246
|
+
* and echoed on every rumor as the `chain-recipient` tag (AC-6).
|
|
247
|
+
*/
|
|
248
|
+
chainRecipient: string;
|
|
249
|
+
/** Total source-asset amount to swap (source micro-units). */
|
|
250
|
+
totalAmount: bigint;
|
|
251
|
+
/** Even-split packet count. EXACTLY ONE of this or `packetAmounts` is required. */
|
|
252
|
+
packetCount?: number;
|
|
253
|
+
/** Explicit per-packet amounts. EXACTLY ONE of this or `packetCount` is required. */
|
|
254
|
+
packetAmounts?: readonly bigint[];
|
|
255
|
+
/** Source-asset balance proof claim. Required unless ChannelManager is wired. */
|
|
256
|
+
claim?: SignedBalanceProofLike;
|
|
257
|
+
/** Rate monitoring callback (fires after each accepted FULFILL). */
|
|
258
|
+
onPacket?: RateMonitorCallback;
|
|
259
|
+
/** Rate deviation threshold (decimal, e.g., 0.02 = 2%). */
|
|
260
|
+
rateDeviationThreshold?: number;
|
|
261
|
+
/** Per-packet timeout in ms. Default 30000. */
|
|
262
|
+
packetTimeoutMs?: number;
|
|
263
|
+
/** Optional AbortSignal. */
|
|
264
|
+
signal?: AbortSignal;
|
|
265
|
+
/**
|
|
266
|
+
* Optional pino-compatible logger. Defaults to a no-op.
|
|
267
|
+
*
|
|
268
|
+
* Note: `streamSwap` calls each method with a single structured-event object
|
|
269
|
+
* (e.g., `logger.warn({ event: 'stream_swap.packet_rejected', code, ... })`).
|
|
270
|
+
* Pino accepts this form (the object is logged as top-level fields and the
|
|
271
|
+
* message is left empty); other loggers that expect `(msg, meta)` should
|
|
272
|
+
* wrap accordingly.
|
|
273
|
+
*/
|
|
274
|
+
logger?: {
|
|
275
|
+
debug: (...a: unknown[]) => void;
|
|
276
|
+
info: (...a: unknown[]) => void;
|
|
277
|
+
warn: (...a: unknown[]) => void;
|
|
278
|
+
error: (...a: unknown[]) => void;
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Rate monitoring callback. Fires after each successful FULFILL decryption,
|
|
283
|
+
* before the next packet is sent. If the callback throws or rejects, the
|
|
284
|
+
* stream is treated as stopped.
|
|
285
|
+
*/
|
|
286
|
+
type RateMonitorCallback = (progress: PacketProgress) => void | Promise<void>;
|
|
287
|
+
/**
|
|
288
|
+
* Progress payload delivered to {@link RateMonitorCallback}. Frozen to
|
|
289
|
+
* prevent callback mutation.
|
|
290
|
+
*/
|
|
291
|
+
interface PacketProgress {
|
|
292
|
+
/** 0-indexed packet number within this streamSwap invocation. */
|
|
293
|
+
index: number;
|
|
294
|
+
/** Total number of packets scheduled. */
|
|
295
|
+
total: number;
|
|
296
|
+
/** Source-asset amount sent for this packet (micro-units). */
|
|
297
|
+
sourceAmount: bigint;
|
|
298
|
+
/** Target-asset claim amount derived for this packet (micro-units). */
|
|
299
|
+
targetAmount: bigint;
|
|
300
|
+
/** Rate advertised on the `SwapPair` at swap start (decimal string). */
|
|
301
|
+
advertisedRate: string;
|
|
302
|
+
/** Effective rate for this packet as JS Number (target / source in whole units). Display-only. */
|
|
303
|
+
effectiveRate: number;
|
|
304
|
+
/** Absolute deviation from advertisedRate as a decimal (e.g., 0.0125 = 1.25%). */
|
|
305
|
+
rateDeviation: number;
|
|
306
|
+
/** Cumulative source sent across accepted packets so far (including this one). */
|
|
307
|
+
cumulativeSource: bigint;
|
|
308
|
+
/** Cumulative target received so far (including this one). */
|
|
309
|
+
cumulativeTarget: bigint;
|
|
310
|
+
/** Controller state at callback time. */
|
|
311
|
+
state: 'running' | 'paused' | 'stopped';
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* An accumulated claim successfully harvested from a single packet.
|
|
315
|
+
*
|
|
316
|
+
* @stable — Story 12.6 (`buildSettlementTx()`) depends on this shape.
|
|
317
|
+
* Breaking changes require a coordinated migration.
|
|
318
|
+
*
|
|
319
|
+
* Story 12.6 ADDITIVE extension: the settlement-context fields
|
|
320
|
+
* `channelId`, `nonce`, `cumulativeAmount`, `recipient`, and
|
|
321
|
+
* `millSignerAddress` are marked optional (`?:`) for one story-cycle of
|
|
322
|
+
* backward compat but are REQUIRED in practice: Story 12.6's
|
|
323
|
+
* `buildSettlementTx()` throws `MISSING_SETTLEMENT_METADATA` when any of
|
|
324
|
+
* these are absent.
|
|
325
|
+
*/
|
|
326
|
+
interface AccumulatedClaim {
|
|
327
|
+
/** 0-indexed position in the swap's packet stream. */
|
|
328
|
+
packetIndex: number;
|
|
329
|
+
/** Source-asset amount sent for this packet (micro-units). */
|
|
330
|
+
sourceAmount: bigint;
|
|
331
|
+
/**
|
|
332
|
+
* Target-asset amount claimed (micro-units).
|
|
333
|
+
*
|
|
334
|
+
* **Source of truth caveat:** This is the expected target amount computed
|
|
335
|
+
* by `applyRate(pair.rate)`. The actual signed-claim amount lives inside
|
|
336
|
+
* `claimBytes`; Story 12.6 is responsible for parsing `claimBytes` per
|
|
337
|
+
* chain and verifying the on-wire signed amount equals this expected amount.
|
|
338
|
+
*/
|
|
339
|
+
targetAmount: bigint;
|
|
340
|
+
/** Decrypted signed claim bytes. Chain-specific encoding per Story 12.4. */
|
|
341
|
+
claimBytes: Uint8Array;
|
|
342
|
+
/** Mill's ephemeral pubkey from the FULFILL (64-char lowercase hex). */
|
|
343
|
+
millEphemeralPubkey: string;
|
|
344
|
+
/** Optional Mill-side claim ID (passed through from handler metadata). */
|
|
345
|
+
claimId?: string;
|
|
346
|
+
/** Swap pair this claim was priced against (copy of `pair` for settlement-time routing). */
|
|
347
|
+
pair: SwapPair;
|
|
348
|
+
/** Unix ms timestamp when this claim was accepted. */
|
|
349
|
+
receivedAt: number;
|
|
350
|
+
/** Channel identifier on the target chain (lowercase hex with 0x prefix for EVM; base58 for Solana). */
|
|
351
|
+
channelId?: string;
|
|
352
|
+
/** Balance-proof nonce (decimal string). Monotonically increasing within a channel. */
|
|
353
|
+
nonce?: string;
|
|
354
|
+
/** Cumulative transferred amount on the channel (target micro-units, decimal string). */
|
|
355
|
+
cumulativeAmount?: string;
|
|
356
|
+
/** Recipient address (the sender's target-asset address). */
|
|
357
|
+
recipient?: string;
|
|
358
|
+
/** Mill's on-chain signer address. */
|
|
359
|
+
millSignerAddress?: string;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Aggregate result of a `streamSwap()` invocation.
|
|
363
|
+
*
|
|
364
|
+
* @stable — Stories 12.6 and 12.8 depend on this shape.
|
|
365
|
+
*/
|
|
366
|
+
interface StreamSwapResult {
|
|
367
|
+
state: 'completed' | 'failed' | 'stopped';
|
|
368
|
+
claims: AccumulatedClaim[];
|
|
369
|
+
rejections: {
|
|
370
|
+
packetIndex: number;
|
|
371
|
+
sourceAmount: bigint;
|
|
372
|
+
code: string;
|
|
373
|
+
message: string;
|
|
374
|
+
}[];
|
|
375
|
+
errors: {
|
|
376
|
+
packetIndex: number;
|
|
377
|
+
cause: Error;
|
|
378
|
+
}[];
|
|
379
|
+
abortReason: 'complete' | 'aborted' | 'stopped' | 'callback-stop' | 'callback-throw' | 'rate-deviation' | 'all-rejected';
|
|
380
|
+
cumulativeSource: bigint;
|
|
381
|
+
cumulativeTarget: bigint;
|
|
382
|
+
packetsSent: number;
|
|
383
|
+
packetsScheduled: number;
|
|
384
|
+
}
|
|
385
|
+
/** External controller returned by {@link streamSwapControlled}. */
|
|
386
|
+
interface StreamSwapController {
|
|
387
|
+
pause(): void;
|
|
388
|
+
resume(): void;
|
|
389
|
+
stop(): void;
|
|
390
|
+
readonly state: 'running' | 'paused' | 'stopped' | 'completed' | 'failed';
|
|
391
|
+
}
|
|
392
|
+
/** Length-split `total` into `count` bigints. Remainder absorbed on last element. */
|
|
393
|
+
declare function chunkAmount(total: bigint, count: number): bigint[];
|
|
394
|
+
/** Build the kind:20032 unsigned "swap rumor" event per AC-4.
|
|
395
|
+
*
|
|
396
|
+
* Story 12.9 AC-1/AC-6: the returned rumor MUST include a `chain-recipient`
|
|
397
|
+
* tag carrying the sender-supplied chain-format payout address for
|
|
398
|
+
* `pair.to.chain`. The value is echoed verbatim per packet — no case-folding
|
|
399
|
+
* or transformation beyond what the sender-side `validateChainAddress`
|
|
400
|
+
* accepts.
|
|
401
|
+
*/
|
|
402
|
+
declare function buildSwapRumor(input: {
|
|
403
|
+
senderPubkey: string;
|
|
404
|
+
pair: SwapPair;
|
|
405
|
+
sourceAmount: bigint;
|
|
406
|
+
packetIndex: number;
|
|
407
|
+
totalPackets: number;
|
|
408
|
+
nonce: Uint8Array;
|
|
409
|
+
createdAt: number;
|
|
410
|
+
chainRecipient: string;
|
|
411
|
+
}): UnsignedEvent;
|
|
412
|
+
/**
|
|
413
|
+
* Decode the FULFILL response `data` (base64-encoded JSON metadata) into
|
|
414
|
+
* the `{ claim, ephemeralPubkey, claimId?, targetAmount?, ...settlement }`
|
|
415
|
+
* shape.
|
|
416
|
+
*
|
|
417
|
+
* Story 12.6 extension: also parses the settlement-context fields
|
|
418
|
+
* (`channelId`, `nonce`, `cumulativeAmount`, `recipient`, `millSignerAddress`).
|
|
419
|
+
* These are OPTIONAL and best-effort (#153): each is threaded through only
|
|
420
|
+
* when it is a well-formed string for the target chain; an absent or malformed
|
|
421
|
+
* settlement field is silently dropped rather than failing the whole decode,
|
|
422
|
+
* so a fulfilled swap still surfaces its signed `claim` + `ephemeralPubkey`.
|
|
423
|
+
* Only `claim` and `ephemeralPubkey` are strictly required.
|
|
424
|
+
*
|
|
425
|
+
* @param chain Optional `pair.to.chain` string for per-chain format validation
|
|
426
|
+
* of channelId / recipient / millSignerAddress. When omitted, format checks
|
|
427
|
+
* fall back to a length-only sanity check.
|
|
428
|
+
*/
|
|
429
|
+
declare function decodeFulfillMetadata(data: string | undefined, chain?: string): {
|
|
430
|
+
claim: string;
|
|
431
|
+
ephemeralPubkey: string;
|
|
432
|
+
claimId?: string;
|
|
433
|
+
/** Optional Mill-reported actual target amount (decimal string). Used for rate deviation when present. */
|
|
434
|
+
targetAmount?: string;
|
|
435
|
+
channelId?: string;
|
|
436
|
+
nonce?: string;
|
|
437
|
+
cumulativeAmount?: string;
|
|
438
|
+
recipient?: string;
|
|
439
|
+
millSignerAddress?: string;
|
|
440
|
+
};
|
|
441
|
+
/**
|
|
442
|
+
* Drive a multi-packet swap against a Mill and return a `StreamSwapResult`.
|
|
443
|
+
*
|
|
444
|
+
* `streamSwap()` does NOT throw on mid-stream failure — inspect the result's
|
|
445
|
+
* `state`, `abortReason`, `rejections[]`, and `errors[]` to diagnose.
|
|
446
|
+
*
|
|
447
|
+
* @example
|
|
448
|
+
* ```ts
|
|
449
|
+
* // Discover the SwapPair from the Mill's kind:10032 peer-info event (Story 12.1).
|
|
450
|
+
* const result = await streamSwap({
|
|
451
|
+
* client: toonClient,
|
|
452
|
+
* millPubkey: mill.pubkey,
|
|
453
|
+
* millIlpAddress: 'g.toon.mill1',
|
|
454
|
+
* pair,
|
|
455
|
+
* senderSecretKey,
|
|
456
|
+
* totalAmount: 1_000_000n,
|
|
457
|
+
* packetCount: 10,
|
|
458
|
+
* onPacket: (p) => console.log('packet', p.index, 'rate', p.effectiveRate),
|
|
459
|
+
* rateDeviationThreshold: 0.02,
|
|
460
|
+
* });
|
|
461
|
+
* // Feed result.claims into buildSettlementTx() from Story 12.6.
|
|
462
|
+
* ```
|
|
463
|
+
*
|
|
464
|
+
* @throws {StreamSwapError} Only for construction-time validation failures
|
|
465
|
+
* (INVALID_AMOUNT, INVALID_CHUNKING, INVALID_PAIR, INVALID_STATE).
|
|
466
|
+
*/
|
|
467
|
+
declare function streamSwap(params: StreamSwapParams): Promise<StreamSwapResult>;
|
|
468
|
+
/**
|
|
469
|
+
* Two-form variant of {@link streamSwap} that additionally returns a
|
|
470
|
+
* {@link StreamSwapController} with `pause()` / `resume()` / `stop()`.
|
|
471
|
+
*
|
|
472
|
+
* @throws {StreamSwapError} Synchronously for construction-time validation.
|
|
473
|
+
*/
|
|
474
|
+
declare function streamSwapControlled(params: StreamSwapParams): {
|
|
475
|
+
result: Promise<StreamSwapResult>;
|
|
476
|
+
controller: StreamSwapController;
|
|
477
|
+
};
|
|
478
|
+
declare const __testing: {
|
|
479
|
+
chunkAmount: typeof chunkAmount;
|
|
480
|
+
decodeFulfillMetadata: typeof decodeFulfillMetadata;
|
|
481
|
+
buildSwapRumor: typeof buildSwapRumor;
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
export { type AccumulatedClaim as A, type DecryptFulfillClaimParams as D, type EncryptFulfillClaimParams as E, type PacketProgress as P, type RateMonitorCallback as R, type StreamSwapController as S, type UnwrapSwapPacketFromToonParams as U, type WrapSwapPacketParams as W, __testing as _, type EncryptFulfillClaimResult as a, type StreamSwapParams as b, type StreamSwapResult as c, type UnwrapSwapPacketParams as d, type UnwrapSwapPacketResult as e, type WrapSwapPacketResult as f, type WrapSwapPacketToToonParams as g, type WrapSwapPacketToToonResult as h, decryptFulfillClaim as i, encryptFulfillClaim as j, streamSwapControlled as k, unwrapSwapPacketFromToon as l, wrapSwapPacketToToon as m, type StreamSwapClient as n, streamSwap as s, unwrapSwapPacket as u, wrapSwapPacket as w };
|
package/dist/swap.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { A as AccumulatedClaim, P as PacketProgress, R as RateMonitorCallback, n as StreamSwapClient, S as StreamSwapController, b as StreamSwapParams, c as StreamSwapResult, i as decryptFulfillClaim, s as streamSwap, k as streamSwapControlled, m as wrapSwapPacketToToon } from './swap-D4Ozr_BM.js';
|
|
2
|
+
import 'nostr-tools/pure';
|
|
3
|
+
import '@toon-protocol/core';
|
package/dist/swap.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
decryptFulfillClaim,
|
|
3
|
+
streamSwap,
|
|
4
|
+
streamSwapControlled,
|
|
5
|
+
wrapSwapPacketToToon
|
|
6
|
+
} from "./chunk-XTHUXP63.js";
|
|
7
|
+
import "./chunk-UP2VWCW5.js";
|
|
8
|
+
export {
|
|
9
|
+
decryptFulfillClaim,
|
|
10
|
+
streamSwap,
|
|
11
|
+
streamSwapControlled,
|
|
12
|
+
wrapSwapPacketToToon
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=swap.js.map
|
package/dist/swap.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toon-protocol/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "TOON SDK for building ILP-gated Nostr services",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -10,22 +10,15 @@
|
|
|
10
10
|
".": {
|
|
11
11
|
"types": "./dist/index.d.ts",
|
|
12
12
|
"import": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./swap": {
|
|
15
|
+
"types": "./dist/swap.d.ts",
|
|
16
|
+
"import": "./dist/swap.js"
|
|
13
17
|
}
|
|
14
18
|
},
|
|
15
19
|
"files": [
|
|
16
20
|
"dist"
|
|
17
21
|
],
|
|
18
|
-
"scripts": {
|
|
19
|
-
"build": "tsup",
|
|
20
|
-
"dev": "tsup --watch",
|
|
21
|
-
"test": "vitest run",
|
|
22
|
-
"test:integration": "vitest run --config vitest.integration.config.ts",
|
|
23
|
-
"test:e2e": "vitest run --config vitest.e2e.config.ts",
|
|
24
|
-
"test:e2e:docker": "vitest run --config vitest.e2e.config.ts -- tests/e2e/docker-publish-event-e2e.test.ts",
|
|
25
|
-
"test:e2e:docker:solana": "vitest run --config vitest.e2e.config.ts -- tests/e2e/docker-solana-settlement-e2e.test.ts",
|
|
26
|
-
"test:e2e:docker:mina": "vitest run --config vitest.e2e.config.ts -- tests/e2e/docker-mina-settlement-e2e.test.ts",
|
|
27
|
-
"test:watch": "vitest"
|
|
28
|
-
},
|
|
29
22
|
"keywords": [
|
|
30
23
|
"toon",
|
|
31
24
|
"nostr",
|
|
@@ -52,11 +45,11 @@
|
|
|
52
45
|
"@scure/bip32": "^2.0.0",
|
|
53
46
|
"@scure/bip39": "^2.0.0",
|
|
54
47
|
"@ardrive/turbo-sdk": "^1.19.0",
|
|
55
|
-
"
|
|
56
|
-
"
|
|
48
|
+
"nostr-tools": "^2.20.0",
|
|
49
|
+
"@toon-protocol/core": "1.5.0"
|
|
57
50
|
},
|
|
58
51
|
"peerDependencies": {
|
|
59
|
-
"@toon-protocol/connector": ">=
|
|
52
|
+
"@toon-protocol/connector": ">=3.3.3",
|
|
60
53
|
"mina-signer": ">=3.0.0"
|
|
61
54
|
},
|
|
62
55
|
"peerDependenciesMeta": {
|
|
@@ -68,14 +61,26 @@
|
|
|
68
61
|
}
|
|
69
62
|
},
|
|
70
63
|
"devDependencies": {
|
|
71
|
-
"@toon-protocol/connector": "^
|
|
72
|
-
"@toon-protocol/relay": "workspace:*",
|
|
64
|
+
"@toon-protocol/connector": "^3.10.0",
|
|
73
65
|
"@types/node": "^20.0.0",
|
|
74
66
|
"@types/ws": "^8.0.0",
|
|
67
|
+
"mina-signer": ">=3.0.0",
|
|
75
68
|
"tsup": "^8.0.0",
|
|
76
69
|
"typescript": "^5.3.0",
|
|
77
70
|
"viem": "^2.0.0",
|
|
78
71
|
"vitest": "^1.0.0",
|
|
79
72
|
"ws": "^8.0.0"
|
|
73
|
+
},
|
|
74
|
+
"scripts": {
|
|
75
|
+
"build": "tsup",
|
|
76
|
+
"dev": "tsup --watch",
|
|
77
|
+
"test": "vitest run",
|
|
78
|
+
"test:integration": "vitest run --config vitest.integration.config.ts",
|
|
79
|
+
"test:e2e": "vitest run --config vitest.e2e.config.ts",
|
|
80
|
+
"test:e2e:docker": "vitest run --config vitest.e2e.config.ts -- tests/e2e/docker-publish-event-e2e.test.ts",
|
|
81
|
+
"test:e2e:docker:solana": "vitest run --config vitest.e2e.config.ts -- tests/e2e/docker-solana-settlement-e2e.test.ts",
|
|
82
|
+
"test:e2e:docker:mina": "vitest run --config vitest.e2e.config.ts -- tests/e2e/docker-mina-settlement-e2e.test.ts",
|
|
83
|
+
"test:e2e:docker:pet": "vitest run --config vitest.e2e.config.ts -- tests/e2e/docker-pet-dvm-e2e.test.ts",
|
|
84
|
+
"test:watch": "vitest"
|
|
80
85
|
}
|
|
81
|
-
}
|
|
86
|
+
}
|