@zkp2p/cash 0.5.3 → 0.5.4-rc.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 +9 -0
- package/README.md +9 -0
- package/dist/{createCashClient-DimDWJU4.d.cts → createCashClient-ECL3HN5H.d.cts} +6 -2
- package/dist/{createCashClient-DimDWJU4.d.ts → createCashClient-ECL3HN5H.d.ts} +6 -2
- package/dist/index.cjs +63 -32
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +64 -32
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/tools.cjs +1 -1
- package/dist/tools.js +1 -1
- package/examples/onchain-demo/package.json +1 -1
- package/examples/upi-staging-cashout.ts +1 -0
- package/llms.txt +10 -0
- package/package.json +2 -2
- package/skills/peer-cash-integration/SKILL.md +18 -0
package/AGENTS.md
CHANGED
|
@@ -334,3 +334,12 @@ wallet. Never wait on a buyer - buyer-side is out of your scope:
|
|
|
334
334
|
|
|
335
335
|
If step 4 ever fails with funds stuck, stop and escalate - do not retry
|
|
336
336
|
blindly.
|
|
337
|
+
|
|
338
|
+
UPI/INR reads the live Chainlink Polygon mainnet proxy
|
|
339
|
+
`0xDA0F8Df6F5dB15b346f4B8D1156722027E194E60` (chain 137), inverts
|
|
340
|
+
USD per INR, and rounds the creation-time maker floor up. Configure its
|
|
341
|
+
read-only RPC with `upiCreationRateRpcUrl` or `upiCreationRateTransport`.
|
|
342
|
+
Alipay/CNY retains the Ethereum registry and `creationRateRpcUrl` /
|
|
343
|
+
`creationRateTransport`. UPI rejects the wrong chain, invalid rounds, and
|
|
344
|
+
observations older than 24 hours; market closures do not bypass freshness.
|
|
345
|
+
This does not change the staging-only UPI opt-in gate.
|
package/README.md
CHANGED
|
@@ -498,3 +498,12 @@ the package, not the contributor entry point.
|
|
|
498
498
|
## License
|
|
499
499
|
|
|
500
500
|
MIT
|
|
501
|
+
|
|
502
|
+
UPI/INR reads the live Chainlink Polygon mainnet proxy
|
|
503
|
+
`0xDA0F8Df6F5dB15b346f4B8D1156722027E194E60` (chain 137), inverts
|
|
504
|
+
USD per INR, and rounds the creation-time maker floor up. Configure its
|
|
505
|
+
read-only RPC with `upiCreationRateRpcUrl` or `upiCreationRateTransport`.
|
|
506
|
+
Alipay/CNY retains the Ethereum registry and `creationRateRpcUrl` /
|
|
507
|
+
`creationRateTransport`. UPI rejects the wrong chain, invalid rounds, and
|
|
508
|
+
observations older than 24 hours; market closures do not bypass freshness.
|
|
509
|
+
This does not change the staging-only UPI opt-in gate.
|
|
@@ -585,8 +585,8 @@ interface CashFillEta {
|
|
|
585
585
|
* idempotent, cacheable.
|
|
586
586
|
*
|
|
587
587
|
* Existing corridors read the same Chainlink feed the protocol uses when an
|
|
588
|
-
* intent is signaled. Creation-rate corridors read Chainlink
|
|
589
|
-
*
|
|
588
|
+
* intent is signaled. Creation-rate corridors read Chainlink on Ethereum (CNY)
|
|
589
|
+
* or Polygon (INR), fixing the fresh snapshot when preparing the deposit.
|
|
590
590
|
*/
|
|
591
591
|
|
|
592
592
|
interface EstimateInput {
|
|
@@ -692,6 +692,10 @@ interface CashClientOptions {
|
|
|
692
692
|
creationRateTransport?: Transport;
|
|
693
693
|
/** Convenience alternative to `creationRateTransport`. */
|
|
694
694
|
creationRateRpcUrl?: string;
|
|
695
|
+
/** Polygon transport used only to snapshot UPI/INR's creation-time rate. */
|
|
696
|
+
upiCreationRateTransport?: Transport;
|
|
697
|
+
/** Convenience alternative to `upiCreationRateTransport`; requires Polygon mainnet. */
|
|
698
|
+
upiCreationRateRpcUrl?: string;
|
|
695
699
|
/** Relay API configuration for source assets outside Base USDC. */
|
|
696
700
|
relay?: RelayOptions;
|
|
697
701
|
/** NEAR Intents 1Click configuration for externally funded source routes. */
|
|
@@ -585,8 +585,8 @@ interface CashFillEta {
|
|
|
585
585
|
* idempotent, cacheable.
|
|
586
586
|
*
|
|
587
587
|
* Existing corridors read the same Chainlink feed the protocol uses when an
|
|
588
|
-
* intent is signaled. Creation-rate corridors read Chainlink
|
|
589
|
-
*
|
|
588
|
+
* intent is signaled. Creation-rate corridors read Chainlink on Ethereum (CNY)
|
|
589
|
+
* or Polygon (INR), fixing the fresh snapshot when preparing the deposit.
|
|
590
590
|
*/
|
|
591
591
|
|
|
592
592
|
interface EstimateInput {
|
|
@@ -692,6 +692,10 @@ interface CashClientOptions {
|
|
|
692
692
|
creationRateTransport?: Transport;
|
|
693
693
|
/** Convenience alternative to `creationRateTransport`. */
|
|
694
694
|
creationRateRpcUrl?: string;
|
|
695
|
+
/** Polygon transport used only to snapshot UPI/INR's creation-time rate. */
|
|
696
|
+
upiCreationRateTransport?: Transport;
|
|
697
|
+
/** Convenience alternative to `upiCreationRateTransport`; requires Polygon mainnet. */
|
|
698
|
+
upiCreationRateRpcUrl?: string;
|
|
695
699
|
/** Relay API configuration for source assets outside Base USDC. */
|
|
696
700
|
relay?: RelayOptions;
|
|
697
701
|
/** NEAR Intents 1Click configuration for externally funded source routes. */
|
package/dist/index.cjs
CHANGED
|
@@ -29,12 +29,14 @@ var CASH_ORDER_STATUSES = [
|
|
|
29
29
|
];
|
|
30
30
|
var CASH_ORDER_POLL_INTERVAL_MS = 5e3;
|
|
31
31
|
var CASH_RETAIN_ON_EMPTY = false;
|
|
32
|
-
|
|
33
|
-
// src/client/creationRate.ts
|
|
34
32
|
var CHAINLINK_FEED_REGISTRY = "0x47Fb2585D2C56Fe188D0E6ec628a38b74fCeeeDf";
|
|
35
33
|
var CNY_DENOMINATION = "0x000000000000000000000000000000000000009c";
|
|
36
|
-
var INR_DENOMINATION = "0x0000000000000000000000000000000000000164";
|
|
37
34
|
var USD_DENOMINATION = "0x0000000000000000000000000000000000000348";
|
|
35
|
+
var POLYGON_INR_USD_FEED = "0xDA0F8Df6F5dB15b346f4B8D1156722027E194E60";
|
|
36
|
+
var DIRECT_FEED_ABI = viem.parseAbi([
|
|
37
|
+
"function decimals() view returns (uint8)",
|
|
38
|
+
"function latestRoundData() view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)"
|
|
39
|
+
]);
|
|
38
40
|
var FEED_REGISTRY_ABI = [
|
|
39
41
|
{
|
|
40
42
|
name: "decimals",
|
|
@@ -73,48 +75,67 @@ function getCreationRateDenomination(platform, currency) {
|
|
|
73
75
|
if (platform.toLowerCase() === "alipay" && currency.toUpperCase() === "CNY") {
|
|
74
76
|
return CNY_DENOMINATION;
|
|
75
77
|
}
|
|
76
|
-
if (platform.toLowerCase() === "upi" && currency.toUpperCase() === "INR") {
|
|
77
|
-
return INR_DENOMINATION;
|
|
78
|
-
}
|
|
79
78
|
throw new Error(`No creation-time rate source for ${platform}/${currency}`);
|
|
80
79
|
}
|
|
81
80
|
function divideRoundUp(numerator, denominator) {
|
|
82
81
|
return (numerator + denominator - 1n) / denominator;
|
|
83
82
|
}
|
|
84
83
|
async function readCashCreationRate(publicClient, platform, currency, nowSeconds = Math.floor(Date.now() / 1e3)) {
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
publicClient.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
84
|
+
const isUpi = platform.toLowerCase() === "upi" && currency.toUpperCase() === "INR";
|
|
85
|
+
const pair = `${currency.toUpperCase()}/USD`;
|
|
86
|
+
let decimals;
|
|
87
|
+
let round;
|
|
88
|
+
if (isUpi) {
|
|
89
|
+
if (await publicClient.getChainId() !== 137) {
|
|
90
|
+
throw new Error("Chainlink INR/USD requires Polygon mainnet (137)");
|
|
91
|
+
}
|
|
92
|
+
[decimals, round] = await Promise.all([
|
|
93
|
+
publicClient.readContract({
|
|
94
|
+
address: POLYGON_INR_USD_FEED,
|
|
95
|
+
abi: DIRECT_FEED_ABI,
|
|
96
|
+
functionName: "decimals"
|
|
97
|
+
}),
|
|
98
|
+
publicClient.readContract({
|
|
99
|
+
address: POLYGON_INR_USD_FEED,
|
|
100
|
+
abi: DIRECT_FEED_ABI,
|
|
101
|
+
functionName: "latestRoundData"
|
|
102
|
+
})
|
|
103
|
+
]);
|
|
104
|
+
} else {
|
|
105
|
+
const args = [
|
|
106
|
+
getCreationRateDenomination(platform, currency),
|
|
107
|
+
USD_DENOMINATION
|
|
108
|
+
];
|
|
109
|
+
[decimals, round] = await Promise.all([
|
|
110
|
+
publicClient.readContract({
|
|
111
|
+
address: CHAINLINK_FEED_REGISTRY,
|
|
112
|
+
abi: FEED_REGISTRY_ABI,
|
|
113
|
+
functionName: "decimals",
|
|
114
|
+
args
|
|
115
|
+
}),
|
|
116
|
+
publicClient.readContract({
|
|
117
|
+
address: CHAINLINK_FEED_REGISTRY,
|
|
118
|
+
abi: FEED_REGISTRY_ABI,
|
|
119
|
+
functionName: "latestRoundData",
|
|
120
|
+
args
|
|
121
|
+
})
|
|
122
|
+
]);
|
|
123
|
+
}
|
|
103
124
|
const [roundId, answer, , updatedAtRaw, answeredInRound] = round;
|
|
104
125
|
if (answer <= 0n || updatedAtRaw <= 0n || answeredInRound < roundId) {
|
|
105
|
-
throw new Error(
|
|
126
|
+
throw new Error(`Chainlink ${pair} returned an invalid round`);
|
|
106
127
|
}
|
|
107
128
|
const updatedAt = Number(updatedAtRaw);
|
|
108
129
|
if (!Number.isSafeInteger(updatedAt) || updatedAt > nowSeconds) {
|
|
109
|
-
throw new Error(
|
|
130
|
+
throw new Error(`Chainlink ${pair} returned an invalid timestamp`);
|
|
110
131
|
}
|
|
111
132
|
if (nowSeconds - updatedAt > CREATION_RATE_MAX_STALENESS_SECONDS) {
|
|
112
|
-
throw new Error(
|
|
133
|
+
throw new Error(`Chainlink ${pair} rate is stale`);
|
|
113
134
|
}
|
|
114
135
|
const rate1e18 = divideRoundUp(10n ** (BigInt(decimals) + 18n), answer);
|
|
115
136
|
const rate = Number(rate1e18) / 1e18;
|
|
116
137
|
if (!Number.isFinite(rate) || rate <= 0) {
|
|
117
|
-
throw new Error(
|
|
138
|
+
throw new Error(`Chainlink ${pair} produced an invalid creation rate`);
|
|
118
139
|
}
|
|
119
140
|
return { rate1e18, rate, updatedAt };
|
|
120
141
|
}
|
|
@@ -1799,10 +1820,11 @@ async function readEstimate(publicClient, input, context = {}) {
|
|
|
1799
1820
|
let rate;
|
|
1800
1821
|
let oracleUpdatedAt;
|
|
1801
1822
|
if (usesCreationRate) {
|
|
1802
|
-
|
|
1823
|
+
const rateClient = creationRatePlatform.toLowerCase() === "upi" ? context.upiCreationRateClient : context.creationRateClient;
|
|
1824
|
+
if (!rateClient) throw errors.oracleUnsupportedCurrency(currency);
|
|
1803
1825
|
try {
|
|
1804
1826
|
const snapshot = await readCashCreationRate(
|
|
1805
|
-
|
|
1827
|
+
rateClient,
|
|
1806
1828
|
creationRatePlatform,
|
|
1807
1829
|
currency,
|
|
1808
1830
|
asOf
|
|
@@ -2830,7 +2852,7 @@ function createCashAttributionReader(options) {
|
|
|
2830
2852
|
// src/client/classify.ts
|
|
2831
2853
|
function isCashPayoutSet(payouts, attributedToCash = false) {
|
|
2832
2854
|
return payouts.length > 0 && payouts.every(
|
|
2833
|
-
(payout) => payout.pricing.marketRate && payout.pricing.spreadBps === 0 || attributedToCash && payout.platform
|
|
2855
|
+
(payout) => payout.pricing.marketRate && payout.pricing.spreadBps === 0 || attributedToCash && isCreationRateCorridor(payout.platform, payout.currency ?? "") && payout.pricing.fixedAtCreation === true && (payout.pricing.fixedRate ?? 0) > 0
|
|
2834
2856
|
);
|
|
2835
2857
|
}
|
|
2836
2858
|
|
|
@@ -2934,6 +2956,10 @@ function createCashClient(options) {
|
|
|
2934
2956
|
chain: chains.mainnet,
|
|
2935
2957
|
transport: creationRateTransport
|
|
2936
2958
|
});
|
|
2959
|
+
const upiCreationRateClient = viem.createPublicClient({
|
|
2960
|
+
chain: chains.polygon,
|
|
2961
|
+
transport: options.upiCreationRateTransport ?? viem.http(options.upiCreationRateRpcUrl)
|
|
2962
|
+
});
|
|
2937
2963
|
const readCashAttribution = createCashAttributionReader({
|
|
2938
2964
|
environment,
|
|
2939
2965
|
...options.indexerUrl ? { indexerUrl: options.indexerUrl } : {},
|
|
@@ -3068,7 +3094,11 @@ function createCashClient(options) {
|
|
|
3068
3094
|
throw new Error(`No creation-time rate reader for ${platform}/${currency}`);
|
|
3069
3095
|
}
|
|
3070
3096
|
try {
|
|
3071
|
-
return await readCashCreationRate(
|
|
3097
|
+
return await readCashCreationRate(
|
|
3098
|
+
platform.toLowerCase() === "upi" ? upiCreationRateClient : creationRateClient,
|
|
3099
|
+
platform,
|
|
3100
|
+
currency
|
|
3101
|
+
);
|
|
3072
3102
|
} catch (err) {
|
|
3073
3103
|
throw errors.oracleReadFailed(currency, err);
|
|
3074
3104
|
}
|
|
@@ -3446,6 +3476,7 @@ function createCashClient(options) {
|
|
|
3446
3476
|
...estimateOptions?.includeEta !== void 0 ? { includeEta: estimateOptions.includeEta } : {},
|
|
3447
3477
|
etaReader: async (etaInput) => fillEtaFromSample(await getFillStatsSample(), etaInput),
|
|
3448
3478
|
creationRateClient,
|
|
3479
|
+
upiCreationRateClient,
|
|
3449
3480
|
...options.relay ? { relay: options.relay } : {}
|
|
3450
3481
|
});
|
|
3451
3482
|
},
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as CashPayoutInfo, I as IntentEntity, a as CashBuyerProfile, b as CashDepositInput, c as CashCatalogFeatures, d as CreateDepositParamsArg, e as CashOrder, f as CashFill, g as CashCapabilities, h as CashoutResult, i as CashEstimate, j as CashFillStats, N as NearIntentsSourceCapabilities, k as NearIntentsDepositInput, l as NearIntentsQuote, m as NearIntentsQuoteInput, n as NearIntentsStatus, o as NearIntentsStatusInput, P as PrepareResult, p as CashPreparedStep, R as RelayExecutionResult, q as RelayQuote, r as RelayStatus, s as CashSourceCapabilities, T as TopUpResult, W as WithdrawResult } from './createCashClient-
|
|
2
|
-
export { B as BASE_CHAIN_ID, t as BASE_USDC_ADDRESS, u as CASH_ATTRIBUTION_CODE, v as CASH_ORDER_POLL_INTERVAL_MS, w as CASH_ORDER_STATUSES, x as CASH_REFERRAL_ATTRIBUTION_PREFIX, y as CASH_RETAIN_ON_EMPTY, z as CashAsset, A as CashChain, D as CashClient, E as CashClientOptions, F as CashCorridorPricing, G as CashFeatureFlags, H as CashFillEta, J as CashLeg, K as CashMultiCurrencyLeg, L as CashNextAction, M as CashOrderState, O as CashPairFillStats, Q as CashPayeeInput, S as CashPayout, U as CashPayoutPricing, V as CashPlatformCapability, X as CashPreparedStepKind, Y as CashReceiveLeg, Z as CashoutInput, _ as CashoutOptions, $ as CuratorPayeeDataInput, a0 as EstimateInput, a1 as EstimateOptions, a2 as MARKET_SPREAD_BPS, a3 as MIN_CASHOUT_AMOUNT, a4 as NEAR_INTENTS_API_URL, a5 as NEAR_INTENTS_BASE_USDC_ASSET_ID, a6 as NEAR_INTENTS_DEFAULT_SLIPPAGE_BPS, a7 as NEAR_INTENTS_STATUSES, a8 as NearIntentsClient, a9 as NearIntentsOptions, aa as NearIntentsQuoteRequest, ab as NearIntentsStatusCode, ac as NearIntentsToken, ad as NearIntentsTradeType, ae as NearIntentsTransaction, af as ORACLE_MIN_CONVERSION_RATE_SENTINEL, ag as OrdersOptions, ah as PreparedCashoutReceipt, ai as RECOMMENDED_MIN_CASHOUT_AMOUNT, aj as RelayOptions, ak as RelayQuoteInput, al as RelaySourceInput, am as RelayTransaction, an as SignerOptions, ao as USDC_DECIMALS, ap as WatchOptions, aq as WithdrawOptions, ar as buildCapabilities, as as createCashClient, at as createNearIntentsClient, au as normalizeCashPayee, av as quoteNearIntentsToBaseUsdc, aw as readNearIntentsSourceCapabilities, ax as readNearIntentsStatus, ay as submitNearIntentsDeposit, az as toCashReferralAttributionCode } from './createCashClient-
|
|
1
|
+
import { C as CashPayoutInfo, I as IntentEntity, a as CashBuyerProfile, b as CashDepositInput, c as CashCatalogFeatures, d as CreateDepositParamsArg, e as CashOrder, f as CashFill, g as CashCapabilities, h as CashoutResult, i as CashEstimate, j as CashFillStats, N as NearIntentsSourceCapabilities, k as NearIntentsDepositInput, l as NearIntentsQuote, m as NearIntentsQuoteInput, n as NearIntentsStatus, o as NearIntentsStatusInput, P as PrepareResult, p as CashPreparedStep, R as RelayExecutionResult, q as RelayQuote, r as RelayStatus, s as CashSourceCapabilities, T as TopUpResult, W as WithdrawResult } from './createCashClient-ECL3HN5H.cjs';
|
|
2
|
+
export { B as BASE_CHAIN_ID, t as BASE_USDC_ADDRESS, u as CASH_ATTRIBUTION_CODE, v as CASH_ORDER_POLL_INTERVAL_MS, w as CASH_ORDER_STATUSES, x as CASH_REFERRAL_ATTRIBUTION_PREFIX, y as CASH_RETAIN_ON_EMPTY, z as CashAsset, A as CashChain, D as CashClient, E as CashClientOptions, F as CashCorridorPricing, G as CashFeatureFlags, H as CashFillEta, J as CashLeg, K as CashMultiCurrencyLeg, L as CashNextAction, M as CashOrderState, O as CashPairFillStats, Q as CashPayeeInput, S as CashPayout, U as CashPayoutPricing, V as CashPlatformCapability, X as CashPreparedStepKind, Y as CashReceiveLeg, Z as CashoutInput, _ as CashoutOptions, $ as CuratorPayeeDataInput, a0 as EstimateInput, a1 as EstimateOptions, a2 as MARKET_SPREAD_BPS, a3 as MIN_CASHOUT_AMOUNT, a4 as NEAR_INTENTS_API_URL, a5 as NEAR_INTENTS_BASE_USDC_ASSET_ID, a6 as NEAR_INTENTS_DEFAULT_SLIPPAGE_BPS, a7 as NEAR_INTENTS_STATUSES, a8 as NearIntentsClient, a9 as NearIntentsOptions, aa as NearIntentsQuoteRequest, ab as NearIntentsStatusCode, ac as NearIntentsToken, ad as NearIntentsTradeType, ae as NearIntentsTransaction, af as ORACLE_MIN_CONVERSION_RATE_SENTINEL, ag as OrdersOptions, ah as PreparedCashoutReceipt, ai as RECOMMENDED_MIN_CASHOUT_AMOUNT, aj as RelayOptions, ak as RelayQuoteInput, al as RelaySourceInput, am as RelayTransaction, an as SignerOptions, ao as USDC_DECIMALS, ap as WatchOptions, aq as WithdrawOptions, ar as buildCapabilities, as as createCashClient, at as createNearIntentsClient, au as normalizeCashPayee, av as quoteNearIntentsToBaseUsdc, aw as readNearIntentsSourceCapabilities, ax as readNearIntentsStatus, ay as submitNearIntentsDeposit, az as toCashReferralAttributionCode } from './createCashClient-ECL3HN5H.cjs';
|
|
3
3
|
import { PublicClient, Log, Abi } from 'viem';
|
|
4
4
|
import { PaymentMethodCatalog, CurrencyType, OracleAdapterOverrides, OnchainCurrency, Zkp2pClient, PreparedTransaction } from '@zkp2p/sdk';
|
|
5
5
|
export { CurrencyType, PreparedTransaction, RuntimeEnv } from '@zkp2p/sdk';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as CashPayoutInfo, I as IntentEntity, a as CashBuyerProfile, b as CashDepositInput, c as CashCatalogFeatures, d as CreateDepositParamsArg, e as CashOrder, f as CashFill, g as CashCapabilities, h as CashoutResult, i as CashEstimate, j as CashFillStats, N as NearIntentsSourceCapabilities, k as NearIntentsDepositInput, l as NearIntentsQuote, m as NearIntentsQuoteInput, n as NearIntentsStatus, o as NearIntentsStatusInput, P as PrepareResult, p as CashPreparedStep, R as RelayExecutionResult, q as RelayQuote, r as RelayStatus, s as CashSourceCapabilities, T as TopUpResult, W as WithdrawResult } from './createCashClient-
|
|
2
|
-
export { B as BASE_CHAIN_ID, t as BASE_USDC_ADDRESS, u as CASH_ATTRIBUTION_CODE, v as CASH_ORDER_POLL_INTERVAL_MS, w as CASH_ORDER_STATUSES, x as CASH_REFERRAL_ATTRIBUTION_PREFIX, y as CASH_RETAIN_ON_EMPTY, z as CashAsset, A as CashChain, D as CashClient, E as CashClientOptions, F as CashCorridorPricing, G as CashFeatureFlags, H as CashFillEta, J as CashLeg, K as CashMultiCurrencyLeg, L as CashNextAction, M as CashOrderState, O as CashPairFillStats, Q as CashPayeeInput, S as CashPayout, U as CashPayoutPricing, V as CashPlatformCapability, X as CashPreparedStepKind, Y as CashReceiveLeg, Z as CashoutInput, _ as CashoutOptions, $ as CuratorPayeeDataInput, a0 as EstimateInput, a1 as EstimateOptions, a2 as MARKET_SPREAD_BPS, a3 as MIN_CASHOUT_AMOUNT, a4 as NEAR_INTENTS_API_URL, a5 as NEAR_INTENTS_BASE_USDC_ASSET_ID, a6 as NEAR_INTENTS_DEFAULT_SLIPPAGE_BPS, a7 as NEAR_INTENTS_STATUSES, a8 as NearIntentsClient, a9 as NearIntentsOptions, aa as NearIntentsQuoteRequest, ab as NearIntentsStatusCode, ac as NearIntentsToken, ad as NearIntentsTradeType, ae as NearIntentsTransaction, af as ORACLE_MIN_CONVERSION_RATE_SENTINEL, ag as OrdersOptions, ah as PreparedCashoutReceipt, ai as RECOMMENDED_MIN_CASHOUT_AMOUNT, aj as RelayOptions, ak as RelayQuoteInput, al as RelaySourceInput, am as RelayTransaction, an as SignerOptions, ao as USDC_DECIMALS, ap as WatchOptions, aq as WithdrawOptions, ar as buildCapabilities, as as createCashClient, at as createNearIntentsClient, au as normalizeCashPayee, av as quoteNearIntentsToBaseUsdc, aw as readNearIntentsSourceCapabilities, ax as readNearIntentsStatus, ay as submitNearIntentsDeposit, az as toCashReferralAttributionCode } from './createCashClient-
|
|
1
|
+
import { C as CashPayoutInfo, I as IntentEntity, a as CashBuyerProfile, b as CashDepositInput, c as CashCatalogFeatures, d as CreateDepositParamsArg, e as CashOrder, f as CashFill, g as CashCapabilities, h as CashoutResult, i as CashEstimate, j as CashFillStats, N as NearIntentsSourceCapabilities, k as NearIntentsDepositInput, l as NearIntentsQuote, m as NearIntentsQuoteInput, n as NearIntentsStatus, o as NearIntentsStatusInput, P as PrepareResult, p as CashPreparedStep, R as RelayExecutionResult, q as RelayQuote, r as RelayStatus, s as CashSourceCapabilities, T as TopUpResult, W as WithdrawResult } from './createCashClient-ECL3HN5H.js';
|
|
2
|
+
export { B as BASE_CHAIN_ID, t as BASE_USDC_ADDRESS, u as CASH_ATTRIBUTION_CODE, v as CASH_ORDER_POLL_INTERVAL_MS, w as CASH_ORDER_STATUSES, x as CASH_REFERRAL_ATTRIBUTION_PREFIX, y as CASH_RETAIN_ON_EMPTY, z as CashAsset, A as CashChain, D as CashClient, E as CashClientOptions, F as CashCorridorPricing, G as CashFeatureFlags, H as CashFillEta, J as CashLeg, K as CashMultiCurrencyLeg, L as CashNextAction, M as CashOrderState, O as CashPairFillStats, Q as CashPayeeInput, S as CashPayout, U as CashPayoutPricing, V as CashPlatformCapability, X as CashPreparedStepKind, Y as CashReceiveLeg, Z as CashoutInput, _ as CashoutOptions, $ as CuratorPayeeDataInput, a0 as EstimateInput, a1 as EstimateOptions, a2 as MARKET_SPREAD_BPS, a3 as MIN_CASHOUT_AMOUNT, a4 as NEAR_INTENTS_API_URL, a5 as NEAR_INTENTS_BASE_USDC_ASSET_ID, a6 as NEAR_INTENTS_DEFAULT_SLIPPAGE_BPS, a7 as NEAR_INTENTS_STATUSES, a8 as NearIntentsClient, a9 as NearIntentsOptions, aa as NearIntentsQuoteRequest, ab as NearIntentsStatusCode, ac as NearIntentsToken, ad as NearIntentsTradeType, ae as NearIntentsTransaction, af as ORACLE_MIN_CONVERSION_RATE_SENTINEL, ag as OrdersOptions, ah as PreparedCashoutReceipt, ai as RECOMMENDED_MIN_CASHOUT_AMOUNT, aj as RelayOptions, ak as RelayQuoteInput, al as RelaySourceInput, am as RelayTransaction, an as SignerOptions, ao as USDC_DECIMALS, ap as WatchOptions, aq as WithdrawOptions, ar as buildCapabilities, as as createCashClient, at as createNearIntentsClient, au as normalizeCashPayee, av as quoteNearIntentsToBaseUsdc, aw as readNearIntentsSourceCapabilities, ax as readNearIntentsStatus, ay as submitNearIntentsDeposit, az as toCashReferralAttributionCode } from './createCashClient-ECL3HN5H.js';
|
|
3
3
|
import { PublicClient, Log, Abi } from 'viem';
|
|
4
4
|
import { PaymentMethodCatalog, CurrencyType, OracleAdapterOverrides, OnchainCurrency, Zkp2pClient, PreparedTransaction } from '@zkp2p/sdk';
|
|
5
5
|
export { CurrencyType, PreparedTransaction, RuntimeEnv } from '@zkp2p/sdk';
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import { MARKET_SPREAD_BPS, ORACLE_MIN_CONVERSION_RATE_SENTINEL, CASH_RETAIN_ON_EMPTY, BASE_USDC_ADDRESS, USDC_DECIMALS, BASE_CHAIN_ID, isCashError, errors, CASH_ORDER_STATUSES, mapChainError, CashError, CASH_ACCESS_GROUP_IDS, CASH_RESTRICTED_PLATFORMS } from './chunk-UIELF4TY.js';
|
|
2
2
|
export { BASE_CHAIN_ID, BASE_USDC_ADDRESS, CASH_ORDER_POLL_INTERVAL_MS, CASH_ORDER_STATUSES, CASH_RETAIN_ON_EMPTY, CashError, MARKET_SPREAD_BPS, ORACLE_MIN_CONVERSION_RATE_SENTINEL, USDC_DECIMALS, errors, isCashError, isUserRejectedError } from './chunk-UIELF4TY.js';
|
|
3
3
|
import { parseAbi, parseEventLogs, isAddress, http, createPublicClient, createWalletClient, encodeFunctionData } from 'viem';
|
|
4
|
-
import { mainnet, base } from 'viem/chains';
|
|
4
|
+
import { mainnet, polygon, base } from 'viem/chains';
|
|
5
5
|
import { getSpreadOracleConfig, currencyInfo, getGatingServiceAddress, resolvePaymentMethodNameFromHash, getCurrencyCodeFromHash, createCompositeDepositId, appendAttributionToCalldata, Zkp2pClient, getPaymentMethodsCatalog, resolvePaymentMethodHashFromCatalog, defaultIndexerEndpoint, IndexerClient, CHAINLINK_ORACLE_FEEDS } from '@zkp2p/sdk';
|
|
6
6
|
import { createClient, MAINNET_RELAY_API } from '@relayprotocol/relay-sdk';
|
|
7
7
|
import { fetchChainConfigs, configureDynamicChains } from '@relayprotocol/relay-sdk/chain-utils';
|
|
8
8
|
import { z } from 'zod';
|
|
9
9
|
|
|
10
|
-
// src/client/creationRate.ts
|
|
11
10
|
var CHAINLINK_FEED_REGISTRY = "0x47Fb2585D2C56Fe188D0E6ec628a38b74fCeeeDf";
|
|
12
11
|
var CNY_DENOMINATION = "0x000000000000000000000000000000000000009c";
|
|
13
|
-
var INR_DENOMINATION = "0x0000000000000000000000000000000000000164";
|
|
14
12
|
var USD_DENOMINATION = "0x0000000000000000000000000000000000000348";
|
|
13
|
+
var POLYGON_INR_USD_FEED = "0xDA0F8Df6F5dB15b346f4B8D1156722027E194E60";
|
|
14
|
+
var DIRECT_FEED_ABI = parseAbi([
|
|
15
|
+
"function decimals() view returns (uint8)",
|
|
16
|
+
"function latestRoundData() view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound)"
|
|
17
|
+
]);
|
|
15
18
|
var FEED_REGISTRY_ABI = [
|
|
16
19
|
{
|
|
17
20
|
name: "decimals",
|
|
@@ -50,48 +53,67 @@ function getCreationRateDenomination(platform, currency) {
|
|
|
50
53
|
if (platform.toLowerCase() === "alipay" && currency.toUpperCase() === "CNY") {
|
|
51
54
|
return CNY_DENOMINATION;
|
|
52
55
|
}
|
|
53
|
-
if (platform.toLowerCase() === "upi" && currency.toUpperCase() === "INR") {
|
|
54
|
-
return INR_DENOMINATION;
|
|
55
|
-
}
|
|
56
56
|
throw new Error(`No creation-time rate source for ${platform}/${currency}`);
|
|
57
57
|
}
|
|
58
58
|
function divideRoundUp(numerator, denominator) {
|
|
59
59
|
return (numerator + denominator - 1n) / denominator;
|
|
60
60
|
}
|
|
61
61
|
async function readCashCreationRate(publicClient, platform, currency, nowSeconds = Math.floor(Date.now() / 1e3)) {
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
publicClient.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
62
|
+
const isUpi = platform.toLowerCase() === "upi" && currency.toUpperCase() === "INR";
|
|
63
|
+
const pair = `${currency.toUpperCase()}/USD`;
|
|
64
|
+
let decimals;
|
|
65
|
+
let round;
|
|
66
|
+
if (isUpi) {
|
|
67
|
+
if (await publicClient.getChainId() !== 137) {
|
|
68
|
+
throw new Error("Chainlink INR/USD requires Polygon mainnet (137)");
|
|
69
|
+
}
|
|
70
|
+
[decimals, round] = await Promise.all([
|
|
71
|
+
publicClient.readContract({
|
|
72
|
+
address: POLYGON_INR_USD_FEED,
|
|
73
|
+
abi: DIRECT_FEED_ABI,
|
|
74
|
+
functionName: "decimals"
|
|
75
|
+
}),
|
|
76
|
+
publicClient.readContract({
|
|
77
|
+
address: POLYGON_INR_USD_FEED,
|
|
78
|
+
abi: DIRECT_FEED_ABI,
|
|
79
|
+
functionName: "latestRoundData"
|
|
80
|
+
})
|
|
81
|
+
]);
|
|
82
|
+
} else {
|
|
83
|
+
const args = [
|
|
84
|
+
getCreationRateDenomination(platform, currency),
|
|
85
|
+
USD_DENOMINATION
|
|
86
|
+
];
|
|
87
|
+
[decimals, round] = await Promise.all([
|
|
88
|
+
publicClient.readContract({
|
|
89
|
+
address: CHAINLINK_FEED_REGISTRY,
|
|
90
|
+
abi: FEED_REGISTRY_ABI,
|
|
91
|
+
functionName: "decimals",
|
|
92
|
+
args
|
|
93
|
+
}),
|
|
94
|
+
publicClient.readContract({
|
|
95
|
+
address: CHAINLINK_FEED_REGISTRY,
|
|
96
|
+
abi: FEED_REGISTRY_ABI,
|
|
97
|
+
functionName: "latestRoundData",
|
|
98
|
+
args
|
|
99
|
+
})
|
|
100
|
+
]);
|
|
101
|
+
}
|
|
80
102
|
const [roundId, answer, , updatedAtRaw, answeredInRound] = round;
|
|
81
103
|
if (answer <= 0n || updatedAtRaw <= 0n || answeredInRound < roundId) {
|
|
82
|
-
throw new Error(
|
|
104
|
+
throw new Error(`Chainlink ${pair} returned an invalid round`);
|
|
83
105
|
}
|
|
84
106
|
const updatedAt = Number(updatedAtRaw);
|
|
85
107
|
if (!Number.isSafeInteger(updatedAt) || updatedAt > nowSeconds) {
|
|
86
|
-
throw new Error(
|
|
108
|
+
throw new Error(`Chainlink ${pair} returned an invalid timestamp`);
|
|
87
109
|
}
|
|
88
110
|
if (nowSeconds - updatedAt > CREATION_RATE_MAX_STALENESS_SECONDS) {
|
|
89
|
-
throw new Error(
|
|
111
|
+
throw new Error(`Chainlink ${pair} rate is stale`);
|
|
90
112
|
}
|
|
91
113
|
const rate1e18 = divideRoundUp(10n ** (BigInt(decimals) + 18n), answer);
|
|
92
114
|
const rate = Number(rate1e18) / 1e18;
|
|
93
115
|
if (!Number.isFinite(rate) || rate <= 0) {
|
|
94
|
-
throw new Error(
|
|
116
|
+
throw new Error(`Chainlink ${pair} produced an invalid creation rate`);
|
|
95
117
|
}
|
|
96
118
|
return { rate1e18, rate, updatedAt };
|
|
97
119
|
}
|
|
@@ -1304,10 +1326,11 @@ async function readEstimate(publicClient, input, context = {}) {
|
|
|
1304
1326
|
let rate;
|
|
1305
1327
|
let oracleUpdatedAt;
|
|
1306
1328
|
if (usesCreationRate) {
|
|
1307
|
-
|
|
1329
|
+
const rateClient = creationRatePlatform.toLowerCase() === "upi" ? context.upiCreationRateClient : context.creationRateClient;
|
|
1330
|
+
if (!rateClient) throw errors.oracleUnsupportedCurrency(currency);
|
|
1308
1331
|
try {
|
|
1309
1332
|
const snapshot = await readCashCreationRate(
|
|
1310
|
-
|
|
1333
|
+
rateClient,
|
|
1311
1334
|
creationRatePlatform,
|
|
1312
1335
|
currency,
|
|
1313
1336
|
asOf
|
|
@@ -2335,7 +2358,7 @@ function createCashAttributionReader(options) {
|
|
|
2335
2358
|
// src/client/classify.ts
|
|
2336
2359
|
function isCashPayoutSet(payouts, attributedToCash = false) {
|
|
2337
2360
|
return payouts.length > 0 && payouts.every(
|
|
2338
|
-
(payout) => payout.pricing.marketRate && payout.pricing.spreadBps === 0 || attributedToCash && payout.platform
|
|
2361
|
+
(payout) => payout.pricing.marketRate && payout.pricing.spreadBps === 0 || attributedToCash && isCreationRateCorridor(payout.platform, payout.currency ?? "") && payout.pricing.fixedAtCreation === true && (payout.pricing.fixedRate ?? 0) > 0
|
|
2339
2362
|
);
|
|
2340
2363
|
}
|
|
2341
2364
|
|
|
@@ -2439,6 +2462,10 @@ function createCashClient(options) {
|
|
|
2439
2462
|
chain: mainnet,
|
|
2440
2463
|
transport: creationRateTransport
|
|
2441
2464
|
});
|
|
2465
|
+
const upiCreationRateClient = createPublicClient({
|
|
2466
|
+
chain: polygon,
|
|
2467
|
+
transport: options.upiCreationRateTransport ?? http(options.upiCreationRateRpcUrl)
|
|
2468
|
+
});
|
|
2442
2469
|
const readCashAttribution = createCashAttributionReader({
|
|
2443
2470
|
environment,
|
|
2444
2471
|
...options.indexerUrl ? { indexerUrl: options.indexerUrl } : {},
|
|
@@ -2573,7 +2600,11 @@ function createCashClient(options) {
|
|
|
2573
2600
|
throw new Error(`No creation-time rate reader for ${platform}/${currency}`);
|
|
2574
2601
|
}
|
|
2575
2602
|
try {
|
|
2576
|
-
return await readCashCreationRate(
|
|
2603
|
+
return await readCashCreationRate(
|
|
2604
|
+
platform.toLowerCase() === "upi" ? upiCreationRateClient : creationRateClient,
|
|
2605
|
+
platform,
|
|
2606
|
+
currency
|
|
2607
|
+
);
|
|
2577
2608
|
} catch (err) {
|
|
2578
2609
|
throw errors.oracleReadFailed(currency, err);
|
|
2579
2610
|
}
|
|
@@ -2951,6 +2982,7 @@ function createCashClient(options) {
|
|
|
2951
2982
|
...estimateOptions?.includeEta !== void 0 ? { includeEta: estimateOptions.includeEta } : {},
|
|
2952
2983
|
etaReader: async (etaInput) => fillEtaFromSample(await getFillStatsSample(), etaInput),
|
|
2953
2984
|
creationRateClient,
|
|
2985
|
+
upiCreationRateClient,
|
|
2954
2986
|
...options.relay ? { relay: options.relay } : {}
|
|
2955
2987
|
});
|
|
2956
2988
|
},
|
package/dist/react.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CurrencyType } from '@zkp2p/sdk';
|
|
2
|
-
import { D as CashClient, a0 as EstimateInput, i as CashEstimate, _ as CashoutOptions, h as CashoutResult, Z as CashoutInput, T as TopUpResult, W as WithdrawResult, e as CashOrder } from './createCashClient-
|
|
2
|
+
import { D as CashClient, a0 as EstimateInput, i as CashEstimate, _ as CashoutOptions, h as CashoutResult, Z as CashoutInput, T as TopUpResult, W as WithdrawResult, e as CashOrder } from './createCashClient-ECL3HN5H.cjs';
|
|
3
3
|
import { WalletClient } from 'viem';
|
|
4
4
|
import '@relayprotocol/relay-sdk';
|
|
5
5
|
|
package/dist/react.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CurrencyType } from '@zkp2p/sdk';
|
|
2
|
-
import { D as CashClient, a0 as EstimateInput, i as CashEstimate, _ as CashoutOptions, h as CashoutResult, Z as CashoutInput, T as TopUpResult, W as WithdrawResult, e as CashOrder } from './createCashClient-
|
|
2
|
+
import { D as CashClient, a0 as EstimateInput, i as CashEstimate, _ as CashoutOptions, h as CashoutResult, Z as CashoutInput, T as TopUpResult, W as WithdrawResult, e as CashOrder } from './createCashClient-ECL3HN5H.js';
|
|
3
3
|
import { WalletClient } from 'viem';
|
|
4
4
|
import '@relayprotocol/relay-sdk';
|
|
5
5
|
|
package/dist/tools.cjs
CHANGED
package/dist/tools.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Peer Cash Demo: the express sell flow stored entirely onchain on Base, after zSwap by z0r0z",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@zkp2p/cash": "0.
|
|
7
|
+
"@zkp2p/cash": "0.5.3",
|
|
8
8
|
"esbuild": "0.28.2",
|
|
9
9
|
"solc": "0.8.36",
|
|
10
10
|
"viem": "2.55.18"
|
package/llms.txt
CHANGED
|
@@ -127,3 +127,13 @@ Key facts:
|
|
|
127
127
|
API key or referral-enrollment transaction is required.
|
|
128
128
|
- [Tools manifest](src/tools/index.ts): JSON-schema definitions of the verbs
|
|
129
129
|
- [Codecs](src/codecs/): zod schemas + lossless JSON round-trips
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
UPI/INR reads the live Chainlink Polygon mainnet proxy
|
|
133
|
+
`0xDA0F8Df6F5dB15b346f4B8D1156722027E194E60` (chain 137), inverts
|
|
134
|
+
USD per INR, and rounds the creation-time maker floor up. Configure its
|
|
135
|
+
read-only RPC with `upiCreationRateRpcUrl` or `upiCreationRateTransport`.
|
|
136
|
+
Alipay/CNY retains the Ethereum registry and `creationRateRpcUrl` /
|
|
137
|
+
`creationRateTransport`. UPI rejects the wrong chain, invalid rounds, and
|
|
138
|
+
observations older than 24 hours; market closures do not bypass freshness.
|
|
139
|
+
This does not change the staging-only UPI opt-in gate.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zkp2p/cash",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.4-rc.1",
|
|
4
4
|
"description": "Peer Cash - offramp-only SDK for routing Relay or NEAR Intents assets to Base USDC, then cashing out to fiat at zero-spread Chainlink market rates.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Peer (https://peer.xyz)",
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
},
|
|
109
109
|
"dependencies": {
|
|
110
110
|
"@relayprotocol/relay-sdk": "^7.0.1",
|
|
111
|
-
"@zkp2p/sdk": "0.14.1",
|
|
111
|
+
"@zkp2p/sdk": "0.14.2-rc.1",
|
|
112
112
|
"zod": "^4.4.3"
|
|
113
113
|
},
|
|
114
114
|
"peerDependencies": {
|
|
@@ -263,3 +263,21 @@ Prove both routes without waiting for a buyer:
|
|
|
263
263
|
|
|
264
264
|
If withdrawal fails with funds stuck: stop, do not retry blindly, escalate to
|
|
265
265
|
a human with the `depositId` and tx hashes.
|
|
266
|
+
|
|
267
|
+
UPI/INR reads the live Chainlink Polygon mainnet proxy
|
|
268
|
+
`0xDA0F8Df6F5dB15b346f4B8D1156722027E194E60` (chain 137), inverts
|
|
269
|
+
USD per INR, and rounds the creation-time maker floor up. Configure its
|
|
270
|
+
read-only RPC with `upiCreationRateRpcUrl` or `upiCreationRateTransport`.
|
|
271
|
+
Alipay/CNY retains the Ethereum registry and `creationRateRpcUrl` /
|
|
272
|
+
`creationRateTransport`. UPI rejects the wrong chain, invalid rounds, and
|
|
273
|
+
observations older than 24 hours; market closures do not bypass freshness.
|
|
274
|
+
This does not change the staging-only UPI opt-in gate.
|
|
275
|
+
|
|
276
|
+
Before a funded UPI QA run, call
|
|
277
|
+
`cash.estimate({ amount: 1000000n, platform: 'upi', currency: 'INR' }, { includeEta: false })`
|
|
278
|
+
using the intended live Polygon RPC. Require a positive finite rate,
|
|
279
|
+
`binding: 'deposit-creation'`, and `oracleUpdatedAt` no more than 86400 seconds
|
|
280
|
+
old and not in the future. Record the observation time and selected chain,
|
|
281
|
+
then stop before funding if the read fails. Unit-test fixtures prove routing,
|
|
282
|
+
not live availability. The authoritative feed listing is
|
|
283
|
+
https://data.chain.link/feeds/polygon/mainnet/inr-usd.
|