@b3dotfun/sdk 0.1.70-alpha.15 → 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/AnySpend.d.ts +2 -1
- package/dist/cjs/anyspend/react/components/AnySpend.js +81 -16
- package/dist/cjs/anyspend/react/components/AnySpendCustomExactIn.js +4 -1
- 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/cjs/anyspend/react/components/common/PureTransferView.d.ts +30 -0
- package/dist/cjs/anyspend/react/components/common/PureTransferView.js +64 -0
- package/dist/cjs/global-account/client-manager.js +32 -0
- package/dist/esm/anyspend/react/components/AnySpend.d.ts +2 -1
- package/dist/esm/anyspend/react/components/AnySpend.js +81 -16
- package/dist/esm/anyspend/react/components/AnySpendCustomExactIn.js +4 -1
- 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/esm/anyspend/react/components/common/PureTransferView.d.ts +30 -0
- package/dist/esm/anyspend/react/components/common/PureTransferView.js +61 -0
- package/dist/esm/global-account/client-manager.js +32 -0
- package/dist/types/anyspend/react/components/AnySpend.d.ts +2 -1
- package/dist/types/anyspend/react/components/QRDeposit.d.ts +9 -1
- package/dist/types/anyspend/react/components/common/PureTransferView.d.ts +30 -0
- package/package.json +1 -1
- package/src/anyspend/react/components/AnySpend.tsx +108 -18
- package/src/anyspend/react/components/AnySpendCustomExactIn.tsx +5 -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
- package/src/anyspend/react/components/common/PureTransferView.tsx +212 -0
- package/src/global-account/client-manager.ts +33 -0
|
@@ -35,6 +35,7 @@ import { LoginStep } from "../../../global-account/react/components/SignInWithB3
|
|
|
35
35
|
import { PanelOnramp } from "./common/PanelOnramp.js";
|
|
36
36
|
import { PanelOnrampPayment } from "./common/PanelOnrampPayment.js";
|
|
37
37
|
import { PointsDetailPanel } from "./common/PointsDetailPanel.js";
|
|
38
|
+
import { PureTransferView } from "./common/PureTransferView.js";
|
|
38
39
|
import { RecipientSelection } from "./common/RecipientSelection.js";
|
|
39
40
|
import { TabSection } from "./common/TabSection.js";
|
|
40
41
|
import { AnySpendCustomizationProvider, useAnySpendCustomization } from "./context/AnySpendCustomizationContext.js";
|
|
@@ -54,6 +55,7 @@ export var PanelView;
|
|
|
54
55
|
PanelView[PanelView["DIRECT_TRANSFER_SUCCESS"] = 10] = "DIRECT_TRANSFER_SUCCESS";
|
|
55
56
|
PanelView[PanelView["FIAT_KYC"] = 11] = "FIAT_KYC";
|
|
56
57
|
PanelView[PanelView["FIAT_AUTH"] = 12] = "FIAT_AUTH";
|
|
58
|
+
PanelView[PanelView["PURE_TRANSFER_ADDRESS"] = 13] = "PURE_TRANSFER_ADDRESS";
|
|
57
59
|
})(PanelView || (PanelView = {}));
|
|
58
60
|
const ANYSPEND_RECIPIENTS_KEY = "anyspend_recipients";
|
|
59
61
|
export function AnySpend(props) {
|
|
@@ -446,7 +448,10 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
|
|
|
446
448
|
walletAddress,
|
|
447
449
|
globalAddress,
|
|
448
450
|
});
|
|
449
|
-
const recipientProfile = useProfile({
|
|
451
|
+
const recipientProfile = useProfile({
|
|
452
|
+
address: effectiveRecipientAddress,
|
|
453
|
+
fresh: true,
|
|
454
|
+
});
|
|
450
455
|
const recipientName = recipientProfile.data?.name;
|
|
451
456
|
// Check token balance for crypto payments
|
|
452
457
|
const effectiveBalanceAddress = senderAddress || connectedEOAWallet?.getAccount()?.address;
|
|
@@ -647,11 +652,26 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
|
|
|
647
652
|
// For fiat tab, check srcAmountOnRamp; for crypto tab, check activeInputAmountInWei
|
|
648
653
|
const hasAmount = activeTab === "fiat" ? srcAmountOnRamp && parseFloat(srcAmountOnRamp) > 0 : activeInputAmountInWei !== "0";
|
|
649
654
|
if (!hasAmount)
|
|
650
|
-
return {
|
|
655
|
+
return {
|
|
656
|
+
text: "Enter an amount",
|
|
657
|
+
disable: true,
|
|
658
|
+
error: false,
|
|
659
|
+
loading: false,
|
|
660
|
+
};
|
|
651
661
|
if (isSameChainSameToken && !allowDirectTransfer)
|
|
652
|
-
return {
|
|
662
|
+
return {
|
|
663
|
+
text: "Select a different token or chain",
|
|
664
|
+
disable: true,
|
|
665
|
+
error: false,
|
|
666
|
+
loading: false,
|
|
667
|
+
};
|
|
653
668
|
if (isLoadingAnyspendQuote && !isSameChainSameToken)
|
|
654
|
-
return {
|
|
669
|
+
return {
|
|
670
|
+
text: "Loading quote...",
|
|
671
|
+
disable: true,
|
|
672
|
+
error: false,
|
|
673
|
+
loading: true,
|
|
674
|
+
};
|
|
655
675
|
if (isCreatingOrder || isCreatingOnrampOrder || isSwitchingOrExecuting)
|
|
656
676
|
return {
|
|
657
677
|
text: isSwitchingOrExecuting ? "Transferring..." : "Creating order...",
|
|
@@ -660,14 +680,29 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
|
|
|
660
680
|
loading: true,
|
|
661
681
|
};
|
|
662
682
|
if ((!anyspendQuote || !anyspendQuote.success) && !(isSameChainSameToken && allowDirectTransfer))
|
|
663
|
-
return {
|
|
683
|
+
return {
|
|
684
|
+
text: "No quote found",
|
|
685
|
+
disable: true,
|
|
686
|
+
error: false,
|
|
687
|
+
loading: false,
|
|
688
|
+
};
|
|
664
689
|
if (activeTab === "fiat") {
|
|
665
690
|
// For fiat: check recipient first, then payment method
|
|
666
691
|
if (!effectiveRecipientAddress)
|
|
667
|
-
return {
|
|
692
|
+
return {
|
|
693
|
+
text: "Select recipient",
|
|
694
|
+
disable: false,
|
|
695
|
+
error: false,
|
|
696
|
+
loading: false,
|
|
697
|
+
};
|
|
668
698
|
// If no fiat payment method selected, show "Select payment method"
|
|
669
699
|
if (selectedFiatPaymentMethod === FiatPaymentMethod.NONE) {
|
|
670
|
-
return {
|
|
700
|
+
return {
|
|
701
|
+
text: "Select payment method",
|
|
702
|
+
disable: false,
|
|
703
|
+
error: false,
|
|
704
|
+
loading: false,
|
|
705
|
+
};
|
|
671
706
|
}
|
|
672
707
|
// If payment method is selected, show "Continue"
|
|
673
708
|
return { text: "Continue", disable: false, error: false, loading: false };
|
|
@@ -676,19 +711,40 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
|
|
|
676
711
|
// For crypto: check payment method first, then recipient
|
|
677
712
|
// If no payment method selected, show "Choose payment method"
|
|
678
713
|
if (effectiveCryptoPaymentMethod === CryptoPaymentMethodType.NONE) {
|
|
679
|
-
return {
|
|
714
|
+
return {
|
|
715
|
+
text: "Choose payment method",
|
|
716
|
+
disable: false,
|
|
717
|
+
error: false,
|
|
718
|
+
loading: false,
|
|
719
|
+
};
|
|
680
720
|
}
|
|
681
721
|
// Check recipient after payment method
|
|
682
722
|
if (!effectiveRecipientAddress)
|
|
683
|
-
return {
|
|
723
|
+
return {
|
|
724
|
+
text: "Select recipient",
|
|
725
|
+
disable: false,
|
|
726
|
+
error: false,
|
|
727
|
+
loading: false,
|
|
728
|
+
};
|
|
684
729
|
// If payment method selected, show appropriate action
|
|
685
730
|
if (effectiveCryptoPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET ||
|
|
686
731
|
effectiveCryptoPaymentMethod === CryptoPaymentMethodType.GLOBAL_WALLET) {
|
|
687
732
|
const buttonText = isSameChainSameToken && allowDirectTransfer ? "Transfer" : "Swap";
|
|
688
|
-
return {
|
|
733
|
+
return {
|
|
734
|
+
text: buttonText,
|
|
735
|
+
disable: false,
|
|
736
|
+
error: false,
|
|
737
|
+
loading: false,
|
|
738
|
+
};
|
|
689
739
|
}
|
|
690
740
|
if (effectiveCryptoPaymentMethod === CryptoPaymentMethodType.TRANSFER_CRYPTO) {
|
|
691
|
-
|
|
741
|
+
const transferText = isSameChainSameToken && allowDirectTransfer ? "Show deposit address" : "Continue to payment";
|
|
742
|
+
return {
|
|
743
|
+
text: transferText,
|
|
744
|
+
disable: false,
|
|
745
|
+
error: false,
|
|
746
|
+
loading: false,
|
|
747
|
+
};
|
|
692
748
|
}
|
|
693
749
|
}
|
|
694
750
|
return { text: "Continue", disable: false, error: false, loading: false };
|
|
@@ -780,8 +836,14 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
|
|
|
780
836
|
// Handle crypto swap creation
|
|
781
837
|
const handleCryptoSwap = async (method) => {
|
|
782
838
|
try {
|
|
783
|
-
|
|
839
|
+
// TRANSFER_CRYPTO is the manual deposit-address flow — no connected wallet to send from.
|
|
840
|
+
const isDirectTransfer = isSameChainSameToken && allowDirectTransfer && method !== CryptoPaymentMethodType.TRANSFER_CRYPTO;
|
|
784
841
|
invariant(effectiveRecipientAddress, "Recipient address is not found");
|
|
842
|
+
// Same-token manual deposit → show recipient address + watch, no relayer order, no fee
|
|
843
|
+
if (isSameChainSameToken && allowDirectTransfer && method === CryptoPaymentMethodType.TRANSFER_CRYPTO) {
|
|
844
|
+
navigateToPanel(PanelView.PURE_TRANSFER_ADDRESS, "forward");
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
785
847
|
const srcAmountBigInt = parseUnits(srcAmount.replace(/,/g, ""), selectedSrcToken.decimals);
|
|
786
848
|
// Handle direct transfer (same chain/token) - bypass backend, transfer directly
|
|
787
849
|
if (isDirectTransfer) {
|
|
@@ -800,9 +862,6 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
|
|
|
800
862
|
}
|
|
801
863
|
// Regular swap flow - use backend
|
|
802
864
|
invariant(anyspendQuote, "Relay price is not found");
|
|
803
|
-
// Debug: Check payment method values
|
|
804
|
-
console.log("handleCryptoSwap - method parameter:", method);
|
|
805
|
-
console.log("handleCryptoSwap - selectedCryptoPaymentMethod state:", selectedCryptoPaymentMethod);
|
|
806
865
|
createOrder({
|
|
807
866
|
recipientAddress: effectiveRecipientAddress,
|
|
808
867
|
orderType: "swap",
|
|
@@ -810,7 +869,11 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
|
|
|
810
869
|
dstChain: isBuyMode ? destinationTokenChainId : selectedDstChainId,
|
|
811
870
|
srcToken: selectedSrcToken,
|
|
812
871
|
dstToken: isBuyMode
|
|
813
|
-
? {
|
|
872
|
+
? {
|
|
873
|
+
...selectedDstToken,
|
|
874
|
+
chainId: destinationTokenChainId,
|
|
875
|
+
address: destinationTokenAddress,
|
|
876
|
+
}
|
|
814
877
|
: selectedDstToken,
|
|
815
878
|
srcAmount: srcAmountBigInt.toString(),
|
|
816
879
|
expectedDstAmount: anyspendQuote?.data?.currencyOut?.amount || "0",
|
|
@@ -1127,6 +1190,7 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
|
|
|
1127
1190
|
const authView = (_jsx("div", { className: "mx-auto w-full max-w-[460px]", children: _jsx(LoginStep, { chain: baseChain, onSuccess: async () => {
|
|
1128
1191
|
// isAuthenticated will be true at this point — the useEffect below handles navigation
|
|
1129
1192
|
} }) }));
|
|
1193
|
+
const pureTransferAddressView = (_jsx(PureTransferView, { mode: mode, recipientAddress: effectiveRecipientAddress ?? "", chainId: selectedSrcChainId, token: selectedSrcToken, displayAmount: srcAmount && srcAmount !== "0" ? `${srcAmount} ${selectedSrcToken.symbol}` : undefined, isActive: activePanel === PanelView.PURE_TRANSFER_ADDRESS, onBack: navigateBack, onClose: () => setB3ModalOpen(false), onTransferDetected: () => onSuccess?.() }));
|
|
1130
1194
|
// Add tabs to the main component when no order is loaded
|
|
1131
1195
|
return (_jsx(StyleRoot, { children: _jsx("div", { className: classes?.container ||
|
|
1132
1196
|
cn("anyspend-container font-inter mx-auto w-full max-w-[460px]", mode === "page" &&
|
|
@@ -1162,5 +1226,6 @@ function AnySpendInner({ sourceChainId, destinationTokenAddress, destinationToke
|
|
|
1162
1226
|
_jsx("div", { className: cn(mode === "page" && "p-6"), children: directTransferSuccessView }, "direct-transfer-success-view"),
|
|
1163
1227
|
_jsx("div", { className: cn(mode === "page" && "p-6"), children: kycView }, "fiat-kyc-view"),
|
|
1164
1228
|
_jsx("div", { className: cn(mode === "page" && "p-6"), children: authView }, "fiat-auth-view"),
|
|
1229
|
+
_jsx("div", { className: cn(mode === "page" && "p-6"), children: pureTransferAddressView }, "pure-transfer-address-view"),
|
|
1165
1230
|
] }) }) }));
|
|
1166
1231
|
}
|
|
@@ -248,7 +248,10 @@ function AnySpendCustomExactInInner({ loadOrder, mode = "modal", recipientAddres
|
|
|
248
248
|
cn("as-main-button relative w-full", btnInfo.error ? "!bg-as-red" : btnInfo.disable ? "!bg-as-on-surface-2" : "!bg-as-brand"), textClassName: cn(btnInfo.error ? "text-white" : btnInfo.disable ? "text-as-secondary" : "text-white"), children: _jsxs("div", { className: "flex items-center justify-center gap-2", children: [btnInfo.loading && _jsx(Loader2, { className: "h-4 w-4 animate-spin" }), btnInfo.text] }) })) }), gasPriceData && !isLoadingGas && effectivePaymentType === "crypto" && !isDirectTransfer && (_jsx(GasIndicator, { gasPrice: gasPriceData, className: classes?.gasIndicator || "mt-2 w-full" })), mainFooter ? mainFooter : null] }));
|
|
249
249
|
const handleCryptoOrder = async () => {
|
|
250
250
|
try {
|
|
251
|
-
|
|
251
|
+
// TRANSFER_CRYPTO is the manual deposit-address flow — no connected wallet to send from.
|
|
252
|
+
const isDirectTransfer = isSameChainSameToken &&
|
|
253
|
+
allowDirectTransfer &&
|
|
254
|
+
effectiveCryptoPaymentMethod !== CryptoPaymentMethodType.TRANSFER_CRYPTO;
|
|
252
255
|
if (!isDirectTransfer) {
|
|
253
256
|
invariant(anyspendQuote, "Relay price is not found");
|
|
254
257
|
}
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { components } from "../../../../anyspend/types/api";
|
|
2
|
+
import type { TransferResult } from "../../hooks/useWatchTransfer";
|
|
3
|
+
export interface PureTransferViewProps {
|
|
4
|
+
mode?: "modal" | "page";
|
|
5
|
+
/** The recipient address where funds will land (the user's own wallet) */
|
|
6
|
+
recipientAddress: string;
|
|
7
|
+
/** Chain ID to watch on */
|
|
8
|
+
chainId: number;
|
|
9
|
+
/** Token the user sends — drives the QR code and balance watcher */
|
|
10
|
+
token: components["schemas"]["Token"];
|
|
11
|
+
/** Human-readable amount to display (display-only; skip row if empty or "0") */
|
|
12
|
+
displayAmount?: string;
|
|
13
|
+
/** Whether this panel is the currently active panel; gates the balance watcher */
|
|
14
|
+
isActive?: boolean;
|
|
15
|
+
/** Callback when back chevron is clicked */
|
|
16
|
+
onBack?: () => void;
|
|
17
|
+
/** Callback when X close button is clicked */
|
|
18
|
+
onClose?: () => void;
|
|
19
|
+
/** Callback when an incoming transfer is detected */
|
|
20
|
+
onTransferDetected?: (result: TransferResult) => void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A self-contained view that shows the recipient address as a QR code + copy button and
|
|
24
|
+
* watches for an incoming same-chain/same-token transfer via `useWatchTransfer`. When a
|
|
25
|
+
* transfer is detected it renders `TransferResultScreen`.
|
|
26
|
+
*
|
|
27
|
+
* This is the "TRANSFER_CRYPTO" path for same-token AnySpend deposits — no relayer order,
|
|
28
|
+
* no fee; the user simply sends the token directly to their own address.
|
|
29
|
+
*/
|
|
30
|
+
export declare function PureTransferView({ mode, recipientAddress, chainId, token, displayAmount, isActive, onBack, onClose, onTransferDetected, }: PureTransferViewProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { getChainName, getPaymentUrl, ZERO_ADDRESS } from "../../../../anyspend/index.js";
|
|
4
|
+
import { CopyToClipboard } from "../../../../global-account/react/index.js";
|
|
5
|
+
import { cn } from "../../../../shared/utils/cn.js";
|
|
6
|
+
import { ChevronLeft, Loader2, X } from "lucide-react";
|
|
7
|
+
import { QRCodeSVG } from "qrcode.react";
|
|
8
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
9
|
+
import { useWatchTransfer } from "../../hooks/useWatchTransfer.js";
|
|
10
|
+
import { ChainWarningText, WarningText } from "./WarningText.js";
|
|
11
|
+
import { TransferResultScreen } from "./TransferResultScreen.js";
|
|
12
|
+
/**
|
|
13
|
+
* A self-contained view that shows the recipient address as a QR code + copy button and
|
|
14
|
+
* watches for an incoming same-chain/same-token transfer via `useWatchTransfer`. When a
|
|
15
|
+
* transfer is detected it renders `TransferResultScreen`.
|
|
16
|
+
*
|
|
17
|
+
* This is the "TRANSFER_CRYPTO" path for same-token AnySpend deposits — no relayer order,
|
|
18
|
+
* no fee; the user simply sends the token directly to their own address.
|
|
19
|
+
*/
|
|
20
|
+
export function PureTransferView({ mode = "modal", recipientAddress, chainId, token, displayAmount, isActive, onBack, onClose, onTransferDetected, }) {
|
|
21
|
+
const [transferResult, setTransferResult] = useState(null);
|
|
22
|
+
// Fix 4: reset transferResult when the panel becomes active again
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (isActive)
|
|
25
|
+
setTransferResult(null);
|
|
26
|
+
}, [isActive]);
|
|
27
|
+
// Fix 3: stabilize the callback so it doesn't re-run the watcher effect every render
|
|
28
|
+
const handleTransferDetected = useCallback((result) => {
|
|
29
|
+
setTransferResult(result);
|
|
30
|
+
onTransferDetected?.(result);
|
|
31
|
+
}, [onTransferDetected]);
|
|
32
|
+
const { isWatching } = useWatchTransfer({
|
|
33
|
+
address: recipientAddress,
|
|
34
|
+
chainId,
|
|
35
|
+
tokenAddress: token.address,
|
|
36
|
+
tokenDecimals: token.decimals,
|
|
37
|
+
// Fix 1: also require a non-empty recipient before enabling the watcher
|
|
38
|
+
enabled: !!recipientAddress && !transferResult && isActive,
|
|
39
|
+
onTransferDetected: handleTransferDetected,
|
|
40
|
+
});
|
|
41
|
+
// Fix 2: guard qrValue when recipientAddress is empty
|
|
42
|
+
const qrValue = useMemo(() => recipientAddress
|
|
43
|
+
? getPaymentUrl(recipientAddress, undefined, token.address === ZERO_ADDRESS ? "ETH" : token.address, chainId, token.decimals)
|
|
44
|
+
: "", [recipientAddress, token.address, token.decimals, chainId]);
|
|
45
|
+
const chainName = useMemo(() => {
|
|
46
|
+
try {
|
|
47
|
+
return getChainName(chainId);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return "the specified chain";
|
|
51
|
+
}
|
|
52
|
+
}, [chainId]);
|
|
53
|
+
// Guard: if no address yet, render nothing
|
|
54
|
+
if (!recipientAddress)
|
|
55
|
+
return null;
|
|
56
|
+
// Once a transfer is detected, hand off to the shared success screen
|
|
57
|
+
if (transferResult) {
|
|
58
|
+
return (_jsx(TransferResultScreen, { mode: mode, transferResult: transferResult, token: token, chainId: chainId, recipientAddress: recipientAddress, onBack: onBack, onClose: onClose }));
|
|
59
|
+
}
|
|
60
|
+
return (_jsx("div", { className: cn("anyspend-container anyspend-pure-transfer font-inter bg-as-surface-primary mx-auto w-full max-w-[460px] p-6"), children: _jsxs("div", { className: "anyspend-pure-transfer-content flex flex-col gap-4", children: [_jsxs("div", { className: "anyspend-pure-transfer-header flex items-center justify-between", children: [onBack ? (_jsx("button", { onClick: onBack, className: "anyspend-pure-transfer-back text-as-secondary hover:text-as-primary", "aria-label": "Go back", children: _jsx(ChevronLeft, { className: "h-5 w-5" }) })) : (_jsx("div", { className: "w-5" })), _jsxs("h2", { className: "anyspend-pure-transfer-title text-as-primary text-base font-semibold", children: ["Send ", token.symbol] }), onClose ? (_jsx("button", { onClick: onClose, className: "anyspend-pure-transfer-close text-as-secondary hover:text-as-primary", "aria-label": "Close", children: _jsx(X, { className: "h-5 w-5" }) })) : (_jsx("div", { className: "w-5" }))] }), displayAmount && (_jsx("div", { className: "anyspend-pure-transfer-amount-badge border-as-border-secondary bg-as-surface-secondary flex items-center justify-center rounded-xl border px-4 py-2", children: _jsx("span", { className: "text-as-primary text-sm font-medium", children: displayAmount }) })), _jsxs("div", { className: "anyspend-pure-transfer-qr-content border-as-stroke flex items-start gap-4 rounded-xl border p-4", children: [_jsxs("div", { className: "anyspend-pure-transfer-qr-code-container flex flex-col items-center gap-2", children: [_jsx("div", { className: "anyspend-pure-transfer-qr-code rounded-lg bg-white p-2", children: _jsx(QRCodeSVG, { value: qrValue, size: 120, level: "M", marginSize: 0 }) }), _jsxs("span", { className: "anyspend-pure-transfer-qr-scan-hint text-as-secondary text-xs", children: ["SCAN WITH ", _jsx("span", { className: "inline-block", children: "\uD83E\uDD8A" })] })] }), _jsxs("div", { className: "anyspend-pure-transfer-address-container flex flex-1 flex-col gap-1", children: [_jsx("span", { className: "anyspend-pure-transfer-address-label text-as-secondary text-sm", children: "Deposit address:" }), _jsxs("div", { className: "anyspend-pure-transfer-address-row flex items-start gap-1", children: [_jsx("span", { className: "anyspend-pure-transfer-address text-as-primary break-all font-mono text-sm leading-relaxed", children: recipientAddress }), _jsx(CopyToClipboard, { text: recipientAddress, className: "anyspend-pure-transfer-copy-icon text-as-secondary hover:text-as-primary mt-0.5 shrink-0" })] })] })] }), _jsx(ChainWarningText, { chainId: chainId }), _jsxs(WarningText, { children: ["Only send ", token.symbol, " on ", chainName, ". Other tokens will not be converted."] }), isWatching && (_jsxs("div", { className: "anyspend-pure-transfer-watching bg-as-brand/10 flex items-center justify-center gap-2 rounded-lg p-3", children: [_jsx(Loader2, { className: "text-as-brand h-4 w-4 animate-spin" }), _jsx("span", { className: "text-as-brand text-sm", children: "Watching for incoming transfer..." })] })), _jsx(CopyToClipboard, { text: recipientAddress, children: _jsx("button", { className: "anyspend-pure-transfer-copy-button bg-as-brand hover:bg-as-brand/90 flex w-full items-center justify-center gap-2 rounded-xl py-3.5 font-medium text-white transition-all", children: "Copy deposit address" }) })] }) }));
|
|
61
|
+
}
|
|
@@ -13,6 +13,36 @@ let socketClientUrl = null;
|
|
|
13
13
|
// REST client instance
|
|
14
14
|
let restClient = null;
|
|
15
15
|
let restClientUrl = null;
|
|
16
|
+
// The pinned @b3dotfun/b3-api client's generated usersMethods predates `setBio`,
|
|
17
|
+
// so `users.setBio(...)` throws "setBio is not a function". Re-register the users
|
|
18
|
+
// service with the full list (mirrors users.shared.ts). Remove once the
|
|
19
|
+
// @b3dotfun/b3-api pin includes setBio.
|
|
20
|
+
const USERS_METHODS = [
|
|
21
|
+
"find",
|
|
22
|
+
"get",
|
|
23
|
+
"create",
|
|
24
|
+
"patch",
|
|
25
|
+
"remove",
|
|
26
|
+
"setReferralCode",
|
|
27
|
+
"syncTwProfiles",
|
|
28
|
+
"setAvatar",
|
|
29
|
+
"setBio",
|
|
30
|
+
"registerUsername",
|
|
31
|
+
"getMe",
|
|
32
|
+
];
|
|
33
|
+
// Re-register on every client the manager builds — the active transport
|
|
34
|
+
// (REST/socket) switches at runtime.
|
|
35
|
+
function ensureUsersCustomMethods(client) {
|
|
36
|
+
try {
|
|
37
|
+
const connection = client.get("connection");
|
|
38
|
+
if (connection?.service) {
|
|
39
|
+
client.use("users", connection.service("users"), { methods: USERS_METHODS });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Non-fatal: keep whatever methods the generated client already exposes.
|
|
44
|
+
}
|
|
45
|
+
}
|
|
16
46
|
/**
|
|
17
47
|
* Creates a socket client
|
|
18
48
|
*/
|
|
@@ -21,6 +51,7 @@ function createSocketClient() {
|
|
|
21
51
|
if (!socketClient || socketClientUrl !== url) {
|
|
22
52
|
socketInstance = io(url, { transports: ["websocket"] });
|
|
23
53
|
socketClient = createClient(socketio(socketInstance), clientOptions);
|
|
54
|
+
ensureUsersCustomMethods(socketClient);
|
|
24
55
|
socketClientUrl = url;
|
|
25
56
|
}
|
|
26
57
|
return socketClient;
|
|
@@ -41,6 +72,7 @@ function createRestClient() {
|
|
|
41
72
|
if (!restClient || restClientUrl !== url) {
|
|
42
73
|
const connection = rest(url).fetch(resolveFetch());
|
|
43
74
|
restClient = createClient(connection, clientOptions);
|
|
75
|
+
ensureUsersCustomMethods(restClient);
|
|
44
76
|
restClientUrl = url;
|
|
45
77
|
}
|
|
46
78
|
return restClient;
|
|
@@ -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;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { components } from "@b3dotfun/sdk/anyspend/types/api";
|
|
2
|
+
import type { TransferResult } from "../../hooks/useWatchTransfer";
|
|
3
|
+
export interface PureTransferViewProps {
|
|
4
|
+
mode?: "modal" | "page";
|
|
5
|
+
/** The recipient address where funds will land (the user's own wallet) */
|
|
6
|
+
recipientAddress: string;
|
|
7
|
+
/** Chain ID to watch on */
|
|
8
|
+
chainId: number;
|
|
9
|
+
/** Token the user sends — drives the QR code and balance watcher */
|
|
10
|
+
token: components["schemas"]["Token"];
|
|
11
|
+
/** Human-readable amount to display (display-only; skip row if empty or "0") */
|
|
12
|
+
displayAmount?: string;
|
|
13
|
+
/** Whether this panel is the currently active panel; gates the balance watcher */
|
|
14
|
+
isActive?: boolean;
|
|
15
|
+
/** Callback when back chevron is clicked */
|
|
16
|
+
onBack?: () => void;
|
|
17
|
+
/** Callback when X close button is clicked */
|
|
18
|
+
onClose?: () => void;
|
|
19
|
+
/** Callback when an incoming transfer is detected */
|
|
20
|
+
onTransferDetected?: (result: TransferResult) => void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A self-contained view that shows the recipient address as a QR code + copy button and
|
|
24
|
+
* watches for an incoming same-chain/same-token transfer via `useWatchTransfer`. When a
|
|
25
|
+
* transfer is detected it renders `TransferResultScreen`.
|
|
26
|
+
*
|
|
27
|
+
* This is the "TRANSFER_CRYPTO" path for same-token AnySpend deposits — no relayer order,
|
|
28
|
+
* no fee; the user simply sends the token directly to their own address.
|
|
29
|
+
*/
|
|
30
|
+
export declare function PureTransferView({ mode, recipientAddress, chainId, token, displayAmount, isActive, onBack, onClose, onTransferDetected, }: PureTransferViewProps): import("react/jsx-runtime").JSX.Element | null;
|