@lumenwipe/sdk 0.8.0 → 0.9.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/CHANGELOG.md +382 -0
- package/LICENSE +201 -0
- package/README.md +99 -12
- package/dist/index.cjs +485 -58
- package/dist/index.d.cts +606 -22
- package/dist/index.d.ts +606 -22
- package/dist/index.js +470 -57
- package/package.json +34 -16
package/README.md
CHANGED
|
@@ -14,6 +14,14 @@ transaction happens server-side; this client just relays typed JSON and XDR stri
|
|
|
14
14
|
npm install @lumenwipe/sdk
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
## Requirements
|
|
18
|
+
|
|
19
|
+
- Node.js 22 or later (`engines.node` is `>=22`; Node 18 and 20 are end-of-life and not tested). No
|
|
20
|
+
runtime dependencies.
|
|
21
|
+
- TypeScript 4.7 or later, with `moduleResolution` set to `node16`, `nodenext` or `bundler`
|
|
22
|
+
(`bundler` needs TypeScript 5.0). The `import` and `require` entry points each ship their own
|
|
23
|
+
declarations.
|
|
24
|
+
|
|
17
25
|
## Quick start
|
|
18
26
|
|
|
19
27
|
```ts
|
|
@@ -27,15 +35,19 @@ const client = new LumenWipeClient({
|
|
|
27
35
|
|
|
28
36
|
const account = await client.getAccount(source);
|
|
29
37
|
|
|
30
|
-
const plan = await client.closePlan({
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
const plan = await client.closePlan({ source, destination });
|
|
39
|
+
if (plan.blockers.length > 0) throw new Error(plan.blockers.map((b) => b.message).join("; "));
|
|
40
|
+
|
|
41
|
+
// Every decision point needs an answer (convert or keep a token, acknowledge an exchange, ...).
|
|
42
|
+
// Show them to the user, plan again with the answers, and close with those same answers.
|
|
43
|
+
const decisions = await askUser(plan.decisionPoints);
|
|
44
|
+
const reviewed = await client.closePlan({ source, destination, decisions });
|
|
35
45
|
|
|
36
46
|
const { transactions, remaining } = await client.closeTransactions({
|
|
37
47
|
source,
|
|
38
48
|
destination,
|
|
49
|
+
decisions,
|
|
50
|
+
planHash: reviewed.planHash,
|
|
39
51
|
});
|
|
40
52
|
|
|
41
53
|
// Sign and submit each transaction, then repeat while remaining.requiresAnotherCall is true.
|
|
@@ -57,13 +69,45 @@ await runClose({
|
|
|
57
69
|
});
|
|
58
70
|
```
|
|
59
71
|
|
|
72
|
+
### Progress
|
|
73
|
+
|
|
74
|
+
Pass `onProgressEvent` to follow the close, for example to show "Transaction 2 of 3: approve in
|
|
75
|
+
your wallet":
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
await runClose({
|
|
79
|
+
...deps,
|
|
80
|
+
onProgressEvent: (event) => {
|
|
81
|
+
if (event.phase === "preparing") return show(`Preparing round ${event.round + 1}…`);
|
|
82
|
+
const of = event.moreRoundsExpected ? `at least ${event.txCount}` : `${event.txCount}`;
|
|
83
|
+
show(`Transaction ${event.txIndex + 1} of ${of}: ${event.phase}`);
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
- `phase`: `preparing` before each round is fetched; then, for each transaction, `verifying`,
|
|
89
|
+
`awaiting-signature` (the signer, for example a wallet, has been asked), `submitting` (signed;
|
|
90
|
+
the network is waited on) and `confirmed`. A resumed transaction starts at
|
|
91
|
+
`awaiting-signature`.
|
|
92
|
+
- `round`: zero-based round of the close. A resumed close keeps the numbering it stopped at.
|
|
93
|
+
- `txIndex`: zero-based position of the transaction in its round (not on `preparing`).
|
|
94
|
+
- `txCount`: how many transactions the round holds (not on `preparing`).
|
|
95
|
+
- `covers`: the plan steps the transaction covers. API metadata until `confirmed`, which carries
|
|
96
|
+
what `onConfirmed` is given: with `reconcile`, a merge only when the client parsed one.
|
|
97
|
+
- `moreRoundsExpected`: the API asked for another round after this one. Rounds are planned from
|
|
98
|
+
live state, so the close then holds at least `txCount` transactions and its total is not
|
|
99
|
+
known; never show `txCount` as the total while this is `true`.
|
|
100
|
+
|
|
101
|
+
The callback only observes: each event is a fresh object, nothing it returns is awaited, and
|
|
102
|
+
anything it throws is ignored. `onProgress(message)` still gets its plain messages.
|
|
103
|
+
|
|
60
104
|
## Headless example
|
|
61
105
|
|
|
62
106
|
[`examples/headless-close.ts`](https://github.com/LumenWipe/lumenwipe/blob/main/packages/sdk/examples/headless-close.ts)
|
|
63
107
|
closes a throwaway testnet account end to end from a plain Node/Bun process, with no browser or
|
|
64
108
|
web UI: it funds two accounts, then runs `getAccount`, `closePlan`, `closeTransactions` and
|
|
65
|
-
`submit` through `runClose`, signing with an in-process keypair. It
|
|
66
|
-
|
|
109
|
+
`submit` through `runClose`, signing with an in-process keypair. It verifies every transaction
|
|
110
|
+
before signing with `@lumenwipe/verify` (`examples/verify.ts`), which is the pattern to copy.
|
|
67
111
|
|
|
68
112
|
```bash
|
|
69
113
|
LUMENWIPE_API_KEY=... bun run --filter '@lumenwipe/sdk' example:headless
|
|
@@ -71,30 +115,71 @@ LUMENWIPE_API_KEY=... bun run --filter '@lumenwipe/sdk' example:headless
|
|
|
71
115
|
|
|
72
116
|
`LUMENWIPE_API_URL` overrides the default `https://api.lumenwipe.com`. Testnet only.
|
|
73
117
|
|
|
118
|
+
## Recording what arrived
|
|
119
|
+
|
|
120
|
+
After a close, read the XLM that reached the destination from the ledger rather than from the
|
|
121
|
+
plan. Collect the hashes `runClose` confirmed, then ask once:
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
const hashes: string[] = [];
|
|
125
|
+
await runClose({ ...deps, onConfirmed: (_tx, hash) => hashes.push(hash) });
|
|
126
|
+
|
|
127
|
+
const delivered = await client.closeDelivered(destination, hashes);
|
|
128
|
+
for (const entry of delivered.entries) {
|
|
129
|
+
if (entry.status === "delivered") record(entry.hash, entry.amountStroops);
|
|
130
|
+
else record(entry.hash, `unavailable: ${entry.reason}`); // not_found, failed or no_delivery
|
|
131
|
+
}
|
|
132
|
+
// Set only when every entry was delivered: never treat its absence as zero.
|
|
133
|
+
if (delivered.totalStroops !== undefined) record("total", delivered.totalStroops);
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Only a merge into the destination, or the relay account's forward of a merged balance, counts.
|
|
137
|
+
A transaction that delivered nothing this way, such as a cleanup round, is `no_delivery`, so pass
|
|
138
|
+
the hashes whose amount you want: up to 20 per call.
|
|
139
|
+
|
|
74
140
|
## Verification is the caller's responsibility
|
|
75
141
|
|
|
76
142
|
This SDK does not verify or sign anything. Every transaction it returns is unsigned XDR built by
|
|
77
143
|
the API from data you provided - before signing, your own code must independently confirm the
|
|
78
144
|
transaction does exactly what the user asked (correct destination, no unexpected operations, no
|
|
79
145
|
added signers, matching amounts) using values from your own inputs, never from the API response
|
|
80
|
-
alone. LumenWipe
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
146
|
+
alone. [`@lumenwipe/verify`](https://github.com/LumenWipe/lumenwipe/tree/main/packages/verify)
|
|
147
|
+
is that check: the verifier LumenWipe's own web client signs with, compiled from
|
|
148
|
+
[`apps/web/lib/stellar/verify.ts`](https://github.com/LumenWipe/lumenwipe/blob/main/apps/web/lib/stellar/verify.ts),
|
|
149
|
+
with a conformance vector set. Pass it as `runClose`'s `verify` (see the docs' Verification page).
|
|
150
|
+
Never sign a transaction from this SDK without it or an equivalent check.
|
|
84
151
|
|
|
85
152
|
## API surface
|
|
86
153
|
|
|
87
|
-
- `health()` - service health
|
|
154
|
+
- `health()` / `healthDeep()` - service health, and whether Stellar RPC is reachable on every
|
|
155
|
+
network (a degraded service throws its 503, never retried).
|
|
156
|
+
- `getExchangeRegistry()` - the exchange registry the API closes against; do not rely on it past
|
|
157
|
+
`validUntil`.
|
|
88
158
|
- `getAccount(address, network?)` - current on-chain state for an account.
|
|
89
159
|
- `getPaths(params, network?)` - conversion path quotes for a source asset.
|
|
90
160
|
- `closePlan(body, network?)` - a preview of the full close plan, with blockers if any step
|
|
91
161
|
cannot be closed safely.
|
|
162
|
+
- `batchPlan(body, network?)` - plans up to 20 accounts into one destination, read-only.
|
|
92
163
|
- `closeTransactions(body, network?)` - builds the next round of unsigned transactions.
|
|
93
164
|
- `submit(signedXdr, network?)` - submits a signed transaction.
|
|
94
165
|
- `mediatorCheck(address, network?)` / `mediatorSign(transaction, network?)` - the exchange
|
|
95
166
|
mediator flow (see the architecture doc).
|
|
167
|
+
- `getTransaction(hash, network?)` / `closeStatus(source, network?)` - look a transaction up by
|
|
168
|
+
hash, and read a close's phase from live state.
|
|
169
|
+
- `closeDelivered(destination, hashes, network?)` - the XLM confirmed close transactions
|
|
170
|
+
delivered to the destination, read from the ledger's result (see below).
|
|
171
|
+
- `feeBumpSponsor(transaction, network?)`, `getAllowances(address, network?)` and
|
|
172
|
+
`revokeAllowance(params, network?)` - fee sponsorship and the allowance inspector.
|
|
173
|
+
- `getStats(network?)` / `getStatsFeed(network?)` / `recordMerge(txHash, network?)` - close
|
|
174
|
+
totals, and counting a close submitted outside the API.
|
|
96
175
|
- `runClose(deps)` - drives the full fetch/verify/sign/submit loop to completion.
|
|
97
176
|
|
|
177
|
+
Every call uses the API's `/v1` paths, except `health()` and `healthDeep()`.
|
|
178
|
+
|
|
179
|
+
`LumenWipeIntegratorClient` is a separate entry point for a wallet's own API keys: `challenge`,
|
|
180
|
+
`session`, `listKeys`, `createKey`, `revokeKey` and `rotateKey`. It takes no API key and never
|
|
181
|
+
sends one; the key calls carry only the wallet's session token. See the SDK docs.
|
|
182
|
+
|
|
98
183
|
Request and response types come from `@lumenwipe/types` and are re-exported from this package, so
|
|
99
184
|
no separate install is needed.
|
|
100
185
|
|
|
@@ -103,6 +188,8 @@ no separate install is needed.
|
|
|
103
188
|
- `LumenWipeApiError` - thrown on any non-2xx API response; carries `status`, `code`, `details`,
|
|
104
189
|
`retryAfterMs`, `requestId` and the parsed error `body`. Narrow with `isApiErrorCode`.
|
|
105
190
|
- `LumenWipeAbortError` - thrown when the `signal` you passed aborts the request.
|
|
191
|
+
- `LumenWipeInvalidNetworkError` - raised before any request when `network` is not `"mainnet"` or
|
|
192
|
+
`"testnet"`.
|
|
106
193
|
- `LumenWipeTimeoutError` - thrown when a request exceeds the configured `timeout`
|
|
107
194
|
(default 30s).
|
|
108
195
|
|