@ldgr/client 0.4.0 → 0.5.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 CHANGED
@@ -67,6 +67,55 @@ Settlement failures return `status: "failed"`; they do not have a transaction
67
67
  hash or fee because the ledger rejected them before appending a transaction.
68
68
  Transport and unexpected response errors still throw.
69
69
 
70
+ ## Silent payments
71
+
72
+ `silentPayments.send()` accepts a reusable LDGR payment code and a
73
+ `LocalLdgrWallet`. It signs and publishes the wallet's immutable sender-DH
74
+ public key, fetches the asset nonce, derives a fresh one-time destination, and
75
+ submits an ordinary transfer. A `NONCE_MISMATCH` causes one fresh derivation;
76
+ an ambiguous transport failure retries the exact signed request instead.
77
+
78
+ ```ts
79
+ import {
80
+ createLdgrClient,
81
+ deriveLocalSilentPaymentIdentity,
82
+ loadLocalWallet,
83
+ } from "@ldgr/client";
84
+
85
+ const client = createLdgrClient({ network: "testnet" });
86
+ const senderWallet = await loadLocalWallet({
87
+ ledgerId: 0,
88
+ recoveryKey: await secretStore.read("ldgr-testnet-sender"),
89
+ });
90
+ // The receiver shares this code out of band; their recovery key never leaves
91
+ // their own wallet.
92
+ const receiver = deriveLocalSilentPaymentIdentity({
93
+ ledgerId: 0,
94
+ recoveryKey: await receiverSecretStore.read("ldgr-testnet-receiver"),
95
+ });
96
+ const result = await client.silentPayments.send({
97
+ wallet: senderWallet,
98
+ paymentCode: receiver.paymentCode,
99
+ asset: "t0",
100
+ amount: "2.50",
101
+ });
102
+ ```
103
+
104
+ If two byte-identical transport attempts both remain ambiguous,
105
+ `SilentPaymentSubmissionUnknownError` exposes the exact `submission`. Pass it
106
+ to `client.silentPayments.retry(error.submission)`; do not start a fresh send,
107
+ which would use the next nonce and could duplicate a payment that already
108
+ settled.
109
+
110
+ `silentPayments.scan(after, limit)` returns a bounded ascending page, its raw
111
+ ledger-sequence `nextAfter` checkpoint, and `moreAvailable`. Matching requires
112
+ the receiver scan secret and belongs in wallet code such as
113
+ `@ldgr/wallet-core`, never on a server that does not own the recovery key. This
114
+ LDGR-specific construction is not BIP-352-compatible and remains experimental.
115
+ Wallet discovery is available on both ledgers, while reusable-code display,
116
+ first-party sending, and private-output spending remain gated pending testnet-first
117
+ operational testing and external cryptographic review.
118
+
70
119
  ```ts
71
120
  import { createLdgrClient } from "@ldgr/client";
72
121
 
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { type LdgrTransferResult, type SendTransferInput } from "./transfer";
2
- import { type Ldgr402Requirement, type LedgerId } from "@ldgr/protocol";
3
- import { type LdgrAccountTransactionPage, type LdgrClientOptions, type LdgrAsset, type Ldgr402Verification, type LdgrTransaction, type PaymentExpectation, type PaymentVerification, type SignedSubmission } from "./types";
2
+ import { type Ldgr402Requirement } from "@ldgr/protocol";
3
+ import { type LdgrAccountTransactionPage, type LdgrClientOptions, type LdgrAsset, type Ldgr402Verification, type LdgrTransaction, type PaymentExpectation, type PaymentVerification, type SignedSubmission, type SilentPaymentSendInput, type SilentPaymentScanPage, type WalletPublicKeyRecord, type WalletPublicKeyRegistration } from "./types";
4
4
  export declare function createLdgrClient(options: LdgrClientOptions): {
5
5
  ledger: {
6
6
  get: () => Promise<{
@@ -26,23 +26,8 @@ export declare function createLdgrClient(options: LdgrClientOptions): {
26
26
  readonly asset: string;
27
27
  readonly nextNonce: string;
28
28
  }>;
29
- getWalletPublicKey: (address: string) => Promise<{
30
- readonly address: string;
31
- readonly publicKey: string;
32
- }>;
33
- registerWalletPublicKey: (address: string, registration: {
34
- readonly version: 1;
35
- readonly action: "wallet_key_register";
36
- readonly chain: "ldgr-v1";
37
- readonly ledgerId: LedgerId;
38
- readonly from: string;
39
- readonly publicKey: string;
40
- readonly timestamp: string;
41
- readonly signature: string;
42
- }) => Promise<{
43
- readonly address: string;
44
- readonly publicKey: string;
45
- }>;
29
+ getWalletPublicKey: (address: string) => Promise<WalletPublicKeyRecord>;
30
+ registerWalletPublicKey: (address: string, registration: WalletPublicKeyRegistration) => Promise<WalletPublicKeyRecord>;
46
31
  listTransactions(address: string, options?: {
47
32
  readonly before?: string;
48
33
  readonly asset?: string;
@@ -94,6 +79,11 @@ export declare function createLdgrClient(options: LdgrClientOptions): {
94
79
  readonly nowMs?: number;
95
80
  }) => Promise<Ldgr402Verification>;
96
81
  };
82
+ silentPayments: {
83
+ scan: (after?: string, limit?: number) => Promise<SilentPaymentScanPage>;
84
+ send: (input: SilentPaymentSendInput) => Promise<LdgrTransferResult>;
85
+ retry(submission: SignedSubmission): Promise<LdgrTransferResult>;
86
+ };
97
87
  transfers: {
98
88
  send: (input: SendTransferInput) => Promise<LdgrTransferResult>;
99
89
  };
@@ -101,10 +91,10 @@ export declare function createLdgrClient(options: LdgrClientOptions): {
101
91
  readonly transactionHash?: string;
102
92
  }>(path: string, signed: SignedSubmission) => Promise<T>;
103
93
  };
104
- export { LdgrClientError } from "./types";
94
+ export { LdgrClientError, SilentPaymentSubmissionUnknownError } from "./types";
105
95
  export { decodeMerchantLdgr402Payment, parseMerchantLdgr402Requirement } from "./ldgr402";
106
- export type { LdgrAccountTransaction, LdgrAccountTransactionPage, Ldgr402Requirement, Ldgr402Verification, LdgrAsset, LdgrClientOptions, LdgrNetwork, LdgrTransaction, PaymentExpectation, PaymentVerification, SignedSubmission } from "./types";
107
- export { createWebCryptoSigner, generateLocalWallet, loadLocalWallet } from "./signer";
108
- export type { LdgrSigner, LocalLdgrWallet } from "./signer";
96
+ export type { LdgrAccountTransaction, LdgrAccountTransactionPage, Ldgr402Requirement, Ldgr402Verification, LdgrAsset, LdgrClientOptions, LdgrNetwork, LdgrTransaction, PaymentExpectation, PaymentVerification, SignedSubmission, SilentPaymentScanPage, SilentPaymentScanRecord, SilentPaymentSendInput, WalletPublicKeyRecord, WalletPublicKeyRegistration } from "./types";
97
+ export { createWebCryptoSigner, deriveLocalSilentPaymentIdentity, generateLocalWallet, loadLocalWallet } from "./signer";
98
+ export type { LdgrSigner, LocalLdgrWallet, LocalSilentPaymentIdentity } from "./signer";
109
99
  export { buildTransferSubmission } from "./transfer";
110
100
  export type { BuildTransferSubmissionInput, LdgrTransferResult, SendTransferInput } from "./transfer";
package/dist/index.js CHANGED
@@ -2,6 +2,15 @@
2
2
  import { resolveLdgrNetwork } from "@ldgr/protocol";
3
3
 
4
4
  // src/types.ts
5
+ class SilentPaymentSubmissionUnknownError extends Error {
6
+ submission;
7
+ constructor(submission, cause) {
8
+ super("Could not confirm silent payment submission; retry this exact signed submission", { cause });
9
+ this.name = "SilentPaymentSubmissionUnknownError";
10
+ this.submission = submission;
11
+ }
12
+ }
13
+
5
14
  class LdgrClientError extends Error {
6
15
  status;
7
16
  code;
@@ -77,6 +86,67 @@ function queryPath(path, query) {
77
86
  return encoded ? `${path}?${encoded}` : path;
78
87
  }
79
88
 
89
+ // src/signer.ts
90
+ import {
91
+ deriveAddress,
92
+ deriveSilentPaymentIdentity,
93
+ decodeEd25519RecoveryKey,
94
+ encodeEd25519RecoveryKey,
95
+ exportEd25519PublicKey,
96
+ generateEd25519KeyPair,
97
+ importEd25519PrivateSeed,
98
+ signCanonicalPayload
99
+ } from "@ldgr/protocol";
100
+ function encodeBase64Url(bytes) {
101
+ let binary = "";
102
+ for (const byte of bytes)
103
+ binary += String.fromCharCode(byte);
104
+ return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
105
+ }
106
+ function deriveLocalSilentPaymentIdentity(input) {
107
+ const { privateSeed } = decodeEd25519RecoveryKey(input.recoveryKey);
108
+ const identity = deriveSilentPaymentIdentity(privateSeed, input.ledgerId);
109
+ return {
110
+ paymentCode: identity.paymentCode,
111
+ senderDhPublicKey: encodeBase64Url(identity.senderDhPublicKey),
112
+ receiverScanPublicKey: encodeBase64Url(identity.receiverScanPublicKey),
113
+ spendPublicKey: encodeBase64Url(identity.spendPublicKey)
114
+ };
115
+ }
116
+ async function createWebCryptoSigner(input) {
117
+ const publicKey = encodeBase64Url(await exportEd25519PublicKey(input.publicKey));
118
+ const address = await deriveAddress(input.publicKey, input.ledgerId);
119
+ return {
120
+ address,
121
+ publicKey,
122
+ ledgerId: input.ledgerId,
123
+ async sign(payload) {
124
+ if (payload.ledgerId !== input.ledgerId)
125
+ throw new TypeError("payload ledgerId does not match signer ledgerId");
126
+ if (payload.from !== address)
127
+ throw new TypeError("payload from does not match signer address");
128
+ if (payload.publicKey !== undefined && payload.publicKey !== publicKey) {
129
+ throw new TypeError("payload publicKey does not match signer publicKey");
130
+ }
131
+ return encodeBase64Url(await signCanonicalPayload(input.privateKey, { ...payload, publicKey }));
132
+ }
133
+ };
134
+ }
135
+ async function generateLocalWallet(input) {
136
+ const keyPair = await generateEd25519KeyPair();
137
+ const signer = await createWebCryptoSigner({ ...input, ...keyPair });
138
+ const privateKeyJwk = await globalThis.crypto.subtle.exportKey("jwk", keyPair.privateKey);
139
+ return {
140
+ ...signer,
141
+ recoveryKey: encodeEd25519RecoveryKey(privateKeyJwk)
142
+ };
143
+ }
144
+ async function loadLocalWallet(input) {
145
+ const { privateSeed } = decodeEd25519RecoveryKey(input.recoveryKey);
146
+ const keyPair = await importEd25519PrivateSeed(privateSeed);
147
+ return { ...await createWebCryptoSigner({ ...input, ...keyPair }), recoveryKey: input.recoveryKey };
148
+ }
149
+
80
150
  // src/transfer.ts
81
151
  import {
82
152
  canonicalizeLedgerAddress,
@@ -127,6 +197,10 @@ async function buildTransferSubmission(input) {
127
197
  // src/index.ts
128
198
  import {
129
199
  decodeLdgr402Payment as decodeLdgr402Payment3,
200
+ decodeEd25519RecoveryKey as decodeEd25519RecoveryKey2,
201
+ decodeSilentPaymentCode,
202
+ deriveSilentPaymentIdentity as deriveSilentPaymentIdentity2,
203
+ deriveSilentPaymentSenderOutput,
130
204
  nativeAssetDefinitionForLedger,
131
205
  parseLdgr402Requirement as parseLdgr402Requirement3,
132
206
  resolveLdgrNetwork as resolveLdgrNetwork2,
@@ -147,55 +221,6 @@ function parseMerchantLdgr402Requirement(input, options = {}) {
147
221
  function decodeMerchantLdgr402Payment(headerValue) {
148
222
  return decodeLdgr402Payment(headerValue);
149
223
  }
150
- // src/signer.ts
151
- import {
152
- deriveAddress,
153
- decodeEd25519RecoveryKey,
154
- encodeEd25519RecoveryKey,
155
- exportEd25519PublicKey,
156
- generateEd25519KeyPair,
157
- importEd25519PrivateSeed,
158
- signCanonicalPayload
159
- } from "@ldgr/protocol";
160
- function encodeBase64Url(bytes) {
161
- let binary = "";
162
- for (const byte of bytes)
163
- binary += String.fromCharCode(byte);
164
- return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
165
- }
166
- async function createWebCryptoSigner(input) {
167
- const publicKey = encodeBase64Url(await exportEd25519PublicKey(input.publicKey));
168
- const address = await deriveAddress(input.publicKey, input.ledgerId);
169
- return {
170
- address,
171
- publicKey,
172
- ledgerId: input.ledgerId,
173
- async sign(payload) {
174
- if (payload.ledgerId !== input.ledgerId)
175
- throw new TypeError("payload ledgerId does not match signer ledgerId");
176
- if (payload.from !== address)
177
- throw new TypeError("payload from does not match signer address");
178
- if (payload.publicKey !== undefined && payload.publicKey !== publicKey) {
179
- throw new TypeError("payload publicKey does not match signer publicKey");
180
- }
181
- return encodeBase64Url(await signCanonicalPayload(input.privateKey, { ...payload, publicKey }));
182
- }
183
- };
184
- }
185
- async function generateLocalWallet(input) {
186
- const keyPair = await generateEd25519KeyPair();
187
- const signer = await createWebCryptoSigner({ ...input, ...keyPair });
188
- const privateKeyJwk = await globalThis.crypto.subtle.exportKey("jwk", keyPair.privateKey);
189
- return {
190
- ...signer,
191
- recoveryKey: encodeEd25519RecoveryKey(privateKeyJwk)
192
- };
193
- }
194
- async function loadLocalWallet(input) {
195
- const { privateSeed } = decodeEd25519RecoveryKey(input.recoveryKey);
196
- const keyPair = await importEd25519PrivateSeed(privateSeed);
197
- return createWebCryptoSigner({ ...input, ...keyPair });
198
- }
199
224
 
200
225
  // src/index.ts
201
226
  function normalizedHash(transactionHash) {
@@ -335,6 +360,98 @@ function createLdgrClient(options) {
335
360
  throw error;
336
361
  }
337
362
  };
363
+ const sendSilentPayment = async (input) => {
364
+ if (ledgerId !== undefined && ledgerId !== input.wallet.ledgerId) {
365
+ throw new TypeError("signer ledgerId does not match client network");
366
+ }
367
+ const requestedAsset = input.asset.trim();
368
+ if (!requestedAsset)
369
+ throw new TypeError("asset is required");
370
+ const asset = resolvePublicAssetCode(requestedAsset, input.wallet.ledgerId);
371
+ decodeSilentPaymentCode(input.paymentCode, input.wallet.ledgerId);
372
+ const assetDefinition = asset === "dng" ? nativeAssetDefinitionForLedger(input.wallet.ledgerId) : await transport.get(`/api/v1/assets/${encodePathSegment(asset)}`);
373
+ const senderIdentity = deriveSilentPaymentIdentity2(decodeEd25519RecoveryKey2(input.wallet.recoveryKey).privateSeed, input.wallet.ledgerId);
374
+ const registrationPayload = {
375
+ version: 1,
376
+ action: "wallet_key_register",
377
+ chain: "ldgr-v1",
378
+ ledgerId: input.wallet.ledgerId,
379
+ from: input.wallet.address,
380
+ publicKey: input.wallet.publicKey,
381
+ senderDhPublicKey: deriveLocalSilentPaymentIdentity(input.wallet).senderDhPublicKey,
382
+ timestamp: String((input.now ?? Date.now)())
383
+ };
384
+ await transport.post(`/api/v1/wallets/${encodePathSegment(input.wallet.address)}/public-key`, { ...registrationPayload, signature: await input.wallet.sign(registrationPayload) });
385
+ const submit = async () => {
386
+ const nonce = await transport.get(`/api/v1/nonces/${encodePathSegment(input.wallet.address)}/${encodePathSegment(asset)}`);
387
+ if (!/^(0|[1-9][0-9]*)$/.test(nonce.nextNonce))
388
+ throw new TypeError("Ledger returned an invalid next nonce");
389
+ const output = deriveSilentPaymentSenderOutput({
390
+ senderDhPrivateKey: senderIdentity.senderDhPrivateKey,
391
+ paymentCode: input.paymentCode,
392
+ chain: "ldgr-v1",
393
+ ledgerId: input.wallet.ledgerId,
394
+ senderAddress: input.wallet.address,
395
+ action: "transfer",
396
+ asset,
397
+ nonce: BigInt(nonce.nextNonce),
398
+ outputIndex: 0
399
+ });
400
+ const submission = await buildTransferSubmission({
401
+ wallet: input.wallet,
402
+ asset,
403
+ assetDefinition,
404
+ amount: input.amount,
405
+ to: output.address,
406
+ nextNonce: nonce.nextNonce,
407
+ ...input.memo === undefined ? {} : { memo: input.memo },
408
+ ...input.metadata === undefined ? {} : { metadata: input.metadata },
409
+ ...input.now === undefined ? {} : { now: input.now }
410
+ });
411
+ const feeSponsor = input.buildFeeSponsor === undefined ? undefined : await input.buildFeeSponsor(submission);
412
+ const requestBody = {
413
+ ...submission,
414
+ ...feeSponsor === undefined ? {} : { feeSponsor },
415
+ submittedAtMs: Date.now()
416
+ };
417
+ let transaction;
418
+ try {
419
+ transaction = await transport.post("/api/v1/transfers", requestBody);
420
+ } catch (error) {
421
+ if (error instanceof LdgrClientError)
422
+ throw error;
423
+ try {
424
+ transaction = await transport.post("/api/v1/transfers", requestBody);
425
+ } catch (retryError) {
426
+ if (retryError instanceof LdgrClientError)
427
+ throw retryError;
428
+ throw new SilentPaymentSubmissionUnknownError(requestBody, retryError);
429
+ }
430
+ }
431
+ if (typeof transaction.transactionHash !== "string" || typeof transaction.fee !== "string" || typeof transaction.feeAsset !== "string") {
432
+ throw new TypeError("LDGR transfer response is missing transaction hash or fee");
433
+ }
434
+ return { status: "settled", transactionHash: transaction.transactionHash, fee: transaction.fee, feeAsset: transaction.feeAsset, transaction };
435
+ };
436
+ try {
437
+ return await submit();
438
+ } catch (error) {
439
+ if (isNonceMismatch(error)) {
440
+ try {
441
+ return await submit();
442
+ } catch (retryError) {
443
+ const failure2 = transferFailure(retryError);
444
+ if (failure2 !== undefined)
445
+ return failure2;
446
+ throw retryError;
447
+ }
448
+ }
449
+ const failure = transferFailure(error);
450
+ if (failure !== undefined)
451
+ return failure;
452
+ throw error;
453
+ }
454
+ };
338
455
  return {
339
456
  ledger: {
340
457
  get: () => transport.get("/api/v1/ledger"),
@@ -404,16 +521,38 @@ function createLdgrClient(options) {
404
521
  listOwned: (address, options2) => transport.get(queryPath(`/api/v1/usernames/owned-by/${encodePathSegment(address)}`, options2 ?? {}))
405
522
  },
406
523
  payments: { verifyTransfer, verifyLdgr402 },
524
+ silentPayments: {
525
+ scan: (after = "0", limit) => transport.get(queryPath("/api/v1/silent-payments/scan", { after, limit })),
526
+ send: sendSilentPayment,
527
+ async retry(submission) {
528
+ try {
529
+ const transaction = await transport.post("/api/v1/transfers", submission);
530
+ if (typeof transaction.transactionHash !== "string" || typeof transaction.fee !== "string" || typeof transaction.feeAsset !== "string") {
531
+ throw new TypeError("LDGR transfer response is missing transaction hash or fee");
532
+ }
533
+ return { status: "settled", transactionHash: transaction.transactionHash, fee: transaction.fee, feeAsset: transaction.feeAsset, transaction };
534
+ } catch (error) {
535
+ const failure = transferFailure(error);
536
+ if (failure !== undefined)
537
+ return failure;
538
+ if (error instanceof LdgrClientError)
539
+ throw error;
540
+ throw new SilentPaymentSubmissionUnknownError(submission, error);
541
+ }
542
+ }
543
+ },
407
544
  transfers: { send: sendTransfer },
408
545
  submitSigned: (path, signed) => transport.post(path, { ...signed, submittedAtMs: Date.now() })
409
546
  };
410
547
  }
411
548
  export {
412
549
  LdgrClientError,
550
+ SilentPaymentSubmissionUnknownError,
413
551
  buildTransferSubmission,
414
552
  createLdgrClient,
415
553
  createWebCryptoSigner,
416
554
  decodeMerchantLdgr402Payment,
555
+ deriveLocalSilentPaymentIdentity,
417
556
  generateLocalWallet,
418
557
  loadLocalWallet,
419
558
  parseMerchantLdgr402Requirement
package/dist/signer.d.ts CHANGED
@@ -9,6 +9,16 @@ export interface LocalLdgrWallet extends LdgrSigner {
9
9
  /** Store this only in the caller's chosen secret manager; it is never sent to LDGR. */
10
10
  readonly recoveryKey: string;
11
11
  }
12
+ export interface LocalSilentPaymentIdentity {
13
+ readonly paymentCode: string;
14
+ readonly senderDhPublicKey: string;
15
+ readonly receiverScanPublicKey: string;
16
+ readonly spendPublicKey: string;
17
+ }
18
+ export declare function deriveLocalSilentPaymentIdentity(input: {
19
+ readonly recoveryKey: string;
20
+ readonly ledgerId: LedgerId;
21
+ }): LocalSilentPaymentIdentity;
12
22
  export declare function createWebCryptoSigner(input: {
13
23
  readonly ledgerId: LedgerId;
14
24
  readonly privateKey: CryptoKey;
@@ -21,4 +31,4 @@ export declare function generateLocalWallet(input: {
21
31
  export declare function loadLocalWallet(input: {
22
32
  readonly recoveryKey: string;
23
33
  readonly ledgerId: LedgerId;
24
- }): Promise<LdgrSigner>;
34
+ }): Promise<LocalLdgrWallet>;
package/dist/types.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { AssetBridgePolicy, LdgrNetwork } from "@ldgr/protocol";
2
+ import type { LocalLdgrWallet } from "./signer";
2
3
  export type LedgerId = 0 | 1;
3
4
  export type { LdgrNetwork } from "@ldgr/protocol";
4
5
  export type LdgrTransaction = {
@@ -75,6 +76,58 @@ export type SignedSubmission = {
75
76
  readonly feeSponsor?: unknown;
76
77
  readonly submittedAtMs?: number;
77
78
  };
79
+ export type WalletPublicKeyRegistration = {
80
+ readonly version: 1;
81
+ readonly action: "wallet_key_register";
82
+ readonly chain: "ldgr-v1";
83
+ readonly ledgerId: LedgerId;
84
+ readonly from: string;
85
+ readonly publicKey: string;
86
+ /** Domain-separated X25519 public key used only by silent-payment senders. */
87
+ readonly senderDhPublicKey?: string;
88
+ readonly timestamp: string;
89
+ readonly signature: string;
90
+ };
91
+ export type WalletPublicKeyRecord = {
92
+ readonly address: string;
93
+ readonly publicKey: string;
94
+ readonly senderDhPublicKey?: string;
95
+ };
96
+ export type SilentPaymentScanRecord = {
97
+ readonly sequence: string;
98
+ readonly transactionHash: string;
99
+ readonly from: string;
100
+ readonly senderDhPublicKey: string;
101
+ readonly action: "transfer" | "transfer_batch";
102
+ readonly asset: string;
103
+ readonly nonce: string;
104
+ readonly outputs: readonly {
105
+ readonly to: string;
106
+ readonly amountAtomic: string;
107
+ }[];
108
+ };
109
+ export type SilentPaymentScanPage = {
110
+ readonly records: readonly SilentPaymentScanRecord[];
111
+ /** Raw committed-sequence checkpoint, including pages with no matching records. */
112
+ readonly nextAfter: string;
113
+ readonly moreAvailable: boolean;
114
+ };
115
+ export type SilentPaymentSendInput = {
116
+ readonly wallet: LocalLdgrWallet;
117
+ readonly paymentCode: string;
118
+ /** Public native id (`t0`/`t1`) or wire asset code. */
119
+ readonly asset: string;
120
+ readonly amount: string;
121
+ readonly memo?: string;
122
+ readonly metadata?: import("@ldgr/protocol").JsonValue;
123
+ readonly now?: () => number;
124
+ /** Recomputed for every signed attempt because it binds the exact payload. */
125
+ readonly buildFeeSponsor?: (submission: Pick<SignedSubmission, "payload" | "signature">) => Promise<unknown>;
126
+ };
127
+ export declare class SilentPaymentSubmissionUnknownError extends Error {
128
+ readonly submission: SignedSubmission;
129
+ constructor(submission: SignedSubmission, cause?: unknown);
130
+ }
78
131
  export type LdgrClientOptions = {
79
132
  /** `testnet` / `0` or `mainnet` / `1`; required to use public native ids `t0` / `t1`. */
80
133
  readonly network?: LdgrNetwork;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ldgr/client",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "types": "./dist/index.d.ts",
6
6
  "exports": {
@@ -12,7 +12,7 @@
12
12
  "files": ["dist", "README.md"],
13
13
  "publishConfig": { "access": "public" },
14
14
  "engines": { "node": ">=20.19.0" },
15
- "dependencies": { "@ldgr/protocol": "^0.4.0" },
15
+ "dependencies": { "@ldgr/protocol": "^0.5.0" },
16
16
  "scripts": {
17
17
  "build": "rm -rf dist && bun build src/index.ts --outdir dist --target node --format esm --packages external && tsc -p tsconfig.build.json",
18
18
  "prepack": "bun run build",