@ldgr/client 0.2.1 → 0.4.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
@@ -35,6 +35,38 @@ console.log(serviceWallet.address); // safe to identify the service wallet
35
35
  This is for backend/Bun service wallets only. Websites should use
36
36
  `@ldgr/wallet-sdk` and an extension-confirmed customer wallet instead.
37
37
 
38
+ ## Send a native or user-issued asset
39
+
40
+ `transfers.send()` resolves the asset metadata, fetches the current nonce,
41
+ builds and signs the transfer, submits it to the selected network, and retries
42
+ once if another process consumed the nonce first. Use
43
+ `LEDRA_TESTNET_ASSET_ID` / `LEDRA_MAINNET_ASSET_ID` from `@ldgr/protocol` for
44
+ the native asset or pass a user-token code.
45
+
46
+ ```ts
47
+ import { createLdgrClient } from "@ldgr/client";
48
+ import { LEDRA_MAINNET_ASSET_ID } from "@ldgr/protocol";
49
+
50
+ const client = createLdgrClient({ network: "mainnet" });
51
+ const result = await client.transfers.send({
52
+ wallet: serviceWallet,
53
+ asset: LEDRA_MAINNET_ASSET_ID,
54
+ amount: "2.50",
55
+ to: recipientAddress,
56
+ memo: "Order 1234",
57
+ });
58
+
59
+ if (result.status === "settled") {
60
+ console.log(result.transactionHash, result.fee, result.feeAsset);
61
+ } else {
62
+ console.error(result.code, result.error);
63
+ }
64
+ ```
65
+
66
+ Settlement failures return `status: "failed"`; they do not have a transaction
67
+ hash or fee because the ledger rejected them before appending a transaction.
68
+ Transport and unexpected response errors still throw.
69
+
38
70
  ```ts
39
71
  import { createLdgrClient } from "@ldgr/client";
40
72
 
@@ -57,3 +89,24 @@ Only fulfil an order after `payment.status === "confirmed"`, and store the
57
89
  transaction hash as the idempotency key. AMM quotes are estimates: pass an
58
90
  explicit minimum output in the signed swap payload and re-quote immediately
59
91
  before signing.
92
+
93
+ ## LDGR402 merchant verification
94
+
95
+ For a persisted LDGR402 requirement and the browser's
96
+ `LDGR402-Payment` header, use the protocol-aware verifier:
97
+
98
+ ```ts
99
+ const verification = await client.payments.verifyLdgr402(
100
+ request.headers.get("LDGR402-Payment") ?? "",
101
+ expectedRequirement,
102
+ );
103
+
104
+ if (verification.status === "confirmed") {
105
+ // Atomically fulfill the challenge using verification.transactionHash.
106
+ }
107
+ ```
108
+
109
+ `verifyLdgr402` checks the proof's ledger and challenge, rejects expired
110
+ requirements, and verifies the exact settled transfer through the
111
+ authoritative Worker. The merchant still owns challenge persistence and
112
+ idempotent fulfillment.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
- import { type LdgrClientOptions, type LdgrAsset, type LdgrTransaction, type PaymentExpectation, type PaymentVerification, type SignedSubmission } from "./types";
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
4
  export declare function createLdgrClient(options: LdgrClientOptions): {
3
5
  ledger: {
4
6
  get: () => Promise<{
@@ -24,6 +26,27 @@ export declare function createLdgrClient(options: LdgrClientOptions): {
24
26
  readonly asset: string;
25
27
  readonly nextNonce: string;
26
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
+ }>;
46
+ listTransactions(address: string, options?: {
47
+ readonly before?: string;
48
+ readonly asset?: string;
49
+ }): Promise<LdgrAccountTransactionPage>;
27
50
  };
28
51
  assets: {
29
52
  get: (asset: string) => Promise<LdgrAsset>;
@@ -66,13 +89,22 @@ export declare function createLdgrClient(options: LdgrClientOptions): {
66
89
  }) => Promise<Record<string, unknown>>;
67
90
  };
68
91
  payments: {
69
- verifyTransfer(transactionHash: string, expected: PaymentExpectation): Promise<PaymentVerification>;
92
+ verifyTransfer: (transactionHash: string, expected: PaymentExpectation) => Promise<PaymentVerification>;
93
+ verifyLdgr402: (proofHeader: string, requirementInput: Ldgr402Requirement, options?: {
94
+ readonly nowMs?: number;
95
+ }) => Promise<Ldgr402Verification>;
96
+ };
97
+ transfers: {
98
+ send: (input: SendTransferInput) => Promise<LdgrTransferResult>;
70
99
  };
71
100
  submitSigned: <T extends {
72
101
  readonly transactionHash?: string;
73
102
  }>(path: string, signed: SignedSubmission) => Promise<T>;
74
103
  };
75
104
  export { LdgrClientError } from "./types";
76
- export type { LdgrAsset, LdgrClientOptions, LdgrNetwork, LdgrTransaction, PaymentExpectation, PaymentVerification, SignedSubmission } from "./types";
105
+ export { decodeMerchantLdgr402Payment, parseMerchantLdgr402Requirement } from "./ldgr402";
106
+ export type { LdgrAccountTransaction, LdgrAccountTransactionPage, Ldgr402Requirement, Ldgr402Verification, LdgrAsset, LdgrClientOptions, LdgrNetwork, LdgrTransaction, PaymentExpectation, PaymentVerification, SignedSubmission } from "./types";
77
107
  export { createWebCryptoSigner, generateLocalWallet, loadLocalWallet } from "./signer";
78
108
  export type { LdgrSigner, LocalLdgrWallet } from "./signer";
109
+ export { buildTransferSubmission } from "./transfer";
110
+ export type { BuildTransferSubmissionInput, LdgrTransferResult, SendTransferInput } from "./transfer";
package/dist/index.js CHANGED
@@ -77,8 +77,76 @@ function queryPath(path, query) {
77
77
  return encoded ? `${path}?${encoded}` : path;
78
78
  }
79
79
 
80
+ // src/transfer.ts
81
+ import {
82
+ canonicalizeLedgerAddress,
83
+ assertEncryptedMessageEnvelope,
84
+ formatAtomicAmount,
85
+ parseDecimalAmount
86
+ } from "@ldgr/protocol";
87
+ async function buildTransferSubmission(input) {
88
+ const recipient = canonicalizeLedgerAddress(input.to, input.wallet.ledgerId);
89
+ const amount = parseDecimalAmount(input.amount.trim(), input.assetDefinition);
90
+ if (amount < 0n || amount === 0n && (input.asset !== "dng" || input.encryptedMessage === undefined)) {
91
+ throw new RangeError("Amount must be greater than zero unless sending an encrypted Ledra message");
92
+ }
93
+ if (input.encryptedMessage !== undefined)
94
+ assertEncryptedMessageEnvelope(input.encryptedMessage);
95
+ if (!/^(0|[1-9][0-9]*)$/.test(input.nextNonce)) {
96
+ throw new TypeError("Ledger returned an invalid next nonce");
97
+ }
98
+ const memo = input.memo?.trim();
99
+ const payload = {
100
+ version: 1,
101
+ action: "transfer",
102
+ chain: "ldgr-v1",
103
+ ledgerId: input.wallet.ledgerId,
104
+ asset: input.asset,
105
+ from: input.wallet.address,
106
+ to: recipient,
107
+ amount,
108
+ nonce: BigInt(input.nextNonce),
109
+ timestamp: BigInt((input.now ?? Date.now)()),
110
+ publicKey: input.wallet.publicKey,
111
+ ...memo ? { memo } : {},
112
+ ...input.metadata === undefined ? {} : { metadata: input.metadata },
113
+ ...input.encryptedMessage === undefined ? {} : { encryptedMessage: input.encryptedMessage }
114
+ };
115
+ const signature = await input.wallet.sign(payload);
116
+ return {
117
+ payload: {
118
+ ...payload,
119
+ amount: formatAtomicAmount(amount, input.assetDefinition),
120
+ nonce: payload.nonce.toString(),
121
+ timestamp: payload.timestamp.toString()
122
+ },
123
+ signature
124
+ };
125
+ }
126
+
80
127
  // src/index.ts
81
- import { resolveLdgrNetwork as resolveLdgrNetwork2, resolvePublicAssetCode } from "@ldgr/protocol";
128
+ import {
129
+ decodeLdgr402Payment as decodeLdgr402Payment3,
130
+ nativeAssetDefinitionForLedger,
131
+ parseLdgr402Requirement as parseLdgr402Requirement3,
132
+ resolveLdgrNetwork as resolveLdgrNetwork2,
133
+ resolvePublicAssetCode
134
+ } from "@ldgr/protocol";
135
+ // src/ldgr402.ts
136
+ import {
137
+ decodeLdgr402Payment,
138
+ parseLdgr402Requirement
139
+ } from "@ldgr/protocol";
140
+ import { decodeLdgr402Payment as decodeLdgr402Payment2, encodeLdgr402Payment, parseLdgr402Payment, parseLdgr402Requirement as parseLdgr402Requirement2 } from "@ldgr/protocol";
141
+ function parseMerchantLdgr402Requirement(input, options = {}) {
142
+ return parseLdgr402Requirement(input, {
143
+ nowMs: options.nowMs,
144
+ allowExpired: true
145
+ });
146
+ }
147
+ function decodeMerchantLdgr402Payment(headerValue) {
148
+ return decodeLdgr402Payment(headerValue);
149
+ }
82
150
  // src/signer.ts
83
151
  import {
84
152
  deriveAddress,
@@ -154,6 +222,18 @@ function paymentReference(metadata) {
154
222
  function transactionFields(transaction) {
155
223
  return typeof transaction.from === "string" && typeof transaction.to === "string" && typeof transaction.asset === "string" && typeof transaction.amount === "string" ? { from: transaction.from, to: transaction.to, asset: transaction.asset, amount: transaction.amount } : null;
156
224
  }
225
+ function isNonceMismatch(error) {
226
+ return error instanceof LdgrClientError && error.code === "NONCE_MISMATCH";
227
+ }
228
+ function transferFailure(error) {
229
+ if (!(error instanceof LdgrClientError) || error.status !== 400 && error.status !== 409)
230
+ return;
231
+ return {
232
+ status: "failed",
233
+ error: error.message,
234
+ ...error.code === undefined ? {} : { code: error.code }
235
+ };
236
+ }
157
237
  function createLdgrClient(options) {
158
238
  const transport = createTransport(options);
159
239
  const ledgerId = options.network === undefined ? undefined : resolveLdgrNetwork2(options.network);
@@ -165,6 +245,96 @@ function createLdgrClient(options) {
165
245
  }
166
246
  return asset;
167
247
  };
248
+ const verifyTransfer = async (transactionHash, expected) => {
249
+ const hash = normalizedHash(transactionHash);
250
+ const expectedAsset = resolveAsset((expected.asset ?? "dng").trim() || "dng");
251
+ let transaction;
252
+ try {
253
+ transaction = await transport.get(`/api/v1/payments/v0.1/transactions/${hash}`);
254
+ } catch (error) {
255
+ if (error instanceof LdgrClientError && error.status === 404)
256
+ return { status: "not_found", transactionHash: hash };
257
+ throw error;
258
+ }
259
+ const fields = transactionFields(transaction);
260
+ if (transaction.action !== "transfer" || fields === null || fields.to !== expected.to || fields.asset !== expectedAsset || !sameDecimalAmount(fields.amount, expected.amount) || expected.reference !== undefined && paymentReference(transaction.metadata) !== expected.reference) {
261
+ return { status: "mismatch", transactionHash: hash };
262
+ }
263
+ return { status: "confirmed", transactionHash: hash, ...fields };
264
+ };
265
+ const verifyLdgr402 = async (proofHeader, requirementInput, options2 = {}) => {
266
+ const proof = decodeLdgr402Payment3(proofHeader);
267
+ const requirement = parseLdgr402Requirement3(requirementInput, {
268
+ nowMs: options2.nowMs,
269
+ allowExpired: true
270
+ });
271
+ const nowMs = options2.nowMs ?? Date.now();
272
+ if (ledgerId === undefined)
273
+ throw new TypeError("network is required for LDGR402 verification");
274
+ if (proof.ledgerId !== requirement.ledgerId || ledgerId !== requirement.ledgerId || proof.challenge !== requirement.challenge) {
275
+ return { status: "mismatch", transactionHash: proof.transactionHash };
276
+ }
277
+ if (requirement.expiresAt <= nowMs) {
278
+ return { status: "expired", transactionHash: proof.transactionHash, challenge: requirement.challenge };
279
+ }
280
+ return verifyTransfer(proof.transactionHash, {
281
+ to: requirement.recipient,
282
+ asset: requirement.asset,
283
+ amount: requirement.amount,
284
+ reference: requirement.challenge
285
+ });
286
+ };
287
+ const sendTransfer = async (input) => {
288
+ if (ledgerId !== undefined && ledgerId !== input.wallet.ledgerId) {
289
+ throw new TypeError("signer ledgerId does not match client network");
290
+ }
291
+ const requestedAsset = input.asset.trim();
292
+ if (!requestedAsset)
293
+ throw new TypeError("asset is required");
294
+ const asset = resolvePublicAssetCode(requestedAsset, input.wallet.ledgerId);
295
+ const assetDefinition = asset === "dng" ? nativeAssetDefinitionForLedger(input.wallet.ledgerId) : await transport.get(`/api/v1/assets/${encodePathSegment(asset)}`);
296
+ const submit = async () => {
297
+ const nonce = await transport.get(`/api/v1/nonces/${encodePathSegment(input.wallet.address)}/${encodePathSegment(asset)}`);
298
+ const submission = await buildTransferSubmission({
299
+ ...input,
300
+ asset,
301
+ assetDefinition,
302
+ nextNonce: nonce.nextNonce
303
+ });
304
+ const transaction = await transport.post("/api/v1/transfers", {
305
+ ...submission,
306
+ submittedAtMs: Date.now()
307
+ });
308
+ if (typeof transaction.transactionHash !== "string" || typeof transaction.fee !== "string" || typeof transaction.feeAsset !== "string") {
309
+ throw new TypeError("LDGR transfer response is missing transaction hash or fee");
310
+ }
311
+ return {
312
+ status: "settled",
313
+ transactionHash: transaction.transactionHash,
314
+ fee: transaction.fee,
315
+ feeAsset: transaction.feeAsset,
316
+ transaction
317
+ };
318
+ };
319
+ try {
320
+ return await submit();
321
+ } catch (error) {
322
+ if (isNonceMismatch(error)) {
323
+ try {
324
+ return await submit();
325
+ } catch (retryError) {
326
+ const failure2 = transferFailure(retryError);
327
+ if (failure2 !== undefined)
328
+ return failure2;
329
+ throw retryError;
330
+ }
331
+ }
332
+ const failure = transferFailure(error);
333
+ if (failure !== undefined)
334
+ return failure;
335
+ throw error;
336
+ }
337
+ };
168
338
  return {
169
339
  ledger: {
170
340
  get: () => transport.get("/api/v1/ledger"),
@@ -173,7 +343,22 @@ function createLdgrClient(options) {
173
343
  accounts: {
174
344
  listBalances: (address) => transport.get(`/api/v1/balances/${encodePathSegment(address)}`),
175
345
  getBalance: (address, asset) => transport.get(`/api/v1/balances/${encodePathSegment(address)}/${encodePathSegment(resolveAsset(asset))}`),
176
- getNextNonce: (address, asset) => transport.get(`/api/v1/nonces/${encodePathSegment(address)}/${encodePathSegment(resolveAsset(asset))}`)
346
+ getNextNonce: (address, asset) => transport.get(`/api/v1/nonces/${encodePathSegment(address)}/${encodePathSegment(resolveAsset(asset))}`),
347
+ getWalletPublicKey: (address) => transport.get(`/api/v1/wallets/${encodePathSegment(address)}/public-key`),
348
+ registerWalletPublicKey: (address, registration) => transport.post(`/api/v1/wallets/${encodePathSegment(address)}/public-key`, registration),
349
+ async listTransactions(address, options2 = {}) {
350
+ const path = queryPath(`/api/v1/explorer/addresses/${encodePathSegment(address)}/transactions`, {
351
+ before: options2.before,
352
+ asset: options2.asset === undefined ? undefined : resolveAsset(options2.asset)
353
+ });
354
+ try {
355
+ return await transport.get(path);
356
+ } catch (error) {
357
+ if (error instanceof LdgrClientError && error.status === 404)
358
+ return { transactions: [] };
359
+ throw error;
360
+ }
361
+ }
177
362
  },
178
363
  assets: {
179
364
  get: (asset) => transport.get(`/api/v1/assets/${encodePathSegment(resolveAsset(asset))}`)
@@ -218,32 +403,18 @@ function createLdgrClient(options) {
218
403
  getPrimary: (address) => transport.get(`/api/v1/usernames/by-address/${encodePathSegment(address)}`),
219
404
  listOwned: (address, options2) => transport.get(queryPath(`/api/v1/usernames/owned-by/${encodePathSegment(address)}`, options2 ?? {}))
220
405
  },
221
- payments: {
222
- async verifyTransfer(transactionHash, expected) {
223
- const hash = normalizedHash(transactionHash);
224
- const expectedAsset = resolveAsset((expected.asset ?? "dng").trim() || "dng");
225
- let transaction;
226
- try {
227
- transaction = await transport.get(`/api/v1/payments/v0.1/transactions/${hash}`);
228
- } catch (error) {
229
- if (error instanceof LdgrClientError && error.status === 404)
230
- return { status: "not_found", transactionHash: hash };
231
- throw error;
232
- }
233
- const fields = transactionFields(transaction);
234
- if (transaction.action !== "transfer" || fields === null || fields.to !== expected.to || fields.asset !== expectedAsset || !sameDecimalAmount(fields.amount, expected.amount) || expected.reference !== undefined && paymentReference(transaction.metadata) !== expected.reference) {
235
- return { status: "mismatch", transactionHash: hash };
236
- }
237
- return { status: "confirmed", transactionHash: hash, ...fields };
238
- }
239
- },
240
- submitSigned: (path, signed) => transport.post(path, signed)
406
+ payments: { verifyTransfer, verifyLdgr402 },
407
+ transfers: { send: sendTransfer },
408
+ submitSigned: (path, signed) => transport.post(path, { ...signed, submittedAtMs: Date.now() })
241
409
  };
242
410
  }
243
411
  export {
244
- loadLocalWallet,
245
- generateLocalWallet,
246
- createWebCryptoSigner,
412
+ LdgrClientError,
413
+ buildTransferSubmission,
247
414
  createLdgrClient,
248
- LdgrClientError
415
+ createWebCryptoSigner,
416
+ decodeMerchantLdgr402Payment,
417
+ generateLocalWallet,
418
+ loadLocalWallet,
419
+ parseMerchantLdgr402Requirement
249
420
  };
@@ -0,0 +1,10 @@
1
+ import { type Ldgr402Requirement } from "@ldgr/protocol";
2
+ export type { Ldgr402Requirement } from "@ldgr/protocol";
3
+ export { decodeLdgr402Payment, encodeLdgr402Payment, parseLdgr402Payment, parseLdgr402Requirement } from "@ldgr/protocol";
4
+ export type { Ldgr402ParseOptions, Ldgr402Payment } from "@ldgr/protocol";
5
+ export type Ldgr402VerificationOptions = {
6
+ readonly nowMs?: number;
7
+ };
8
+ export declare function parseMerchantLdgr402Requirement(input: unknown, options?: Ldgr402VerificationOptions): Ldgr402Requirement;
9
+ export declare function decodeMerchantLdgr402Payment(headerValue: string): import("@ldgr/protocol").Ldgr402Payment;
10
+ export type { Ldgr402Verification } from "./types";
@@ -0,0 +1,42 @@
1
+ import { type AssetDefinition, type JsonValue } from "@ldgr/protocol";
2
+ import type { LdgrSigner } from "./signer";
3
+ import type { LdgrTransaction } from "./types";
4
+ export type BuildTransferSubmissionInput = {
5
+ readonly wallet: LdgrSigner;
6
+ readonly asset: string;
7
+ readonly assetDefinition: AssetDefinition;
8
+ readonly amount: string;
9
+ readonly to: string;
10
+ readonly nextNonce: string;
11
+ readonly memo?: string;
12
+ readonly metadata?: JsonValue;
13
+ readonly encryptedMessage?: string;
14
+ readonly now?: () => number;
15
+ };
16
+ export type SendTransferInput = {
17
+ readonly wallet: LdgrSigner;
18
+ /** Public native id (`t0`/`t1`) or wire asset code. */
19
+ readonly asset: string;
20
+ readonly amount: string;
21
+ readonly to: string;
22
+ readonly memo?: string;
23
+ readonly metadata?: JsonValue;
24
+ readonly encryptedMessage?: string;
25
+ readonly now?: () => number;
26
+ };
27
+ export type LdgrTransferResult = {
28
+ readonly status: "settled";
29
+ readonly transactionHash: string;
30
+ readonly fee: string;
31
+ readonly feeAsset: string;
32
+ readonly transaction: LdgrTransaction;
33
+ } | {
34
+ readonly status: "failed";
35
+ /** Settlement failures happen before append, so there is no hash or fee. */
36
+ readonly error: string;
37
+ readonly code?: string;
38
+ };
39
+ export declare function buildTransferSubmission(input: BuildTransferSubmissionInput): Promise<{
40
+ readonly payload: Record<string, unknown>;
41
+ readonly signature: string;
42
+ }>;
package/dist/types.d.ts CHANGED
@@ -7,11 +7,30 @@ export type LdgrTransaction = {
7
7
  readonly to?: string;
8
8
  readonly asset?: string;
9
9
  readonly amount?: string;
10
+ readonly fee?: string;
11
+ readonly feeAsset?: string;
10
12
  readonly metadata?: unknown;
11
13
  readonly sequence?: string;
12
14
  readonly transactionHash: string;
13
15
  readonly [key: string]: unknown;
14
16
  };
17
+ export type LdgrAccountTransaction = {
18
+ readonly sequence: string;
19
+ readonly action: string;
20
+ readonly asset: string;
21
+ readonly from: string;
22
+ readonly to?: string;
23
+ readonly amount?: string;
24
+ readonly fee?: string;
25
+ readonly transactionHash: string;
26
+ readonly serverTimestamp: string;
27
+ readonly metadata?: unknown;
28
+ readonly encryptedMessage?: string;
29
+ };
30
+ export type LdgrAccountTransactionPage = {
31
+ readonly transactions: readonly LdgrAccountTransaction[];
32
+ readonly nextBefore?: string;
33
+ };
15
34
  /** Public asset metadata used to format user-facing balances and transfer amounts. */
16
35
  export type LdgrAsset = {
17
36
  readonly code: string;
@@ -20,6 +39,11 @@ export type LdgrAsset = {
20
39
  readonly decimals: number;
21
40
  readonly active: boolean;
22
41
  readonly bridgePolicy: AssetBridgePolicy;
42
+ /** Optional operator-registered token logo. */
43
+ readonly iconUri?: string;
44
+ /** Ops-set display-only verification state. */
45
+ readonly verified?: boolean;
46
+ readonly verificationKind?: "official" | "partner";
23
47
  readonly [key: string]: unknown;
24
48
  };
25
49
  export type PaymentExpectation = {
@@ -39,10 +63,17 @@ export type PaymentVerification = {
39
63
  readonly status: "not_found" | "mismatch";
40
64
  readonly transactionHash: string;
41
65
  };
66
+ export type Ldgr402Verification = PaymentVerification | {
67
+ readonly status: "expired";
68
+ readonly transactionHash: string;
69
+ readonly challenge: string;
70
+ };
71
+ export type { Ldgr402Requirement } from "@ldgr/protocol";
42
72
  export type SignedSubmission = {
43
73
  readonly payload: Record<string, unknown>;
44
74
  readonly signature: string;
45
75
  readonly feeSponsor?: unknown;
76
+ readonly submittedAtMs?: number;
46
77
  };
47
78
  export type LdgrClientOptions = {
48
79
  /** `testnet` / `0` or `mainnet` / `1`; required to use public native ids `t0` / `t1`. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ldgr/client",
3
- "version": "0.2.1",
3
+ "version": "0.4.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.2.0" },
15
+ "dependencies": { "@ldgr/protocol": "^0.4.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",
@@ -20,6 +20,6 @@
20
20
  "typecheck": "tsc -p tsconfig.json"
21
21
  },
22
22
  "devDependencies": {
23
- "typescript": "^5.9.3"
23
+ "typescript": "^7.0.2"
24
24
  }
25
25
  }