@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.
Files changed (59) hide show
  1. package/build/components/Common/TokenChainLogo/index.d.ts +1 -7
  2. package/build/components/Spinners/TokenLogoSpinner/index.d.ts +1 -6
  3. package/build/hooks/useDepositAddressOptions.d.ts +2 -3
  4. package/build/hooks/usePaymentState.d.ts +5 -11
  5. package/build/hooks/useRozoPay.d.ts +4 -4
  6. package/build/hooks/useSolanaPaymentOptions.d.ts +1 -1
  7. package/build/hooks/useStellarPaymentOptions.d.ts +1 -1
  8. package/build/hooks/useWalletPaymentOptions.d.ts +7 -12
  9. package/build/package.json.js +2 -2
  10. package/build/payment/createPaymentPayload.d.ts +3 -3
  11. package/build/payment/paymentFsm.d.ts +2 -25
  12. package/build/provider/PayContext.d.ts +3 -3
  13. package/build/src/components/Common/AmountInput/index.js +1 -1
  14. package/build/src/components/Common/AmountInput/index.js.map +1 -1
  15. package/build/src/components/Common/ConnectorList/index.js +3 -2
  16. package/build/src/components/Common/ConnectorList/index.js.map +1 -1
  17. package/build/src/components/Common/TokenChainLogo/index.js +3 -26
  18. package/build/src/components/Common/TokenChainLogo/index.js.map +1 -1
  19. package/build/src/components/Pages/PayWithToken/index.js +4 -15
  20. package/build/src/components/Pages/PayWithToken/index.js.map +1 -1
  21. package/build/src/components/Pages/SelectDepositAddressChain/index.js +17 -6
  22. package/build/src/components/Pages/SelectDepositAddressChain/index.js.map +1 -1
  23. package/build/src/components/Pages/SelectMethod/index.js.map +1 -1
  24. package/build/src/components/Pages/Solana/PayWithSolanaToken/index.js +26 -64
  25. package/build/src/components/Pages/Solana/PayWithSolanaToken/index.js.map +1 -1
  26. package/build/src/components/Pages/Stellar/PayWithStellarToken/index.js +38 -40
  27. package/build/src/components/Pages/Stellar/PayWithStellarToken/index.js.map +1 -1
  28. package/build/src/components/Pages/WaitingDepositAddress/index.js +33 -110
  29. package/build/src/components/Pages/WaitingDepositAddress/index.js.map +1 -1
  30. package/build/src/components/RozoPayModal/index.js +31 -3
  31. package/build/src/components/RozoPayModal/index.js.map +1 -1
  32. package/build/src/components/Spinners/TokenLogoSpinner/index.js +2 -5
  33. package/build/src/components/Spinners/TokenLogoSpinner/index.js.map +1 -1
  34. package/build/src/hooks/useDepositAddressOptions.js +9 -19
  35. package/build/src/hooks/useDepositAddressOptions.js.map +1 -1
  36. package/build/src/hooks/usePaymentState.js +72 -195
  37. package/build/src/hooks/usePaymentState.js.map +1 -1
  38. package/build/src/hooks/useRozoPay.js +2 -4
  39. package/build/src/hooks/useRozoPay.js.map +1 -1
  40. package/build/src/hooks/useSolanaPaymentOptions.js +12 -45
  41. package/build/src/hooks/useSolanaPaymentOptions.js.map +1 -1
  42. package/build/src/hooks/useStellarPaymentOptions.js +10 -40
  43. package/build/src/hooks/useStellarPaymentOptions.js.map +1 -1
  44. package/build/src/hooks/useTokenOptions.js +3 -4
  45. package/build/src/hooks/useTokenOptions.js.map +1 -1
  46. package/build/src/hooks/useWalletPaymentOptions.js +21 -54
  47. package/build/src/hooks/useWalletPaymentOptions.js.map +1 -1
  48. package/build/src/payment/createPaymentPayload.js +13 -38
  49. package/build/src/payment/createPaymentPayload.js.map +1 -1
  50. package/build/src/payment/paymentEffects.js +5 -4
  51. package/build/src/payment/paymentEffects.js.map +1 -1
  52. package/build/src/payment/paymentFsm.js.map +1 -1
  53. package/build/src/utils/format.js +13 -82
  54. package/build/src/utils/format.js.map +1 -1
  55. package/build/src/utils/token.js +5 -21
  56. package/build/src/utils/token.js.map +1 -1
  57. package/build/utils/format.d.ts +3 -44
  58. package/build/utils/token.d.ts +2 -6
  59. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"useDepositAddressOptions.js","sources":["../../../src/hooks/useDepositAddressOptions.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useDepositAddressOptions.js","sources":["../../../src/hooks/useDepositAddressOptions.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
@@ -1,11 +1,10 @@
1
- import { ExternalPaymentOptions, rozoStellar, TokenSymbol, assert, isNativeToken, getPayment, checkoutPayment, buildCheckoutPayload, createPayment, debugJson, normalizeTokenAddress, formatPaymentResponseToHydratedOrder, bscUSDT, assertNotNull, isValidSolanaAddress, solanaSOL, stellarXLM, rozoStellarUSDC, rozoStellarEURC, solana, rozoSolana, stellar, FeeType, getChainById, getKnownToken, generateSolanaDeepLink, generateEVMDeepLink, writeRozoPayOrderID } from '@rozoai/intent-common';
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, SystemProgram, Transaction } from '@solana/web3.js';
3
+ import { VersionedTransaction, PublicKey, Transaction } from '@solana/web3.js';
4
4
  import { useRef, useState, useEffect, useCallback, useMemo } from 'react';
5
- import { getAddress, erc20Abi, hexToBytes, parseUnits } from 'viem';
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, allow paying with USDC/USDT/native tokens
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 must be
171
- // paid with EURC only. Everything else defaults to USDC + USDT plus
172
- // native tokens as payable sources (payout is still fixed to
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
- const rotateToNative = walletOption != null && isNativeToken(walletOption.required.token.token);
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: sourceAmount,
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
- // Use required.amount (base units, already priced by the backend) —
369
- // NOT required.usd, which only equals the token amount for 1:1-USD-pegged
370
- // tokens (USDC/USDT). For native ETH/BNB/POL this would send the wrong
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/token check: previousChain=${previousChainId}, currentChain=${required.token.chainId}, previousToken=${previousTokenAddress}, currentToken=${required.token.token}, tokenChanged=${tokenChanged}, hasExistingPayment=${hasExistingPayment}, needRozoPayment=${needRozoPayment}`);
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
- // Case-insensitive native detection: `required.token.token` is checksummed
392
- // (from the API) while viem's `ethAddress` is lowercase, so a direct `===`
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 (required.token.chainId !== bscUSDT.chainId) {
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
- const isNativeSol = walletPaymentOption.required.token.token === solanaSOL.token;
651
- if (isNativeSol) {
652
- if (!rozoPayment.sourceAmount) {
653
- throw new Error("Source amount is required for native SOL payment");
654
- }
655
- // Native SOL: plain lamport transfer, no token account / mint involved.
656
- // Use required.amount (already computed by the backend in lamports,
657
- // correctly priced from USD via the live SOL/USD rate) instead of
658
- // re-deriving from required.usd, which would incorrectly treat SOL
659
- // as pegged 1:1 to USD.
660
- log("[PAY SOLANA] Transaction details (native SOL):", {
661
- fromKey: fromKey.toString(),
662
- toKey: toKey.toString(),
663
- amount: walletPaymentOption.required.amount,
664
- usd: walletPaymentOption.required.usd,
665
- memo: rozoPayment.memo,
666
- });
667
- // Keep as bigint — SystemProgram.transfer accepts number | bigint.
668
- // Converting to Number would lose precision for amounts > 2^53 base units.
669
- // Always follow Rozo Payment Source.Amount as the source of truth for SOL amounts.
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 destAsset;
786
- if (walletPaymentOption.required.token.token === stellarXLM.token) {
787
- destAsset = Asset.native();
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
- destAsset = new Asset(walletPaymentOption.required.token.symbol, rozoStellarEURC.token.split(":")[1]);
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: paymentAmount,
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
- amount: sourceAmountUnits,
908
- token: option.token,
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
- }, actualFeeType);
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: sourceAmountUnits,
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(sourceAmountUnits, preferredToken.decimals).toString(),
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: sourceAmountUnits,
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 undefined;
858
+ return null;
982
859
  }
983
860
  finally {
984
861
  // Remove from processing set when done (allow retries after completion/failure)