@zkp2p/cash 0.1.9 → 0.2.1
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/AGENTS.md +19 -2
- package/README.md +52 -5
- package/dist/{chunk-TR6JVYYF.js → chunk-5VBP3IWK.js} +59 -2
- package/dist/{createCashClient-BhOytyHE.d.cts → createCashClient-Clg5Fa1H.d.cts} +42 -15
- package/dist/{createCashClient-BhOytyHE.d.ts → createCashClient-Clg5Fa1H.d.ts} +42 -15
- package/dist/index.cjs +239 -85
- package/dist/index.d.cts +36 -26
- package/dist/index.d.ts +36 -26
- package/dist/index.js +182 -87
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/tools.cjs +26 -10
- package/dist/tools.d.cts +29 -10
- package/dist/tools.d.ts +29 -10
- package/dist/tools.js +26 -10
- package/docs/lifecycle-and-recovery.md +12 -3
- package/llms.txt +4 -2
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -23,6 +23,12 @@ var CASH_ORDER_STATUSES = [
|
|
|
23
23
|
];
|
|
24
24
|
var CASH_ORDER_POLL_INTERVAL_MS = 5e3;
|
|
25
25
|
var CASH_RETAIN_ON_EMPTY = false;
|
|
26
|
+
function payoutCurrencies(payout) {
|
|
27
|
+
if (payout.currency === void 0 === (payout.currencies === void 0)) {
|
|
28
|
+
throw new Error("Pass exactly one of payout currency or currencies");
|
|
29
|
+
}
|
|
30
|
+
return payout.currencies ?? [payout.currency];
|
|
31
|
+
}
|
|
26
32
|
function isMarketRateSupported(currency, adapters) {
|
|
27
33
|
return sdk.getSpreadOracleConfig(currency, adapters) != null;
|
|
28
34
|
}
|
|
@@ -54,14 +60,32 @@ async function prepareCashDepositParams(client, input, adapters) {
|
|
|
54
60
|
const runtimeEnv = client.runtimeEnv;
|
|
55
61
|
const catalog = sdk.getPaymentMethodsCatalog(chainId, runtimeEnv);
|
|
56
62
|
const intentGatingService = sdk.getGatingServiceAddress(chainId, runtimeEnv);
|
|
63
|
+
const processorNames = payouts.map((p) => p.processorName);
|
|
64
|
+
const paymentMethodsOverride = processorNames.map(
|
|
65
|
+
(name) => sdk.resolvePaymentMethodHashFromCatalog(name, catalog)
|
|
66
|
+
);
|
|
57
67
|
for (const payout of payouts) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
const currencies = payoutCurrencies(payout);
|
|
69
|
+
if (currencies.length === 0 || new Set(currencies).size !== currencies.length) {
|
|
70
|
+
throw new Error("Payout currencies must be non-empty and unique");
|
|
71
|
+
}
|
|
72
|
+
const supportedCurrencyHashes = new Set(
|
|
73
|
+
(catalog[payout.processorName.toLowerCase()]?.currencies ?? []).map(
|
|
74
|
+
(hash) => hash.toLowerCase()
|
|
75
|
+
)
|
|
76
|
+
);
|
|
77
|
+
for (const currency of currencies) {
|
|
78
|
+
if (!isMarketRateSupported(currency, adapters)) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
`${currency} has no live market-rate oracle feed; Peer Cash supports market-rate currencies only.`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
const currencyHash = sdk.currencyInfo[currency]?.currencyCodeHash;
|
|
84
|
+
if (!currencyHash || !supportedCurrencyHashes.has(currencyHash.toLowerCase())) {
|
|
85
|
+
throw new Error(`${payout.processorName} does not support ${currency}`);
|
|
86
|
+
}
|
|
62
87
|
}
|
|
63
88
|
}
|
|
64
|
-
const processorNames = payouts.map((p) => p.processorName);
|
|
65
89
|
const { hashedOnchainIds } = await client.registerPayeeDetails({
|
|
66
90
|
processorNames,
|
|
67
91
|
payeeData: payouts.map((p) => p.payeeData)
|
|
@@ -69,22 +93,24 @@ async function prepareCashDepositParams(client, input, adapters) {
|
|
|
69
93
|
if (hashedOnchainIds.length !== payouts.length) {
|
|
70
94
|
throw new Error("Payee registration returned an unexpected number of hashes");
|
|
71
95
|
}
|
|
72
|
-
const paymentMethodsOverride = processorNames.map(
|
|
73
|
-
(name) => sdk.resolvePaymentMethodHashFromCatalog(name, catalog)
|
|
74
|
-
);
|
|
75
96
|
const paymentMethodDataOverride = hashedOnchainIds.map((hid) => ({
|
|
76
97
|
intentGatingService,
|
|
77
98
|
payeeDetails: hid,
|
|
78
99
|
data: "0x"
|
|
79
100
|
}));
|
|
80
|
-
const currenciesOverride = payouts.map(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
101
|
+
const currenciesOverride = payouts.map(
|
|
102
|
+
(payout) => payoutCurrencies(payout).map((currency) => {
|
|
103
|
+
const tuple = buildMarketRateCurrencyOverride(currency, adapters);
|
|
104
|
+
if (!tuple) throw new Error(`Failed to build market-rate config for ${currency}`);
|
|
105
|
+
return tuple;
|
|
106
|
+
})
|
|
107
|
+
);
|
|
108
|
+
const conversionRates = payouts.map(
|
|
109
|
+
(payout) => payoutCurrencies(payout).map((currency) => ({
|
|
110
|
+
currency,
|
|
111
|
+
conversionRate: ORACLE_MIN_CONVERSION_RATE_SENTINEL.toString()
|
|
112
|
+
}))
|
|
113
|
+
);
|
|
88
114
|
const intentAmountRange = input.intentAmountRange ?? buildIntentAmountRange(input.amount);
|
|
89
115
|
return {
|
|
90
116
|
token: input.token ?? BASE_USDC_ADDRESS,
|
|
@@ -303,35 +329,41 @@ function toPricing(tuple) {
|
|
|
303
329
|
};
|
|
304
330
|
}
|
|
305
331
|
function derivePayouts(paymentMethods, currencies, catalog) {
|
|
306
|
-
|
|
332
|
+
const payouts = [];
|
|
333
|
+
for (const method of paymentMethods) {
|
|
307
334
|
const platformHash = method.paymentMethodHash ?? "";
|
|
308
335
|
if (!platformHash) return [];
|
|
309
336
|
let platform;
|
|
310
337
|
try {
|
|
311
338
|
platform = sdk.resolvePaymentMethodNameFromHash(platformHash, catalog);
|
|
312
339
|
} catch {
|
|
313
|
-
|
|
340
|
+
return [];
|
|
314
341
|
}
|
|
342
|
+
if (!platform) return [];
|
|
315
343
|
const tuples = currencies.filter(
|
|
316
344
|
(c) => (c.paymentMethodHash ?? "").toLowerCase() === platformHash.toLowerCase()
|
|
317
345
|
);
|
|
318
346
|
const base2 = {
|
|
319
|
-
|
|
347
|
+
platform,
|
|
320
348
|
platformHash,
|
|
321
349
|
payeeHash: method.payeeDetailsHash ?? "",
|
|
322
350
|
active: method.active ?? true
|
|
323
351
|
};
|
|
324
|
-
if (tuples.length === 0)
|
|
325
|
-
|
|
352
|
+
if (tuples.length === 0) {
|
|
353
|
+
payouts.push({ ...base2, pricing: toPricing(void 0) });
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
for (const tuple of tuples) {
|
|
326
357
|
const currency = tuple.currencyCode != null ? sdk.getCurrencyCodeFromHash(tuple.currencyCode) : void 0;
|
|
327
|
-
|
|
358
|
+
payouts.push({
|
|
328
359
|
...base2,
|
|
329
360
|
...currency !== void 0 ? { currency } : {},
|
|
330
361
|
...tuple.currencyCode != null ? { currencyHash: tuple.currencyCode } : {},
|
|
331
362
|
pricing: toPricing(tuple)
|
|
332
|
-
};
|
|
333
|
-
}
|
|
334
|
-
}
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return payouts;
|
|
335
367
|
}
|
|
336
368
|
|
|
337
369
|
// src/engine/buyerProfile.ts
|
|
@@ -375,21 +407,33 @@ function resolveCashDepositId(params) {
|
|
|
375
407
|
events = viem.parseEventLogs({
|
|
376
408
|
abi: params.abi,
|
|
377
409
|
eventName: "DepositReceived",
|
|
378
|
-
logs: params.logs
|
|
410
|
+
logs: [...params.logs]
|
|
379
411
|
});
|
|
380
412
|
} catch {
|
|
381
413
|
return null;
|
|
382
414
|
}
|
|
383
|
-
const
|
|
384
|
-
|
|
415
|
+
const matchingEvents = events.filter((event2) => {
|
|
416
|
+
if (params.expectedEscrowAddress !== void 0 && event2.address.toLowerCase() !== params.expectedEscrowAddress.toLowerCase()) {
|
|
417
|
+
return false;
|
|
418
|
+
}
|
|
419
|
+
if (params.expectedToken !== void 0 && String(event2.args.token ?? "").toLowerCase() !== params.expectedToken.toLowerCase()) {
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
return true;
|
|
423
|
+
});
|
|
424
|
+
if (matchingEvents.length !== 1) return null;
|
|
425
|
+
const event = matchingEvents[0];
|
|
385
426
|
const rawId = event.args.depositId;
|
|
386
427
|
if (rawId === void 0 || rawId === null) return null;
|
|
387
428
|
const onchainDepositId = BigInt(rawId);
|
|
429
|
+
const rawAmount = event.args.amount;
|
|
430
|
+
const amount = rawAmount === void 0 || rawAmount === null ? void 0 : BigInt(rawAmount);
|
|
388
431
|
const escrowAddress = event.address.toLowerCase();
|
|
389
432
|
return {
|
|
390
433
|
onchainDepositId,
|
|
391
434
|
escrowAddress,
|
|
392
|
-
compositeId: sdk.createCompositeDepositId(escrowAddress, onchainDepositId)
|
|
435
|
+
compositeId: sdk.createCompositeDepositId(escrowAddress, onchainDepositId),
|
|
436
|
+
...amount === void 0 ? {} : { amount }
|
|
393
437
|
};
|
|
394
438
|
}
|
|
395
439
|
function parseCompositeDepositId(compositeId) {
|
|
@@ -403,26 +447,6 @@ function parseCompositeDepositId(compositeId) {
|
|
|
403
447
|
const onchainDepositId = BigInt(rawDepositId);
|
|
404
448
|
return { escrowAddress: canonicalEscrowAddress, onchainDepositId };
|
|
405
449
|
}
|
|
406
|
-
|
|
407
|
-
// src/client/platformGroups.ts
|
|
408
|
-
var PLATFORM_METHOD_GROUPS = {
|
|
409
|
-
zelle: ["zelle", "zelle-chase", "zelle-bofa", "zelle-citi"]
|
|
410
|
-
};
|
|
411
|
-
var METHOD_TO_BASE_PLATFORM = new Map(
|
|
412
|
-
Object.entries(PLATFORM_METHOD_GROUPS).flatMap(
|
|
413
|
-
([platform, methods]) => methods.map((method) => [method, platform])
|
|
414
|
-
)
|
|
415
|
-
);
|
|
416
|
-
function basePlatformForMethod(method) {
|
|
417
|
-
return METHOD_TO_BASE_PLATFORM.get(method) ?? method;
|
|
418
|
-
}
|
|
419
|
-
function paymentMethodsForPlatform(platform, catalog) {
|
|
420
|
-
const configured = PLATFORM_METHOD_GROUPS[platform];
|
|
421
|
-
const methods = configured ?? [platform];
|
|
422
|
-
return methods.filter((method) => catalog[method] !== void 0);
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// src/client/capabilities.ts
|
|
426
450
|
var MIN_CASHOUT_AMOUNT = 10000n;
|
|
427
451
|
var RECOMMENDED_MIN_CASHOUT_AMOUNT = 1000000n;
|
|
428
452
|
var PAYEE_HINTS = {
|
|
@@ -441,20 +465,13 @@ var PAYEE_HINTS = {
|
|
|
441
465
|
var IDENTITY_ATTESTATION_PLATFORMS = /* @__PURE__ */ new Set(["wise", "paypal"]);
|
|
442
466
|
function buildCapabilities(environment) {
|
|
443
467
|
const catalog = sdk.getPaymentMethodsCatalog(BASE_CHAIN_ID, environment);
|
|
444
|
-
const
|
|
445
|
-
for (const [method, entry] of Object.entries(catalog)) {
|
|
446
|
-
const platform = basePlatformForMethod(method);
|
|
468
|
+
const platforms = Object.entries(catalog).map(([platform, entry]) => {
|
|
447
469
|
const currencies2 = (entry.currencies ?? []).map((hash) => sdk.getCurrencyCodeFromHash(hash)).filter(
|
|
448
470
|
(code) => code != null && isMarketRateSupported(code)
|
|
449
471
|
);
|
|
450
|
-
const aggregate = currenciesByPlatform.get(platform) ?? /* @__PURE__ */ new Set();
|
|
451
|
-
for (const currency of currencies2) aggregate.add(currency);
|
|
452
|
-
currenciesByPlatform.set(platform, aggregate);
|
|
453
|
-
}
|
|
454
|
-
const platforms = [...currenciesByPlatform.entries()].map(([platform, currencies2]) => {
|
|
455
472
|
return {
|
|
456
473
|
platform,
|
|
457
|
-
currencies: [...currencies2].sort(),
|
|
474
|
+
currencies: [...new Set(currencies2)].sort(),
|
|
458
475
|
payeeHint: PAYEE_HINTS[platform] ?? "Your payment handle for this platform",
|
|
459
476
|
requiresIdentityAttestation: IDENTITY_ATTESTATION_PLATFORMS.has(platform)
|
|
460
477
|
};
|
|
@@ -542,6 +559,12 @@ var errors = {
|
|
|
542
559
|
retryable: false,
|
|
543
560
|
remediation: `Use a positive minimum no greater than the maximum, and a maximum no greater than the cash-out amount.`
|
|
544
561
|
}),
|
|
562
|
+
invalidPayoutCurrencies: (platform, reason) => new CashError({
|
|
563
|
+
code: "INVALID_PAYOUT_CURRENCIES",
|
|
564
|
+
message: `The ${platform} payout currency set is invalid: ${reason}.`,
|
|
565
|
+
retryable: false,
|
|
566
|
+
remediation: `Pass one or more unique currencies listed for ${platform} by capabilities().`
|
|
567
|
+
}),
|
|
545
568
|
activeIntentBlocksWithdrawal: (depositId) => new CashError({
|
|
546
569
|
code: "ACTIVE_INTENT_BLOCKS_WITHDRAWAL",
|
|
547
570
|
message: `Order ${depositId} has a live buyer intent; escrow blocks withdrawal while a buyer may still deliver.`,
|
|
@@ -745,7 +768,12 @@ var errors = {
|
|
|
745
768
|
code: "DEPOSIT_RESOLUTION_FAILED",
|
|
746
769
|
message: `Deposit transaction ${txHash} succeeded but no DepositReceived event was found in the receipt.`,
|
|
747
770
|
retryable: false,
|
|
748
|
-
remediation: `Inspect the transaction on Basescan; recover the depositId from the DepositReceived log manually, then resume with order(depositId)
|
|
771
|
+
remediation: `Inspect the transaction on Basescan; recover the depositId from the DepositReceived log manually, then resume with order(depositId).`,
|
|
772
|
+
recovery: {
|
|
773
|
+
kind: "inspect-base-transaction",
|
|
774
|
+
transactionHash: txHash,
|
|
775
|
+
operation: "cashout"
|
|
776
|
+
}
|
|
749
777
|
}),
|
|
750
778
|
signerRequired: (verb) => new CashError({
|
|
751
779
|
code: "SIGNER_REQUIRED",
|
|
@@ -783,6 +811,15 @@ var errors = {
|
|
|
783
811
|
},
|
|
784
812
|
{ cause }
|
|
785
813
|
),
|
|
814
|
+
transactionRejected: (verb, cause) => new CashError(
|
|
815
|
+
{
|
|
816
|
+
code: "TRANSACTION_REJECTED",
|
|
817
|
+
message: `The ${verb} wallet request was cancelled.`,
|
|
818
|
+
retryable: true,
|
|
819
|
+
remediation: `Retry the original Peer Cash action and approve the wallet request when you are ready.`
|
|
820
|
+
},
|
|
821
|
+
{ cause }
|
|
822
|
+
),
|
|
786
823
|
transactionSubmissionUnknown: (operation, cause, recovery) => new CashError(
|
|
787
824
|
{
|
|
788
825
|
code: "TRANSACTION_SUBMISSION_UNKNOWN",
|
|
@@ -826,6 +863,7 @@ var errors = {
|
|
|
826
863
|
};
|
|
827
864
|
function mapChainError(verb, err, context = {}) {
|
|
828
865
|
if (isCashError(err)) return err;
|
|
866
|
+
if (isUserRejectedError(err)) return errors.transactionRejected(verb, err);
|
|
829
867
|
const message = err instanceof Error ? err.message : String(err);
|
|
830
868
|
if (/\bpaused\b/i.test(message)) return errors.escrowPaused();
|
|
831
869
|
if (/exceeds balance|insufficient token balance/i.test(message)) {
|
|
@@ -836,6 +874,42 @@ function mapChainError(verb, err, context = {}) {
|
|
|
836
874
|
}
|
|
837
875
|
return errors.chainCallFailed(verb, err);
|
|
838
876
|
}
|
|
877
|
+
function hasUserRejectionText(value) {
|
|
878
|
+
const normalized = value.toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
879
|
+
return normalized.includes("userrejected") || normalized.includes("userdenied") || normalized.includes("requestrejected") || normalized.includes("rejectedrequest") || /(^|[^a-z0-9])action[_ -]?rejected(?:error)?($|[^a-z0-9])/i.test(value) || normalized === "actionrejected" || normalized === "actionrejectederror";
|
|
880
|
+
}
|
|
881
|
+
function isUserRejectedError(value) {
|
|
882
|
+
const seen = /* @__PURE__ */ new Set();
|
|
883
|
+
const text = [];
|
|
884
|
+
let current = value;
|
|
885
|
+
while (current !== null && !seen.has(current)) {
|
|
886
|
+
seen.add(current);
|
|
887
|
+
if (current === -32003 || current === "-32003") return false;
|
|
888
|
+
if (current === 4001 || current === "4001" || current === 5e3 || current === "5000") {
|
|
889
|
+
return true;
|
|
890
|
+
}
|
|
891
|
+
if (typeof current === "string") {
|
|
892
|
+
text.push(current);
|
|
893
|
+
break;
|
|
894
|
+
}
|
|
895
|
+
if (typeof current !== "object" && typeof current !== "function") break;
|
|
896
|
+
const detail = current;
|
|
897
|
+
if (detail.code === -32003 || detail.code === "-32003" || detail.name === "TransactionRejectedRpcError") {
|
|
898
|
+
return false;
|
|
899
|
+
}
|
|
900
|
+
if (detail.code === 4001 || detail.code === "4001" || detail.code === 5e3 || detail.code === "5000" || detail.code === "ACTION_REJECTED" || detail.name === "UserRejectedRequestError") {
|
|
901
|
+
return true;
|
|
902
|
+
}
|
|
903
|
+
text.push(
|
|
904
|
+
...[detail.name, detail.message, detail.code].filter(
|
|
905
|
+
(part) => typeof part === "string"
|
|
906
|
+
)
|
|
907
|
+
);
|
|
908
|
+
if (detail.cause === void 0) break;
|
|
909
|
+
current = detail.cause;
|
|
910
|
+
}
|
|
911
|
+
return text.some(hasUserRejectionText);
|
|
912
|
+
}
|
|
839
913
|
var FILL_STATS_WINDOW_SECONDS = 30 * 24 * 60 * 60;
|
|
840
914
|
var FILL_STATS_PAGE_LIMIT = 250;
|
|
841
915
|
function toUnixSeconds2(value) {
|
|
@@ -896,7 +970,7 @@ function computeFillStatsSample(deposits, nowSeconds, environment) {
|
|
|
896
970
|
}
|
|
897
971
|
const currency = normalizeCurrencyCode(intent.fiatCurrency);
|
|
898
972
|
if (!method || !currency) continue;
|
|
899
|
-
const pair = `${
|
|
973
|
+
const pair = `${method}:${currency}`;
|
|
900
974
|
fillCounts.set(pair, (fillCounts.get(pair) ?? 0) + 1);
|
|
901
975
|
if (createdAt === void 0 || createdAt < windowStart || fulfilledAt < createdAt) continue;
|
|
902
976
|
const previousPairFill = firstFillByPair.get(pair);
|
|
@@ -961,7 +1035,7 @@ async function readFillStatsSample(client, environment) {
|
|
|
961
1035
|
}
|
|
962
1036
|
function fillEtaFromSample(sample, input) {
|
|
963
1037
|
const currency = input.currency.toUpperCase();
|
|
964
|
-
const seconds = input.platform ? sample.stats[`${
|
|
1038
|
+
const seconds = input.platform ? sample.stats[`${input.platform}:${currency}`]?.medianFillSeconds : sample.medianFillSecondsByCurrency.get(currency);
|
|
965
1039
|
return {
|
|
966
1040
|
...seconds !== void 0 ? { seconds } : {},
|
|
967
1041
|
label: etaLabel(seconds)
|
|
@@ -1524,6 +1598,36 @@ async function readEstimate(publicClient, input, context = {}) {
|
|
|
1524
1598
|
return estimate;
|
|
1525
1599
|
}
|
|
1526
1600
|
|
|
1601
|
+
// src/client/payee.ts
|
|
1602
|
+
function normalizePaypalHandle(value) {
|
|
1603
|
+
const withoutProtocol = value.replace(/^https?:\/\//i, "").replace(/^www\./i, "");
|
|
1604
|
+
if (/^paypal\.me(?:[?#].*)?$/i.test(withoutProtocol)) return "";
|
|
1605
|
+
const withoutDomain = withoutProtocol.replace(/^paypal\.me\//i, "");
|
|
1606
|
+
const [pathWithoutQuery = ""] = withoutDomain.split(/[?#]/, 1);
|
|
1607
|
+
const [username = ""] = pathWithoutQuery.replace(/^\/+/, "").split("/", 1);
|
|
1608
|
+
return username.replace(/^@+/, "").trim().toLowerCase();
|
|
1609
|
+
}
|
|
1610
|
+
function normalizeCashPayee(platform, payee) {
|
|
1611
|
+
if (typeof payee !== "string") return payee;
|
|
1612
|
+
const trimmed = payee.trim();
|
|
1613
|
+
switch (platform) {
|
|
1614
|
+
case "venmo":
|
|
1615
|
+
return { offchainId: trimmed.replace(/^@+/, "") };
|
|
1616
|
+
case "cashapp":
|
|
1617
|
+
return { offchainId: trimmed.replace(/^\$+/, "") };
|
|
1618
|
+
case "chime":
|
|
1619
|
+
return { offchainId: trimmed.toLowerCase() };
|
|
1620
|
+
case "n26":
|
|
1621
|
+
return { offchainId: trimmed.replace(/\s/g, "") };
|
|
1622
|
+
case "paypal":
|
|
1623
|
+
return { offchainId: normalizePaypalHandle(trimmed) };
|
|
1624
|
+
case "zelle":
|
|
1625
|
+
return { offchainId: trimmed.toLowerCase() };
|
|
1626
|
+
default:
|
|
1627
|
+
return { offchainId: trimmed };
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1527
1631
|
// src/client/createCashClient.ts
|
|
1528
1632
|
var DEFAULT_RPC_URL = "https://mainnet.base.org";
|
|
1529
1633
|
var FILL_STATS_CACHE_MS = 15 * 60 * 1e3;
|
|
@@ -1565,7 +1669,7 @@ async function submitAndConfirm(client, verb, send) {
|
|
|
1565
1669
|
hash = await send();
|
|
1566
1670
|
} catch (err) {
|
|
1567
1671
|
const mapped = mapChainError(verb, err);
|
|
1568
|
-
if (isKnownPreBroadcastFailure(
|
|
1672
|
+
if (isKnownPreBroadcastFailure(mapped)) throw mapped;
|
|
1569
1673
|
throw errors.transactionSubmissionUnknown(verb, err, {
|
|
1570
1674
|
kind: "inspect-base-operation-submission",
|
|
1571
1675
|
operation: verb
|
|
@@ -1580,12 +1684,8 @@ async function submitAndConfirm(client, verb, send) {
|
|
|
1580
1684
|
if (receipt.status === "reverted") throw errors.transactionFailed(hash);
|
|
1581
1685
|
return hash;
|
|
1582
1686
|
}
|
|
1583
|
-
function isKnownPreBroadcastFailure(
|
|
1584
|
-
|
|
1585
|
-
return true;
|
|
1586
|
-
}
|
|
1587
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1588
|
-
return /user rejected|user denied|rejected request|action_rejected/i.test(message);
|
|
1687
|
+
function isKnownPreBroadcastFailure(mapped) {
|
|
1688
|
+
return mapped.code === "TRANSACTION_REJECTED" || mapped.code === "INSUFFICIENT_TOKEN_BALANCE" || mapped.code === "ALLOWANCE_NOT_VISIBLE" || mapped.code === "ESCROW_PAUSED";
|
|
1589
1689
|
}
|
|
1590
1690
|
function depositOrderOptions(deposit) {
|
|
1591
1691
|
const remaining = toBigIntOrUndefined(deposit.remainingDeposits);
|
|
@@ -1658,24 +1758,39 @@ function createCashClient(options) {
|
|
|
1658
1758
|
}
|
|
1659
1759
|
function validatePayout(input) {
|
|
1660
1760
|
const { receive } = input;
|
|
1661
|
-
const catalog = sdk.getPaymentMethodsCatalog(BASE_CHAIN_ID, environment);
|
|
1662
1761
|
const platform = buildCapabilities(environment).platforms.find(
|
|
1663
1762
|
(capability) => capability.platform === receive.platform
|
|
1664
1763
|
);
|
|
1665
1764
|
if (!platform) throw errors.unsupportedPlatform(receive.platform);
|
|
1666
|
-
if (
|
|
1667
|
-
throw errors.
|
|
1765
|
+
if (receive.currency === void 0 === (receive.currencies === void 0)) {
|
|
1766
|
+
throw errors.invalidPayoutCurrencies(
|
|
1767
|
+
receive.platform,
|
|
1768
|
+
"pass exactly one of currency or currencies"
|
|
1769
|
+
);
|
|
1668
1770
|
}
|
|
1669
|
-
|
|
1670
|
-
|
|
1771
|
+
const currencies = receive.currencies !== void 0 ? [...receive.currencies] : [receive.currency];
|
|
1772
|
+
if (currencies.length === 0) {
|
|
1773
|
+
throw errors.invalidPayoutCurrencies(receive.platform, "at least one currency is required");
|
|
1774
|
+
}
|
|
1775
|
+
if (new Set(currencies).size !== currencies.length) {
|
|
1776
|
+
throw errors.invalidPayoutCurrencies(receive.platform, "currencies must be unique");
|
|
1777
|
+
}
|
|
1778
|
+
for (const currency of currencies) {
|
|
1779
|
+
if (!isMarketRateSupported(currency)) {
|
|
1780
|
+
throw errors.oracleUnsupportedCurrency(currency);
|
|
1781
|
+
}
|
|
1782
|
+
if (!platform.currencies.includes(currency)) {
|
|
1783
|
+
throw errors.unsupportedPlatformCurrency(receive.platform, currency);
|
|
1784
|
+
}
|
|
1671
1785
|
}
|
|
1672
|
-
const paymentMethods = paymentMethodsForPlatform(receive.platform, catalog);
|
|
1673
1786
|
return {
|
|
1674
|
-
payouts:
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1787
|
+
payouts: [
|
|
1788
|
+
{
|
|
1789
|
+
processorName: receive.platform,
|
|
1790
|
+
...currencies.length === 1 ? { currency: currencies[0] } : { currencies },
|
|
1791
|
+
payeeData: normalizeCashPayee(receive.platform, receive.payee)
|
|
1792
|
+
}
|
|
1793
|
+
]
|
|
1679
1794
|
};
|
|
1680
1795
|
}
|
|
1681
1796
|
function validateDepositInput(amount, input, payoutInput = validatePayout(input)) {
|
|
@@ -1692,6 +1807,14 @@ function createCashClient(options) {
|
|
|
1692
1807
|
...range ? { intentAmountRange: range } : {}
|
|
1693
1808
|
};
|
|
1694
1809
|
}
|
|
1810
|
+
function isCashPayoutSet(payouts) {
|
|
1811
|
+
const first = payouts[0];
|
|
1812
|
+
return Boolean(
|
|
1813
|
+
first && payouts.every(
|
|
1814
|
+
(payout) => payout.platformHash.toLowerCase() === first.platformHash.toLowerCase() && payout.payeeHash.toLowerCase() === first.payeeHash.toLowerCase() && payout.pricing.marketRate && payout.pricing.spreadBps === 0
|
|
1815
|
+
)
|
|
1816
|
+
);
|
|
1817
|
+
}
|
|
1695
1818
|
async function buildDepositParams(client, depositInput) {
|
|
1696
1819
|
try {
|
|
1697
1820
|
return await prepareCashDepositParams(client, depositInput);
|
|
@@ -1746,9 +1869,10 @@ function createCashClient(options) {
|
|
|
1746
1869
|
deposit.currencies ?? [],
|
|
1747
1870
|
sdk.getPaymentMethodsCatalog(BASE_CHAIN_ID, environment)
|
|
1748
1871
|
);
|
|
1872
|
+
if (!isCashPayoutSet(payouts)) throw errors.orderNotFound(compositeId);
|
|
1749
1873
|
return deriveCashOrder(compositeId, deposit.intents ?? [], {
|
|
1750
1874
|
...depositOrderOptions(deposit),
|
|
1751
|
-
|
|
1875
|
+
payouts
|
|
1752
1876
|
});
|
|
1753
1877
|
}
|
|
1754
1878
|
function escrowContext(depositId) {
|
|
@@ -1962,9 +2086,9 @@ function createCashClient(options) {
|
|
|
1962
2086
|
}
|
|
1963
2087
|
},
|
|
1964
2088
|
async cashout(input, opts) {
|
|
2089
|
+
const payoutInput = validatePayout(input);
|
|
1965
2090
|
const client = await signingClient("cashout", opts);
|
|
1966
2091
|
const owner = opts.signer.account.address;
|
|
1967
|
-
const payoutInput = validatePayout(input);
|
|
1968
2092
|
let sourceResult;
|
|
1969
2093
|
let cashoutAmount = input.amount;
|
|
1970
2094
|
if (input.source) {
|
|
@@ -2040,7 +2164,7 @@ function createCashClient(options) {
|
|
|
2040
2164
|
const mapped = mapChainError("createDeposit", err, {
|
|
2041
2165
|
requiredAmount: depositInput2.amount
|
|
2042
2166
|
});
|
|
2043
|
-
if (isKnownPreBroadcastFailure(
|
|
2167
|
+
if (isKnownPreBroadcastFailure(mapped)) {
|
|
2044
2168
|
throw errors.sourceRouteCompletedCashoutFailed(routedSource, mapped);
|
|
2045
2169
|
}
|
|
2046
2170
|
throw errors.sourceCashoutSubmissionUnknown(routedSource, owner, mapped);
|
|
@@ -2096,7 +2220,7 @@ function createCashClient(options) {
|
|
|
2096
2220
|
const mapped = mapChainError("createDeposit", err, {
|
|
2097
2221
|
requiredAmount: depositInput.amount
|
|
2098
2222
|
});
|
|
2099
|
-
if (isKnownPreBroadcastFailure(
|
|
2223
|
+
if (isKnownPreBroadcastFailure(mapped)) throw mapped;
|
|
2100
2224
|
throw errors.transactionSubmissionUnknown("cashout", err, {
|
|
2101
2225
|
kind: "inspect-base-cashout-submission",
|
|
2102
2226
|
amount: depositInput.amount.toString(),
|
|
@@ -2164,6 +2288,32 @@ function createCashClient(options) {
|
|
|
2164
2288
|
register: { hashedOnchainIds }
|
|
2165
2289
|
};
|
|
2166
2290
|
},
|
|
2291
|
+
finalizePreparedCashout(receipt) {
|
|
2292
|
+
if (receipt.status === "reverted") {
|
|
2293
|
+
throw errors.transactionFailed(receipt.transactionHash);
|
|
2294
|
+
}
|
|
2295
|
+
const abi = readClient.escrowV2Abi ?? readClient.escrowAbi;
|
|
2296
|
+
const expectedEscrowAddress = readClient.escrowV2Address ?? readClient.escrowAddress;
|
|
2297
|
+
const resolved = resolveCashDepositId({
|
|
2298
|
+
logs: receipt.logs,
|
|
2299
|
+
abi,
|
|
2300
|
+
expectedEscrowAddress,
|
|
2301
|
+
expectedToken: BASE_USDC_ADDRESS
|
|
2302
|
+
});
|
|
2303
|
+
if (!resolved || resolved.amount === void 0) {
|
|
2304
|
+
throw errors.depositResolutionFailed(receipt.transactionHash);
|
|
2305
|
+
}
|
|
2306
|
+
return {
|
|
2307
|
+
depositId: resolved.compositeId,
|
|
2308
|
+
txHash: receipt.transactionHash,
|
|
2309
|
+
escrowAddress: resolved.escrowAddress,
|
|
2310
|
+
onchainDepositId: resolved.onchainDepositId,
|
|
2311
|
+
order: deriveCashOrder(resolved.compositeId, [], {
|
|
2312
|
+
remainingAmount: resolved.amount,
|
|
2313
|
+
status: "ACTIVE"
|
|
2314
|
+
})
|
|
2315
|
+
};
|
|
2316
|
+
},
|
|
2167
2317
|
async order(depositId) {
|
|
2168
2318
|
return fetchOrder(depositId);
|
|
2169
2319
|
},
|
|
@@ -2195,7 +2345,7 @@ function createCashClient(options) {
|
|
|
2195
2345
|
deposit.currencies ?? [],
|
|
2196
2346
|
catalog
|
|
2197
2347
|
);
|
|
2198
|
-
if (
|
|
2348
|
+
if (!isCashPayoutSet(payouts)) {
|
|
2199
2349
|
return [];
|
|
2200
2350
|
}
|
|
2201
2351
|
return [
|
|
@@ -2464,7 +2614,7 @@ var cashPayoutPricingJsonSchema = zod.z.object({
|
|
|
2464
2614
|
marketRate: zod.z.boolean()
|
|
2465
2615
|
});
|
|
2466
2616
|
var cashPayoutInfoJsonSchema = zod.z.object({
|
|
2467
|
-
platform: zod.z.string()
|
|
2617
|
+
platform: zod.z.string(),
|
|
2468
2618
|
platformHash: zod.z.string(),
|
|
2469
2619
|
currency: zod.z.string().optional(),
|
|
2470
2620
|
currencyHash: zod.z.string().optional(),
|
|
@@ -2680,6 +2830,7 @@ var CASH_ERROR_CODES = defineCashErrorCodes([
|
|
|
2680
2830
|
"UNSUPPORTED_PLATFORM_CURRENCY",
|
|
2681
2831
|
"AMOUNT_BELOW_MINIMUM",
|
|
2682
2832
|
"INVALID_INTENT_AMOUNT_RANGE",
|
|
2833
|
+
"INVALID_PAYOUT_CURRENCIES",
|
|
2683
2834
|
"ACTIVE_INTENT_BLOCKS_WITHDRAWAL",
|
|
2684
2835
|
"NOTHING_TO_WITHDRAW",
|
|
2685
2836
|
"INSUFFICIENT_AVAILABLE_FUNDS",
|
|
@@ -2708,6 +2859,7 @@ var CASH_ERROR_CODES = defineCashErrorCodes([
|
|
|
2708
2859
|
"SIGNER_CHAIN_MISMATCH",
|
|
2709
2860
|
"SIGNER_CHAIN_UNAVAILABLE",
|
|
2710
2861
|
"WATCH_TIMEOUT",
|
|
2862
|
+
"TRANSACTION_REJECTED",
|
|
2711
2863
|
"TRANSACTION_FAILED",
|
|
2712
2864
|
"TRANSACTION_SUBMISSION_UNKNOWN",
|
|
2713
2865
|
"TRANSACTION_STATUS_UNKNOWN"
|
|
@@ -3224,7 +3376,9 @@ exports.intentStatusSchema = intentStatusSchema;
|
|
|
3224
3376
|
exports.isCashError = isCashError;
|
|
3225
3377
|
exports.isFillLive = isFillLive;
|
|
3226
3378
|
exports.isMarketRateSupported = isMarketRateSupported;
|
|
3379
|
+
exports.isUserRejectedError = isUserRejectedError;
|
|
3227
3380
|
exports.nonNegativeBigintString = nonNegativeBigintString;
|
|
3381
|
+
exports.normalizeCashPayee = normalizeCashPayee;
|
|
3228
3382
|
exports.orderFromJson = orderFromJson;
|
|
3229
3383
|
exports.orderToJson = orderToJson;
|
|
3230
3384
|
exports.parseCompositeDepositId = parseCompositeDepositId;
|