@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 +53 -0
- package/dist/index.d.ts +35 -3
- package/dist/index.js +197 -26
- package/dist/ldgr402.d.ts +10 -0
- package/dist/transfer.d.ts +42 -0
- package/dist/types.d.ts +31 -0
- package/package.json +3 -3
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
|
|
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)
|
|
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
|
|
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 {
|
|
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
|
-
|
|
223
|
-
|
|
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
|
-
|
|
245
|
-
|
|
246
|
-
createWebCryptoSigner,
|
|
412
|
+
LdgrClientError,
|
|
413
|
+
buildTransferSubmission,
|
|
247
414
|
createLdgrClient,
|
|
248
|
-
|
|
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.
|
|
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.
|
|
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": "^
|
|
23
|
+
"typescript": "^7.0.2"
|
|
24
24
|
}
|
|
25
25
|
}
|