@rozoai/intent-pay 0.1.38-beta.3 → 0.1.38-beta.4
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/build/components/Common/TokenChainLogo/index.d.ts +1 -7
- package/build/components/Spinners/TokenLogoSpinner/index.d.ts +1 -6
- package/build/hooks/useDepositAddressOptions.d.ts +2 -3
- package/build/hooks/usePaymentState.d.ts +5 -11
- package/build/hooks/useRozoPay.d.ts +4 -4
- package/build/hooks/useSolanaPaymentOptions.d.ts +1 -1
- package/build/hooks/useStellarPaymentOptions.d.ts +1 -1
- package/build/hooks/useWalletPaymentOptions.d.ts +7 -12
- package/build/package.json.js +2 -2
- package/build/payment/createPaymentPayload.d.ts +3 -3
- package/build/payment/paymentFsm.d.ts +2 -25
- package/build/provider/PayContext.d.ts +3 -3
- package/build/src/components/Common/AmountInput/index.js +1 -1
- package/build/src/components/Common/AmountInput/index.js.map +1 -1
- package/build/src/components/Common/ConnectorList/index.js +3 -2
- package/build/src/components/Common/ConnectorList/index.js.map +1 -1
- package/build/src/components/Common/TokenChainLogo/index.js +3 -26
- package/build/src/components/Common/TokenChainLogo/index.js.map +1 -1
- package/build/src/components/Pages/PayWithToken/index.js +4 -15
- package/build/src/components/Pages/PayWithToken/index.js.map +1 -1
- package/build/src/components/Pages/SelectDepositAddressChain/index.js +17 -6
- package/build/src/components/Pages/SelectDepositAddressChain/index.js.map +1 -1
- package/build/src/components/Pages/SelectMethod/index.js.map +1 -1
- package/build/src/components/Pages/Solana/PayWithSolanaToken/index.js +26 -64
- package/build/src/components/Pages/Solana/PayWithSolanaToken/index.js.map +1 -1
- package/build/src/components/Pages/Stellar/PayWithStellarToken/index.js +38 -40
- package/build/src/components/Pages/Stellar/PayWithStellarToken/index.js.map +1 -1
- package/build/src/components/Pages/WaitingDepositAddress/index.js +33 -110
- package/build/src/components/Pages/WaitingDepositAddress/index.js.map +1 -1
- package/build/src/components/RozoPayModal/index.js +31 -3
- package/build/src/components/RozoPayModal/index.js.map +1 -1
- package/build/src/components/Spinners/TokenLogoSpinner/index.js +2 -5
- package/build/src/components/Spinners/TokenLogoSpinner/index.js.map +1 -1
- package/build/src/hooks/useDepositAddressOptions.js +9 -19
- package/build/src/hooks/useDepositAddressOptions.js.map +1 -1
- package/build/src/hooks/usePaymentState.js +72 -195
- package/build/src/hooks/usePaymentState.js.map +1 -1
- package/build/src/hooks/useRozoPay.js +2 -4
- package/build/src/hooks/useRozoPay.js.map +1 -1
- package/build/src/hooks/useSolanaPaymentOptions.js +12 -45
- package/build/src/hooks/useSolanaPaymentOptions.js.map +1 -1
- package/build/src/hooks/useStellarPaymentOptions.js +10 -40
- package/build/src/hooks/useStellarPaymentOptions.js.map +1 -1
- package/build/src/hooks/useTokenOptions.js +3 -4
- package/build/src/hooks/useTokenOptions.js.map +1 -1
- package/build/src/hooks/useWalletPaymentOptions.js +21 -54
- package/build/src/hooks/useWalletPaymentOptions.js.map +1 -1
- package/build/src/payment/createPaymentPayload.js +13 -38
- package/build/src/payment/createPaymentPayload.js.map +1 -1
- package/build/src/payment/paymentEffects.js +5 -4
- package/build/src/payment/paymentEffects.js.map +1 -1
- package/build/src/payment/paymentFsm.js.map +1 -1
- package/build/src/utils/format.js +13 -82
- package/build/src/utils/format.js.map +1 -1
- package/build/src/utils/token.js +5 -21
- package/build/src/utils/token.js.map +1 -1
- package/build/utils/format.d.ts +3 -44
- package/build/utils/token.d.ts +2 -6
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDepositAddressOptions.js","sources":["../../../src/hooks/useDepositAddressOptions.ts"],"sourcesContent":[null],"names":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"useDepositAddressOptions.js","sources":["../../../src/hooks/useDepositAddressOptions.ts"],"sourcesContent":[null],"names":[],"mappings":";;;;AA8BA,MAAM,eAAe,GAAG;;AAEtB,IAAA;QACE,EAAE,EAAE,4BAA4B,CAAC,aAAa;QAC9C,OAAO,EAAE,YAAY,CAAC,OAAO;AAC7B,QAAA,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE,QAAQ,CAAC,OAAO;AACzB,QAAA,KAAK,EAAE,YAAY;AACpB,KAAA;AACD,IAAA;QACE,EAAE,EAAE,4BAA4B,CAAC,aAAa;QAC9C,OAAO,EAAE,YAAY,CAAC,OAAO;AAC7B,QAAA,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE,QAAQ,CAAC,OAAO;AACzB,QAAA,KAAK,EAAE,YAAY;AACpB,KAAA;;AAED,IAAA;QACE,EAAE,EAAE,4BAA4B,CAAC,SAAS;QAC1C,OAAO,EAAE,QAAQ,CAAC,OAAO;AACzB,QAAA,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE,IAAI,CAAC,OAAO;AACrB,QAAA,KAAK,EAAE,QAAQ;AAChB,KAAA;AACD,IAAA;QACE,EAAE,EAAE,4BAA4B,CAAC,SAAS;QAC1C,OAAO,EAAE,QAAQ,CAAC,OAAO;AACzB,QAAA,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE,IAAI,CAAC,OAAO;AACrB,QAAA,KAAK,EAAE,QAAQ;AAChB,KAAA;;AAED,IAAA;QACE,EAAE,EAAE,4BAA4B,CAAC,WAAW;QAC5C,OAAO,EAAE,cAAc,CAAC,OAAO;AAC/B,QAAA,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE,cAAc,CAAC,OAAO;AAC/B,QAAA,KAAK,EAAE,cAAc;AACtB,KAAA;AACD,IAAA;QACE,EAAE,EAAE,4BAA4B,CAAC,WAAW;QAC5C,OAAO,EAAE,cAAc,CAAC,OAAO;AAC/B,QAAA,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE,cAAc,CAAC,OAAO;AAC/B,QAAA,KAAK,EAAE,cAAc;AACtB,KAAA;CACF,CAAC;AAEF;;;;;;;;;;;;AAYG;AACG,SAAU,wBAAwB,CAAC,EACvC,IAAI,EACJ,WAAW,EACX,SAAS,GACsB,EAAA;IAC/B,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAwC;AACjF,QAAA,OAAO,EAAE,WAAW,IAAI,IAAI,IAAI,WAAW,GAAG,CAAC;AAC/C,QAAA,QAAQ,EAAE,CAAC,uBAAuB,EAAE,WAAW,CAAC;QAChD,OAAO,EAAE,YAAW;AAClB,YAAA,IAAI;AACF,gBAAA,OAAO,MAAM,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC;AAC/C,oBAAA,WAAW,EAAE,WAAY;AAC1B,iBAAA,CAAC,CAAC;aACJ;YAAC,OAAO,GAAG,EAAE;;AAEZ,gBAAA,OAAO,CAAC,KAAK,CAAC,wCAAwC,EAAE,GAAG,CAAC,CAAC;AAC7D,gBAAA,OAAO,eAAe,CAAC;aACxB;SACF;AACD,QAAA,SAAS,EAAE,MAAM;AACjB,QAAA,KAAK,EAAE,CAAC;AACR,QAAA,oBAAoB,EAAE,KAAK;AAC5B,KAAA,CAAC,CAAC;;AAGH,IAAA,MAAM,eAAe,GAAG,OAAO,CAAC,MAAK;AACnC,QAAA,MAAM,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC;AAC3B,QAAA,IAAI,SAAS,EAAE,eAAe,IAAI,SAAS,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE;YACtE,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,KAC3B,SAAS,CAAC,eAAe,EAAE,IAAI,CAC7B,CAAC,EAAE,KACD,EAAE,CAAC,KAAK,IAAI,IAAI;gBAChB,qBAAqB,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC;AACnD,oBAAA,qBAAqB,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CACpE,CACF,CAAC;SACH;AACD,QAAA,OAAO,OAAO,CAAC;KAChB,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC,CAAC;IAEvC,OAAO;AACL,QAAA,OAAO,EAAE,eAAe;AACxB,QAAA,OAAO,EAAE,SAAS;AAClB,QAAA,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI;KACpC,CAAC;AACJ;;;;"}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { ExternalPaymentOptions, rozoStellar, TokenSymbol, assert,
|
|
1
|
+
import { ExternalPaymentOptions, rozoStellar, TokenSymbol, assert, getPayment, checkoutPayment, buildCheckoutPayload, createPayment, debugJson, formatPaymentResponseToHydratedOrder, bscUSDT, assertNotNull, isValidSolanaAddress, rozoSolanaUSDC, rozoStellarUSDC, rozoStellarEURC, solana, rozoSolana, stellar, getChainById, getKnownToken, generateSolanaDeepLink, generateEVMDeepLink, writeRozoPayOrderID } from '@rozoai/intent-common';
|
|
2
2
|
import { useWallet, useConnection } from '@solana/wallet-adapter-react';
|
|
3
|
-
import { VersionedTransaction, PublicKey,
|
|
3
|
+
import { VersionedTransaction, PublicKey, Transaction } from '@solana/web3.js';
|
|
4
4
|
import { useRef, useState, useEffect, useCallback, useMemo } from 'react';
|
|
5
|
-
import { getAddress, erc20Abi, hexToBytes
|
|
5
|
+
import { parseUnits, zeroAddress, getAddress, erc20Abi, hexToBytes } from 'viem';
|
|
6
6
|
import { useAccount, useSwitchChain, useSendTransaction, useWriteContract, useWalletClient, useCapabilities } from 'wagmi';
|
|
7
7
|
import { useWriteContracts } from 'wagmi/experimental';
|
|
8
|
-
import { tokenBaseAmountToDecimalString, tokenAmountToBaseUnits } from '../utils/format.js';
|
|
9
8
|
import { convertPreferredSymbolsToTokens } from '../utils/token.js';
|
|
10
9
|
import { createMemoInstruction } from '@solana/spl-memo';
|
|
11
10
|
import { getAssociatedTokenAddress, createAssociatedTokenAccountInstruction, TOKEN_PROGRAM_ID, createTransferCheckedInstruction } from '@solana/spl-token';
|
|
@@ -53,7 +52,7 @@ function usePaymentState({ trpc, lockPayParams, setRoute, log, redirectReturnUrl
|
|
|
53
52
|
const { data: walletClient } = useWalletClient();
|
|
54
53
|
const { data: walletCapabilities, isPending: capabilitiesPending } = useCapabilities();
|
|
55
54
|
const { writeContractsAsync } = useWriteContracts();
|
|
56
|
-
// order.metadata.dataSuffix survives ROZO_INVOICE_URL redirects where the
|
|
55
|
+
// ponytail: order.metadata.dataSuffix survives ROZO_INVOICE_URL redirects where the
|
|
57
56
|
// consumer's wagmi config (globalDataSuffix) is absent. Invoice checkout fetches the
|
|
58
57
|
// order first and passes metadata.dataSuffix into getDefaultConfig, but this fallback
|
|
59
58
|
// covers the in-SDK path where the order is already loaded.
|
|
@@ -162,26 +161,12 @@ function usePaymentState({ trpc, lockPayParams, setRoute, log, redirectReturnUrl
|
|
|
162
161
|
}
|
|
163
162
|
// payId mode: derive filtering params from the loaded order so that
|
|
164
163
|
// payment option hooks apply the same token filtering as appId mode.
|
|
165
|
-
// e.g. if destination is USDC,
|
|
166
|
-
// (we don't support payout to native tokens yet, but paying *with* native
|
|
167
|
-
// source balances is fine). EURC destinations are payable only with EURC.
|
|
164
|
+
// e.g. if destination is USDC, only show USDC/USDT sources (not EURC).
|
|
168
165
|
if (pay.order && pay.order.destFinalCallTokenAmount) {
|
|
169
166
|
const destSymbol = pay.order.destFinalCallTokenAmount.token.symbol;
|
|
170
|
-
// Derive preferredSymbol from destination: EURC destinations
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
// toChain/toToken and never native).
|
|
174
|
-
const preferredSymbol = destSymbol === TokenSymbol.EURC
|
|
175
|
-
? [TokenSymbol.EURC]
|
|
176
|
-
: [
|
|
177
|
-
TokenSymbol.USDC,
|
|
178
|
-
TokenSymbol.USDT,
|
|
179
|
-
TokenSymbol.ETH,
|
|
180
|
-
TokenSymbol.BNB,
|
|
181
|
-
TokenSymbol.POL,
|
|
182
|
-
TokenSymbol.SOL,
|
|
183
|
-
TokenSymbol.XLM,
|
|
184
|
-
];
|
|
167
|
+
// Derive preferredSymbol from destination: EURC destinations show EURC,
|
|
168
|
+
// everything else defaults to USDC + USDT.
|
|
169
|
+
const preferredSymbol = destSymbol === TokenSymbol.EURC ? [TokenSymbol.EURC] : [TokenSymbol.USDC, TokenSymbol.USDT];
|
|
185
170
|
const preferredTokens = convertPreferredSymbolsToTokens(preferredSymbol, undefined);
|
|
186
171
|
return {
|
|
187
172
|
toChain: pay.order.destFinalCallTokenAmount.token.chainId,
|
|
@@ -229,7 +214,6 @@ function usePaymentState({ trpc, lockPayParams, setRoute, log, redirectReturnUrl
|
|
|
229
214
|
const depositAddressOptions = useDepositAddressOptions({
|
|
230
215
|
trpc,
|
|
231
216
|
usdRequired,
|
|
232
|
-
mode: pay.order?.mode,
|
|
233
217
|
payParams: stablePayParams,
|
|
234
218
|
});
|
|
235
219
|
const chainOrderUsdLimits = useOrderUsdLimits({ trpc });
|
|
@@ -269,14 +253,15 @@ function usePaymentState({ trpc, lockPayParams, setRoute, log, redirectReturnUrl
|
|
|
269
253
|
// Read from ref instead of closure to get latest value and avoid stale state
|
|
270
254
|
const payParams = currPayParamsRef.current;
|
|
271
255
|
const order = pay.order;
|
|
256
|
+
// Narrow order to the specific shapes supported by buildCreatePaymentPayload.
|
|
257
|
+
// In error/idle states `pay.order` can be a plain RozoPayOrder or null,
|
|
258
|
+
// which is not assignable to the expected OrderLike type.
|
|
259
|
+
const orderForPayload = order && "org" in order
|
|
260
|
+
? order
|
|
261
|
+
: undefined;
|
|
272
262
|
// If payment already exists (payId mode or rozoPaymentId set), checkout instead of create.
|
|
273
|
-
// backend forbids `checkout` when rotating to a native source token
|
|
274
|
-
// (SOL/ETH/XLM) — "create a new order instead" — so route native sources to the
|
|
275
|
-
// create branch below instead of calling checkout.
|
|
276
263
|
const existingPayId = order?.externalId ?? rozoPaymentId ?? undefined;
|
|
277
|
-
|
|
278
|
-
const shouldCheckout = !!existingPayId && !rotateToNative;
|
|
279
|
-
if (!payParams || shouldCheckout) {
|
|
264
|
+
if (!payParams || existingPayId) {
|
|
280
265
|
if (!existingPayId) {
|
|
281
266
|
throw new Error("No pay params provided");
|
|
282
267
|
}
|
|
@@ -285,20 +270,11 @@ function usePaymentState({ trpc, lockPayParams, setRoute, log, redirectReturnUrl
|
|
|
285
270
|
if (!paymentRes?.data) {
|
|
286
271
|
throw new Error("Failed to fetch payment");
|
|
287
272
|
}
|
|
288
|
-
// Use required.amount (base units, already priced by the backend)
|
|
289
|
-
// converted to a human-readable token amount — NOT required.usd,
|
|
290
|
-
// which only equals the token amount for 1:1-USD-pegged tokens
|
|
291
|
-
// (USDC/USDT). For native ETH/BNB/POL/SOL/XLM this would send the
|
|
292
|
-
// wrong amount to the backend (e.g. "1.00" instead of "0.0005").
|
|
293
|
-
// Some endpoints return required.amount as an already-human-readable
|
|
294
|
-
// decimal string instead of integer base units, so detect the shape
|
|
295
|
-
// first — BigInt() throws on decimal strings.
|
|
296
|
-
const sourceAmount = tokenBaseAmountToDecimalString(walletOption.required.amount, walletOption.required.token.decimals);
|
|
297
273
|
const checkoutRes = await checkoutPayment(existingPayId, buildCheckoutPayload(paymentRes.data, {
|
|
298
274
|
chainId: walletOption.required.token.chainId,
|
|
299
275
|
tokenSymbol: walletOption.required.token.symbol,
|
|
300
276
|
tokenAddress: walletOption.required.token.token,
|
|
301
|
-
amount:
|
|
277
|
+
amount: String(walletOption.required.usd),
|
|
302
278
|
}));
|
|
303
279
|
if (!checkoutRes?.data) {
|
|
304
280
|
throw new Error("Failed to checkout payment");
|
|
@@ -317,14 +293,6 @@ function usePaymentState({ trpc, lockPayParams, setRoute, log, redirectReturnUrl
|
|
|
317
293
|
}
|
|
318
294
|
}
|
|
319
295
|
try {
|
|
320
|
-
// Narrow order to the specific shapes supported by buildCreatePaymentPayload.
|
|
321
|
-
// In error/idle states `pay.order` can be a plain RozoPayOrder or null,
|
|
322
|
-
// which lacks `destFinalCallTokenAmount`; passing it through would throw
|
|
323
|
-
// when we derive the destination amount. Fall back to the payParams-only
|
|
324
|
-
// branch (order undefined) in that case.
|
|
325
|
-
const orderForPayload = order && "org" in order
|
|
326
|
-
? order
|
|
327
|
-
: undefined;
|
|
328
296
|
const payload = buildCreatePaymentPayload({
|
|
329
297
|
payParams,
|
|
330
298
|
order: orderForPayload,
|
|
@@ -365,34 +333,22 @@ function usePaymentState({ trpc, lockPayParams, setRoute, log, redirectReturnUrl
|
|
|
365
333
|
if (!validatePaymentRequirements(walletOption, pay.paymentState)) {
|
|
366
334
|
throw new Error(`[PAY TOKEN] required token ${debugJson(required)} does not match fees token ${debugJson(fees)}`);
|
|
367
335
|
}
|
|
368
|
-
//
|
|
369
|
-
//
|
|
370
|
-
|
|
371
|
-
// amount (e.g. 1.00 ETH instead of ~0.0006 ETH for a $1 payment).
|
|
372
|
-
const paymentAmount = tokenAmountToBaseUnits(required.amount, required.token.decimals);
|
|
336
|
+
// @NOTE: Fee handled by Rozo API
|
|
337
|
+
// const paymentAmount = BigInt(required.amount) + BigInt(fees.amount);
|
|
338
|
+
const paymentAmount = parseUnits(required.usd.toString(), required.token.decimals);
|
|
373
339
|
// Check if we need to create a new Rozo payment (cache this check)
|
|
374
340
|
const previousChainId = pay.order.preferredChainId ? Number(pay.order.preferredChainId) : null;
|
|
375
|
-
const previousTokenAddress = pay.order.preferredTokenAddress;
|
|
376
341
|
// If we have an existing payment and chain differs, MUST create new payment
|
|
377
342
|
// Also check if order has externalId (existing payment) and we're on different chain
|
|
378
343
|
const hasExistingPayment = pay.order.externalId != null;
|
|
379
|
-
// Check both chain AND token change - if user switches token on same chain,
|
|
380
|
-
// we still need to checkout to update source token info
|
|
381
|
-
const tokenChanged = previousTokenAddress != null &&
|
|
382
|
-
normalizeTokenAddress(Number(previousChainId), previousTokenAddress) !==
|
|
383
|
-
normalizeTokenAddress(required.token.chainId, required.token.token);
|
|
384
344
|
const needRozoPayment = (previousChainId !== null && previousChainId !== required.token.chainId) ||
|
|
385
|
-
tokenChanged ||
|
|
386
345
|
(hasExistingPayment &&
|
|
387
346
|
previousChainId === null &&
|
|
388
347
|
pay.order.destFinalCallTokenAmount?.token?.chainId !== required.token.chainId);
|
|
389
|
-
log?.(`[PAY TOKEN] Chain
|
|
348
|
+
log?.(`[PAY TOKEN] Chain check: previous=${previousChainId}, current=${required.token.chainId}, hasExistingPayment=${hasExistingPayment}, needRozoPayment=${needRozoPayment}`);
|
|
390
349
|
// Prepare transaction parameters early (before async operations)
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
// would misclassify native ETH as an ERC20 and take the wrong transfer path.
|
|
394
|
-
const isNative = isNativeToken(required.token.token);
|
|
395
|
-
const tokenAddress = isNative ? null : getAddress(required.token.token);
|
|
350
|
+
const isNativeToken = required.token.token === zeroAddress;
|
|
351
|
+
const tokenAddress = isNativeToken ? null : getAddress(required.token.token);
|
|
396
352
|
// Get hydrated order efficiently with parallel preparation
|
|
397
353
|
let hydratedOrder;
|
|
398
354
|
let paymentId;
|
|
@@ -489,14 +445,7 @@ function usePaymentState({ trpc, lockPayParams, setRoute, log, redirectReturnUrl
|
|
|
489
445
|
// Execute transaction with optimized error handling
|
|
490
446
|
const paymentTxHash = await (async () => {
|
|
491
447
|
try {
|
|
492
|
-
if (
|
|
493
|
-
await switchChainAsync({ chainId: required.token.chainId });
|
|
494
|
-
}
|
|
495
|
-
log?.(`[PAY ERC20] dataSuffix: ${resolvedDataSuffix ?? "(none)"}`);
|
|
496
|
-
// EIP-5792 path: only when wallet advertises dataSuffix capability (Base App / Coinbase Wallet).
|
|
497
|
-
const chainCapabilities = walletCapabilities?.[required.token.chainId];
|
|
498
|
-
const supportsDataSuffix = !capabilitiesPending && resolvedDataSuffix != null && !!chainCapabilities?.dataSuffix;
|
|
499
|
-
if (isNative) {
|
|
448
|
+
if (isNativeToken) {
|
|
500
449
|
// dataSuffix intentionally omitted — appending data to bare ETH transfers
|
|
501
450
|
// changes wallet UI display; builder-code attribution targets contract calls.
|
|
502
451
|
return await sendTransactionAsync({
|
|
@@ -505,6 +454,13 @@ function usePaymentState({ trpc, lockPayParams, setRoute, log, redirectReturnUrl
|
|
|
505
454
|
});
|
|
506
455
|
}
|
|
507
456
|
else {
|
|
457
|
+
if (required.token.chainId !== bscUSDT.chainId) {
|
|
458
|
+
await switchChainAsync({ chainId: required.token.chainId });
|
|
459
|
+
}
|
|
460
|
+
log?.(`[PAY ERC20] dataSuffix: ${resolvedDataSuffix ?? "(none)"}`);
|
|
461
|
+
// EIP-5792 path: only when wallet advertises dataSuffix capability (Base App / Coinbase Wallet).
|
|
462
|
+
const chainCapabilities = walletCapabilities?.[required.token.chainId];
|
|
463
|
+
const supportsDataSuffix = !capabilitiesPending && resolvedDataSuffix != null && !!chainCapabilities?.dataSuffix;
|
|
508
464
|
if (supportsDataSuffix) {
|
|
509
465
|
if (!walletClient)
|
|
510
466
|
throw new Error("No walletClient available");
|
|
@@ -637,9 +593,11 @@ function usePaymentState({ trpc, lockPayParams, setRoute, log, redirectReturnUrl
|
|
|
637
593
|
throw new Error(`Invalid Solana destination address format: ${rozoPayment.destAddress}`);
|
|
638
594
|
}
|
|
639
595
|
// Set up token addresses
|
|
596
|
+
let mintAddress;
|
|
640
597
|
let fromKey;
|
|
641
598
|
let toKey;
|
|
642
599
|
try {
|
|
600
|
+
mintAddress = new PublicKey(walletPaymentOption.required.token.token);
|
|
643
601
|
fromKey = new PublicKey(payerPublicKey);
|
|
644
602
|
toKey = new PublicKey(rozoPayment.destAddress);
|
|
645
603
|
}
|
|
@@ -647,79 +605,32 @@ function usePaymentState({ trpc, lockPayParams, setRoute, log, redirectReturnUrl
|
|
|
647
605
|
throw new Error(`Invalid Solana address format: ${error.message}. ` +
|
|
648
606
|
`Destination address: ${rozoPayment.destAddress}`);
|
|
649
607
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
const lamports = parseUnits(rozoPayment.sourceAmount, solanaSOL.decimals);
|
|
671
|
-
log("[PAY SOLANA] Transfer amount (lamports):", lamports.toString());
|
|
672
|
-
// Use the parsed lamports value from parseUnits to ensure precision.
|
|
673
|
-
instructions.push(SystemProgram.transfer({
|
|
674
|
-
fromPubkey: fromKey,
|
|
675
|
-
toPubkey: toKey,
|
|
676
|
-
lamports: lamports,
|
|
677
|
-
}));
|
|
678
|
-
}
|
|
679
|
-
else {
|
|
680
|
-
let mintAddress;
|
|
681
|
-
try {
|
|
682
|
-
mintAddress = new PublicKey(walletPaymentOption.required.token.token);
|
|
683
|
-
}
|
|
684
|
-
catch (error) {
|
|
685
|
-
throw new Error(`Invalid Solana address format: ${error.message}. ` +
|
|
686
|
-
`Destination address: ${rozoPayment.destAddress}`);
|
|
687
|
-
}
|
|
688
|
-
log("[PAY SOLANA] Transaction details:", {
|
|
689
|
-
tokenMint: mintAddress.toString(),
|
|
690
|
-
fromKey: fromKey.toString(),
|
|
691
|
-
toKey: toKey.toString(),
|
|
692
|
-
amount: walletPaymentOption.required.amount,
|
|
693
|
-
usd: walletPaymentOption.required.usd,
|
|
694
|
-
memo: rozoPayment.memo,
|
|
695
|
-
});
|
|
696
|
-
// Get token accounts for sender and recipient
|
|
697
|
-
log("[PAY SOLANA] Deriving associated token accounts...");
|
|
698
|
-
const senderTokenAccount = await getAssociatedTokenAddress(mintAddress, fromKey);
|
|
699
|
-
const recipientTokenAccount = await getAssociatedTokenAddress(mintAddress, toKey);
|
|
700
|
-
log("[PAY SOLANA] Sender token account:", senderTokenAccount.toString());
|
|
701
|
-
log("[PAY SOLANA] Recipient token account:", recipientTokenAccount.toString());
|
|
702
|
-
// Check if recipient token account exists
|
|
703
|
-
log("[PAY SOLANA] Checking if recipient token account exists...");
|
|
704
|
-
const recipientTokenInfo = await connection.getAccountInfo(recipientTokenAccount);
|
|
705
|
-
// Create recipient token account if it doesn't exist
|
|
706
|
-
if (!recipientTokenInfo) {
|
|
707
|
-
log("[PAY SOLANA] Creating recipient token account...");
|
|
708
|
-
instructions.push(createAssociatedTokenAccountInstruction(payerPublicKey, recipientTokenAccount, toKey, mintAddress, TOKEN_PROGRAM_ID));
|
|
709
|
-
}
|
|
710
|
-
// Add transfer instruction
|
|
711
|
-
// Use required.amount (already computed by the backend in the
|
|
712
|
-
// token's base units, correctly priced from USD) instead of
|
|
713
|
-
// re-deriving from required.usd with a hardcoded 6-decimal
|
|
714
|
-
// assumption, which silently breaks for non-1:1-USD or
|
|
715
|
-
// differently-decimaled SPL tokens.
|
|
716
|
-
log("[PAY SOLANA] Adding transfer instruction...");
|
|
717
|
-
// Keep as bigint — createTransferCheckedInstruction accepts number | bigint.
|
|
718
|
-
// Converting to Number would lose precision for amounts > 2^53 base units.
|
|
719
|
-
const transferAmount = tokenAmountToBaseUnits(walletPaymentOption.required.amount, walletPaymentOption.required.token.decimals);
|
|
720
|
-
log("[PAY SOLANA] Transfer amount (base units):", transferAmount.toString());
|
|
721
|
-
instructions.push(createTransferCheckedInstruction(senderTokenAccount, mintAddress, recipientTokenAccount, fromKey, transferAmount, walletPaymentOption.required.token.decimals));
|
|
608
|
+
log("[PAY SOLANA] Transaction details:", {
|
|
609
|
+
tokenMint: mintAddress.toString(),
|
|
610
|
+
fromKey: fromKey.toString(),
|
|
611
|
+
toKey: toKey.toString(),
|
|
612
|
+
amount: walletPaymentOption.required.usd,
|
|
613
|
+
memo: rozoPayment.memo,
|
|
614
|
+
});
|
|
615
|
+
// Get token accounts for sender and recipient
|
|
616
|
+
log("[PAY SOLANA] Deriving associated token accounts...");
|
|
617
|
+
const senderTokenAccount = await getAssociatedTokenAddress(mintAddress, fromKey);
|
|
618
|
+
const recipientTokenAccount = await getAssociatedTokenAddress(mintAddress, toKey);
|
|
619
|
+
log("[PAY SOLANA] Sender token account:", senderTokenAccount.toString());
|
|
620
|
+
log("[PAY SOLANA] Recipient token account:", recipientTokenAccount.toString());
|
|
621
|
+
// Check if recipient token account exists
|
|
622
|
+
log("[PAY SOLANA] Checking if recipient token account exists...");
|
|
623
|
+
const recipientTokenInfo = await connection.getAccountInfo(recipientTokenAccount);
|
|
624
|
+
// Create recipient token account if it doesn't exist
|
|
625
|
+
if (!recipientTokenInfo) {
|
|
626
|
+
log("[PAY SOLANA] Creating recipient token account...");
|
|
627
|
+
instructions.push(createAssociatedTokenAccountInstruction(payerPublicKey, recipientTokenAccount, toKey, mintAddress, TOKEN_PROGRAM_ID));
|
|
722
628
|
}
|
|
629
|
+
// Add transfer instruction
|
|
630
|
+
log("[PAY SOLANA] Adding transfer instruction...");
|
|
631
|
+
const transferAmount = parseFloat(walletPaymentOption.required.usd.toString()) * 1_000_000;
|
|
632
|
+
log("[PAY SOLANA] Transfer amount (with decimals):", transferAmount);
|
|
633
|
+
instructions.push(createTransferCheckedInstruction(senderTokenAccount, mintAddress, recipientTokenAccount, fromKey, transferAmount, rozoSolanaUSDC.decimals));
|
|
723
634
|
// Add memo if provided
|
|
724
635
|
if (rozoPayment.memo) {
|
|
725
636
|
log("[PAY SOLANA] Adding memo instruction:", rozoPayment.memo);
|
|
@@ -782,29 +693,18 @@ function usePaymentState({ trpc, lockPayParams, setRoute, log, redirectReturnUrl
|
|
|
782
693
|
await setStellarWallet(stellarConnector);
|
|
783
694
|
}
|
|
784
695
|
const sourceAccount = await stellarServer.loadAccount(stellarPublicKey);
|
|
785
|
-
let
|
|
786
|
-
if (walletPaymentOption.required.token.token ===
|
|
787
|
-
|
|
788
|
-
}
|
|
789
|
-
else if (walletPaymentOption.required.token.token === rozoStellarUSDC.token) {
|
|
790
|
-
destAsset = new Asset(walletPaymentOption.required.token.symbol, rozoStellarUSDC.token.split(":")[1]);
|
|
696
|
+
let issuer = "";
|
|
697
|
+
if (walletPaymentOption.required.token.token === rozoStellarUSDC.token) {
|
|
698
|
+
issuer = rozoStellarUSDC.token.split(":")[1];
|
|
791
699
|
}
|
|
792
700
|
else if (walletPaymentOption.required.token.token === rozoStellarEURC.token) {
|
|
793
|
-
|
|
701
|
+
issuer = rozoStellarEURC.token.split(":")[1];
|
|
794
702
|
}
|
|
795
703
|
else {
|
|
796
704
|
throw new Error("Unsupported token");
|
|
797
705
|
}
|
|
706
|
+
const destAsset = new Asset(walletPaymentOption.required.token.symbol, issuer);
|
|
798
707
|
const fee = String(await stellarServer.fetchBaseFee());
|
|
799
|
-
// Use required.amount (base units, already priced by the backend)
|
|
800
|
-
// converted to a human-readable token amount — NOT required.usd,
|
|
801
|
-
// which only equals the token amount for 1:1-USD-pegged tokens
|
|
802
|
-
// (USDC/EURC). For native XLM this would send the wrong amount
|
|
803
|
-
// (e.g. "1.00" XLM instead of the correct ~2.7 XLM for a $1 payment).
|
|
804
|
-
// Some endpoints return required.amount as an already-human-readable
|
|
805
|
-
// decimal string instead of integer base units, so detect the shape
|
|
806
|
-
// first — BigInt() throws on decimal strings.
|
|
807
|
-
const paymentAmount = tokenBaseAmountToDecimalString(walletPaymentOption.required.amount, walletPaymentOption.required.token.decimals);
|
|
808
708
|
// Build transaction
|
|
809
709
|
const transaction = new TransactionBuilder(sourceAccount, {
|
|
810
710
|
fee,
|
|
@@ -813,7 +713,7 @@ function usePaymentState({ trpc, lockPayParams, setRoute, log, redirectReturnUrl
|
|
|
813
713
|
.addOperation(Operation.payment({
|
|
814
714
|
destination: destinationAddress,
|
|
815
715
|
asset: destAsset,
|
|
816
|
-
amount:
|
|
716
|
+
amount: String(walletPaymentOption.required.usd),
|
|
817
717
|
}))
|
|
818
718
|
.setTimeout(180);
|
|
819
719
|
if (rozoPayment.memo) {
|
|
@@ -888,29 +788,17 @@ function usePaymentState({ trpc, lockPayParams, setRoute, log, redirectReturnUrl
|
|
|
888
788
|
}
|
|
889
789
|
else {
|
|
890
790
|
log?.("[PAY DEPOSIT ADDRESS] hydrating order");
|
|
891
|
-
// hydrateOrder needs required.amount so buildCreatePaymentPayload
|
|
892
|
-
// can set preferredAmountUnits. For BOTH native (SOL/ETH/XLM) and
|
|
893
|
-
// 1:1-USD-pegged sources, the amount must come from the fee response
|
|
894
|
-
// (BE-computed source amount in source units, fee-inclusive) — NOT the
|
|
895
|
-
// destination USD value. Otherwise tokenBaseAmountToDecimalString
|
|
896
|
-
// formats the toUnits through the source decimals and the BE rejects
|
|
897
|
-
// the underflowing pay-in (e.g. "10" with 6 decimals → "0.00001").
|
|
898
|
-
const isNativeSource = isNativeToken(option.token.token);
|
|
899
|
-
const sourceAmountUnits = String(fees?.source?.amount ??
|
|
900
|
-
(isNativeSource ? "" : (pay.order?.destFinalCallTokenAmount?.usd ?? "0")));
|
|
901
|
-
if (isNativeSource && sourceAmountUnits === "") {
|
|
902
|
-
throw new Error("Fee source amount required for native token deposit");
|
|
903
|
-
}
|
|
904
|
-
const actualFeeType = payParams?.feeType ?? FeeType.ExactIn;
|
|
905
791
|
const result = await pay.hydrateOrder(undefined, {
|
|
906
792
|
required: {
|
|
907
|
-
|
|
908
|
-
|
|
793
|
+
token: {
|
|
794
|
+
token: option.token.token,
|
|
795
|
+
chainId: option.token.chainId,
|
|
796
|
+
},
|
|
909
797
|
},
|
|
910
798
|
fees: {
|
|
911
799
|
usd: fees?.source?.fee != null ? parseFloat(fees.source.fee) : 0,
|
|
912
800
|
},
|
|
913
|
-
}
|
|
801
|
+
});
|
|
914
802
|
order = result.order;
|
|
915
803
|
}
|
|
916
804
|
log?.(`[PAY DEPOSIT ADDRESS] hydrated order ${order.id} for ${order.usdValue} USD, checking out with deposit address: ${option}`);
|
|
@@ -927,23 +815,12 @@ function usePaymentState({ trpc, lockPayParams, setRoute, log, redirectReturnUrl
|
|
|
927
815
|
if (!preferredToken) {
|
|
928
816
|
throw new Error("Preferred token not found");
|
|
929
817
|
}
|
|
930
|
-
// Amount the user must send, in source-token units. Prefer the backend's
|
|
931
|
-
// source.amount (surfaced via metadata by formatPaymentResponseToHydratedOrder)
|
|
932
|
-
// over usdValue: for native sources (SOL/ETH/XLM) the source amount differs
|
|
933
|
-
// from the USD/destination value, so usdValue would show e.g. "1.3 SOL"
|
|
934
|
-
// instead of the correct ~0.0169 SOL. For 1:1-USD sources they coincide.
|
|
935
|
-
const isNativeDepositSource = isNativeToken(option.token.token);
|
|
936
|
-
const metaSourceAmount = order.metadata?.sourceAmountUnits;
|
|
937
|
-
if (isNativeDepositSource && metaSourceAmount == null) {
|
|
938
|
-
throw new Error("sourceAmountUnits missing on hydrated order for native deposit source");
|
|
939
|
-
}
|
|
940
|
-
const sourceAmountUnits = metaSourceAmount != null ? String(metaSourceAmount) : String(order.usdValue);
|
|
941
818
|
let uriDeeplink = null;
|
|
942
819
|
// Use Solana deep link if it's a Solana chain.
|
|
943
820
|
// Solana pay-in no longer requires a memo.
|
|
944
821
|
if ([solana.chainId, rozoSolana.chainId].includes(preferredToken.chainId)) {
|
|
945
822
|
uriDeeplink = generateSolanaDeepLink({
|
|
946
|
-
amountUnits:
|
|
823
|
+
amountUnits: order.destFinalCallTokenAmount.usd.toString(),
|
|
947
824
|
recipientAddress: order.intentAddr,
|
|
948
825
|
tokenAddress: preferredToken.token,
|
|
949
826
|
});
|
|
@@ -955,7 +832,7 @@ function usePaymentState({ trpc, lockPayParams, setRoute, log, redirectReturnUrl
|
|
|
955
832
|
// Otherwise use EVM deep link
|
|
956
833
|
else {
|
|
957
834
|
uriDeeplink = generateEVMDeepLink({
|
|
958
|
-
amountUnits: parseUnits(
|
|
835
|
+
amountUnits: parseUnits(order.destFinalCallTokenAmount.usd.toString(), preferredToken.decimals).toString(),
|
|
959
836
|
chainId: preferredToken.chainId,
|
|
960
837
|
recipientAddress: order.intentAddr,
|
|
961
838
|
tokenAddress: preferredToken.token,
|
|
@@ -963,7 +840,7 @@ function usePaymentState({ trpc, lockPayParams, setRoute, log, redirectReturnUrl
|
|
|
963
840
|
}
|
|
964
841
|
return {
|
|
965
842
|
address: order.intentAddr,
|
|
966
|
-
amount:
|
|
843
|
+
amount: String(order.usdValue),
|
|
967
844
|
suffix: `${option.token.symbol} ${chain.name}`,
|
|
968
845
|
uri: uriDeeplink ?? "",
|
|
969
846
|
expirationS: Math.floor(Date.now() / 1000) + 300,
|
|
@@ -978,7 +855,7 @@ function usePaymentState({ trpc, lockPayParams, setRoute, log, redirectReturnUrl
|
|
|
978
855
|
order: pay.order,
|
|
979
856
|
message,
|
|
980
857
|
});
|
|
981
|
-
return
|
|
858
|
+
return null;
|
|
982
859
|
}
|
|
983
860
|
finally {
|
|
984
861
|
// Remove from processing set when done (allow retries after completion/failure)
|