@piprail/sdk 1.4.0 → 1.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.
@@ -10,6 +10,11 @@ var _chunkIQGT65WScjs = require('./chunk-IQGT65WS.cjs');
10
10
  // src/drivers/solana/index.ts
11
11
  var _web3js = require('@solana/web3.js');
12
12
 
13
+
14
+
15
+
16
+ var _spltoken = require('@solana/spl-token');
17
+
13
18
  // src/drivers/solana/chains.ts
14
19
  var SOL_DECIMALS = 9;
15
20
  var SOLANA_MAINNET = {
@@ -39,7 +44,7 @@ var SOLANA_MAINNET = {
39
44
 
40
45
 
41
46
 
42
- var _spltoken = require('@solana/spl-token');
47
+
43
48
  async function paySolana(params) {
44
49
  const { connection, keypair, accept } = params;
45
50
  const payTo = new (0, _web3js.PublicKey)(accept.payTo);
@@ -102,7 +107,7 @@ async function verifySolana(params) {
102
107
  commitment: "confirmed",
103
108
  maxSupportedTransactionVersion: 0
104
109
  });
105
- } catch (e) {
110
+ } catch (e2) {
106
111
  return notFound(signature);
107
112
  }
108
113
  if (!tx) return notFound(signature);
@@ -278,7 +283,7 @@ function makeSolanaNetwork(preset, rpcUrl) {
278
283
  }
279
284
  try {
280
285
  new (0, _web3js.PublicKey)(payTo);
281
- } catch (e2) {
286
+ } catch (e3) {
282
287
  throw new (0, _chunkIQGT65WScjs.WrongFamilyError)(
283
288
  `chain ${network} is Solana, but payTo "${payTo}" is not a base58 address.`
284
289
  );
@@ -332,6 +337,23 @@ function makeSolanaNetwork(preset, rpcUrl) {
332
337
  detail: "1 signature + recipient token-account rent (~0.00204 SOL, if not already created)"
333
338
  });
334
339
  },
340
+ async balanceOf(wallet, asset) {
341
+ const owner = wallet._native.publicKey;
342
+ const native = await connection.getBalance(owner).then((n) => BigInt(n)).catch(() => null);
343
+ if (asset === "native") return { token: native, native };
344
+ let token;
345
+ try {
346
+ const ata = _spltoken.getAssociatedTokenAddressSync.call(void 0, new (0, _web3js.PublicKey)(asset), owner);
347
+ token = (await _spltoken.getAccount.call(void 0, connection, ata, "confirmed")).amount;
348
+ } catch (e) {
349
+ token = e instanceof _spltoken.TokenAccountNotFoundError ? 0n : null;
350
+ }
351
+ return { token, native };
352
+ },
353
+ // No receive prerequisite — the payer's tx idempotently creates the recipient's ATA (pay.ts).
354
+ async recipientReady() {
355
+ return { ready: "n/a" };
356
+ },
335
357
  async verify(ref, accept) {
336
358
  return verifySolana({ connection, signature: ref, accept });
337
359
  }
@@ -276,6 +276,10 @@ function resolveStellarWallet(config) {
276
276
  }
277
277
 
278
278
  // src/drivers/stellar/index.ts
279
+ function isStellarNotFound(e) {
280
+ const x = e;
281
+ return x?.response?.status === 404 || x?.name === "NotFoundError";
282
+ }
279
283
  var stellarDriver = {
280
284
  family: "stellar",
281
285
  resolve(opts) {
@@ -394,6 +398,52 @@ function makeStellarNetwork(preset, rpcUrl) {
394
398
  detail: "base fee 100 stroops (1 operation)"
395
399
  });
396
400
  },
401
+ async balanceOf(wallet, asset) {
402
+ let owner;
403
+ try {
404
+ owner = resolveStellarWallet(wallet._native).publicKey();
405
+ } catch {
406
+ return { token: null, native: null };
407
+ }
408
+ let lines;
409
+ try {
410
+ const account = await server.loadAccount(owner);
411
+ lines = account.balances;
412
+ } catch (e) {
413
+ return isStellarNotFound(e) ? { token: 0n, native: 0n } : { token: null, native: null };
414
+ }
415
+ const toBase = (s) => {
416
+ try {
417
+ return s != null ? parseUnits(s, STELLAR_DECIMALS) : 0n;
418
+ } catch {
419
+ return 0n;
420
+ }
421
+ };
422
+ const native = toBase(lines.find((b) => b.asset_type === "native")?.balance);
423
+ if (asset === "native") return { token: native, native };
424
+ const parts = parseStellarAssetId(asset);
425
+ const line = parts ? lines.find(
426
+ (b) => (b.asset_type === "credit_alphanum4" || b.asset_type === "credit_alphanum12") && b.asset_code === parts.code && b.asset_issuer === parts.issuer
427
+ ) : void 0;
428
+ return { token: toBase(line?.balance), native };
429
+ },
430
+ async recipientReady(payTo, asset) {
431
+ let lines;
432
+ try {
433
+ const account = await server.loadAccount(payTo);
434
+ lines = account.balances;
435
+ } catch (e) {
436
+ if (isStellarNotFound(e)) return { ready: false, reason: "INACTIVE" };
437
+ return { ready: "unknown" };
438
+ }
439
+ if (asset === "native") return { ready: true };
440
+ const parts = parseStellarAssetId(asset);
441
+ if (!parts) return { ready: "unknown" };
442
+ const hasTrustline = lines.some(
443
+ (b) => (b.asset_type === "credit_alphanum4" || b.asset_type === "credit_alphanum12") && b.asset_code === parts.code && b.asset_issuer === parts.issuer
444
+ );
445
+ return hasTrustline ? { ready: true } : { ready: false, reason: "NO_TRUSTLINE" };
446
+ },
397
447
  async verify(_ref, accept) {
398
448
  return verifyStellar({ reader, accept });
399
449
  }
@@ -276,6 +276,10 @@ function resolveStellarWallet(config) {
276
276
  }
277
277
 
278
278
  // src/drivers/stellar/index.ts
279
+ function isStellarNotFound(e) {
280
+ const x = e;
281
+ return _optionalChain([x, 'optionalAccess', _9 => _9.response, 'optionalAccess', _10 => _10.status]) === 404 || _optionalChain([x, 'optionalAccess', _11 => _11.name]) === "NotFoundError";
282
+ }
279
283
  var stellarDriver = {
280
284
  family: "stellar",
281
285
  resolve(opts) {
@@ -394,6 +398,52 @@ function makeStellarNetwork(preset, rpcUrl) {
394
398
  detail: "base fee 100 stroops (1 operation)"
395
399
  });
396
400
  },
401
+ async balanceOf(wallet, asset) {
402
+ let owner;
403
+ try {
404
+ owner = resolveStellarWallet(wallet._native).publicKey();
405
+ } catch (e6) {
406
+ return { token: null, native: null };
407
+ }
408
+ let lines;
409
+ try {
410
+ const account = await server.loadAccount(owner);
411
+ lines = account.balances;
412
+ } catch (e) {
413
+ return isStellarNotFound(e) ? { token: 0n, native: 0n } : { token: null, native: null };
414
+ }
415
+ const toBase = (s) => {
416
+ try {
417
+ return s != null ? _chunkIQGT65WScjs.parseUnits.call(void 0, s, STELLAR_DECIMALS) : 0n;
418
+ } catch (e7) {
419
+ return 0n;
420
+ }
421
+ };
422
+ const native = toBase(_optionalChain([lines, 'access', _12 => _12.find, 'call', _13 => _13((b) => b.asset_type === "native"), 'optionalAccess', _14 => _14.balance]));
423
+ if (asset === "native") return { token: native, native };
424
+ const parts = parseStellarAssetId(asset);
425
+ const line = parts ? lines.find(
426
+ (b) => (b.asset_type === "credit_alphanum4" || b.asset_type === "credit_alphanum12") && b.asset_code === parts.code && b.asset_issuer === parts.issuer
427
+ ) : void 0;
428
+ return { token: toBase(_optionalChain([line, 'optionalAccess', _15 => _15.balance])), native };
429
+ },
430
+ async recipientReady(payTo, asset) {
431
+ let lines;
432
+ try {
433
+ const account = await server.loadAccount(payTo);
434
+ lines = account.balances;
435
+ } catch (e) {
436
+ if (isStellarNotFound(e)) return { ready: false, reason: "INACTIVE" };
437
+ return { ready: "unknown" };
438
+ }
439
+ if (asset === "native") return { ready: true };
440
+ const parts = parseStellarAssetId(asset);
441
+ if (!parts) return { ready: "unknown" };
442
+ const hasTrustline = lines.some(
443
+ (b) => (b.asset_type === "credit_alphanum4" || b.asset_type === "credit_alphanum12") && b.asset_code === parts.code && b.asset_issuer === parts.issuer
444
+ );
445
+ return hasTrustline ? { ready: true } : { ready: false, reason: "NO_TRUSTLINE" };
446
+ },
397
447
  async verify(_ref, accept) {
398
448
  return verifyStellar({ reader, accept });
399
449
  }
@@ -294,6 +294,29 @@ function makeSuiNetwork(preset, rpcUrl) {
294
294
  detail: "\u22480.003 SUI (computation + storage; storage is largely rebated on success)"
295
295
  });
296
296
  },
297
+ async balanceOf(wallet, asset) {
298
+ let owner;
299
+ try {
300
+ owner = resolveSuiKeypair(wallet._native).toSuiAddress();
301
+ } catch {
302
+ return { token: null, native: null };
303
+ }
304
+ const readBal = async (coinType) => {
305
+ try {
306
+ return BigInt((await client.getBalance({ owner, coinType })).totalBalance);
307
+ } catch {
308
+ return null;
309
+ }
310
+ };
311
+ const native = await readBal("0x2::sui::SUI");
312
+ if (asset === "native") return { token: native, native };
313
+ const token = await readBal(asset);
314
+ return { token, native };
315
+ },
316
+ // No receive prerequisite — any Sui address receives SUI/coins immediately.
317
+ async recipientReady() {
318
+ return { ready: "n/a" };
319
+ },
297
320
  async verify(ref, accept) {
298
321
  return verifySui({ reader, digest: ref, accept });
299
322
  }
@@ -294,6 +294,29 @@ function makeSuiNetwork(preset, rpcUrl) {
294
294
  detail: "\u22480.003 SUI (computation + storage; storage is largely rebated on success)"
295
295
  });
296
296
  },
297
+ async balanceOf(wallet, asset) {
298
+ let owner;
299
+ try {
300
+ owner = resolveSuiKeypair(wallet._native).toSuiAddress();
301
+ } catch (e3) {
302
+ return { token: null, native: null };
303
+ }
304
+ const readBal = async (coinType) => {
305
+ try {
306
+ return BigInt((await client.getBalance({ owner, coinType })).totalBalance);
307
+ } catch (e4) {
308
+ return null;
309
+ }
310
+ };
311
+ const native = await readBal("0x2::sui::SUI");
312
+ if (asset === "native") return { token: native, native };
313
+ const token = await readBal(asset);
314
+ return { token, native };
315
+ },
316
+ // No receive prerequisite — any Sui address receives SUI/coins immediately.
317
+ async recipientReady() {
318
+ return { ready: "n/a" };
319
+ },
297
320
  async verify(ref, accept) {
298
321
  return verifySui({ reader, digest: ref, accept });
299
322
  }
@@ -346,6 +346,29 @@ function makeTonNetwork(preset, rpcUrl) {
346
346
  const detail = accept.asset === "native" ? "~0.01 TON network fee" : "~0.05 TON attached for the jetton transfer (leftover refunded)";
347
347
  return nativeCost({ symbol: "TON", decimals: TON_DECIMALS, fee, basis: "heuristic", detail });
348
348
  },
349
+ async balanceOf(wallet, asset) {
350
+ let owner;
351
+ try {
352
+ owner = (await resolveTonWallet(wallet._native)).contract.address.toString();
353
+ } catch {
354
+ return { token: null, native: null };
355
+ }
356
+ const native = await client.getBalance(Address2.parse(owner)).then((b) => BigInt(b)).catch(() => null);
357
+ if (asset === "native") return { token: native, native };
358
+ let token;
359
+ try {
360
+ const jw = await jettonWalletFor(asset, owner);
361
+ const { stack } = await client.runMethod(jw, "get_wallet_data");
362
+ token = stack.readBigNumber();
363
+ } catch {
364
+ token = null;
365
+ }
366
+ return { token, native };
367
+ },
368
+ // No receive prerequisite — the payer's gas auto-deploys the recipient's jetton wallet.
369
+ async recipientReady() {
370
+ return { ready: "n/a" };
371
+ },
349
372
  async verify(_ref, accept) {
350
373
  const watch = await watchAccountFor(accept);
351
374
  return verifyTon({ client, watch, accept });
@@ -346,6 +346,29 @@ function makeTonNetwork(preset, rpcUrl) {
346
346
  const detail = accept.asset === "native" ? "~0.01 TON network fee" : "~0.05 TON attached for the jetton transfer (leftover refunded)";
347
347
  return _chunkIQGT65WScjs.nativeCost.call(void 0, { symbol: "TON", decimals: TON_DECIMALS, fee, basis: "heuristic", detail });
348
348
  },
349
+ async balanceOf(wallet, asset) {
350
+ let owner;
351
+ try {
352
+ owner = (await resolveTonWallet(wallet._native)).contract.address.toString();
353
+ } catch (e8) {
354
+ return { token: null, native: null };
355
+ }
356
+ const native = await client.getBalance(_core.Address.parse(owner)).then((b) => BigInt(b)).catch(() => null);
357
+ if (asset === "native") return { token: native, native };
358
+ let token;
359
+ try {
360
+ const jw = await jettonWalletFor(asset, owner);
361
+ const { stack } = await client.runMethod(jw, "get_wallet_data");
362
+ token = stack.readBigNumber();
363
+ } catch (e9) {
364
+ token = null;
365
+ }
366
+ return { token, native };
367
+ },
368
+ // No receive prerequisite — the payer's gas auto-deploys the recipient's jetton wallet.
369
+ async recipientReady() {
370
+ return { ready: "n/a" };
371
+ },
349
372
  async verify(_ref, accept) {
350
373
  const watch = await watchAccountFor(accept);
351
374
  return verifyTon({ client, watch, accept });
@@ -460,6 +460,34 @@ function makeTronNetwork(preset, rpcUrl) {
460
460
  detail: "~30k energy + bandwidth (sender not staked; pass { from } for a precise estimate)"
461
461
  });
462
462
  },
463
+ async balanceOf(wallet, asset) {
464
+ const owner = tronWeb.address.fromPrivateKey(
465
+ resolveTronPrivateKey(wallet._native)
466
+ );
467
+ if (!owner) return { token: null, native: null };
468
+ const native = await tronWeb.trx.getBalance(owner).then((n) => BigInt(n)).catch(() => null);
469
+ if (asset === "native") return { token: native, native };
470
+ let token = null;
471
+ try {
472
+ tronWeb.setAddress(owner);
473
+ const res = await tronWeb.transactionBuilder.triggerConstantContract(
474
+ asset,
475
+ "balanceOf(address)",
476
+ {},
477
+ [{ type: "address", value: owner }],
478
+ owner
479
+ );
480
+ const hex = res?.constant_result?.[0];
481
+ token = hex == null ? null : BigInt("0x" + hex);
482
+ } catch {
483
+ token = null;
484
+ }
485
+ return { token, native };
486
+ },
487
+ // No receive prerequisite — any Tron account receives TRX/TRC-20 immediately.
488
+ async recipientReady() {
489
+ return { ready: "n/a" };
490
+ },
463
491
  async verify(ref, accept) {
464
492
  const txid = stripTronPrefix(ref);
465
493
  if (accept.asset === "native") {
@@ -460,6 +460,34 @@ function makeTronNetwork(preset, rpcUrl) {
460
460
  detail: "~30k energy + bandwidth (sender not staked; pass { from } for a precise estimate)"
461
461
  });
462
462
  },
463
+ async balanceOf(wallet, asset) {
464
+ const owner = tronWeb.address.fromPrivateKey(
465
+ resolveTronPrivateKey(wallet._native)
466
+ );
467
+ if (!owner) return { token: null, native: null };
468
+ const native = await tronWeb.trx.getBalance(owner).then((n) => BigInt(n)).catch(() => null);
469
+ if (asset === "native") return { token: native, native };
470
+ let token = null;
471
+ try {
472
+ tronWeb.setAddress(owner);
473
+ const res = await tronWeb.transactionBuilder.triggerConstantContract(
474
+ asset,
475
+ "balanceOf(address)",
476
+ {},
477
+ [{ type: "address", value: owner }],
478
+ owner
479
+ );
480
+ const hex = _optionalChain([res, 'optionalAccess', _25 => _25.constant_result, 'optionalAccess', _26 => _26[0]]);
481
+ token = hex == null ? null : BigInt("0x" + hex);
482
+ } catch (e9) {
483
+ token = null;
484
+ }
485
+ return { token, native };
486
+ },
487
+ // No receive prerequisite — any Tron account receives TRX/TRC-20 immediately.
488
+ async recipientReady() {
489
+ return { ready: "n/a" };
490
+ },
463
491
  async verify(ref, accept) {
464
492
  const txid = stripTronPrefix(ref);
465
493
  if (accept.asset === "native") {
@@ -11,6 +11,7 @@ var _chunkCQREG5LEcjs = require('./chunk-CQREG5LE.cjs');
11
11
 
12
12
 
13
13
 
14
+
14
15
  var _chunkIQGT65WScjs = require('./chunk-IQGT65WS.cjs');
15
16
 
16
17
  // src/drivers/xrpl/index.ts
@@ -285,6 +286,9 @@ function resolveXrplWallet(config) {
285
286
  }
286
287
 
287
288
  // src/drivers/xrpl/index.ts
289
+ function isXrplActNotFound(e) {
290
+ return /actNotFound/i.test(String(_nullishCoalesce(_optionalChain([e, 'optionalAccess', _5 => _5.message]), () => ( e))));
291
+ }
288
292
  var xrplDriver = {
289
293
  family: "xrpl",
290
294
  resolve(opts) {
@@ -305,7 +309,7 @@ function makeXrplNetwork(preset, rpcUrl) {
305
309
  const json = await res.json();
306
310
  const result = json.result;
307
311
  if (!result || result.status === "error" || result.error) {
308
- throw new Error(`XRPL RPC ${method} error: ${_nullishCoalesce(_optionalChain([result, 'optionalAccess', _5 => _5.error]), () => ( "unknown"))}`);
312
+ throw new Error(`XRPL RPC ${method} error: ${_nullishCoalesce(_optionalChain([result, 'optionalAccess', _6 => _6.error]), () => ( "unknown"))}`);
309
313
  }
310
314
  return result;
311
315
  }
@@ -439,6 +443,62 @@ function makeXrplNetwork(preset, rpcUrl) {
439
443
  });
440
444
  }
441
445
  },
446
+ async balanceOf(wallet, asset) {
447
+ let owner;
448
+ try {
449
+ owner = resolveXrplWallet(wallet._native).classicAddress;
450
+ } catch (e6) {
451
+ return { token: null, native: null };
452
+ }
453
+ let native = null;
454
+ try {
455
+ const r = await rpc("account_info", {
456
+ account: owner,
457
+ ledger_index: "validated"
458
+ });
459
+ native = BigInt(r.account_data.Balance);
460
+ } catch (e) {
461
+ native = isXrplActNotFound(e) ? 0n : null;
462
+ }
463
+ if (asset === "native") return { token: native, native };
464
+ let token = null;
465
+ try {
466
+ const [currencyHex, issuer] = asset.split(":");
467
+ const r = await rpc(
468
+ "account_lines",
469
+ { account: owner, ledger_index: "validated" }
470
+ );
471
+ const line = r.lines.find(
472
+ (l) => l.currency.toUpperCase() === (_nullishCoalesce(currencyHex, () => ( ""))).toUpperCase() && l.account === issuer
473
+ );
474
+ token = line ? _chunkIQGT65WScjs.parseUnits.call(void 0, line.balance, XRP_DECIMALS) : 0n;
475
+ } catch (e) {
476
+ token = isXrplActNotFound(e) ? 0n : null;
477
+ }
478
+ return { token, native };
479
+ },
480
+ async recipientReady(payTo, asset) {
481
+ try {
482
+ await rpc("account_info", { account: payTo, ledger_index: "validated" });
483
+ } catch (e) {
484
+ if (isXrplActNotFound(e)) return { ready: false, reason: "INACTIVE" };
485
+ return { ready: "unknown" };
486
+ }
487
+ if (asset === "native") return { ready: true };
488
+ try {
489
+ const [currencyHex, issuer] = asset.split(":");
490
+ const r = await rpc("account_lines", {
491
+ account: payTo,
492
+ ledger_index: "validated"
493
+ });
494
+ const has = r.lines.some(
495
+ (l) => l.currency.toUpperCase() === (_nullishCoalesce(currencyHex, () => ( ""))).toUpperCase() && l.account === issuer
496
+ );
497
+ return has ? { ready: true } : { ready: false, reason: "NO_TRUSTLINE" };
498
+ } catch (e7) {
499
+ return { ready: "unknown" };
500
+ }
501
+ },
442
502
  async verify(_ref, accept) {
443
503
  return verifyXrpl({ reader, accept });
444
504
  }
@@ -9,6 +9,7 @@ import {
9
9
  WrongFamilyError,
10
10
  floorUnits,
11
11
  nativeCost,
12
+ parseUnits,
12
13
  rejectForeignToken,
13
14
  toInsufficientFundsError
14
15
  } from "./chunk-QDS6FBZP.js";
@@ -285,6 +286,9 @@ function resolveXrplWallet(config) {
285
286
  }
286
287
 
287
288
  // src/drivers/xrpl/index.ts
289
+ function isXrplActNotFound(e) {
290
+ return /actNotFound/i.test(String(e?.message ?? e));
291
+ }
288
292
  var xrplDriver = {
289
293
  family: "xrpl",
290
294
  resolve(opts) {
@@ -439,6 +443,62 @@ function makeXrplNetwork(preset, rpcUrl) {
439
443
  });
440
444
  }
441
445
  },
446
+ async balanceOf(wallet, asset) {
447
+ let owner;
448
+ try {
449
+ owner = resolveXrplWallet(wallet._native).classicAddress;
450
+ } catch {
451
+ return { token: null, native: null };
452
+ }
453
+ let native = null;
454
+ try {
455
+ const r = await rpc("account_info", {
456
+ account: owner,
457
+ ledger_index: "validated"
458
+ });
459
+ native = BigInt(r.account_data.Balance);
460
+ } catch (e) {
461
+ native = isXrplActNotFound(e) ? 0n : null;
462
+ }
463
+ if (asset === "native") return { token: native, native };
464
+ let token = null;
465
+ try {
466
+ const [currencyHex, issuer] = asset.split(":");
467
+ const r = await rpc(
468
+ "account_lines",
469
+ { account: owner, ledger_index: "validated" }
470
+ );
471
+ const line = r.lines.find(
472
+ (l) => l.currency.toUpperCase() === (currencyHex ?? "").toUpperCase() && l.account === issuer
473
+ );
474
+ token = line ? parseUnits(line.balance, XRP_DECIMALS) : 0n;
475
+ } catch (e) {
476
+ token = isXrplActNotFound(e) ? 0n : null;
477
+ }
478
+ return { token, native };
479
+ },
480
+ async recipientReady(payTo, asset) {
481
+ try {
482
+ await rpc("account_info", { account: payTo, ledger_index: "validated" });
483
+ } catch (e) {
484
+ if (isXrplActNotFound(e)) return { ready: false, reason: "INACTIVE" };
485
+ return { ready: "unknown" };
486
+ }
487
+ if (asset === "native") return { ready: true };
488
+ try {
489
+ const [currencyHex, issuer] = asset.split(":");
490
+ const r = await rpc("account_lines", {
491
+ account: payTo,
492
+ ledger_index: "validated"
493
+ });
494
+ const has = r.lines.some(
495
+ (l) => l.currency.toUpperCase() === (currencyHex ?? "").toUpperCase() && l.account === issuer
496
+ );
497
+ return has ? { ready: true } : { ready: false, reason: "NO_TRUSTLINE" };
498
+ } catch {
499
+ return { ready: "unknown" };
500
+ }
501
+ },
442
502
  async verify(_ref, accept) {
443
503
  return verifyXrpl({ reader, accept });
444
504
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piprail/sdk",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Accept x402 crypto payments across 28 chains — every major EVM chain plus Solana, TON, Tron, NEAR, Sui, Aptos, Algorand, Stellar & XRPL — in a couple of lines. No backend, no database, no fee; payments settle straight to your wallet.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",