@joeywallet/wallet-sdk 0.2.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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +705 -0
  3. package/dist/client.d.ts +55 -0
  4. package/dist/client.d.ts.map +1 -0
  5. package/dist/client.js +224 -0
  6. package/dist/client.js.map +1 -0
  7. package/dist/detect.d.ts +45 -0
  8. package/dist/detect.d.ts.map +1 -0
  9. package/dist/detect.js +238 -0
  10. package/dist/detect.js.map +1 -0
  11. package/dist/errors.d.ts +75 -0
  12. package/dist/errors.d.ts.map +1 -0
  13. package/dist/errors.js +120 -0
  14. package/dist/errors.js.map +1 -0
  15. package/dist/index.d.ts +13 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +13 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/mutation.d.ts +46 -0
  20. package/dist/mutation.d.ts.map +1 -0
  21. package/dist/mutation.js +67 -0
  22. package/dist/mutation.js.map +1 -0
  23. package/dist/provider.d.ts +159 -0
  24. package/dist/provider.d.ts.map +1 -0
  25. package/dist/provider.js +142 -0
  26. package/dist/provider.js.map +1 -0
  27. package/dist/react.d.ts +76 -0
  28. package/dist/react.d.ts.map +1 -0
  29. package/dist/react.js +225 -0
  30. package/dist/react.js.map +1 -0
  31. package/dist/types.d.ts +391 -0
  32. package/dist/types.d.ts.map +1 -0
  33. package/dist/types.js +38 -0
  34. package/dist/types.js.map +1 -0
  35. package/dist/vanilla.d.ts +58 -0
  36. package/dist/vanilla.d.ts.map +1 -0
  37. package/dist/vanilla.js +161 -0
  38. package/dist/vanilla.js.map +1 -0
  39. package/package.json +70 -0
  40. package/src/client.ts +342 -0
  41. package/src/detect.ts +278 -0
  42. package/src/errors.ts +133 -0
  43. package/src/index.ts +97 -0
  44. package/src/mutation.ts +109 -0
  45. package/src/provider.ts +229 -0
  46. package/src/react.ts +375 -0
  47. package/src/types.ts +440 -0
  48. package/src/vanilla.ts +239 -0
@@ -0,0 +1,391 @@
1
+ /**
2
+ * Public types for `@joeywallet/wallet-sdk`.
3
+ *
4
+ * These mirror the injected provider's surface field for field
5
+ * (`apps/extension/src/provider/`). Where the two could drift, the provider
6
+ * wins: this package is a convenience layer, not a second definition of the
7
+ * protocol.
8
+ *
9
+ * Nothing here imports from `xrpl`. A published `.d.ts` that did would fail to
10
+ * resolve for the (many) dapps that talk to a wallet without depending on
11
+ * xrpl.js, and `skipLibCheck` only hides that, it does not fix it. Instead the
12
+ * transaction argument of every signing method is generic over
13
+ * {@link TransactionLike}, so a caller who *does* have xrpl.js can pass a
14
+ * `Payment` / `TrustSet` / `SubmittableTransaction` and keep full checking on
15
+ * their own side. `test/xrpl-types.test.ts` pins that assignability against the
16
+ * real xrpl.js types.
17
+ */
18
+ /** CAIP-2 chain ids, as XLS-72d defines them. Mainnet 0, testnet 1, devnet 2. */
19
+ export declare const JOEY_CHAINS: readonly ["xrpl:0", "xrpl:1", "xrpl:2"];
20
+ export type JoeyChain = (typeof JOEY_CHAINS)[number];
21
+ export interface JoeyNetwork {
22
+ chain: JoeyChain;
23
+ /** The XRPL `NetworkID`. Same number as the chain id suffix. */
24
+ networkId: number;
25
+ /** The wallet's own name for the network, e.g. `mainnet`. */
26
+ name: string;
27
+ }
28
+ export declare function isJoeyChain(value: unknown): value is JoeyChain;
29
+ /** `xrpl:0` for network id 0, and so on. Throws for anything else. */
30
+ export declare function chainForNetworkId(networkId: number): JoeyChain;
31
+ /** The `NetworkID` a chain id refers to, or `null` when it is not an XRPL chain. */
32
+ export declare function networkIdForChain(chain: string): number | null;
33
+ /** An account as the wallet reports it. Never carries secret material. */
34
+ export interface JoeyAccount {
35
+ /** Classic `r...` address. */
36
+ address: string;
37
+ /** Hex-encoded public key. Absent for a watch-only account, which cannot sign. */
38
+ publicKey?: string;
39
+ /** The nickname the user gave the account, when they chose to share it. */
40
+ label?: string;
41
+ }
42
+ export interface IssuedCurrencyAmount {
43
+ /** Three-character code or 40-character hex. */
44
+ currency: string;
45
+ issuer: string;
46
+ /** Decimal string. Never a JS number — XRPL values exceed float64 precision. */
47
+ value: string;
48
+ }
49
+ export interface MPTAmount {
50
+ mpt_issuance_id: string;
51
+ value: string;
52
+ }
53
+ /** A bare string is drops of XRP. */
54
+ export type Amount = string | IssuedCurrencyAmount | MPTAmount;
55
+ export interface Memo {
56
+ Memo: {
57
+ /** Hex-encoded. */
58
+ MemoData?: string;
59
+ MemoType?: string;
60
+ MemoFormat?: string;
61
+ };
62
+ }
63
+ export interface Signer {
64
+ Signer: {
65
+ Account: string;
66
+ TxnSignature: string;
67
+ SigningPubKey: string;
68
+ };
69
+ }
70
+ export interface PathStep {
71
+ account?: string;
72
+ currency?: string;
73
+ issuer?: string;
74
+ type?: number;
75
+ type_hex?: string;
76
+ }
77
+ export type Path = PathStep[];
78
+ /**
79
+ * The common fields of every XRPL transaction, used only as a generic
80
+ * constraint.
81
+ *
82
+ * `Flags` is deliberately `number | object` rather than a flags interface:
83
+ * xrpl.js models per-transaction flags as separate interfaces, and a TypeScript
84
+ * interface is not assignable to an index-signature type, so anything narrower
85
+ * here would reject a real `Payment`.
86
+ */
87
+ export interface TransactionLike {
88
+ TransactionType: string;
89
+ Account?: string;
90
+ Fee?: string;
91
+ Sequence?: number;
92
+ AccountTxnID?: string;
93
+ Flags?: number | object;
94
+ LastLedgerSequence?: number;
95
+ Memos?: Memo[];
96
+ NetworkID?: number;
97
+ Signers?: Signer[];
98
+ SourceTag?: number;
99
+ SigningPubKey?: string;
100
+ TicketSequence?: number;
101
+ TxnSignature?: string;
102
+ }
103
+ /**
104
+ * A transaction written inline, with no xrpl.js types to hand. The index
105
+ * signature is what lets an object literal carry `Destination`, `Amount` and
106
+ * the rest without an excess-property error.
107
+ */
108
+ export interface AnyTransaction extends TransactionLike {
109
+ [field: string]: unknown;
110
+ }
111
+ export interface ConnectParams {
112
+ /** Chain the dapp wants. Rejected with 4902 when it is not an XRPL chain. */
113
+ chain?: JoeyChain;
114
+ /**
115
+ * Only return accounts this origin has already been granted, with no prompt.
116
+ * Resolves with an empty `accounts` array rather than an error, so it says
117
+ * nothing about whether a wallet is installed, locked, or in use.
118
+ */
119
+ silent?: boolean;
120
+ /** Your dapp's name, shown on the approval screen. Up to 128 characters. */
121
+ name?: string;
122
+ /**
123
+ * An `https:` or `data:` URL for your dapp's icon, shown beside the name.
124
+ * Anything else is ignored rather than rendered.
125
+ */
126
+ icon?: string;
127
+ }
128
+ /**
129
+ * The fields every signing method accepts on top of its own.
130
+ *
131
+ * Both are optional and both are worth sending. Omitting `account` is only safe
132
+ * for an origin the user granted exactly one address; omitting `chain` means
133
+ * you are signing whatever network the wallet happens to be on.
134
+ */
135
+ export interface SigningContextParams {
136
+ /**
137
+ * Which granted address signs. Defaults to the first the user granted.
138
+ *
139
+ * A user may grant several, so send this whenever your transaction carries an
140
+ * `Account`. The wallet refuses to sign a transaction whose `Account` is not
141
+ * the signing address — `INVALID_PARAMS` (-32602), before the user is
142
+ * prompted — rather than producing a valid signature over somebody else's
143
+ * transaction and resolving as if it had worked.
144
+ *
145
+ * `signTransactionFor` is the exception, and there `tx_signer` says who
146
+ * signs: a multisign entry is by definition a signature over a transaction
147
+ * belonging to another account.
148
+ */
149
+ account?: string;
150
+ /**
151
+ * The chain you believe you are on.
152
+ *
153
+ * When it is not the chain the wallet is on, the request is refused with
154
+ * `CHAIN_DISCONNECTED` (4901) rather than signed. Joey has no
155
+ * `switchNetwork`: a page-driven, wallet-wide network switch is a phishing
156
+ * surface, so the user changes network in the wallet and the dapp is told
157
+ * through `networkChanged`.
158
+ */
159
+ chain?: JoeyChain;
160
+ }
161
+ export interface ConnectResult {
162
+ accounts: JoeyAccount[];
163
+ /** `null` when the wallet granted no accounts, so there is no chain to report. */
164
+ chain: JoeyChain | null;
165
+ networkId: number | null;
166
+ }
167
+ export interface SignTransactionParams<TTx extends TransactionLike = AnyTransaction> extends SigningContextParams {
168
+ tx_json: TTx;
169
+ /** Let the wallet fill Fee / Sequence / LastLedgerSequence. Default true. */
170
+ autofill?: boolean;
171
+ }
172
+ export interface SignTransactionResult {
173
+ /**
174
+ * The transaction as signed — decoded back out of `tx_blob`, not echoed from
175
+ * what you sent.
176
+ *
177
+ * That is the point of it: it carries the `Fee`, `Sequence` and
178
+ * `LastLedgerSequence` the wallet filled in, the `SigningPubKey` and
179
+ * `TxnSignature` it produced, and any normalisation the serialiser applied.
180
+ * If it does not say what you expected, the bytes are what it says and not
181
+ * what you sent.
182
+ */
183
+ tx_json: Record<string, unknown>;
184
+ /** Hex-encoded signed transaction blob, ready to submit. */
185
+ tx_blob: string;
186
+ /** Hash of the signed blob. Not a confirmation on its own. */
187
+ hash: string;
188
+ }
189
+ export interface SignAndSubmitTransactionResult extends SignTransactionResult {
190
+ /** Preliminary engine result, e.g. `tesSUCCESS`. Not final until validated. */
191
+ engine_result?: string;
192
+ engine_result_message?: string;
193
+ }
194
+ export interface SignTransactionForParams<TTx extends TransactionLike = AnyTransaction> extends SigningContextParams {
195
+ /**
196
+ * The address whose signature is being produced, which is *not* the
197
+ * transaction's `Account` — that stays the multisigned account.
198
+ *
199
+ * It must be one of the addresses the user granted this origin; the wallet
200
+ * answers `UNAUTHORIZED` (4100) rather than substituting one of its own.
201
+ */
202
+ tx_signer: string;
203
+ tx_json: TTx;
204
+ /**
205
+ * **Ignored on this method.** Joey never autofills a multisign entry, and
206
+ * passing `true` does not make it.
207
+ *
208
+ * The field is here because the parameter shape is shared with the other
209
+ * signing methods, not because it does anything. Your `tx_json` is signed
210
+ * exactly as you sent it, so it must already carry `Fee`, `Sequence` and
211
+ * `LastLedgerSequence` — otherwise you get a real signature over a
212
+ * transaction `rippled` will not accept, and no error until you submit it.
213
+ *
214
+ * The reason is that a multisign signature is one of several over *identical
215
+ * bytes*. All three fields are inside the signed bytes, so two signers who
216
+ * approve a few seconds apart would read two different `LastLedgerSequence`
217
+ * values and the assembled transaction would validate at most one of their
218
+ * signatures. The `Fee` is worse: the rule is `base_fee x (1 + signatures)`,
219
+ * a wallet contributes one signature and cannot know how many others the
220
+ * signer list requires, and a coordinator cannot raise a `Fee` afterwards
221
+ * without discarding every signature it has already collected. The one party
222
+ * who can choose these is the coordinator assembling the transaction — you.
223
+ */
224
+ autofill?: boolean;
225
+ }
226
+ /**
227
+ * N independent transactions, one approval, signed in order.
228
+ *
229
+ * **This is not XLS-56 `Batch`, and the two must not be confused.** A `Batch`
230
+ * is a single transaction that carries others inside `RawTransactions` and
231
+ * commits them atomically on-ledger; Joey refuses to sign one for a website
232
+ * (see {@link JOEY_DAPP_FORBIDDEN_TRANSACTION_TYPES}) because its approval
233
+ * screen renders the outer transaction and a user cannot consent to inner ones
234
+ * they were never shown. `signTransactionBulk` is the opposite arrangement:
235
+ * ordinary, separate transactions, each rendered on its own page of the
236
+ * approval, each signed on its own, with no on-ledger atomicity at all. If
237
+ * transaction 3 fails, 1 and 2 have still happened.
238
+ */
239
+ export interface SignTransactionBulkParams<TTx extends TransactionLike = AnyTransaction> extends SigningContextParams {
240
+ /** At most `MAX_BULK_TRANSACTIONS` entries; the wallet rejects a longer list. */
241
+ tx_list: Array<{
242
+ tx_json: TTx;
243
+ }>;
244
+ autofill?: boolean;
245
+ /**
246
+ * Whether the wallet broadcasts each transaction after signing it, or hands
247
+ * the signed blobs back for you to submit.
248
+ *
249
+ * Required, with no default, because the two are not interchangeable and a
250
+ * dapp that guesses wrong either double-spends or never spends. Joey mobile
251
+ * defaults this to `true` over WalletConnect and the extension defaults it to
252
+ * `false`; state your intent and neither default applies to you.
253
+ */
254
+ submit: boolean;
255
+ }
256
+ /**
257
+ * What became of one transaction of a bulk request that failed part way.
258
+ *
259
+ * The five values divide on two questions only the wallet can answer: did this
260
+ * transaction get a definite answer, and is the replay protection it holds —
261
+ * its sequence number, or its ticket — still reachable?
262
+ *
263
+ * - `submitted` — validated `tesSUCCESS`. It happened; `hash` is on the ledger.
264
+ * - `failed` — a definite answer that is not success. `engine_result` says
265
+ * which. Resubmitting this blob is pointless.
266
+ * - `unknown` — **do not treat this as `failed`.** Either the submission got
267
+ * no answer at all, or it sits behind one that did not. It may yet be
268
+ * validated, so resubmitting is not safe. Resolve it by its `hash` first.
269
+ * - `signed` — signed, never broadcast, and still submittable exactly as it
270
+ * stands: it spends a ticket nothing touched, or it holds the sequence
271
+ * number the account is now at. Submit the `signed` entries in the order
272
+ * they appear — they are a chain.
273
+ * - `stranded` — signed, never broadcast, and dead. The sequence it holds is
274
+ * either already consumed or sits behind a gap this batch will never fill,
275
+ * so it can never apply. Discard it and ask the user again.
276
+ *
277
+ * The last two are decided per entry against the account's actual sequence, not
278
+ * per batch off the failing transaction's code — the code alone is right only
279
+ * for a contiguous run the wallet numbered itself, and both a ticket and a
280
+ * `Sequence` you set yourself break that assumption, in opposite directions.
281
+ */
282
+ export type BulkEntryStatus = 'submitted' | 'failed' | 'unknown' | 'signed' | 'stranded';
283
+ /** One entry of {@link SignTransactionBulkFailure.results}. */
284
+ export interface BulkEntryResult extends SignTransactionResult {
285
+ status: BulkEntryStatus;
286
+ /** The ledger's own token for this transaction, when it produced one. */
287
+ engine_result?: string;
288
+ engine_result_message?: string;
289
+ }
290
+ /**
291
+ * The `data` on the error a partially-executed `signTransactionBulk` rejects
292
+ * with.
293
+ *
294
+ * A bulk request with `submit: true` signs every transaction before it
295
+ * broadcasts any of them, so the blobs exist whatever happens at index 3 and
296
+ * you get all of them back. Resume from `failedIndex` rather than asking the
297
+ * user to approve the whole batch again.
298
+ *
299
+ * ```ts
300
+ * try {
301
+ * await joey.signTransactionBulk({ tx_list, submit: true })
302
+ * } catch (error) {
303
+ * const data = (error as JoeyRpcError).data as SignTransactionBulkFailure | undefined
304
+ * if (data) {
305
+ * // data.results[i].status tells you what to do with entry i.
306
+ * }
307
+ * }
308
+ * ```
309
+ *
310
+ * Joey mobile rejects a bulk request over WalletConnect with the same
311
+ * `failedIndex` — zero-based, every earlier transaction succeeded, and the one
312
+ * thing that transfers between the two wallets unchanged. The record carrying
313
+ * it does not: mobile's `data` is a JSON *string* (WalletConnect types error
314
+ * `data` as one) holding `{failedIndex, signedTxs}`, where `signedTxs` is bare
315
+ * `tx_json` with no `status` on it, and its `message` is the engine token
316
+ * alone. Branch on the container and the array name; do not write one handler
317
+ * for both and expect it to parse.
318
+ */
319
+ export interface SignTransactionBulkFailure {
320
+ /**
321
+ * Zero-based index of the first entry that did not succeed.
322
+ *
323
+ * The error's `message` names the same number the same way — "transaction at
324
+ * index 2 of 5 did not succeed: tecUNFUNDED_PAYMENT" — so the sentence and
325
+ * the field cannot be read as disagreeing. It said "transaction 2 of 5" for
326
+ * that case until this was written, which is the third transaction and the
327
+ * one wording that can be read two ways.
328
+ */
329
+ failedIndex: number;
330
+ /** Every transaction in the batch, in the order you sent them. */
331
+ results: BulkEntryResult[];
332
+ }
333
+ /**
334
+ * Sign-in modes.
335
+ *
336
+ * One, and it is the safe one: `caip122` signs a human-readable CAIP-122 /
337
+ * EIP-4361 string under a non-transaction domain separator, so the signature is
338
+ * cryptographically incapable of being a transaction signature.
339
+ *
340
+ * **`xaman` has been removed.** It signed the `{TransactionType:'SignIn'}`
341
+ * pseudo-transaction, and the property that made it safe — `rippled` has no
342
+ * such type, so the blob is unsubmittable — is exactly why it could not be
343
+ * produced: `ripple-binary-codec` has no `SignIn` either, so serialising one
344
+ * threw, every time, *after* the user had approved. The wallet refuses
345
+ * `mode: 'xaman'` by name rather than silently signing a CAIP-122 message in
346
+ * its place, so an existing Xaman integration gets one clear error instead of a
347
+ * result with no `tx_blob` in it.
348
+ */
349
+ export type SignInMode = 'caip122';
350
+ export interface SignInParams {
351
+ /** Default, and the only value. */
352
+ mode?: SignInMode;
353
+ /** Human-readable line the wallet shows. At most 512 characters. */
354
+ statement?: string;
355
+ /** The wallet generates one when omitted. At most 128 characters. */
356
+ nonce?: string;
357
+ /**
358
+ * Up to 16 URI strings naming the scope you are asking for.
359
+ *
360
+ * Shown on the approval screen and written into the message's `Resources:`
361
+ * section, so they are part of what the signature covers: rebuild the message
362
+ * with the same list, in the same order, to verify one.
363
+ */
364
+ resources?: string[];
365
+ }
366
+ export interface SignInResult {
367
+ address: string;
368
+ publicKey: string;
369
+ /** Hex-encoded signature. */
370
+ signature: string;
371
+ /** The exact string that was signed. Rebuild it to verify the signature. */
372
+ message: string;
373
+ }
374
+ export interface JoeyEventMap {
375
+ /** The origin became authorised. */
376
+ connect: {
377
+ accounts: JoeyAccount[];
378
+ chain: JoeyChain | null;
379
+ };
380
+ /** The origin lost authorisation, or the wallet locked. */
381
+ disconnect: {
382
+ reason?: string;
383
+ };
384
+ /** The granted account set changed. Empty means the grant was revoked. */
385
+ accountsChanged: JoeyAccount[];
386
+ /** `null` when the wallet reported a chain this SDK does not recognise. */
387
+ networkChanged: JoeyNetwork | null;
388
+ }
389
+ export type JoeyEventName = keyof JoeyEventMap;
390
+ export type JoeyEventListener<K extends JoeyEventName> = (payload: JoeyEventMap[K]) => void;
391
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Public types for `@joeywallet/wallet-sdk`.
3
+ *
4
+ * These mirror the injected provider's surface field for field
5
+ * (`apps/extension/src/provider/`). Where the two could drift, the provider
6
+ * wins: this package is a convenience layer, not a second definition of the
7
+ * protocol.
8
+ *
9
+ * Nothing here imports from `xrpl`. A published `.d.ts` that did would fail to
10
+ * resolve for the (many) dapps that talk to a wallet without depending on
11
+ * xrpl.js, and `skipLibCheck` only hides that, it does not fix it. Instead the
12
+ * transaction argument of every signing method is generic over
13
+ * {@link TransactionLike}, so a caller who *does* have xrpl.js can pass a
14
+ * `Payment` / `TrustSet` / `SubmittableTransaction` and keep full checking on
15
+ * their own side. `test/xrpl-types.test.ts` pins that assignability against the
16
+ * real xrpl.js types.
17
+ */
18
+ /* ------------------------------------------------------------------- chains */
19
+ /** CAIP-2 chain ids, as XLS-72d defines them. Mainnet 0, testnet 1, devnet 2. */
20
+ export const JOEY_CHAINS = ['xrpl:0', 'xrpl:1', 'xrpl:2'];
21
+ export function isJoeyChain(value) {
22
+ return typeof value === 'string' && JOEY_CHAINS.includes(value);
23
+ }
24
+ /** `xrpl:0` for network id 0, and so on. Throws for anything else. */
25
+ export function chainForNetworkId(networkId) {
26
+ const chain = `xrpl:${networkId}`;
27
+ if (!isJoeyChain(chain)) {
28
+ throw new RangeError(`Unrecognised XRPL network id: ${networkId}`);
29
+ }
30
+ return chain;
31
+ }
32
+ /** The `NetworkID` a chain id refers to, or `null` when it is not an XRPL chain. */
33
+ export function networkIdForChain(chain) {
34
+ if (!isJoeyChain(chain))
35
+ return null;
36
+ return Number(chain.slice('xrpl:'.length));
37
+ }
38
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,gFAAgF;AAEhF,iFAAiF;AACjF,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAU,CAAA;AAYlE,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAK,WAAiC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;AACxF,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,iBAAiB,CAAC,SAAiB;IACjD,MAAM,KAAK,GAAG,QAAQ,SAAS,EAAE,CAAA;IACjC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,UAAU,CAAC,iCAAiC,SAAS,EAAE,CAAC,CAAA;IACpE,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC7C,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACpC,OAAO,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;AAC5C,CAAC"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * `@joeywallet/wallet-sdk/vanilla`.
3
+ *
4
+ * A connection session with a subscribe/getState store, for pages that have no
5
+ * framework. It holds the two pieces of state every dapp ends up re-writing by
6
+ * hand — the connected account and the current network — and keeps them correct
7
+ * when the user switches account or network in the wallet.
8
+ */
9
+ import type { Joey } from './client.js';
10
+ import { JoeyRpcError } from './errors.js';
11
+ import type { ConnectParams, JoeyNetwork } from './types.js';
12
+ export interface JoeySessionState {
13
+ /** `null` while detection is still running, and if it never finds a provider. */
14
+ joey: Joey | null;
15
+ isAvailable: boolean;
16
+ isReady: boolean;
17
+ isConnecting: boolean;
18
+ accounts: string[];
19
+ /** The first granted address. `null` when this origin has no grant. */
20
+ account: string | null;
21
+ network: JoeyNetwork | null;
22
+ error: JoeyRpcError | null;
23
+ }
24
+ export interface JoeySession {
25
+ getState(): JoeySessionState;
26
+ /** Called immediately with the current state, then on every change. */
27
+ subscribe(listener: (state: JoeySessionState) => void): () => void;
28
+ connect(params?: ConnectParams): Promise<string | null>;
29
+ disconnect(): Promise<void>;
30
+ /** Removes every listener this session registered. */
31
+ destroy(): void;
32
+ }
33
+ export interface CreateJoeySessionOptions {
34
+ /** Try a silent reconnect once the provider is found. Default true. */
35
+ autoConnect?: boolean;
36
+ /** How long to wait for a late-injected provider. Default 3000ms. */
37
+ detectTimeoutMs?: number;
38
+ }
39
+ export declare function createJoeySession(options?: CreateJoeySessionOptions): JoeySession;
40
+ export interface BindConnectButtonOptions {
41
+ /** Text while disconnected. Default "Connect Joey". */
42
+ connectLabel?: string;
43
+ /** Rendered with the connected address. Default is a truncated address. */
44
+ connectedLabel?(address: string): string;
45
+ /** Text when no provider was found. Default "Install Joey". */
46
+ notInstalledLabel?: string;
47
+ /** Where to send a user with no wallet. Opened in a new tab when clicked. */
48
+ installUrl?: string;
49
+ onError?(error: JoeyRpcError): void;
50
+ }
51
+ /**
52
+ * Wire a `<button>` to a session: label, disabled state and click handler.
53
+ *
54
+ * The whole point of the vanilla entry point — this is the twenty lines every
55
+ * script-tag dapp writes, and gets subtly wrong around the not-installed case.
56
+ */
57
+ export declare function bindConnectButton(button: HTMLButtonElement, session: JoeySession, options?: BindConnectButtonOptions): () => void;
58
+ //# sourceMappingURL=vanilla.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vanilla.d.ts","sourceRoot":"","sources":["../src/vanilla.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAEvC,OAAO,EAAoB,YAAY,EAAE,MAAM,aAAa,CAAA;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE5D,MAAM,WAAW,gBAAgB;IAC/B,iFAAiF;IACjF,IAAI,EAAE,IAAI,GAAG,IAAI,CAAA;IACjB,WAAW,EAAE,OAAO,CAAA;IACpB,OAAO,EAAE,OAAO,CAAA;IAChB,YAAY,EAAE,OAAO,CAAA;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,uEAAuE;IACvE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,OAAO,EAAE,WAAW,GAAG,IAAI,CAAA;IAC3B,KAAK,EAAE,YAAY,GAAG,IAAI,CAAA;CAC3B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,IAAI,gBAAgB,CAAA;IAC5B,uEAAuE;IACvE,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAA;IAClE,OAAO,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IACvD,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAC3B,sDAAsD;IACtD,OAAO,IAAI,IAAI,CAAA;CAChB;AAED,MAAM,WAAW,wBAAwB;IACvC,uEAAuE;IACvE,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,qEAAqE;IACrE,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,wBAA6B,GAAG,WAAW,CA+HrF;AAED,MAAM,WAAW,wBAAwB;IACvC,uDAAuD;IACvD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,2EAA2E;IAC3E,cAAc,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAA;IACxC,+DAA+D;IAC/D,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAA;CACpC;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,WAAW,EACpB,OAAO,GAAE,wBAA6B,GACrC,MAAM,IAAI,CA4CZ"}
@@ -0,0 +1,161 @@
1
+ import { getJoey, waitForJoey } from './detect.js';
2
+ import { JOEY_ERROR_CODES, JoeyRpcError } from './errors.js';
3
+ export function createJoeySession(options = {}) {
4
+ const { autoConnect = true, detectTimeoutMs = 3000 } = options;
5
+ const initial = getJoey();
6
+ let state = {
7
+ joey: initial,
8
+ isAvailable: initial !== null,
9
+ isReady: initial !== null,
10
+ isConnecting: false,
11
+ accounts: [],
12
+ account: null,
13
+ network: null,
14
+ error: null,
15
+ };
16
+ const listeners = new Set();
17
+ const teardown = [];
18
+ let destroyed = false;
19
+ const set = (patch) => {
20
+ const next = { ...state, ...patch };
21
+ // `account` is always derived, never set directly, so the two can't drift.
22
+ next.account = next.accounts[0] ?? null;
23
+ state = next;
24
+ for (const listener of listeners)
25
+ listener(state);
26
+ };
27
+ const attach = (joey) => {
28
+ teardown.push(joey.on('accountsChanged', (accounts) => set({ accounts: accounts.map((account) => account.address) })));
29
+ teardown.push(joey.on('networkChanged', (network) => set({ network })));
30
+ teardown.push(joey.on('connect', (result) => set({ accounts: result.accounts.map((account) => account.address) })));
31
+ teardown.push(joey.on('disconnect', () => set({ accounts: [] })));
32
+ };
33
+ const hydrate = async (joey) => {
34
+ const [accounts, network] = await Promise.all([
35
+ joey.getAccounts().catch(() => []),
36
+ joey.getNetwork().catch(() => null),
37
+ ]);
38
+ if (!destroyed)
39
+ set({ accounts, network });
40
+ };
41
+ const adopt = async (joey) => {
42
+ if (destroyed)
43
+ return;
44
+ set({ joey, isAvailable: true, isReady: true });
45
+ attach(joey);
46
+ if (autoConnect) {
47
+ try {
48
+ const result = await joey.connect({ silent: true });
49
+ if (destroyed)
50
+ return;
51
+ if (result.accounts.length > 0) {
52
+ set({ accounts: result.accounts.map((account) => account.address) });
53
+ const network = await joey.getNetwork().catch(() => null);
54
+ if (!destroyed)
55
+ set({ network });
56
+ return;
57
+ }
58
+ }
59
+ catch {
60
+ // Not yet authorised. Expected on a first visit.
61
+ }
62
+ }
63
+ await hydrate(joey);
64
+ };
65
+ if (initial !== null) {
66
+ void adopt(initial);
67
+ }
68
+ else {
69
+ void waitForJoey({ timeoutMs: detectTimeoutMs })
70
+ .then(adopt)
71
+ .catch(() => {
72
+ if (!destroyed)
73
+ set({ isReady: true });
74
+ });
75
+ }
76
+ return {
77
+ getState: () => state,
78
+ subscribe(listener) {
79
+ listeners.add(listener);
80
+ listener(state);
81
+ return () => listeners.delete(listener);
82
+ },
83
+ async connect(params) {
84
+ const joey = state.joey ?? getJoey();
85
+ if (joey === null) {
86
+ const error = new JoeyRpcError(JOEY_ERROR_CODES.DISCONNECTED, 'Joey Wallet is not installed, or its provider has not been injected into this page.');
87
+ set({ error });
88
+ throw error;
89
+ }
90
+ set({ isConnecting: true, error: null });
91
+ try {
92
+ const result = await joey.connect(params);
93
+ const accounts = result.accounts.map((account) => account.address);
94
+ const network = await joey.getNetwork().catch(() => state.network);
95
+ set({ accounts, network, isConnecting: false });
96
+ return accounts[0] ?? null;
97
+ }
98
+ catch (cause) {
99
+ const error = JoeyRpcError.from(cause);
100
+ set({ isConnecting: false, error });
101
+ throw error;
102
+ }
103
+ },
104
+ async disconnect() {
105
+ const joey = state.joey;
106
+ if (joey === null)
107
+ return;
108
+ await joey.disconnect();
109
+ set({ accounts: [] });
110
+ },
111
+ destroy() {
112
+ destroyed = true;
113
+ for (const off of teardown.splice(0))
114
+ off();
115
+ listeners.clear();
116
+ },
117
+ };
118
+ }
119
+ /**
120
+ * Wire a `<button>` to a session: label, disabled state and click handler.
121
+ *
122
+ * The whole point of the vanilla entry point — this is the twenty lines every
123
+ * script-tag dapp writes, and gets subtly wrong around the not-installed case.
124
+ */
125
+ export function bindConnectButton(button, session, options = {}) {
126
+ const { connectLabel = 'Connect Joey', connectedLabel = (address) => `${address.slice(0, 6)}…${address.slice(-4)}`, notInstalledLabel = 'Install Joey', installUrl, onError, } = options;
127
+ const render = (state) => {
128
+ if (!state.isReady) {
129
+ button.disabled = true;
130
+ button.textContent = connectLabel;
131
+ return;
132
+ }
133
+ if (!state.isAvailable) {
134
+ button.disabled = installUrl === undefined;
135
+ button.textContent = notInstalledLabel;
136
+ return;
137
+ }
138
+ button.disabled = state.isConnecting;
139
+ button.textContent = state.account !== null ? connectedLabel(state.account) : connectLabel;
140
+ };
141
+ const onClick = () => {
142
+ const state = session.getState();
143
+ if (!state.isAvailable) {
144
+ if (installUrl !== undefined)
145
+ window.open(installUrl, '_blank', 'noopener,noreferrer');
146
+ return;
147
+ }
148
+ // `session.connect` already stored the error in state; this catch only
149
+ // exists so the rejection is not unhandled.
150
+ void session.connect().catch((error) => {
151
+ onError?.(JoeyRpcError.from(error));
152
+ });
153
+ };
154
+ button.addEventListener('click', onClick);
155
+ const unsubscribe = session.subscribe(render);
156
+ return () => {
157
+ button.removeEventListener('click', onClick);
158
+ unsubscribe();
159
+ };
160
+ }
161
+ //# sourceMappingURL=vanilla.js.map