@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.
- package/dist/cjs/anyspend/react/components/AnySpendDeposit.js +16 -2
- package/dist/cjs/anyspend/react/components/QRDeposit.d.ts +9 -1
- package/dist/cjs/anyspend/react/components/QRDeposit.js +3 -3
- package/dist/esm/anyspend/react/components/AnySpendDeposit.js +16 -2
- package/dist/esm/anyspend/react/components/QRDeposit.d.ts +9 -1
- package/dist/esm/anyspend/react/components/QRDeposit.js +4 -4
- package/dist/types/anyspend/react/components/QRDeposit.d.ts +9 -1
- package/package.json +1 -1
- package/src/anyspend/react/components/AnySpendDeposit.tsx +18 -1
- package/src/anyspend/react/components/QRDeposit.tsx +55 -4
- package/src/anyspend/react/components/__tests__/QRDeposit.test.tsx +39 -1
|
@@ -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:
|
|
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:
|
|
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
|
@@ -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
|
-
|
|
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
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
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
|
-
|
|
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
|
|