@piprail/sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +160 -0
- package/ERRORS.md +182 -0
- package/LICENSE +21 -0
- package/README.md +497 -0
- package/STANDARDS.md +123 -0
- package/dist/chunk-3TQJJ4SQ.js +157 -0
- package/dist/chunk-CQREG5LE.cjs +8 -0
- package/dist/chunk-FURB5RP7.js +8 -0
- package/dist/chunk-WQWNPAYQ.cjs +157 -0
- package/dist/index.cjs +1681 -0
- package/dist/index.d.cts +4578 -0
- package/dist/index.d.ts +4578 -0
- package/dist/index.js +1681 -0
- package/dist/near-4P5XNMMB.cjs +346 -0
- package/dist/near-RVXGF7TW.js +346 -0
- package/dist/solana-7PZG3CDO.js +342 -0
- package/dist/solana-F7H4YDW5.cjs +342 -0
- package/dist/stellar-BPPQTLNI.cjs +389 -0
- package/dist/stellar-PAZ352JL.js +389 -0
- package/dist/sui-6N4ZPAGD.js +304 -0
- package/dist/sui-XV4YYSGV.cjs +304 -0
- package/dist/ton-E5RLUPD2.cjs +366 -0
- package/dist/ton-EFZKQAAK.js +366 -0
- package/dist/tron-243DT6PF.js +372 -0
- package/dist/tron-3UDH7KGF.cjs +372 -0
- package/dist/xrpl-6NRFT5CA.cjs +449 -0
- package/dist/xrpl-7GWXDAVZ.js +449 -0
- package/package.json +143 -0
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
import {
|
|
2
|
+
delay
|
|
3
|
+
} from "./chunk-FURB5RP7.js";
|
|
4
|
+
import {
|
|
5
|
+
ConfirmationTimeoutError,
|
|
6
|
+
InsufficientFundsError,
|
|
7
|
+
UnknownTokenError,
|
|
8
|
+
WrongFamilyError,
|
|
9
|
+
floorUnits,
|
|
10
|
+
nativeCost,
|
|
11
|
+
rejectForeignToken,
|
|
12
|
+
toInsufficientFundsError
|
|
13
|
+
} from "./chunk-3TQJJ4SQ.js";
|
|
14
|
+
|
|
15
|
+
// src/drivers/xrpl/index.ts
|
|
16
|
+
import { isValidClassicAddress } from "xrpl";
|
|
17
|
+
|
|
18
|
+
// src/drivers/xrpl/chains.ts
|
|
19
|
+
var XRP_DECIMALS = 6;
|
|
20
|
+
var XRP_SYMBOL = "XRP";
|
|
21
|
+
var XRPL_MAINNET = {
|
|
22
|
+
caip2: "xrpl:0",
|
|
23
|
+
// Public community cluster (serves HTTPS JSON-RPC). Not for sustained
|
|
24
|
+
// business use — pass your own `rpcUrl` in production.
|
|
25
|
+
defaultRpc: "https://xrplcluster.com/",
|
|
26
|
+
tokens: {
|
|
27
|
+
// Circle USDC — issuer Domain (`https://circle.com`) + currency code verified
|
|
28
|
+
// live on mainnet (gateway_balances) before shipping.
|
|
29
|
+
USDC: {
|
|
30
|
+
issuer: "rGm7WCVp9gb4jZHWTEtGUr4dd74z2XuWhE",
|
|
31
|
+
currencyHex: "5553444300000000000000000000000000000000",
|
|
32
|
+
decimals: 6,
|
|
33
|
+
symbol: "USDC"
|
|
34
|
+
},
|
|
35
|
+
// Ripple RLUSD — issuer Domain (`https://ripple.com/`) + currency code
|
|
36
|
+
// verified live on mainnet before shipping. Note: the RLUSD issuer sets
|
|
37
|
+
// requireDestinationTag, so payments always carry a DestinationTag (set below).
|
|
38
|
+
RLUSD: {
|
|
39
|
+
issuer: "rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De",
|
|
40
|
+
currencyHex: "524C555344000000000000000000000000000000",
|
|
41
|
+
decimals: 6,
|
|
42
|
+
symbol: "RLUSD"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
function xrplAssetId(currencyHex, issuer) {
|
|
47
|
+
return `${currencyHex}:${issuer}`;
|
|
48
|
+
}
|
|
49
|
+
function parseXrplAssetId(asset) {
|
|
50
|
+
if (asset === "native") return null;
|
|
51
|
+
const i = asset.indexOf(":");
|
|
52
|
+
if (i <= 0 || i === asset.length - 1) return null;
|
|
53
|
+
return { currencyHex: asset.slice(0, i), issuer: asset.slice(i + 1) };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// src/drivers/xrpl/pay.ts
|
|
57
|
+
function nonceToMemoHex(nonce) {
|
|
58
|
+
return Buffer.from(nonce, "utf8").toString("hex").toUpperCase();
|
|
59
|
+
}
|
|
60
|
+
function nonceToDestinationTag(nonce) {
|
|
61
|
+
let h = 2166136261;
|
|
62
|
+
for (let i = 0; i < nonce.length; i += 1) {
|
|
63
|
+
h ^= nonce.charCodeAt(i);
|
|
64
|
+
h = Math.imul(h, 16777619);
|
|
65
|
+
}
|
|
66
|
+
const tag = h >>> 0;
|
|
67
|
+
return tag === 0 ? 1 : tag;
|
|
68
|
+
}
|
|
69
|
+
async function payXrpl(params) {
|
|
70
|
+
const { client, wallet, accept } = params;
|
|
71
|
+
try {
|
|
72
|
+
const [sequence, feeDrops, ledgerIndex] = await Promise.all([
|
|
73
|
+
client.accountSequence(wallet.classicAddress),
|
|
74
|
+
client.feeDrops(),
|
|
75
|
+
client.currentLedgerIndex()
|
|
76
|
+
]);
|
|
77
|
+
const tx = {
|
|
78
|
+
TransactionType: "Payment",
|
|
79
|
+
Account: wallet.classicAddress,
|
|
80
|
+
Destination: accept.payTo,
|
|
81
|
+
DestinationTag: nonceToDestinationTag(accept.extra.nonce),
|
|
82
|
+
Amount: amountForAccept(accept),
|
|
83
|
+
Memos: [{ Memo: { MemoData: nonceToMemoHex(accept.extra.nonce) } }],
|
|
84
|
+
Sequence: sequence,
|
|
85
|
+
Fee: feeForSubmit(feeDrops),
|
|
86
|
+
LastLedgerSequence: ledgerIndex + 20,
|
|
87
|
+
Flags: 0
|
|
88
|
+
// NOT tfPartialPayment — require full delivery
|
|
89
|
+
};
|
|
90
|
+
const signed = wallet.sign(tx);
|
|
91
|
+
const res = await client.submit(signed.tx_blob);
|
|
92
|
+
const code = res.engine_result;
|
|
93
|
+
if (code.startsWith("tes")) return res.tx_json?.hash ?? signed.hash;
|
|
94
|
+
if (isAffordabilityCode(code)) {
|
|
95
|
+
throw new InsufficientFundsError(
|
|
96
|
+
`XRPL payment rejected (${code}): insufficient balance/reserve, or no trustline for this asset.`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
throw new Error(
|
|
100
|
+
`XRPL payment rejected: ${code}${res.engine_result_message ? ` \u2014 ${res.engine_result_message}` : ""}`
|
|
101
|
+
);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
if (err instanceof InsufficientFundsError) throw err;
|
|
104
|
+
throw toInsufficientFundsError(err) ?? err;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function amountForAccept(accept) {
|
|
108
|
+
if (accept.asset === "native") return accept.amount;
|
|
109
|
+
const parts = parseXrplAssetId(accept.asset);
|
|
110
|
+
if (!parts) throw new Error(`XRPL: malformed asset id "${accept.asset}".`);
|
|
111
|
+
return {
|
|
112
|
+
currency: parts.currencyHex,
|
|
113
|
+
issuer: parts.issuer,
|
|
114
|
+
value: accept.extra.amountFormatted
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function feeForSubmit(openLedgerFee) {
|
|
118
|
+
const fee = Number(openLedgerFee);
|
|
119
|
+
return String(Number.isFinite(fee) && fee > 12 ? Math.ceil(fee) : 12);
|
|
120
|
+
}
|
|
121
|
+
function isAffordabilityCode(code) {
|
|
122
|
+
return /^(tecUNFUNDED|tecPATH_DRY|tecPATH_PARTIAL|tecINSUFF|tecNO_LINE_INSUF|terINSUF|tecNO_LINE)/.test(
|
|
123
|
+
code
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/drivers/xrpl/verify.ts
|
|
128
|
+
var RIPPLE_EPOCH_OFFSET = 946684800;
|
|
129
|
+
async function verifyXrpl(params) {
|
|
130
|
+
const { reader, accept } = params;
|
|
131
|
+
const required = BigInt(accept.amount);
|
|
132
|
+
const nonce = accept.extra.nonce;
|
|
133
|
+
const wantMemo = nonceToMemoHex(nonce);
|
|
134
|
+
const wantAsset = parseXrplAssetId(accept.asset);
|
|
135
|
+
let txs;
|
|
136
|
+
try {
|
|
137
|
+
txs = await reader.transactionsForAccount(accept.payTo, 50);
|
|
138
|
+
} catch {
|
|
139
|
+
return rpcFailed(nonce);
|
|
140
|
+
}
|
|
141
|
+
const tx = txs.find(
|
|
142
|
+
(t) => t.TransactionType === "Payment" && t.Destination === accept.payTo && hasNonceMemo(t.Memos, wantMemo)
|
|
143
|
+
);
|
|
144
|
+
if (!tx) return notFound(nonce);
|
|
145
|
+
if (!tx.validated) {
|
|
146
|
+
return {
|
|
147
|
+
ok: false,
|
|
148
|
+
error: "insufficient_confirmations",
|
|
149
|
+
detail: `XRPL tx ${tx.hash} for nonce ${nonce} is not yet validated \u2014 retry.`
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
if (tx.result !== "tesSUCCESS") {
|
|
153
|
+
return {
|
|
154
|
+
ok: false,
|
|
155
|
+
error: "tx_reverted",
|
|
156
|
+
detail: `XRPL tx ${tx.hash} for nonce ${nonce} failed on-ledger (${tx.result}).`
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
if (typeof tx.date === "number") {
|
|
160
|
+
const ageSeconds = Math.floor(Date.now() / 1e3) - (tx.date + RIPPLE_EPOCH_OFFSET);
|
|
161
|
+
if (ageSeconds > accept.maxTimeoutSeconds) {
|
|
162
|
+
return {
|
|
163
|
+
ok: false,
|
|
164
|
+
error: "payment_expired",
|
|
165
|
+
detail: `Payment is ${ageSeconds}s old; max allowed is ${accept.maxTimeoutSeconds}s.`
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
const paid = deliveredBaseUnits(tx.delivered_amount, wantAsset, accept.extra.decimals);
|
|
170
|
+
if (paid === null) {
|
|
171
|
+
return {
|
|
172
|
+
ok: false,
|
|
173
|
+
error: "transfer_not_found",
|
|
174
|
+
detail: `XRPL tx ${tx.hash} carries our nonce but delivered no matching ${wantAsset ? "IOU" : "XRP"} to ${accept.payTo} (wrong asset, or delivered_amount unavailable).`
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
if (paid < required) {
|
|
178
|
+
return {
|
|
179
|
+
ok: false,
|
|
180
|
+
error: "amount_too_low",
|
|
181
|
+
detail: `Delivered ${paid}, required ${required}.`
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
ok: true,
|
|
186
|
+
receipt: {
|
|
187
|
+
scheme: "onchain-proof",
|
|
188
|
+
success: true,
|
|
189
|
+
network: accept.network,
|
|
190
|
+
transaction: tx.hash,
|
|
191
|
+
asset: accept.asset,
|
|
192
|
+
amount: accept.amount,
|
|
193
|
+
payer: tx.Account,
|
|
194
|
+
payTo: accept.payTo,
|
|
195
|
+
verifiedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
function hasNonceMemo(memos, wantMemo) {
|
|
200
|
+
if (!memos) return false;
|
|
201
|
+
return memos.some((m) => (m.Memo?.MemoData ?? "").toUpperCase() === wantMemo);
|
|
202
|
+
}
|
|
203
|
+
function deliveredBaseUnits(delivered, want, decimals) {
|
|
204
|
+
if (delivered === void 0) return null;
|
|
205
|
+
if (want === null) {
|
|
206
|
+
if (typeof delivered !== "string") return null;
|
|
207
|
+
if (!/^\d+$/.test(delivered)) return null;
|
|
208
|
+
return BigInt(delivered);
|
|
209
|
+
}
|
|
210
|
+
if (typeof delivered === "string") return null;
|
|
211
|
+
if (delivered.currency.toUpperCase() !== want.currencyHex.toUpperCase() || delivered.issuer !== want.issuer) {
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
try {
|
|
215
|
+
return floorUnits(delivered.value, decimals);
|
|
216
|
+
} catch {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
function notFound(nonce) {
|
|
221
|
+
return {
|
|
222
|
+
ok: false,
|
|
223
|
+
error: "transfer_not_found",
|
|
224
|
+
detail: `No matching XRPL payment found for nonce ${nonce} (not yet validated, or wrong recipient/amount/asset/memo).`
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function rpcFailed(nonce) {
|
|
228
|
+
return {
|
|
229
|
+
ok: false,
|
|
230
|
+
error: "tx_not_found",
|
|
231
|
+
detail: `Could not read XRPL for nonce ${nonce} (transient RPC failure) \u2014 retry.`
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// src/drivers/xrpl/wallet.ts
|
|
236
|
+
import { Wallet, isValidSecret } from "xrpl";
|
|
237
|
+
function assertXrplWallet(wallet, network) {
|
|
238
|
+
if (typeof wallet !== "object" || wallet === null) {
|
|
239
|
+
throw new WrongFamilyError(
|
|
240
|
+
`chain ${network} is XRPL; wallet must be { seed } (s\u2026 seed) or { wallet }.`
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
if ("privateKey" in wallet || "walletClient" in wallet) {
|
|
244
|
+
throw new WrongFamilyError(
|
|
245
|
+
`chain ${network} is XRPL; an EVM wallet can't be used \u2014 pass { seed } (s\u2026 seed) or { wallet }.`
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
if ("secretKey" in wallet || "signer" in wallet || "mnemonic" in wallet || "keyPair" in wallet || "secret" in wallet || "keypair" in wallet) {
|
|
249
|
+
throw new WrongFamilyError(
|
|
250
|
+
`chain ${network} is XRPL; that looks like a Solana/TON/Stellar wallet \u2014 pass { seed } (s\u2026 seed) or { wallet }.`
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
if (!("seed" in wallet) && !("wallet" in wallet)) {
|
|
254
|
+
throw new WrongFamilyError(
|
|
255
|
+
`chain ${network} is XRPL; wallet must be { seed } (s\u2026 seed) or { wallet }.`
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
return wallet;
|
|
259
|
+
}
|
|
260
|
+
function resolveXrplWallet(config) {
|
|
261
|
+
if (config.wallet) return config.wallet;
|
|
262
|
+
if (config.seed) {
|
|
263
|
+
if (!isValidSecret(config.seed)) {
|
|
264
|
+
throw new WrongFamilyError("XRPL wallet { seed } is not a valid s\u2026 secret seed.");
|
|
265
|
+
}
|
|
266
|
+
return Wallet.fromSeed(config.seed);
|
|
267
|
+
}
|
|
268
|
+
throw new WrongFamilyError("XRPL wallet needs { seed } (s\u2026 seed) or { wallet }.");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// src/drivers/xrpl/index.ts
|
|
272
|
+
var xrplDriver = {
|
|
273
|
+
family: "xrpl",
|
|
274
|
+
resolve(opts) {
|
|
275
|
+
if (opts.chain !== "xrpl") return null;
|
|
276
|
+
const rpcUrl = opts.rpcUrl ?? XRPL_MAINNET.defaultRpc;
|
|
277
|
+
return makeXrplNetwork(XRPL_MAINNET, rpcUrl);
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
function makeXrplNetwork(preset, rpcUrl) {
|
|
281
|
+
const network = preset.caip2;
|
|
282
|
+
async function rpc(method, params) {
|
|
283
|
+
const res = await fetch(rpcUrl, {
|
|
284
|
+
method: "POST",
|
|
285
|
+
headers: { "content-type": "application/json" },
|
|
286
|
+
body: JSON.stringify({ method, params: [params] })
|
|
287
|
+
});
|
|
288
|
+
if (!res.ok) throw new Error(`XRPL RPC ${method} HTTP ${res.status}`);
|
|
289
|
+
const json = await res.json();
|
|
290
|
+
const result = json.result;
|
|
291
|
+
if (!result || result.status === "error" || result.error) {
|
|
292
|
+
throw new Error(`XRPL RPC ${method} error: ${result?.error ?? "unknown"}`);
|
|
293
|
+
}
|
|
294
|
+
return result;
|
|
295
|
+
}
|
|
296
|
+
const payClient = {
|
|
297
|
+
async accountSequence(account) {
|
|
298
|
+
const r = await rpc("account_info", {
|
|
299
|
+
account,
|
|
300
|
+
ledger_index: "current"
|
|
301
|
+
});
|
|
302
|
+
return r.account_data.Sequence;
|
|
303
|
+
},
|
|
304
|
+
async feeDrops() {
|
|
305
|
+
const r = await rpc("fee", {});
|
|
306
|
+
return r.drops.open_ledger_fee;
|
|
307
|
+
},
|
|
308
|
+
async currentLedgerIndex() {
|
|
309
|
+
const r = await rpc("ledger_current", {});
|
|
310
|
+
return r.ledger_current_index;
|
|
311
|
+
},
|
|
312
|
+
async submit(txBlob) {
|
|
313
|
+
return rpc("submit", { tx_blob: txBlob });
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
const reader = {
|
|
317
|
+
async transactionsForAccount(account, limit) {
|
|
318
|
+
const r = await rpc("account_tx", {
|
|
319
|
+
account,
|
|
320
|
+
ledger_index_min: -1,
|
|
321
|
+
ledger_index_max: -1,
|
|
322
|
+
forward: false,
|
|
323
|
+
limit
|
|
324
|
+
});
|
|
325
|
+
return r.transactions.map(mapAccountTxEntry);
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
return {
|
|
329
|
+
family: "xrpl",
|
|
330
|
+
network,
|
|
331
|
+
supports: (n) => n === network,
|
|
332
|
+
resolveToken(token) {
|
|
333
|
+
if (token === "native") {
|
|
334
|
+
return { asset: "native", decimals: XRP_DECIMALS, symbol: XRP_SYMBOL };
|
|
335
|
+
}
|
|
336
|
+
if (typeof token === "string") {
|
|
337
|
+
const info = preset.tokens[token.toUpperCase()];
|
|
338
|
+
if (!info) {
|
|
339
|
+
const known = Object.keys(preset.tokens).join(", ") || "(none built in)";
|
|
340
|
+
throw new UnknownTokenError(
|
|
341
|
+
`token "${token}" isn't built in for XRPL (known: ${known}). Pass { issuer, currencyHex, decimals } for a custom IOU, or use 'native'.`
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
return {
|
|
345
|
+
asset: xrplAssetId(info.currencyHex, info.issuer),
|
|
346
|
+
decimals: info.decimals,
|
|
347
|
+
symbol: info.symbol
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
rejectForeignToken(token, "xrpl", network);
|
|
351
|
+
const t = token;
|
|
352
|
+
if (!t.issuer || !t.currencyHex || typeof t.decimals !== "number") {
|
|
353
|
+
throw new WrongFamilyError(
|
|
354
|
+
`chain ${network} is XRPL; a custom token must be { issuer, currencyHex, decimals }.`
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
return {
|
|
358
|
+
asset: xrplAssetId(t.currencyHex, t.issuer),
|
|
359
|
+
decimals: t.decimals,
|
|
360
|
+
symbol: t.symbol ?? t.currencyHex
|
|
361
|
+
};
|
|
362
|
+
},
|
|
363
|
+
describeAsset(asset) {
|
|
364
|
+
if (asset === "native") return { symbol: XRP_SYMBOL, decimals: XRP_DECIMALS };
|
|
365
|
+
for (const info of Object.values(preset.tokens)) {
|
|
366
|
+
if (xrplAssetId(info.currencyHex, info.issuer) === asset) {
|
|
367
|
+
return { symbol: info.symbol, decimals: info.decimals };
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return null;
|
|
371
|
+
},
|
|
372
|
+
assertValidPayTo(payTo) {
|
|
373
|
+
if (payTo.startsWith("0x")) {
|
|
374
|
+
throw new WrongFamilyError(
|
|
375
|
+
`chain ${network} is XRPL, but payTo "${payTo}" looks like an EVM address.`
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
if (!isValidClassicAddress(payTo)) {
|
|
379
|
+
throw new WrongFamilyError(
|
|
380
|
+
`chain ${network} is XRPL, but payTo "${payTo}" is not a valid XRPL account (r\u2026).`
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
bindWallet(wallet) {
|
|
385
|
+
return { _native: assertXrplWallet(wallet, network) };
|
|
386
|
+
},
|
|
387
|
+
async send(wallet, accept) {
|
|
388
|
+
const w = resolveXrplWallet(wallet._native);
|
|
389
|
+
return payXrpl({ client: payClient, wallet: w, accept });
|
|
390
|
+
},
|
|
391
|
+
async confirm(ref) {
|
|
392
|
+
for (let i = 0; i < 12; i += 1) {
|
|
393
|
+
try {
|
|
394
|
+
const tx = await rpc("tx", {
|
|
395
|
+
transaction: ref
|
|
396
|
+
});
|
|
397
|
+
if (tx.validated) return { height: String(tx.ledger_index ?? 0) };
|
|
398
|
+
} catch {
|
|
399
|
+
}
|
|
400
|
+
await delay(1500);
|
|
401
|
+
}
|
|
402
|
+
throw new ConfirmationTimeoutError(`XRPL tx ${ref} not validated on-ledger in time.`);
|
|
403
|
+
},
|
|
404
|
+
async estimateCost() {
|
|
405
|
+
try {
|
|
406
|
+
const drops = await payClient.feeDrops();
|
|
407
|
+
const n = Number(drops);
|
|
408
|
+
const fee = BigInt(Number.isFinite(n) && n > 12 ? Math.ceil(n) : 12);
|
|
409
|
+
return nativeCost({
|
|
410
|
+
symbol: XRP_SYMBOL,
|
|
411
|
+
decimals: XRP_DECIMALS,
|
|
412
|
+
fee,
|
|
413
|
+
basis: "estimated",
|
|
414
|
+
detail: `network fee ${fee} drops`
|
|
415
|
+
});
|
|
416
|
+
} catch {
|
|
417
|
+
return nativeCost({
|
|
418
|
+
symbol: XRP_SYMBOL,
|
|
419
|
+
decimals: XRP_DECIMALS,
|
|
420
|
+
fee: 12n,
|
|
421
|
+
basis: "heuristic",
|
|
422
|
+
detail: "~12 drops (open-ledger fee unavailable)"
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
},
|
|
426
|
+
async verify(_ref, accept) {
|
|
427
|
+
return verifyXrpl({ reader, accept });
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
function mapAccountTxEntry(entry) {
|
|
432
|
+
const e = entry;
|
|
433
|
+
const t = e.tx_json ?? e.tx ?? {};
|
|
434
|
+
const meta = e.meta ?? e.metaData ?? {};
|
|
435
|
+
return {
|
|
436
|
+
hash: e.hash ?? t.hash ?? "",
|
|
437
|
+
validated: Boolean(e.validated),
|
|
438
|
+
result: meta.TransactionResult ?? "",
|
|
439
|
+
TransactionType: t.TransactionType ?? "",
|
|
440
|
+
Account: t.Account ?? "",
|
|
441
|
+
Destination: t.Destination,
|
|
442
|
+
Memos: t.Memos,
|
|
443
|
+
delivered_amount: meta.delivered_amount ?? meta.DeliveredAmount,
|
|
444
|
+
date: typeof t.date === "number" ? t.date : void 0
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
export {
|
|
448
|
+
xrplDriver
|
|
449
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@piprail/sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Accept x402 crypto payments across 24 chains — every major EVM chain plus Solana, TON, Tron, NEAR, Sui, Stellar & XRPL — in a couple of lines. No backend, no database, no fee; payments settle straight to your wallet.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.cts",
|
|
17
|
+
"default": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"README.md",
|
|
24
|
+
"ERRORS.md",
|
|
25
|
+
"STANDARDS.md",
|
|
26
|
+
"CHANGELOG.md",
|
|
27
|
+
"LICENSE"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsup",
|
|
31
|
+
"dev": "tsup --watch",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"test:watch": "vitest",
|
|
34
|
+
"typecheck": "tsc --noEmit",
|
|
35
|
+
"typecheck:test": "tsc -p tsconfig.test.json",
|
|
36
|
+
"prepublishOnly": "npm run build && npm test && npm run typecheck && npm run typecheck:test"
|
|
37
|
+
},
|
|
38
|
+
"keywords": [
|
|
39
|
+
"piprail",
|
|
40
|
+
"x402",
|
|
41
|
+
"402",
|
|
42
|
+
"payment-required",
|
|
43
|
+
"multichain",
|
|
44
|
+
"evm",
|
|
45
|
+
"solana",
|
|
46
|
+
"ton",
|
|
47
|
+
"tron",
|
|
48
|
+
"near",
|
|
49
|
+
"sui",
|
|
50
|
+
"stellar",
|
|
51
|
+
"xrpl",
|
|
52
|
+
"base",
|
|
53
|
+
"bnb-chain",
|
|
54
|
+
"ethereum",
|
|
55
|
+
"usdc",
|
|
56
|
+
"usdt",
|
|
57
|
+
"stablecoin",
|
|
58
|
+
"crypto-payments",
|
|
59
|
+
"agent-payments",
|
|
60
|
+
"ai-agent",
|
|
61
|
+
"express-middleware",
|
|
62
|
+
"payment-middleware"
|
|
63
|
+
],
|
|
64
|
+
"author": "PipRail",
|
|
65
|
+
"license": "MIT",
|
|
66
|
+
"repository": {
|
|
67
|
+
"type": "git",
|
|
68
|
+
"url": "git+https://github.com/piprail/piprail.git",
|
|
69
|
+
"directory": "sdk"
|
|
70
|
+
},
|
|
71
|
+
"homepage": "https://piprail.com",
|
|
72
|
+
"bugs": "https://github.com/piprail/piprail/issues",
|
|
73
|
+
"engines": {
|
|
74
|
+
"node": ">=20"
|
|
75
|
+
},
|
|
76
|
+
"peerDependencies": {
|
|
77
|
+
"@mysten/sui": ">=2 <3",
|
|
78
|
+
"@solana/spl-token": "^0.4.0",
|
|
79
|
+
"@solana/web3.js": "^1.95.0",
|
|
80
|
+
"@stellar/stellar-sdk": ">=13 <16",
|
|
81
|
+
"@ton/core": ">=0.59 <1",
|
|
82
|
+
"@ton/crypto": ">=3 <4",
|
|
83
|
+
"@ton/ton": ">=15 <17",
|
|
84
|
+
"bs58": "^5.0.0",
|
|
85
|
+
"near-api-js": ">=7 <8",
|
|
86
|
+
"tronweb": ">=6 <7",
|
|
87
|
+
"viem": "^2.21.0",
|
|
88
|
+
"xrpl": ">=4 <5"
|
|
89
|
+
},
|
|
90
|
+
"peerDependenciesMeta": {
|
|
91
|
+
"@solana/web3.js": {
|
|
92
|
+
"optional": true
|
|
93
|
+
},
|
|
94
|
+
"@solana/spl-token": {
|
|
95
|
+
"optional": true
|
|
96
|
+
},
|
|
97
|
+
"@stellar/stellar-sdk": {
|
|
98
|
+
"optional": true
|
|
99
|
+
},
|
|
100
|
+
"bs58": {
|
|
101
|
+
"optional": true
|
|
102
|
+
},
|
|
103
|
+
"@ton/ton": {
|
|
104
|
+
"optional": true
|
|
105
|
+
},
|
|
106
|
+
"@ton/core": {
|
|
107
|
+
"optional": true
|
|
108
|
+
},
|
|
109
|
+
"@ton/crypto": {
|
|
110
|
+
"optional": true
|
|
111
|
+
},
|
|
112
|
+
"xrpl": {
|
|
113
|
+
"optional": true
|
|
114
|
+
},
|
|
115
|
+
"tronweb": {
|
|
116
|
+
"optional": true
|
|
117
|
+
},
|
|
118
|
+
"@mysten/sui": {
|
|
119
|
+
"optional": true
|
|
120
|
+
},
|
|
121
|
+
"near-api-js": {
|
|
122
|
+
"optional": true
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
"devDependencies": {
|
|
126
|
+
"@mysten/sui": "^2.17.0",
|
|
127
|
+
"@solana/spl-token": "^0.4.14",
|
|
128
|
+
"@solana/web3.js": "^1.98.4",
|
|
129
|
+
"@stellar/stellar-sdk": "^15.1.0",
|
|
130
|
+
"@ton/core": "^0.63.1",
|
|
131
|
+
"@ton/crypto": "^3.3.0",
|
|
132
|
+
"@ton/ton": "^16.2.4",
|
|
133
|
+
"@types/node": "^22.10.0",
|
|
134
|
+
"bs58": "^5.0.0",
|
|
135
|
+
"near-api-js": "^7.2.0",
|
|
136
|
+
"tronweb": "^6.3.0",
|
|
137
|
+
"tsup": "^8.3.5",
|
|
138
|
+
"typescript": "^5.6.3",
|
|
139
|
+
"viem": "^2.51.2",
|
|
140
|
+
"vitest": "^2.1.8",
|
|
141
|
+
"xrpl": "^4.6.0"
|
|
142
|
+
}
|
|
143
|
+
}
|