@b3dotfun/sdk 0.1.70-alpha.16 → 0.1.70-alpha.17

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.
@@ -5,6 +5,7 @@ const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const anyspend_1 = require("../../../anyspend");
6
6
  const react_1 = require("../../../global-account/react");
7
7
  const cn_1 = require("../../../shared/utils/cn");
8
+ const number_1 = require("../../../shared/utils/number");
8
9
  const react_2 = require("@web3icons/react");
9
10
  const lucide_react_1 = require("lucide-react");
10
11
  const react_3 = require("react");
@@ -228,7 +229,9 @@ function AnySpendDeposit({ loadOrder, mode = "modal", recipientAddress, paymentT
228
229
  "anyspend-deposit-option-button anyspend-deposit-crypto-button border-border-primary hover:border-as-brand hover:bg-as-surface-secondary flex w-full items-center justify-between rounded-xl border p-4 text-left shadow-sm transition-all", children: [(0, jsx_runtime_1.jsx)("div", { className: depositClasses?.optionContent || "anyspend-deposit-option-content", children: (0, jsx_runtime_1.jsxs)("div", { className: depositClasses?.optionInfo || "anyspend-deposit-option-info", children: [(0, jsx_runtime_1.jsx)("span", { className: depositClasses?.optionTitle || "anyspend-deposit-option-title text-as-primary font-medium", children: "Deposit Crypto" }), (0, jsx_runtime_1.jsx)("p", { className: depositClasses?.optionDescription ||
229
230
  "anyspend-deposit-option-description text-as-secondary text-xs", children: "Swap from any token on any chain" })] }) }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { className: depositClasses?.optionChevron || "anyspend-deposit-option-chevron text-as-secondary h-5 w-5" })] }), (0, jsx_runtime_1.jsxs)("div", { className: depositClasses?.divider || "anyspend-deposit-divider flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)("div", { className: depositClasses?.dividerLine || "bg-as-stroke h-px flex-1" }), (0, jsx_runtime_1.jsx)("span", { className: depositClasses?.dividerText || "anyspend-deposit-divider-text text-as-secondary text-sm", children: "More options" }), (0, jsx_runtime_1.jsx)("div", { className: depositClasses?.dividerLine || "bg-as-stroke h-px flex-1" })] }), (0, jsx_runtime_1.jsxs)("button", { onClick: handleSelectQrDeposit, className: depositClasses?.qrButton ||
230
231
  "anyspend-deposit-option-button anyspend-deposit-qr-button border-border-primary hover:border-as-brand hover:bg-as-surface-secondary flex w-full items-center justify-between rounded-xl border p-4 text-left shadow-sm transition-all", children: [(0, jsx_runtime_1.jsxs)("div", { className: depositClasses?.optionContent || "anyspend-deposit-option-content flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)(QrCodeIcon_1.QrCodeIcon, { className: depositClasses?.optionIcon || "anyspend-deposit-option-icon h-10 w-10" }), (0, jsx_runtime_1.jsxs)("div", { className: depositClasses?.optionInfo || "anyspend-deposit-option-info", children: [(0, jsx_runtime_1.jsx)("span", { className: depositClasses?.optionTitle || "anyspend-deposit-option-title text-as-primary font-medium", children: "Deposit with QR Code" }), (0, jsx_runtime_1.jsx)("p", { className: depositClasses?.optionDescription ||
231
- "anyspend-deposit-option-description text-as-secondary text-xs", children: "Send tokens directly to deposit address" })] })] }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { className: depositClasses?.optionChevron || "anyspend-deposit-option-chevron text-as-secondary h-5 w-5" })] }), showFiatOption && ((0, jsx_runtime_1.jsxs)("button", { onClick: handleSelectFiat, className: depositClasses?.fiatButton ||
232
+ "anyspend-deposit-option-description text-as-secondary text-xs", children: pureTransferOnly
233
+ ? "Send tokens directly to deposit address"
234
+ : "Send any token — we'll convert it for you" })] })] }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { className: depositClasses?.optionChevron || "anyspend-deposit-option-chevron text-as-secondary h-5 w-5" })] }), showFiatOption && ((0, jsx_runtime_1.jsxs)("button", { onClick: handleSelectFiat, className: depositClasses?.fiatButton ||
232
235
  "anyspend-deposit-option-button anyspend-deposit-fiat-button border-border-primary hover:border-as-brand hover:bg-as-surface-secondary flex w-full items-center justify-between rounded-xl border p-4 text-left transition-all", children: [(0, jsx_runtime_1.jsxs)("div", { className: depositClasses?.optionContent || "anyspend-deposit-option-content flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)(CreditCardIcon_1.CreditCardIcon, { className: depositClasses?.optionIcon || "anyspend-deposit-option-icon h-10 w-10" }), (0, jsx_runtime_1.jsxs)("div", { className: depositClasses?.optionInfo || "anyspend-deposit-option-info", children: [(0, jsx_runtime_1.jsx)("span", { className: depositClasses?.optionTitle || "anyspend-deposit-option-title text-as-primary font-medium", children: "Fund with Fiat" }), (0, jsx_runtime_1.jsx)("p", { className: depositClasses?.optionDescription ||
233
236
  "anyspend-deposit-option-description text-as-secondary text-xs", children: "Pay with card or bank transfer" })] })] }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronRight, { className: depositClasses?.optionChevron || "anyspend-deposit-option-chevron text-as-secondary h-5 w-5" })] }))] }), (0, jsx_runtime_1.jsx)(WarningText_1.ChainWarningText, { chainId: destinationTokenChainId, classes: classes?.chainWarningText || { root: "mt-2" } })] })] }));
234
237
  }
@@ -245,10 +248,21 @@ function AnySpendDeposit({ loadOrder, mode = "modal", recipientAddress, paymentT
245
248
  // `KNOWN_FUNDING_TOKENS` key — so `hasKnownDestination` is always true and this
246
249
  // branch never fires for them. The guard is defensive for FUTURE callers that pass
247
250
  // an unknown token together with `pureTransferOnly=true`.
251
+ // Only surface the fixed destination amount on the QR path when its decimals are trustworthy
252
+ // (a known funding token or a resolved API lookup) — otherwise `destinationToken.decimals`
253
+ // is the generic 18 fallback and formatting a 6-decimal token's wei against it would be off by
254
+ // 10^12. When unresolved we still show the destination token/chain, just without an amount.
255
+ const destinationDecimalsResolved = hasKnownDestination || !!destinationTokenData;
256
+ const destinationAmountDisplay = destinationTokenAmount &&
257
+ /^\d+$/.test(destinationTokenAmount) &&
258
+ BigInt(destinationTokenAmount) > 0n &&
259
+ destinationDecimalsResolved
260
+ ? (0, number_1.formatTokenAmount)(BigInt(destinationTokenAmount), destinationToken.decimals, 6, false)
261
+ : undefined;
248
262
  if (pureTransferOnly && !hasKnownDestination && !destinationTokenData && isDestinationTokenLoading) {
249
263
  return ((0, jsx_runtime_1.jsx)("div", { className: (0, cn_1.cn)("anyspend-deposit anyspend-deposit-qr-loading font-inter bg-as-surface-primary mx-auto w-full max-w-[460px] p-6", mode === "page" && "border-as-border-secondary overflow-hidden rounded-2xl border shadow-xl"), children: (0, jsx_runtime_1.jsxs)("div", { className: "anyspend-deposit-qr-loading-content flex flex-col items-center justify-center gap-4 py-12", children: [(0, jsx_runtime_1.jsx)(react_1.Skeleton, { className: "h-8 w-8 rounded-full" }), (0, jsx_runtime_1.jsx)(react_1.Skeleton, { className: "h-4 w-40" })] }) }));
250
264
  }
251
- return ((0, jsx_runtime_1.jsx)(QRDeposit_1.QRDeposit, { mode: mode, recipientAddress: recipientAddress, destinationToken: destinationToken, destinationChainId: destinationTokenChainId, pureTransferOnly: pureTransferOnly, depositContractConfig: depositContractConfig, onBack: handleBack, onClose: onClose ?? handleBack, onSuccess: handleQRDepositSuccess, classes: classes?.qrDeposit }));
265
+ return ((0, jsx_runtime_1.jsx)(QRDeposit_1.QRDeposit, { mode: mode, recipientAddress: recipientAddress, destinationToken: destinationToken, destinationChainId: destinationTokenChainId, destinationAmountDisplay: destinationAmountDisplay, pureTransferOnly: pureTransferOnly, depositContractConfig: depositContractConfig, onBack: handleBack, onClose: onClose ?? handleBack, onSuccess: handleQRDepositSuccess, classes: classes?.qrDeposit }));
252
266
  }
253
267
  // Deposit view
254
268
  return ((0, jsx_runtime_1.jsxs)("div", { className: depositClasses?.form || "anyspend-deposit anyspend-deposit-form relative", children: [shouldShowChainSelection && ((0, jsx_runtime_1.jsxs)("button", { onClick: handleBack, className: depositClasses?.backButton ||
@@ -14,6 +14,14 @@ export interface QRDepositProps {
14
14
  destinationToken: components["schemas"]["Token"];
15
15
  /** The destination chain ID */
16
16
  destinationChainId: number;
17
+ /**
18
+ * Human-formatted destination amount (e.g. "9.365547"), shown as an APPROXIMATE
19
+ * target in swap mode only. The QR deposit path accepts an arbitrary sent amount,
20
+ * so this is never a guaranteed receive figure — the UI frames it as "any amount
21
+ * works". Omitted when no fixed amount is set or the destination decimals are
22
+ * unresolved (to avoid a mis-scaled number). Ignored in pure-transfer mode.
23
+ */
24
+ destinationAmountDisplay?: string;
17
25
  /**
18
26
  * When true, the deposit is a PURE TRANSFER: the destination token/chain mirror
19
27
  * the user's currently selected source token/chain, so the exact token the user
@@ -54,4 +62,4 @@ export interface QRDepositProps {
54
62
  * onSuccess={(txHash) => console.log("Deposit complete:", txHash)}
55
63
  * />
56
64
  */
57
- export declare function QRDeposit({ mode, recipientAddress, sourceToken: sourceTokenProp, sourceChainId: sourceChainIdProp, destinationToken, destinationChainId, pureTransferOnly, creatorAddress, depositContractConfig, onBack, onClose, onOrderCreated, onSuccess, classes, }: QRDepositProps): import("react/jsx-runtime").JSX.Element;
65
+ export declare function QRDeposit({ mode, recipientAddress, sourceToken: sourceTokenProp, sourceChainId: sourceChainIdProp, destinationToken, destinationChainId, destinationAmountDisplay, pureTransferOnly, creatorAddress, depositContractConfig, onBack, onClose, onOrderCreated, onSuccess, classes, }: QRDepositProps): import("react/jsx-runtime").JSX.Element;
@@ -42,7 +42,7 @@ const DEFAULT_ETH_ON_BASE = {
42
42
  * onSuccess={(txHash) => console.log("Deposit complete:", txHash)}
43
43
  * />
44
44
  */
45
- function QRDeposit({ mode = "modal", recipientAddress, sourceToken: sourceTokenProp, sourceChainId: sourceChainIdProp, destinationToken, destinationChainId, pureTransferOnly = false, creatorAddress, depositContractConfig, onBack, onClose, onOrderCreated, onSuccess, classes, }) {
45
+ function QRDeposit({ mode = "modal", recipientAddress, sourceToken: sourceTokenProp, sourceChainId: sourceChainIdProp, destinationToken, destinationChainId, destinationAmountDisplay, pureTransferOnly = false, creatorAddress, depositContractConfig, onBack, onClose, onOrderCreated, onSuccess, classes, }) {
46
46
  const [copied, setCopied] = (0, react_2.useState)(false);
47
47
  const [orderId, setOrderId] = (0, react_2.useState)();
48
48
  const [globalAddress, setGlobalAddress] = (0, react_2.useState)();
@@ -174,8 +174,8 @@ function QRDeposit({ mode = "modal", recipientAddress, sourceToken: sourceTokenP
174
174
  }
175
175
  return ((0, jsx_runtime_1.jsx)("div", { className: classes?.container ||
176
176
  (0, cn_1.cn)("anyspend-container anyspend-qr-deposit font-inter bg-as-surface-primary mx-auto w-full max-w-[460px] p-6", mode === "page" && "border-as-border-secondary overflow-hidden rounded-2xl border shadow-xl"), children: (0, jsx_runtime_1.jsxs)("div", { className: classes?.content || "anyspend-qr-deposit-content flex flex-col gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: classes?.header || "anyspend-qr-header flex items-center justify-between", children: [(0, jsx_runtime_1.jsx)("button", { onClick: handleBack, className: classes?.backButton || "anyspend-qr-back-button text-as-secondary hover:text-as-primary", children: (0, jsx_runtime_1.jsx)("svg", { className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) }) }), (0, jsx_runtime_1.jsx)("h2", { className: classes?.title || "anyspend-qr-title text-as-primary text-base font-semibold", children: "Deposit" }), onClose ? ((0, jsx_runtime_1.jsx)("button", { onClick: handleClose, className: classes?.closeButton || "anyspend-qr-close-button text-as-secondary hover:text-as-primary", children: (0, jsx_runtime_1.jsx)("svg", { className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })) : ((0, jsx_runtime_1.jsx)("div", { className: "w-5" }))] }), (0, jsx_runtime_1.jsxs)("div", { className: classes?.tokenSelectorContainer || "anyspend-qr-token-selector flex flex-col gap-1.5", children: [(0, jsx_runtime_1.jsx)("label", { className: classes?.tokenSelectorLabel || "anyspend-qr-token-label text-as-secondary text-sm", children: "Send" }), (0, jsx_runtime_1.jsx)(relay_kit_ui_1.TokenSelector, { chainIdsFilter: (0, anyspend_1.getAvailableChainIds)("from"), context: "from", fromChainWalletVMSupported: true, isValidAddress: true, lockedChainIds: (0, anyspend_1.getAvailableChainIds)("from"), multiWalletSupportEnabled: true, onAnalyticEvent: undefined, setToken: handleTokenSelect, supportedWalletVMs: ["evm"], token: undefined, trigger: (0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "outline", role: "combobox", className: classes?.tokenSelectorTrigger ||
177
- "anyspend-qr-token-trigger border-as-stroke bg-as-surface-secondary flex h-auto w-full items-center justify-between gap-2 rounded-xl border px-3 py-2.5", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [sourceToken.metadata?.logoURI ? ((0, jsx_runtime_1.jsx)(ChainTokenIcon_1.ChainTokenIcon, { chainUrl: anyspend_1.ALL_CHAINS[sourceChainId]?.logoUrl, tokenUrl: sourceToken.metadata.logoURI, className: "h-8 min-h-8 w-8 min-w-8" })) : ((0, jsx_runtime_1.jsx)("div", { className: "h-8 w-8 rounded-full bg-gray-700" })), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-start gap-0", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-as-primary font-semibold", children: sourceToken.symbol }), (0, jsx_runtime_1.jsx)("div", { className: "text-as-primary/70 text-xs", children: anyspend_1.ALL_CHAINS[sourceChainId]?.name ?? "Unknown" })] })] }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-70" })] }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: classes?.qrContent || "anyspend-qr-content border-as-stroke flex items-start gap-4 rounded-xl border p-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: classes?.qrCodeContainer || "anyspend-qr-code-container flex flex-col items-center gap-2", children: [(0, jsx_runtime_1.jsx)("div", { className: classes?.qrCode || "anyspend-qr-code rounded-lg bg-white p-2", children: (0, jsx_runtime_1.jsx)(qrcode_react_1.QRCodeSVG, { value: qrValue, size: 120, level: "M", marginSize: 0 }) }), (0, jsx_runtime_1.jsxs)("span", { className: classes?.qrScanHint || "anyspend-qr-scan-hint text-as-secondary text-xs", children: ["SCAN WITH ", (0, jsx_runtime_1.jsx)("span", { className: "inline-block", children: "\uD83E\uDD8A" })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: classes?.addressContainer || "anyspend-qr-address-container flex flex-1 flex-col gap-1", children: [(0, jsx_runtime_1.jsx)("span", { className: classes?.addressLabel || "anyspend-qr-address-label text-as-secondary text-sm", children: "Deposit address:" }), (0, jsx_runtime_1.jsxs)("div", { className: classes?.addressRow || "anyspend-qr-address-row flex items-start gap-1", children: [(0, jsx_runtime_1.jsx)("span", { className: classes?.address || "anyspend-qr-address text-as-primary break-all font-mono text-sm leading-relaxed", children: displayAddress }), (0, jsx_runtime_1.jsx)("button", { onClick: handleCopyAddress, className: classes?.addressCopyIcon ||
178
- "anyspend-qr-copy-icon text-as-secondary hover:text-as-primary mt-0.5 shrink-0", children: copied ? (0, jsx_runtime_1.jsx)(lucide_react_1.Check, { className: "h-4 w-4" }) : (0, jsx_runtime_1.jsx)(lucide_react_1.Copy, { className: "h-4 w-4" }) })] })] })] }), (0, jsx_runtime_1.jsx)(WarningText_1.ChainWarningText, { chainId: effectiveDestinationChainId }), (0, jsx_runtime_1.jsxs)(WarningText_1.WarningText, { children: ["Only send ", sourceToken.symbol, " on ", anyspend_1.ALL_CHAINS[sourceChainId]?.name ?? "the specified chain", ". Other tokens will not be converted."] }), isPureTransfer && isWatchingTransfer && ((0, jsx_runtime_1.jsxs)("div", { className: classes?.watchingIndicator ||
177
+ "anyspend-qr-token-trigger border-as-stroke bg-as-surface-secondary flex h-auto w-full items-center justify-between gap-2 rounded-xl border px-3 py-2.5", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [sourceToken.metadata?.logoURI ? ((0, jsx_runtime_1.jsx)(ChainTokenIcon_1.ChainTokenIcon, { chainUrl: anyspend_1.ALL_CHAINS[sourceChainId]?.logoUrl, tokenUrl: sourceToken.metadata.logoURI, className: "h-8 min-h-8 w-8 min-w-8" })) : ((0, jsx_runtime_1.jsx)("div", { className: "h-8 w-8 rounded-full bg-gray-700" })), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-start gap-0", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-as-primary font-semibold", children: sourceToken.symbol }), (0, jsx_runtime_1.jsx)("div", { className: "text-as-primary/70 text-xs", children: anyspend_1.ALL_CHAINS[sourceChainId]?.name ?? "Unknown" })] })] }), (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-70" })] }) })] }), !isPureTransfer && ((0, jsx_runtime_1.jsxs)("div", { className: "anyspend-qr-receive flex flex-col gap-1.5", children: [(0, jsx_runtime_1.jsx)("label", { className: "anyspend-qr-receive-label text-as-secondary text-sm", children: "Receive" }), (0, jsx_runtime_1.jsxs)("div", { className: "anyspend-qr-receive-row border-as-stroke bg-as-surface-secondary flex w-full items-center gap-2 rounded-xl border px-3 py-2.5", children: [effectiveDestinationToken.metadata?.logoURI ? ((0, jsx_runtime_1.jsx)(ChainTokenIcon_1.ChainTokenIcon, { chainUrl: anyspend_1.ALL_CHAINS[effectiveDestinationChainId]?.logoUrl, tokenUrl: effectiveDestinationToken.metadata.logoURI, className: "h-8 min-h-8 w-8 min-w-8" })) : ((0, jsx_runtime_1.jsx)("div", { className: "h-8 w-8 rounded-full bg-gray-700" })), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-start gap-0", children: [(0, jsx_runtime_1.jsx)("div", { className: "text-as-primary font-semibold", children: effectiveDestinationToken.symbol || "Token" }), (0, jsx_runtime_1.jsx)("div", { className: "text-as-primary/70 text-xs", children: anyspend_1.ALL_CHAINS[effectiveDestinationChainId]?.name ?? "Unknown" })] })] })] })), (0, jsx_runtime_1.jsxs)("div", { className: classes?.qrContent || "anyspend-qr-content border-as-stroke flex items-start gap-4 rounded-xl border p-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: classes?.qrCodeContainer || "anyspend-qr-code-container flex flex-col items-center gap-2", children: [(0, jsx_runtime_1.jsx)("div", { className: classes?.qrCode || "anyspend-qr-code rounded-lg bg-white p-2", children: (0, jsx_runtime_1.jsx)(qrcode_react_1.QRCodeSVG, { value: qrValue, size: 120, level: "M", marginSize: 0 }) }), (0, jsx_runtime_1.jsxs)("span", { className: classes?.qrScanHint || "anyspend-qr-scan-hint text-as-secondary text-xs", children: ["SCAN WITH ", (0, jsx_runtime_1.jsx)("span", { className: "inline-block", children: "\uD83E\uDD8A" })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: classes?.addressContainer || "anyspend-qr-address-container flex flex-1 flex-col gap-1", children: [(0, jsx_runtime_1.jsx)("span", { className: classes?.addressLabel || "anyspend-qr-address-label text-as-secondary text-sm", children: "Deposit address:" }), (0, jsx_runtime_1.jsxs)("div", { className: classes?.addressRow || "anyspend-qr-address-row flex items-start gap-1", children: [(0, jsx_runtime_1.jsx)("span", { className: classes?.address || "anyspend-qr-address text-as-primary break-all font-mono text-sm leading-relaxed", children: displayAddress }), (0, jsx_runtime_1.jsx)("button", { onClick: handleCopyAddress, className: classes?.addressCopyIcon ||
178
+ "anyspend-qr-copy-icon text-as-secondary hover:text-as-primary mt-0.5 shrink-0", children: copied ? (0, jsx_runtime_1.jsx)(lucide_react_1.Check, { className: "h-4 w-4" }) : (0, jsx_runtime_1.jsx)(lucide_react_1.Copy, { className: "h-4 w-4" }) })] })] })] }), (0, jsx_runtime_1.jsx)(WarningText_1.ChainWarningText, { chainId: effectiveDestinationChainId }), isPureTransfer ? ((0, jsx_runtime_1.jsxs)(WarningText_1.WarningText, { children: ["Only send ", sourceToken.symbol, " on ", anyspend_1.ALL_CHAINS[sourceChainId]?.name ?? "the specified chain", ". Other tokens will not be converted."] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(WarningText_1.WarningText, { children: ["Only send ", sourceToken.symbol, " on ", anyspend_1.ALL_CHAINS[sourceChainId]?.name ?? "the specified chain", " to this address \u2014 it will be converted to ", effectiveDestinationToken.symbol || "the destination token", " on", " ", anyspend_1.ALL_CHAINS[effectiveDestinationChainId]?.name ?? "the destination chain", "."] }), destinationAmountDisplay && ((0, jsx_runtime_1.jsxs)("p", { className: "anyspend-qr-amount-hint text-as-secondary text-xs", children: ["Target \u2248 ", destinationAmountDisplay, " ", effectiveDestinationToken.symbol || "tokens", ". Any amount of", " ", sourceToken.symbol || "tokens", " works \u2014 you'll receive whatever your deposit converts to."] }))] })), isPureTransfer && isWatchingTransfer && ((0, jsx_runtime_1.jsxs)("div", { className: classes?.watchingIndicator ||
179
179
  "anyspend-qr-watching flex items-center justify-center gap-2 rounded-lg bg-blue-500/10 p-3", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "h-4 w-4 animate-spin text-blue-500" }), (0, jsx_runtime_1.jsx)("span", { className: "text-sm text-blue-500", children: "Watching for incoming transfer..." })] })), (0, jsx_runtime_1.jsx)("button", { onClick: handleCopyAddress, className: classes?.copyButton ||
180
180
  "anyspend-qr-copy-button flex w-full items-center justify-center gap-2 rounded-xl bg-blue-500 py-3.5 font-medium text-white transition-all hover:bg-blue-600", children: "Copy deposit address" })] }) }));
181
181
  }
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { USDC_BASE } from "../../../anyspend/index.js";
3
3
  import { Skeleton, useAccountWallet, useSimBalance, useTokenData } from "../../../global-account/react/index.js";
4
4
  import { cn } from "../../../shared/utils/cn.js";
5
+ import { formatTokenAmount } from "../../../shared/utils/number.js";
5
6
  import { NetworkArbitrumOne, NetworkBase, NetworkBinanceSmartChain, NetworkEthereum, NetworkOptimism, NetworkPolygonPos, } from "@web3icons/react";
6
7
  import { ChevronRight } from "lucide-react";
7
8
  import { useCallback, useEffect, useMemo, useState } from "react";
@@ -225,7 +226,9 @@ export function AnySpendDeposit({ loadOrder, mode = "modal", recipientAddress, p
225
226
  "anyspend-deposit-option-button anyspend-deposit-crypto-button border-border-primary hover:border-as-brand hover:bg-as-surface-secondary flex w-full items-center justify-between rounded-xl border p-4 text-left shadow-sm transition-all", children: [_jsx("div", { className: depositClasses?.optionContent || "anyspend-deposit-option-content", children: _jsxs("div", { className: depositClasses?.optionInfo || "anyspend-deposit-option-info", children: [_jsx("span", { className: depositClasses?.optionTitle || "anyspend-deposit-option-title text-as-primary font-medium", children: "Deposit Crypto" }), _jsx("p", { className: depositClasses?.optionDescription ||
226
227
  "anyspend-deposit-option-description text-as-secondary text-xs", children: "Swap from any token on any chain" })] }) }), _jsx(ChevronRight, { className: depositClasses?.optionChevron || "anyspend-deposit-option-chevron text-as-secondary h-5 w-5" })] }), _jsxs("div", { className: depositClasses?.divider || "anyspend-deposit-divider flex items-center gap-3", children: [_jsx("div", { className: depositClasses?.dividerLine || "bg-as-stroke h-px flex-1" }), _jsx("span", { className: depositClasses?.dividerText || "anyspend-deposit-divider-text text-as-secondary text-sm", children: "More options" }), _jsx("div", { className: depositClasses?.dividerLine || "bg-as-stroke h-px flex-1" })] }), _jsxs("button", { onClick: handleSelectQrDeposit, className: depositClasses?.qrButton ||
227
228
  "anyspend-deposit-option-button anyspend-deposit-qr-button border-border-primary hover:border-as-brand hover:bg-as-surface-secondary flex w-full items-center justify-between rounded-xl border p-4 text-left shadow-sm transition-all", children: [_jsxs("div", { className: depositClasses?.optionContent || "anyspend-deposit-option-content flex items-center gap-3", children: [_jsx(QrCodeIcon, { className: depositClasses?.optionIcon || "anyspend-deposit-option-icon h-10 w-10" }), _jsxs("div", { className: depositClasses?.optionInfo || "anyspend-deposit-option-info", children: [_jsx("span", { className: depositClasses?.optionTitle || "anyspend-deposit-option-title text-as-primary font-medium", children: "Deposit with QR Code" }), _jsx("p", { className: depositClasses?.optionDescription ||
228
- "anyspend-deposit-option-description text-as-secondary text-xs", children: "Send tokens directly to deposit address" })] })] }), _jsx(ChevronRight, { className: depositClasses?.optionChevron || "anyspend-deposit-option-chevron text-as-secondary h-5 w-5" })] }), showFiatOption && (_jsxs("button", { onClick: handleSelectFiat, className: depositClasses?.fiatButton ||
229
+ "anyspend-deposit-option-description text-as-secondary text-xs", children: pureTransferOnly
230
+ ? "Send tokens directly to deposit address"
231
+ : "Send any token — we'll convert it for you" })] })] }), _jsx(ChevronRight, { className: depositClasses?.optionChevron || "anyspend-deposit-option-chevron text-as-secondary h-5 w-5" })] }), showFiatOption && (_jsxs("button", { onClick: handleSelectFiat, className: depositClasses?.fiatButton ||
229
232
  "anyspend-deposit-option-button anyspend-deposit-fiat-button border-border-primary hover:border-as-brand hover:bg-as-surface-secondary flex w-full items-center justify-between rounded-xl border p-4 text-left transition-all", children: [_jsxs("div", { className: depositClasses?.optionContent || "anyspend-deposit-option-content flex items-center gap-3", children: [_jsx(CreditCardIcon, { className: depositClasses?.optionIcon || "anyspend-deposit-option-icon h-10 w-10" }), _jsxs("div", { className: depositClasses?.optionInfo || "anyspend-deposit-option-info", children: [_jsx("span", { className: depositClasses?.optionTitle || "anyspend-deposit-option-title text-as-primary font-medium", children: "Fund with Fiat" }), _jsx("p", { className: depositClasses?.optionDescription ||
230
233
  "anyspend-deposit-option-description text-as-secondary text-xs", children: "Pay with card or bank transfer" })] })] }), _jsx(ChevronRight, { className: depositClasses?.optionChevron || "anyspend-deposit-option-chevron text-as-secondary h-5 w-5" })] }))] }), _jsx(ChainWarningText, { chainId: destinationTokenChainId, classes: classes?.chainWarningText || { root: "mt-2" } })] })] }));
231
234
  }
@@ -242,10 +245,21 @@ export function AnySpendDeposit({ loadOrder, mode = "modal", recipientAddress, p
242
245
  // `KNOWN_FUNDING_TOKENS` key — so `hasKnownDestination` is always true and this
243
246
  // branch never fires for them. The guard is defensive for FUTURE callers that pass
244
247
  // an unknown token together with `pureTransferOnly=true`.
248
+ // Only surface the fixed destination amount on the QR path when its decimals are trustworthy
249
+ // (a known funding token or a resolved API lookup) — otherwise `destinationToken.decimals`
250
+ // is the generic 18 fallback and formatting a 6-decimal token's wei against it would be off by
251
+ // 10^12. When unresolved we still show the destination token/chain, just without an amount.
252
+ const destinationDecimalsResolved = hasKnownDestination || !!destinationTokenData;
253
+ const destinationAmountDisplay = destinationTokenAmount &&
254
+ /^\d+$/.test(destinationTokenAmount) &&
255
+ BigInt(destinationTokenAmount) > 0n &&
256
+ destinationDecimalsResolved
257
+ ? formatTokenAmount(BigInt(destinationTokenAmount), destinationToken.decimals, 6, false)
258
+ : undefined;
245
259
  if (pureTransferOnly && !hasKnownDestination && !destinationTokenData && isDestinationTokenLoading) {
246
260
  return (_jsx("div", { className: cn("anyspend-deposit anyspend-deposit-qr-loading font-inter bg-as-surface-primary mx-auto w-full max-w-[460px] p-6", mode === "page" && "border-as-border-secondary overflow-hidden rounded-2xl border shadow-xl"), children: _jsxs("div", { className: "anyspend-deposit-qr-loading-content flex flex-col items-center justify-center gap-4 py-12", children: [_jsx(Skeleton, { className: "h-8 w-8 rounded-full" }), _jsx(Skeleton, { className: "h-4 w-40" })] }) }));
247
261
  }
248
- return (_jsx(QRDeposit, { mode: mode, recipientAddress: recipientAddress, destinationToken: destinationToken, destinationChainId: destinationTokenChainId, pureTransferOnly: pureTransferOnly, depositContractConfig: depositContractConfig, onBack: handleBack, onClose: onClose ?? handleBack, onSuccess: handleQRDepositSuccess, classes: classes?.qrDeposit }));
262
+ return (_jsx(QRDeposit, { mode: mode, recipientAddress: recipientAddress, destinationToken: destinationToken, destinationChainId: destinationTokenChainId, destinationAmountDisplay: destinationAmountDisplay, pureTransferOnly: pureTransferOnly, depositContractConfig: depositContractConfig, onBack: handleBack, onClose: onClose ?? handleBack, onSuccess: handleQRDepositSuccess, classes: classes?.qrDeposit }));
249
263
  }
250
264
  // Deposit view
251
265
  return (_jsxs("div", { className: depositClasses?.form || "anyspend-deposit anyspend-deposit-form relative", children: [shouldShowChainSelection && (_jsxs("button", { onClick: handleBack, className: depositClasses?.backButton ||
@@ -14,6 +14,14 @@ export interface QRDepositProps {
14
14
  destinationToken: components["schemas"]["Token"];
15
15
  /** The destination chain ID */
16
16
  destinationChainId: number;
17
+ /**
18
+ * Human-formatted destination amount (e.g. "9.365547"), shown as an APPROXIMATE
19
+ * target in swap mode only. The QR deposit path accepts an arbitrary sent amount,
20
+ * so this is never a guaranteed receive figure — the UI frames it as "any amount
21
+ * works". Omitted when no fixed amount is set or the destination decimals are
22
+ * unresolved (to avoid a mis-scaled number). Ignored in pure-transfer mode.
23
+ */
24
+ destinationAmountDisplay?: string;
17
25
  /**
18
26
  * When true, the deposit is a PURE TRANSFER: the destination token/chain mirror
19
27
  * the user's currently selected source token/chain, so the exact token the user
@@ -54,4 +62,4 @@ export interface QRDepositProps {
54
62
  * onSuccess={(txHash) => console.log("Deposit complete:", txHash)}
55
63
  * />
56
64
  */
57
- export declare function QRDeposit({ mode, recipientAddress, sourceToken: sourceTokenProp, sourceChainId: sourceChainIdProp, destinationToken, destinationChainId, pureTransferOnly, creatorAddress, depositContractConfig, onBack, onClose, onOrderCreated, onSuccess, classes, }: QRDepositProps): import("react/jsx-runtime").JSX.Element;
65
+ export declare function QRDeposit({ mode, recipientAddress, sourceToken: sourceTokenProp, sourceChainId: sourceChainIdProp, destinationToken, destinationChainId, destinationAmountDisplay, pureTransferOnly, creatorAddress, depositContractConfig, onBack, onClose, onOrderCreated, onSuccess, classes, }: QRDepositProps): import("react/jsx-runtime").JSX.Element;
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { ALL_CHAINS, getAvailableChainIds, getPaymentUrl, isSameChainAndToken, ZERO_ADDRESS, } from "../../../anyspend/index.js";
3
3
  import { Button, toast } from "../../../global-account/react/index.js";
4
4
  import { cn } from "../../../shared/utils/cn.js";
@@ -39,7 +39,7 @@ const DEFAULT_ETH_ON_BASE = {
39
39
  * onSuccess={(txHash) => console.log("Deposit complete:", txHash)}
40
40
  * />
41
41
  */
42
- export function QRDeposit({ mode = "modal", recipientAddress, sourceToken: sourceTokenProp, sourceChainId: sourceChainIdProp, destinationToken, destinationChainId, pureTransferOnly = false, creatorAddress, depositContractConfig, onBack, onClose, onOrderCreated, onSuccess, classes, }) {
42
+ export function QRDeposit({ mode = "modal", recipientAddress, sourceToken: sourceTokenProp, sourceChainId: sourceChainIdProp, destinationToken, destinationChainId, destinationAmountDisplay, pureTransferOnly = false, creatorAddress, depositContractConfig, onBack, onClose, onOrderCreated, onSuccess, classes, }) {
43
43
  const [copied, setCopied] = useState(false);
44
44
  const [orderId, setOrderId] = useState();
45
45
  const [globalAddress, setGlobalAddress] = useState();
@@ -171,8 +171,8 @@ export function QRDeposit({ mode = "modal", recipientAddress, sourceToken: sourc
171
171
  }
172
172
  return (_jsx("div", { className: classes?.container ||
173
173
  cn("anyspend-container anyspend-qr-deposit font-inter bg-as-surface-primary mx-auto w-full max-w-[460px] p-6", mode === "page" && "border-as-border-secondary overflow-hidden rounded-2xl border shadow-xl"), children: _jsxs("div", { className: classes?.content || "anyspend-qr-deposit-content flex flex-col gap-4", children: [_jsxs("div", { className: classes?.header || "anyspend-qr-header flex items-center justify-between", children: [_jsx("button", { onClick: handleBack, className: classes?.backButton || "anyspend-qr-back-button text-as-secondary hover:text-as-primary", children: _jsx("svg", { className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) }) }), _jsx("h2", { className: classes?.title || "anyspend-qr-title text-as-primary text-base font-semibold", children: "Deposit" }), onClose ? (_jsx("button", { onClick: handleClose, className: classes?.closeButton || "anyspend-qr-close-button text-as-secondary hover:text-as-primary", children: _jsx("svg", { className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })) : (_jsx("div", { className: "w-5" }))] }), _jsxs("div", { className: classes?.tokenSelectorContainer || "anyspend-qr-token-selector flex flex-col gap-1.5", children: [_jsx("label", { className: classes?.tokenSelectorLabel || "anyspend-qr-token-label text-as-secondary text-sm", children: "Send" }), _jsx(TokenSelector, { chainIdsFilter: getAvailableChainIds("from"), context: "from", fromChainWalletVMSupported: true, isValidAddress: true, lockedChainIds: getAvailableChainIds("from"), multiWalletSupportEnabled: true, onAnalyticEvent: undefined, setToken: handleTokenSelect, supportedWalletVMs: ["evm"], token: undefined, trigger: _jsxs(Button, { variant: "outline", role: "combobox", className: classes?.tokenSelectorTrigger ||
174
- "anyspend-qr-token-trigger border-as-stroke bg-as-surface-secondary flex h-auto w-full items-center justify-between gap-2 rounded-xl border px-3 py-2.5", children: [_jsxs("div", { className: "flex items-center gap-2", children: [sourceToken.metadata?.logoURI ? (_jsx(ChainTokenIcon, { chainUrl: ALL_CHAINS[sourceChainId]?.logoUrl, tokenUrl: sourceToken.metadata.logoURI, className: "h-8 min-h-8 w-8 min-w-8" })) : (_jsx("div", { className: "h-8 w-8 rounded-full bg-gray-700" })), _jsxs("div", { className: "flex flex-col items-start gap-0", children: [_jsx("div", { className: "text-as-primary font-semibold", children: sourceToken.symbol }), _jsx("div", { className: "text-as-primary/70 text-xs", children: ALL_CHAINS[sourceChainId]?.name ?? "Unknown" })] })] }), _jsx(ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-70" })] }) })] }), _jsxs("div", { className: classes?.qrContent || "anyspend-qr-content border-as-stroke flex items-start gap-4 rounded-xl border p-4", children: [_jsxs("div", { className: classes?.qrCodeContainer || "anyspend-qr-code-container flex flex-col items-center gap-2", children: [_jsx("div", { className: classes?.qrCode || "anyspend-qr-code rounded-lg bg-white p-2", children: _jsx(QRCodeSVG, { value: qrValue, size: 120, level: "M", marginSize: 0 }) }), _jsxs("span", { className: classes?.qrScanHint || "anyspend-qr-scan-hint text-as-secondary text-xs", children: ["SCAN WITH ", _jsx("span", { className: "inline-block", children: "\uD83E\uDD8A" })] })] }), _jsxs("div", { className: classes?.addressContainer || "anyspend-qr-address-container flex flex-1 flex-col gap-1", children: [_jsx("span", { className: classes?.addressLabel || "anyspend-qr-address-label text-as-secondary text-sm", children: "Deposit address:" }), _jsxs("div", { className: classes?.addressRow || "anyspend-qr-address-row flex items-start gap-1", children: [_jsx("span", { className: classes?.address || "anyspend-qr-address text-as-primary break-all font-mono text-sm leading-relaxed", children: displayAddress }), _jsx("button", { onClick: handleCopyAddress, className: classes?.addressCopyIcon ||
175
- "anyspend-qr-copy-icon text-as-secondary hover:text-as-primary mt-0.5 shrink-0", children: copied ? _jsx(Check, { className: "h-4 w-4" }) : _jsx(Copy, { className: "h-4 w-4" }) })] })] })] }), _jsx(ChainWarningText, { chainId: effectiveDestinationChainId }), _jsxs(WarningText, { children: ["Only send ", sourceToken.symbol, " on ", ALL_CHAINS[sourceChainId]?.name ?? "the specified chain", ". Other tokens will not be converted."] }), isPureTransfer && isWatchingTransfer && (_jsxs("div", { className: classes?.watchingIndicator ||
174
+ "anyspend-qr-token-trigger border-as-stroke bg-as-surface-secondary flex h-auto w-full items-center justify-between gap-2 rounded-xl border px-3 py-2.5", children: [_jsxs("div", { className: "flex items-center gap-2", children: [sourceToken.metadata?.logoURI ? (_jsx(ChainTokenIcon, { chainUrl: ALL_CHAINS[sourceChainId]?.logoUrl, tokenUrl: sourceToken.metadata.logoURI, className: "h-8 min-h-8 w-8 min-w-8" })) : (_jsx("div", { className: "h-8 w-8 rounded-full bg-gray-700" })), _jsxs("div", { className: "flex flex-col items-start gap-0", children: [_jsx("div", { className: "text-as-primary font-semibold", children: sourceToken.symbol }), _jsx("div", { className: "text-as-primary/70 text-xs", children: ALL_CHAINS[sourceChainId]?.name ?? "Unknown" })] })] }), _jsx(ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-70" })] }) })] }), !isPureTransfer && (_jsxs("div", { className: "anyspend-qr-receive flex flex-col gap-1.5", children: [_jsx("label", { className: "anyspend-qr-receive-label text-as-secondary text-sm", children: "Receive" }), _jsxs("div", { className: "anyspend-qr-receive-row border-as-stroke bg-as-surface-secondary flex w-full items-center gap-2 rounded-xl border px-3 py-2.5", children: [effectiveDestinationToken.metadata?.logoURI ? (_jsx(ChainTokenIcon, { chainUrl: ALL_CHAINS[effectiveDestinationChainId]?.logoUrl, tokenUrl: effectiveDestinationToken.metadata.logoURI, className: "h-8 min-h-8 w-8 min-w-8" })) : (_jsx("div", { className: "h-8 w-8 rounded-full bg-gray-700" })), _jsxs("div", { className: "flex flex-col items-start gap-0", children: [_jsx("div", { className: "text-as-primary font-semibold", children: effectiveDestinationToken.symbol || "Token" }), _jsx("div", { className: "text-as-primary/70 text-xs", children: ALL_CHAINS[effectiveDestinationChainId]?.name ?? "Unknown" })] })] })] })), _jsxs("div", { className: classes?.qrContent || "anyspend-qr-content border-as-stroke flex items-start gap-4 rounded-xl border p-4", children: [_jsxs("div", { className: classes?.qrCodeContainer || "anyspend-qr-code-container flex flex-col items-center gap-2", children: [_jsx("div", { className: classes?.qrCode || "anyspend-qr-code rounded-lg bg-white p-2", children: _jsx(QRCodeSVG, { value: qrValue, size: 120, level: "M", marginSize: 0 }) }), _jsxs("span", { className: classes?.qrScanHint || "anyspend-qr-scan-hint text-as-secondary text-xs", children: ["SCAN WITH ", _jsx("span", { className: "inline-block", children: "\uD83E\uDD8A" })] })] }), _jsxs("div", { className: classes?.addressContainer || "anyspend-qr-address-container flex flex-1 flex-col gap-1", children: [_jsx("span", { className: classes?.addressLabel || "anyspend-qr-address-label text-as-secondary text-sm", children: "Deposit address:" }), _jsxs("div", { className: classes?.addressRow || "anyspend-qr-address-row flex items-start gap-1", children: [_jsx("span", { className: classes?.address || "anyspend-qr-address text-as-primary break-all font-mono text-sm leading-relaxed", children: displayAddress }), _jsx("button", { onClick: handleCopyAddress, className: classes?.addressCopyIcon ||
175
+ "anyspend-qr-copy-icon text-as-secondary hover:text-as-primary mt-0.5 shrink-0", children: copied ? _jsx(Check, { className: "h-4 w-4" }) : _jsx(Copy, { className: "h-4 w-4" }) })] })] })] }), _jsx(ChainWarningText, { chainId: effectiveDestinationChainId }), isPureTransfer ? (_jsxs(WarningText, { children: ["Only send ", sourceToken.symbol, " on ", ALL_CHAINS[sourceChainId]?.name ?? "the specified chain", ". Other tokens will not be converted."] })) : (_jsxs(_Fragment, { children: [_jsxs(WarningText, { children: ["Only send ", sourceToken.symbol, " on ", ALL_CHAINS[sourceChainId]?.name ?? "the specified chain", " to this address \u2014 it will be converted to ", effectiveDestinationToken.symbol || "the destination token", " on", " ", ALL_CHAINS[effectiveDestinationChainId]?.name ?? "the destination chain", "."] }), destinationAmountDisplay && (_jsxs("p", { className: "anyspend-qr-amount-hint text-as-secondary text-xs", children: ["Target \u2248 ", destinationAmountDisplay, " ", effectiveDestinationToken.symbol || "tokens", ". Any amount of", " ", sourceToken.symbol || "tokens", " works \u2014 you'll receive whatever your deposit converts to."] }))] })), isPureTransfer && isWatchingTransfer && (_jsxs("div", { className: classes?.watchingIndicator ||
176
176
  "anyspend-qr-watching flex items-center justify-center gap-2 rounded-lg bg-blue-500/10 p-3", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin text-blue-500" }), _jsx("span", { className: "text-sm text-blue-500", children: "Watching for incoming transfer..." })] })), _jsx("button", { onClick: handleCopyAddress, className: classes?.copyButton ||
177
177
  "anyspend-qr-copy-button flex w-full items-center justify-center gap-2 rounded-xl bg-blue-500 py-3.5 font-medium text-white transition-all hover:bg-blue-600", children: "Copy deposit address" })] }) }));
178
178
  }
@@ -14,6 +14,14 @@ export interface QRDepositProps {
14
14
  destinationToken: components["schemas"]["Token"];
15
15
  /** The destination chain ID */
16
16
  destinationChainId: number;
17
+ /**
18
+ * Human-formatted destination amount (e.g. "9.365547"), shown as an APPROXIMATE
19
+ * target in swap mode only. The QR deposit path accepts an arbitrary sent amount,
20
+ * so this is never a guaranteed receive figure — the UI frames it as "any amount
21
+ * works". Omitted when no fixed amount is set or the destination decimals are
22
+ * unresolved (to avoid a mis-scaled number). Ignored in pure-transfer mode.
23
+ */
24
+ destinationAmountDisplay?: string;
17
25
  /**
18
26
  * When true, the deposit is a PURE TRANSFER: the destination token/chain mirror
19
27
  * the user's currently selected source token/chain, so the exact token the user
@@ -54,4 +62,4 @@ export interface QRDepositProps {
54
62
  * onSuccess={(txHash) => console.log("Deposit complete:", txHash)}
55
63
  * />
56
64
  */
57
- export declare function QRDeposit({ mode, recipientAddress, sourceToken: sourceTokenProp, sourceChainId: sourceChainIdProp, destinationToken, destinationChainId, pureTransferOnly, creatorAddress, depositContractConfig, onBack, onClose, onOrderCreated, onSuccess, classes, }: QRDepositProps): import("react/jsx-runtime").JSX.Element;
65
+ export declare function QRDeposit({ mode, recipientAddress, sourceToken: sourceTokenProp, sourceChainId: sourceChainIdProp, destinationToken, destinationChainId, destinationAmountDisplay, pureTransferOnly, creatorAddress, depositContractConfig, onBack, onClose, onOrderCreated, onSuccess, classes, }: QRDepositProps): import("react/jsx-runtime").JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b3dotfun/sdk",
3
- "version": "0.1.70-alpha.16",
3
+ "version": "0.1.70-alpha.17",
4
4
  "source": "src/index.ts",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "react-native": "./dist/cjs/index.native.js",
@@ -3,6 +3,7 @@ import { components } from "@b3dotfun/sdk/anyspend/types/api";
3
3
  import { GetQuoteResponse } from "@b3dotfun/sdk/anyspend/types/api_req_res";
4
4
  import { Skeleton, useAccountWallet, useSimBalance, useTokenData } from "@b3dotfun/sdk/global-account/react";
5
5
  import { cn } from "@b3dotfun/sdk/shared/utils/cn";
6
+ import { formatTokenAmount } from "@b3dotfun/sdk/shared/utils/number";
6
7
  import {
7
8
  NetworkArbitrumOne,
8
9
  NetworkBase,
@@ -631,7 +632,9 @@ export function AnySpendDeposit({
631
632
  "anyspend-deposit-option-description text-as-secondary text-xs"
632
633
  }
633
634
  >
634
- Send tokens directly to deposit address
635
+ {pureTransferOnly
636
+ ? "Send tokens directly to deposit address"
637
+ : "Send any token — we'll convert it for you"}
635
638
  </p>
636
639
  </div>
637
640
  </div>
@@ -700,6 +703,19 @@ export function AnySpendDeposit({
700
703
  // `KNOWN_FUNDING_TOKENS` key — so `hasKnownDestination` is always true and this
701
704
  // branch never fires for them. The guard is defensive for FUTURE callers that pass
702
705
  // an unknown token together with `pureTransferOnly=true`.
706
+ // Only surface the fixed destination amount on the QR path when its decimals are trustworthy
707
+ // (a known funding token or a resolved API lookup) — otherwise `destinationToken.decimals`
708
+ // is the generic 18 fallback and formatting a 6-decimal token's wei against it would be off by
709
+ // 10^12. When unresolved we still show the destination token/chain, just without an amount.
710
+ const destinationDecimalsResolved = hasKnownDestination || !!destinationTokenData;
711
+ const destinationAmountDisplay =
712
+ destinationTokenAmount &&
713
+ /^\d+$/.test(destinationTokenAmount) &&
714
+ BigInt(destinationTokenAmount) > 0n &&
715
+ destinationDecimalsResolved
716
+ ? formatTokenAmount(BigInt(destinationTokenAmount), destinationToken.decimals, 6, false)
717
+ : undefined;
718
+
703
719
  if (pureTransferOnly && !hasKnownDestination && !destinationTokenData && isDestinationTokenLoading) {
704
720
  return (
705
721
  <div
@@ -722,6 +738,7 @@ export function AnySpendDeposit({
722
738
  recipientAddress={recipientAddress}
723
739
  destinationToken={destinationToken}
724
740
  destinationChainId={destinationTokenChainId}
741
+ destinationAmountDisplay={destinationAmountDisplay}
725
742
  pureTransferOnly={pureTransferOnly}
726
743
  depositContractConfig={depositContractConfig}
727
744
  onBack={handleBack}
@@ -36,6 +36,14 @@ export interface QRDepositProps {
36
36
  destinationToken: components["schemas"]["Token"];
37
37
  /** The destination chain ID */
38
38
  destinationChainId: number;
39
+ /**
40
+ * Human-formatted destination amount (e.g. "9.365547"), shown as an APPROXIMATE
41
+ * target in swap mode only. The QR deposit path accepts an arbitrary sent amount,
42
+ * so this is never a guaranteed receive figure — the UI frames it as "any amount
43
+ * works". Omitted when no fixed amount is set or the destination decimals are
44
+ * unresolved (to avoid a mis-scaled number). Ignored in pure-transfer mode.
45
+ */
46
+ destinationAmountDisplay?: string;
39
47
  /**
40
48
  * When true, the deposit is a PURE TRANSFER: the destination token/chain mirror
41
49
  * the user's currently selected source token/chain, so the exact token the user
@@ -96,6 +104,7 @@ export function QRDeposit({
96
104
  sourceChainId: sourceChainIdProp,
97
105
  destinationToken,
98
106
  destinationChainId,
107
+ destinationAmountDisplay,
99
108
  pureTransferOnly = false,
100
109
  creatorAddress,
101
110
  depositContractConfig,
@@ -410,6 +419,32 @@ export function QRDeposit({
410
419
  />
411
420
  </div>
412
421
 
422
+ {/* Receive (swap mode only) — makes the conversion explicit: what the sent token
423
+ becomes and on which chain. Gated on the computed isPureTransfer (not the static
424
+ prop) so a runtime source change back to the same token correctly hides it. */}
425
+ {!isPureTransfer && (
426
+ <div className="anyspend-qr-receive flex flex-col gap-1.5">
427
+ <label className="anyspend-qr-receive-label text-as-secondary text-sm">Receive</label>
428
+ <div className="anyspend-qr-receive-row border-as-stroke bg-as-surface-secondary flex w-full items-center gap-2 rounded-xl border px-3 py-2.5">
429
+ {effectiveDestinationToken.metadata?.logoURI ? (
430
+ <ChainTokenIcon
431
+ chainUrl={ALL_CHAINS[effectiveDestinationChainId]?.logoUrl}
432
+ tokenUrl={effectiveDestinationToken.metadata.logoURI}
433
+ className="h-8 min-h-8 w-8 min-w-8"
434
+ />
435
+ ) : (
436
+ <div className="h-8 w-8 rounded-full bg-gray-700" />
437
+ )}
438
+ <div className="flex flex-col items-start gap-0">
439
+ <div className="text-as-primary font-semibold">{effectiveDestinationToken.symbol || "Token"}</div>
440
+ <div className="text-as-primary/70 text-xs">
441
+ {ALL_CHAINS[effectiveDestinationChainId]?.name ?? "Unknown"}
442
+ </div>
443
+ </div>
444
+ </div>
445
+ </div>
446
+ )}
447
+
413
448
  {/* QR Code and Address - horizontal layout */}
414
449
  <div
415
450
  className={
@@ -454,10 +489,26 @@ export function QRDeposit({
454
489
 
455
490
  {/* Warnings */}
456
491
  <ChainWarningText chainId={effectiveDestinationChainId} />
457
- <WarningText>
458
- Only send {sourceToken.symbol} on {ALL_CHAINS[sourceChainId]?.name ?? "the specified chain"}. Other tokens
459
- will not be converted.
460
- </WarningText>
492
+ {isPureTransfer ? (
493
+ <WarningText>
494
+ Only send {sourceToken.symbol} on {ALL_CHAINS[sourceChainId]?.name ?? "the specified chain"}. Other tokens
495
+ will not be converted.
496
+ </WarningText>
497
+ ) : (
498
+ <>
499
+ <WarningText>
500
+ Only send {sourceToken.symbol} on {ALL_CHAINS[sourceChainId]?.name ?? "the specified chain"} to this
501
+ address — it will be converted to {effectiveDestinationToken.symbol || "the destination token"} on{" "}
502
+ {ALL_CHAINS[effectiveDestinationChainId]?.name ?? "the destination chain"}.
503
+ </WarningText>
504
+ {destinationAmountDisplay && (
505
+ <p className="anyspend-qr-amount-hint text-as-secondary text-xs">
506
+ Target ≈ {destinationAmountDisplay} {effectiveDestinationToken.symbol || "tokens"}. Any amount of{" "}
507
+ {sourceToken.symbol || "tokens"} works — you'll receive whatever your deposit converts to.
508
+ </p>
509
+ )}
510
+ </>
511
+ )}
461
512
 
462
513
  {/* Watching indicator for pure transfers */}
463
514
  {isPureTransfer && isWatchingTransfer && (
@@ -164,6 +164,11 @@ describe("QRDeposit pureTransferOnly (Fund Wallet)", () => {
164
164
 
165
165
  // The displayed deposit address is the recipient's own address, NOT a global/order address.
166
166
  expect(screen.getByText(RECIPIENT)).toBeTruthy();
167
+
168
+ // Pure-transfer UI is unchanged: NO "Receive" row (there is no distinct destination),
169
+ // and the accurate "will not be converted" warning is kept verbatim.
170
+ expect(screen.queryByText("Receive")).toBeNull();
171
+ expect(screen.getByText(/Other tokens will not be converted/)).toBeTruthy();
167
172
  });
168
173
 
169
174
  it("Test B — switching the source token stays a pure transfer (no order; mirror follows selection)", async () => {
@@ -206,7 +211,14 @@ describe("QRDeposit pureTransferOnly (Fund Wallet)", () => {
206
211
 
207
212
  // No pureTransferOnly: source defaults to ETH-on-Base, which differs from the USDC dest,
208
213
  // so this is a real swap and the normal deposit_first order path must run.
209
- render(<QRDeposit recipientAddress={RECIPIENT} destinationToken={USDC_BASE} destinationChainId={8453} />);
214
+ const { container } = render(
215
+ <QRDeposit
216
+ recipientAddress={RECIPIENT}
217
+ destinationToken={USDC_BASE}
218
+ destinationChainId={8453}
219
+ destinationAmountDisplay="9.365547"
220
+ />,
221
+ );
210
222
 
211
223
  // The flag is what gates the pure-transfer behavior: without it, an order IS created once.
212
224
  expect(createOrderSpy).toHaveBeenCalledTimes(1);
@@ -218,6 +230,32 @@ describe("QRDeposit pureTransferOnly (Fund Wallet)", () => {
218
230
  expect(orderArgs.dstToken.decimals).toBe(6);
219
231
  // Source defaulted to native ETH on Base (the differing token that forces a swap).
220
232
  expect(orderArgs.srcToken.address).toBe("0x0000000000000000000000000000000000000000");
233
+
234
+ // Swap-mode UX clarity: a read-only "Receive" row now discloses the destination token…
235
+ expect(screen.getByText("Receive")).toBeTruthy();
236
+ // …the misleading "will not be converted" copy is gone…
237
+ expect(screen.queryByText(/will not be converted/)).toBeNull();
238
+ // …the warning states the conversion explicitly, and the fixed amount shows as an
239
+ // approximate target (never a guaranteed receive figure).
240
+ expect(container.textContent).toContain("it will be converted to USDC on Base");
241
+ expect(container.textContent).toContain("Target ≈ 9.365547 USDC");
242
+ });
243
+
244
+ it("Test C2 — swap mode without a fixed amount shows the Receive row but no target hint", async () => {
245
+ const QRDeposit = await loadQRDeposit();
246
+
247
+ // Swap mode (source ETH-on-Base != USDC dest) but NO destinationAmountDisplay — the common
248
+ // open-ended funding case. The destination must still be disclosed, but there must be no
249
+ // dangling "Target ≈" line (and no blank amount).
250
+ const { container } = render(
251
+ <QRDeposit recipientAddress={RECIPIENT} destinationToken={USDC_BASE} destinationChainId={8453} />,
252
+ );
253
+
254
+ // Destination is still disclosed…
255
+ expect(screen.getByText("Receive")).toBeTruthy();
256
+ expect(container.textContent).toContain("it will be converted to USDC on Base");
257
+ // …but no amount hint is rendered when no amount is provided.
258
+ expect(container.textContent).not.toContain("Target ≈");
221
259
  });
222
260
  });
223
261