@b3dotfun/sdk 0.0.49-alpha.0 → 0.0.49-alpha.1
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 +8 -4
- package/dist/cjs/anyspend/react/components/AnySpendCustom.js +12 -3
- package/dist/cjs/anyspend/react/components/AnyspendDepositHype.js +10 -3
- package/dist/cjs/anyspend/react/components/common/CryptoPaySection.d.ts +3 -1
- package/dist/cjs/anyspend/react/components/common/CryptoPaySection.js +2 -2
- package/dist/cjs/anyspend/react/components/common/CryptoReceiveSection.d.ts +3 -1
- package/dist/cjs/anyspend/react/components/common/CryptoReceiveSection.js +2 -2
- package/dist/cjs/anyspend/react/components/common/FeeBreakDown.d.ts +12 -0
- package/dist/cjs/anyspend/react/components/common/FeeBreakDown.js +19 -0
- package/dist/cjs/anyspend/react/components/common/FeeDetailPanel.d.ts +8 -0
- package/dist/cjs/anyspend/react/components/common/FeeDetailPanel.js +116 -0
- package/dist/cjs/anyspend/react/components/common/PanelOnramp.d.ts +2 -1
- package/dist/cjs/anyspend/react/components/common/PanelOnramp.js +36 -21
- package/dist/cjs/anyspend/react/components/common/PanelOnrampPayment.js +2 -1
- package/dist/cjs/anyspend/react/components/common/PointsDetailPanel.js +1 -2
- package/dist/cjs/anyspend/react/hooks/useAnyspendFlow.d.ts +3 -1
- package/dist/cjs/anyspend/react/hooks/useAnyspendFlow.js +1 -0
- package/dist/cjs/anyspend/react/hooks/useAnyspendOrderHistory.d.ts +24 -0
- package/dist/cjs/anyspend/types/api.d.ts +119 -176
- package/dist/cjs/global-account/react/components/ui/tooltip.js +1 -1
- package/dist/esm/anyspend/react/components/AnySpend.d.ts +2 -1
- package/dist/esm/anyspend/react/components/AnySpend.js +8 -4
- package/dist/esm/anyspend/react/components/AnySpendCustom.js +14 -5
- package/dist/esm/anyspend/react/components/AnyspendDepositHype.js +10 -3
- package/dist/esm/anyspend/react/components/common/CryptoPaySection.d.ts +3 -1
- package/dist/esm/anyspend/react/components/common/CryptoPaySection.js +4 -4
- package/dist/esm/anyspend/react/components/common/CryptoReceiveSection.d.ts +3 -1
- package/dist/esm/anyspend/react/components/common/CryptoReceiveSection.js +4 -4
- package/dist/esm/anyspend/react/components/common/FeeBreakDown.d.ts +12 -0
- package/dist/esm/anyspend/react/components/common/FeeBreakDown.js +16 -0
- package/dist/esm/anyspend/react/components/common/FeeDetailPanel.d.ts +8 -0
- package/dist/esm/anyspend/react/components/common/FeeDetailPanel.js +113 -0
- package/dist/esm/anyspend/react/components/common/PanelOnramp.d.ts +2 -1
- package/dist/esm/anyspend/react/components/common/PanelOnramp.js +38 -23
- package/dist/esm/anyspend/react/components/common/PanelOnrampPayment.js +2 -1
- package/dist/esm/anyspend/react/components/common/PointsDetailPanel.js +2 -3
- package/dist/esm/anyspend/react/hooks/useAnyspendFlow.d.ts +3 -1
- package/dist/esm/anyspend/react/hooks/useAnyspendFlow.js +1 -0
- package/dist/esm/anyspend/react/hooks/useAnyspendOrderHistory.d.ts +24 -0
- package/dist/esm/anyspend/types/api.d.ts +119 -176
- package/dist/esm/global-account/react/components/ui/tooltip.js +1 -1
- package/dist/styles/index.css +1 -1
- package/dist/types/anyspend/react/components/AnySpend.d.ts +2 -1
- package/dist/types/anyspend/react/components/common/CryptoPaySection.d.ts +3 -1
- package/dist/types/anyspend/react/components/common/CryptoReceiveSection.d.ts +3 -1
- package/dist/types/anyspend/react/components/common/FeeBreakDown.d.ts +12 -0
- package/dist/types/anyspend/react/components/common/FeeDetailPanel.d.ts +8 -0
- package/dist/types/anyspend/react/components/common/PanelOnramp.d.ts +2 -1
- package/dist/types/anyspend/react/hooks/useAnyspendFlow.d.ts +3 -1
- package/dist/types/anyspend/react/hooks/useAnyspendOrderHistory.d.ts +24 -0
- package/dist/types/anyspend/types/api.d.ts +119 -176
- package/package.json +1 -1
- package/src/anyspend/react/components/AnySpend.tsx +20 -0
- package/src/anyspend/react/components/AnySpendCustom.tsx +69 -7
- package/src/anyspend/react/components/AnyspendDepositHype.tsx +23 -0
- package/src/anyspend/react/components/common/CryptoPaySection.tsx +14 -2
- package/src/anyspend/react/components/common/CryptoReceiveSection.tsx +14 -2
- package/src/anyspend/react/components/common/FeeBreakDown.tsx +105 -0
- package/src/anyspend/react/components/common/FeeDetailPanel.tsx +307 -0
- package/src/anyspend/react/components/common/PanelOnramp.tsx +58 -27
- package/src/anyspend/react/components/common/PanelOnrampPayment.tsx +18 -6
- package/src/anyspend/react/components/common/PointsDetailPanel.tsx +1 -13
- package/src/anyspend/react/hooks/useAnyspendFlow.ts +1 -0
- package/src/anyspend/types/api.ts +121 -176
- package/src/global-account/react/components/ui/tooltip.tsx +11 -9
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
TextShimmer,
|
|
23
23
|
Tooltip,
|
|
24
24
|
TooltipContent,
|
|
25
|
+
TooltipProvider,
|
|
25
26
|
TooltipTrigger,
|
|
26
27
|
TransitionPanel,
|
|
27
28
|
useAccountWallet,
|
|
@@ -37,7 +38,7 @@ import { shortenAddress } from "@b3dotfun/sdk/shared/utils/formatAddress";
|
|
|
37
38
|
import { formatTokenAmount, formatUnits } from "@b3dotfun/sdk/shared/utils/number";
|
|
38
39
|
import { simpleHashChainToChainName } from "@b3dotfun/sdk/shared/utils/simplehash";
|
|
39
40
|
import invariant from "invariant";
|
|
40
|
-
import { ChevronRight, ChevronRightCircle, Loader2 } from "lucide-react";
|
|
41
|
+
import { ChevronRight, ChevronRightCircle, Info, Loader2 } from "lucide-react";
|
|
41
42
|
import { motion } from "motion/react";
|
|
42
43
|
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
|
43
44
|
import { toast } from "sonner";
|
|
@@ -45,6 +46,7 @@ import { base } from "viem/chains";
|
|
|
45
46
|
import { useFeatureFlags } from "../contexts/FeatureFlagsContext";
|
|
46
47
|
import { AnySpendFingerprintWrapper, getFingerprintConfig } from "./AnySpendFingerprintWrapper";
|
|
47
48
|
import { CryptoPaymentMethod, CryptoPaymentMethodType } from "./common/CryptoPaymentMethod";
|
|
49
|
+
import { FeeBreakDown } from "./common/FeeBreakDown";
|
|
48
50
|
import { FiatPaymentMethod, FiatPaymentMethodComponent } from "./common/FiatPaymentMethod";
|
|
49
51
|
import { OrderDetails } from "./common/OrderDetails";
|
|
50
52
|
import { OrderHistory } from "./common/OrderHistory";
|
|
@@ -77,6 +79,7 @@ function generateGetRelayQuoteRequest({
|
|
|
77
79
|
contractType,
|
|
78
80
|
encodedData,
|
|
79
81
|
spenderAddress,
|
|
82
|
+
onrampVendor,
|
|
80
83
|
}: {
|
|
81
84
|
orderType: components["schemas"]["Order"]["type"];
|
|
82
85
|
srcChainId: number;
|
|
@@ -90,6 +93,7 @@ function generateGetRelayQuoteRequest({
|
|
|
90
93
|
contractType?: components["schemas"]["NftContract"]["type"];
|
|
91
94
|
encodedData: string;
|
|
92
95
|
spenderAddress?: string;
|
|
96
|
+
onrampVendor?: components["schemas"]["OnrampMetadata"]["vendor"];
|
|
93
97
|
}): GetQuoteRequest {
|
|
94
98
|
switch (orderType) {
|
|
95
99
|
case "mint_nft": {
|
|
@@ -105,6 +109,7 @@ function generateGetRelayQuoteRequest({
|
|
|
105
109
|
contractAddress: contractAddress,
|
|
106
110
|
tokenId: tokenId,
|
|
107
111
|
contractType: contractType,
|
|
112
|
+
onrampVendor,
|
|
108
113
|
};
|
|
109
114
|
}
|
|
110
115
|
case "join_tournament": {
|
|
@@ -117,6 +122,7 @@ function generateGetRelayQuoteRequest({
|
|
|
117
122
|
recipientAddress,
|
|
118
123
|
price: dstAmount,
|
|
119
124
|
contractAddress: contractAddress,
|
|
125
|
+
onrampVendor,
|
|
120
126
|
};
|
|
121
127
|
}
|
|
122
128
|
case "fund_tournament": {
|
|
@@ -129,6 +135,7 @@ function generateGetRelayQuoteRequest({
|
|
|
129
135
|
recipientAddress,
|
|
130
136
|
fundAmount: dstAmount,
|
|
131
137
|
contractAddress: contractAddress,
|
|
138
|
+
onrampVendor,
|
|
132
139
|
};
|
|
133
140
|
}
|
|
134
141
|
case "custom": {
|
|
@@ -145,6 +152,7 @@ function generateGetRelayQuoteRequest({
|
|
|
145
152
|
to: contractAddress,
|
|
146
153
|
spenderAddress: spenderAddress,
|
|
147
154
|
},
|
|
155
|
+
onrampVendor,
|
|
148
156
|
};
|
|
149
157
|
}
|
|
150
158
|
default: {
|
|
@@ -336,6 +344,7 @@ function AnySpendCustomInner({
|
|
|
336
344
|
contractType: orderType === "mint_nft" ? metadata?.nftContract?.type : undefined,
|
|
337
345
|
encodedData: encodedData,
|
|
338
346
|
spenderAddress: spenderAddress,
|
|
347
|
+
onrampVendor: selectedFiatPaymentMethod === FiatPaymentMethod.STRIPE ? "stripe-web2" : undefined,
|
|
339
348
|
});
|
|
340
349
|
}, [
|
|
341
350
|
activeTab,
|
|
@@ -351,6 +360,7 @@ function AnySpendCustomInner({
|
|
|
351
360
|
spenderAddress,
|
|
352
361
|
srcChainId,
|
|
353
362
|
srcToken,
|
|
363
|
+
selectedFiatPaymentMethod,
|
|
354
364
|
]);
|
|
355
365
|
const { anyspendQuote, isLoadingAnyspendQuote } = useAnyspendQuote(getRelayQuoteRequest);
|
|
356
366
|
|
|
@@ -960,14 +970,40 @@ function AnySpendCustomInner({
|
|
|
960
970
|
className="relative flex w-full items-center justify-between"
|
|
961
971
|
>
|
|
962
972
|
<div className="flex items-center gap-2">
|
|
963
|
-
<span className="text-as-tertiarry text-sm">
|
|
973
|
+
<span className="text-as-tertiarry flex items-center gap-1.5 text-sm">
|
|
964
974
|
Total <span className="text-as-tertiarry">(with fee)</span>
|
|
975
|
+
{anyspendQuote?.data?.fee && (
|
|
976
|
+
<TooltipProvider>
|
|
977
|
+
<Tooltip>
|
|
978
|
+
<TooltipTrigger asChild>
|
|
979
|
+
<button className="text-as-primary/40 hover:text-as-primary/60 transition-colors">
|
|
980
|
+
<Info className="h-4 w-4" />
|
|
981
|
+
</button>
|
|
982
|
+
</TooltipTrigger>
|
|
983
|
+
<TooltipContent side="top">
|
|
984
|
+
<FeeBreakDown fee={anyspendQuote.data.fee} />
|
|
985
|
+
</TooltipContent>
|
|
986
|
+
</Tooltip>
|
|
987
|
+
</TooltipProvider>
|
|
988
|
+
)}
|
|
965
989
|
</span>
|
|
966
990
|
{renderPointsBadge()}
|
|
967
991
|
</div>
|
|
968
|
-
<
|
|
969
|
-
|
|
970
|
-
|
|
992
|
+
<div className="flex flex-col items-end gap-0.5">
|
|
993
|
+
<span className="text-as-primary font-semibold">
|
|
994
|
+
{formattedSrcAmount || "--"} {srcToken.symbol}
|
|
995
|
+
</span>
|
|
996
|
+
{anyspendQuote?.data?.fee?.type === "standard_fee" && anyspendQuote.data.currencyIn?.amountUsd && (
|
|
997
|
+
<span className="text-as-secondary text-xs">
|
|
998
|
+
incl. $
|
|
999
|
+
{(
|
|
1000
|
+
(Number(anyspendQuote.data.currencyIn.amountUsd) * anyspendQuote.data.fee.finalFeeBps) /
|
|
1001
|
+
10000
|
|
1002
|
+
).toFixed(2)}{" "}
|
|
1003
|
+
fee
|
|
1004
|
+
</span>
|
|
1005
|
+
)}
|
|
1006
|
+
</div>
|
|
971
1007
|
</motion.div>
|
|
972
1008
|
</div>
|
|
973
1009
|
</div>
|
|
@@ -1086,12 +1122,38 @@ function AnySpendCustomInner({
|
|
|
1086
1122
|
className="relative flex w-full items-center justify-between"
|
|
1087
1123
|
>
|
|
1088
1124
|
<div className="flex items-center gap-2">
|
|
1089
|
-
<span className="text-as-tertiarry text-sm">
|
|
1125
|
+
<span className="text-as-tertiarry flex items-center gap-1.5 text-sm">
|
|
1090
1126
|
Total <span className="text-as-tertiarry">(USD)</span>
|
|
1127
|
+
{anyspendQuote?.data?.fee && (
|
|
1128
|
+
<TooltipProvider>
|
|
1129
|
+
<Tooltip>
|
|
1130
|
+
<TooltipTrigger asChild>
|
|
1131
|
+
<button className="text-as-primary/40 hover:text-as-primary/60 transition-colors">
|
|
1132
|
+
<Info className="h-4 w-4" />
|
|
1133
|
+
</button>
|
|
1134
|
+
</TooltipTrigger>
|
|
1135
|
+
<TooltipContent side="top">
|
|
1136
|
+
<FeeBreakDown fee={anyspendQuote.data.fee} />
|
|
1137
|
+
</TooltipContent>
|
|
1138
|
+
</Tooltip>
|
|
1139
|
+
</TooltipProvider>
|
|
1140
|
+
)}
|
|
1091
1141
|
</span>
|
|
1092
1142
|
{renderPointsBadge()}
|
|
1093
1143
|
</div>
|
|
1094
|
-
<
|
|
1144
|
+
<div className="flex flex-col items-end gap-0.5">
|
|
1145
|
+
<span className="text-as-primary text-xl font-semibold">${srcFiatAmount || "0.00"}</span>
|
|
1146
|
+
{anyspendQuote?.data?.fee?.type === "stripeweb2_fee" && anyspendQuote.data.fee.originalAmount && (
|
|
1147
|
+
<span className="text-as-secondary text-xs">
|
|
1148
|
+
incl. $
|
|
1149
|
+
{(
|
|
1150
|
+
(Number(anyspendQuote.data.fee.originalAmount) - Number(anyspendQuote.data.fee.finalAmount)) /
|
|
1151
|
+
1e6
|
|
1152
|
+
).toFixed(2)}{" "}
|
|
1153
|
+
fee
|
|
1154
|
+
</span>
|
|
1155
|
+
)}
|
|
1156
|
+
</div>
|
|
1095
1157
|
</motion.div>
|
|
1096
1158
|
</div>
|
|
1097
1159
|
|
|
@@ -12,6 +12,7 @@ import { AnySpendFingerprintWrapper, getFingerprintConfig } from "./AnySpendFing
|
|
|
12
12
|
import { CryptoPaySection } from "./common/CryptoPaySection";
|
|
13
13
|
import { CryptoPaymentMethod, CryptoPaymentMethodType } from "./common/CryptoPaymentMethod";
|
|
14
14
|
import { CryptoReceiveSection } from "./common/CryptoReceiveSection";
|
|
15
|
+
import { FeeDetailPanel } from "./common/FeeDetailPanel";
|
|
15
16
|
import { FiatPaymentMethod, FiatPaymentMethodComponent } from "./common/FiatPaymentMethod";
|
|
16
17
|
import { OrderDetails } from "./common/OrderDetails";
|
|
17
18
|
import { PointsDetailPanel } from "./common/PointsDetailPanel";
|
|
@@ -80,6 +81,7 @@ function AnySpendDepositHypeInner({
|
|
|
80
81
|
srcAmount,
|
|
81
82
|
setSrcAmount,
|
|
82
83
|
dstAmount,
|
|
84
|
+
isSrcInputDirty,
|
|
83
85
|
setIsSrcInputDirty,
|
|
84
86
|
selectedCryptoPaymentMethod,
|
|
85
87
|
setSelectedCryptoPaymentMethod,
|
|
@@ -207,6 +209,7 @@ function AnySpendDepositHypeInner({
|
|
|
207
209
|
setSelectedSrcToken={setSelectedSrcToken}
|
|
208
210
|
srcAmount={srcAmount}
|
|
209
211
|
setSrcAmount={setSrcAmount}
|
|
212
|
+
isSrcInputDirty={isSrcInputDirty}
|
|
210
213
|
setIsSrcInputDirty={setIsSrcInputDirty}
|
|
211
214
|
selectedCryptoPaymentMethod={selectedCryptoPaymentMethod}
|
|
212
215
|
onSelectCryptoPaymentMethod={() => setActivePanel(PanelView.CRYPTO_PAYMENT_METHOD)}
|
|
@@ -236,6 +239,7 @@ function AnySpendDepositHypeInner({
|
|
|
236
239
|
recipientSelectionPanelIndex={PanelView.RECIPIENT_SELECTION}
|
|
237
240
|
anyspendQuote={anyspendQuote}
|
|
238
241
|
onShowPointsDetail={() => setActivePanel(PanelView.POINTS_DETAIL)}
|
|
242
|
+
onShowFeeDetail={() => setActivePanel(PanelView.FEE_DETAIL)}
|
|
239
243
|
customUsdInputValues={customUsdInputValues}
|
|
240
244
|
/>
|
|
241
245
|
</motion.div>
|
|
@@ -272,12 +276,14 @@ function AnySpendDepositHypeInner({
|
|
|
272
276
|
selectedDstChainId={base.id}
|
|
273
277
|
setSelectedDstChainId={() => {}}
|
|
274
278
|
setSelectedDstToken={() => {}}
|
|
279
|
+
isSrcInputDirty={isSrcInputDirty}
|
|
275
280
|
onChangeDstAmount={value => {
|
|
276
281
|
setIsSrcInputDirty(false);
|
|
277
282
|
setSrcAmount(value);
|
|
278
283
|
}}
|
|
279
284
|
anyspendQuote={anyspendQuote}
|
|
280
285
|
onShowPointsDetail={() => setActivePanel(PanelView.POINTS_DETAIL)}
|
|
286
|
+
onShowFeeDetail={() => setActivePanel(PanelView.FEE_DETAIL)}
|
|
281
287
|
/>
|
|
282
288
|
)}
|
|
283
289
|
</div>
|
|
@@ -470,6 +476,20 @@ function AnySpendDepositHypeInner({
|
|
|
470
476
|
/>
|
|
471
477
|
);
|
|
472
478
|
|
|
479
|
+
const feeDetailView = anyspendQuote?.data?.fee ? (
|
|
480
|
+
<FeeDetailPanel
|
|
481
|
+
fee={anyspendQuote.data.fee}
|
|
482
|
+
transactionAmountUsd={
|
|
483
|
+
paymentType === "fiat"
|
|
484
|
+
? parseFloat(srcAmount)
|
|
485
|
+
: anyspendQuote.data.currencyIn?.amountUsd
|
|
486
|
+
? Number(anyspendQuote.data.currencyIn.amountUsd)
|
|
487
|
+
: undefined
|
|
488
|
+
}
|
|
489
|
+
onBack={() => setActivePanel(PanelView.MAIN)}
|
|
490
|
+
/>
|
|
491
|
+
) : null;
|
|
492
|
+
|
|
473
493
|
// If showing token selection, render with panel transitions
|
|
474
494
|
return (
|
|
475
495
|
<StyleRoot>
|
|
@@ -522,6 +542,9 @@ function AnySpendDepositHypeInner({
|
|
|
522
542
|
<div key="points-detail-view" className={cn(mode === "page" && "p-6")}>
|
|
523
543
|
{pointsDetailView}
|
|
524
544
|
</div>,
|
|
545
|
+
<div key="fee-detail-view" className={cn(mode === "page" && "p-6")}>
|
|
546
|
+
{feeDetailView}
|
|
547
|
+
</div>,
|
|
525
548
|
]}
|
|
526
549
|
</TransitionPanel>
|
|
527
550
|
</div>
|
|
@@ -2,7 +2,7 @@ import { useAccountWallet, useProfile, useTokenData } from "@b3dotfun/sdk/global
|
|
|
2
2
|
import { formatUsername } from "@b3dotfun/sdk/shared/utils";
|
|
3
3
|
import { shortenAddress } from "@b3dotfun/sdk/shared/utils/formatAddress";
|
|
4
4
|
import { formatDisplayNumber } from "@b3dotfun/sdk/shared/utils/number";
|
|
5
|
-
import { ChevronRight } from "lucide-react";
|
|
5
|
+
import { ChevronRight, Info } from "lucide-react";
|
|
6
6
|
import { motion } from "motion/react";
|
|
7
7
|
import { useEffect, useRef } from "react";
|
|
8
8
|
import { components } from "../../../types/api";
|
|
@@ -18,6 +18,7 @@ interface CryptoPaySectionProps {
|
|
|
18
18
|
setSelectedSrcToken: (token: components["schemas"]["Token"]) => void;
|
|
19
19
|
srcAmount: string;
|
|
20
20
|
setSrcAmount: (amount: string) => void;
|
|
21
|
+
isSrcInputDirty: boolean;
|
|
21
22
|
setIsSrcInputDirty: (dirty: boolean) => void;
|
|
22
23
|
// Payment method state
|
|
23
24
|
selectedCryptoPaymentMethod: CryptoPaymentMethodType;
|
|
@@ -26,6 +27,8 @@ interface CryptoPaySectionProps {
|
|
|
26
27
|
anyspendQuote?: any;
|
|
27
28
|
// Token selection callback
|
|
28
29
|
onTokenSelect?: (token: components["schemas"]["Token"], event: { preventDefault: () => void }) => void;
|
|
30
|
+
// Fee detail callback
|
|
31
|
+
onShowFeeDetail?: () => void;
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
export function CryptoPaySection({
|
|
@@ -35,11 +38,13 @@ export function CryptoPaySection({
|
|
|
35
38
|
setSelectedSrcToken,
|
|
36
39
|
srcAmount,
|
|
37
40
|
setSrcAmount,
|
|
41
|
+
isSrcInputDirty,
|
|
38
42
|
setIsSrcInputDirty,
|
|
39
43
|
selectedCryptoPaymentMethod,
|
|
40
44
|
onSelectCryptoPaymentMethod,
|
|
41
45
|
anyspendQuote,
|
|
42
46
|
onTokenSelect,
|
|
47
|
+
onShowFeeDetail,
|
|
43
48
|
}: CryptoPaySectionProps) {
|
|
44
49
|
const { connectedSmartWallet, connectedEOAWallet } = useAccountWallet();
|
|
45
50
|
const { data: srcTokenMetadata } = useTokenData(selectedSrcToken?.chainId, selectedSrcToken?.address);
|
|
@@ -89,7 +94,14 @@ export function CryptoPaySection({
|
|
|
89
94
|
className="pay-section bg-as-surface-secondary border-as-border-secondary relative flex w-full flex-col gap-2 rounded-2xl border p-4 sm:p-6"
|
|
90
95
|
>
|
|
91
96
|
<div className="flex items-center justify-between">
|
|
92
|
-
<div className="text-as-primary/50 flex h-7 items-center text-sm">
|
|
97
|
+
<div className="text-as-primary/50 flex h-7 items-center gap-1.5 text-sm">
|
|
98
|
+
Pay
|
|
99
|
+
{!isSrcInputDirty && anyspendQuote?.data?.fee && onShowFeeDetail && (
|
|
100
|
+
<button onClick={onShowFeeDetail} className="text-as-primary/40 hover:text-as-primary/60 transition-colors">
|
|
101
|
+
<Info className="h-4 w-4" />
|
|
102
|
+
</button>
|
|
103
|
+
)}
|
|
104
|
+
</div>
|
|
93
105
|
<button
|
|
94
106
|
className="text-as-tertiarry flex h-7 items-center gap-2 text-sm transition-colors focus:!outline-none"
|
|
95
107
|
onClick={onSelectCryptoPaymentMethod}
|
|
@@ -2,7 +2,7 @@ import { formatUsername } from "@b3dotfun/sdk/shared/utils";
|
|
|
2
2
|
import { cn } from "@b3dotfun/sdk/shared/utils/cn";
|
|
3
3
|
import { shortenAddress } from "@b3dotfun/sdk/shared/utils/formatAddress";
|
|
4
4
|
import { formatDisplayNumber } from "@b3dotfun/sdk/shared/utils/number";
|
|
5
|
-
import { ChevronRight } from "lucide-react";
|
|
5
|
+
import { ChevronRight, Info } from "lucide-react";
|
|
6
6
|
import { motion } from "motion/react";
|
|
7
7
|
import { components } from "../../../types/api";
|
|
8
8
|
import { useFeatureFlags } from "../../contexts/FeatureFlagsContext";
|
|
@@ -23,6 +23,7 @@ interface CryptoReceiveSectionProps {
|
|
|
23
23
|
selectedDstChainId?: number;
|
|
24
24
|
setSelectedDstChainId?: (chainId: number) => void;
|
|
25
25
|
setSelectedDstToken?: (token: components["schemas"]["Token"]) => void;
|
|
26
|
+
isSrcInputDirty: boolean;
|
|
26
27
|
onChangeDstAmount?: (value: string) => void;
|
|
27
28
|
// Quote data
|
|
28
29
|
anyspendQuote?: any;
|
|
@@ -31,6 +32,8 @@ interface CryptoReceiveSectionProps {
|
|
|
31
32
|
dstTokenLogoURI?: string;
|
|
32
33
|
// Points navigation
|
|
33
34
|
onShowPointsDetail?: () => void;
|
|
35
|
+
// Fee detail navigation
|
|
36
|
+
onShowFeeDetail?: () => void;
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
export function CryptoReceiveSection({
|
|
@@ -44,11 +47,13 @@ export function CryptoReceiveSection({
|
|
|
44
47
|
selectedDstChainId,
|
|
45
48
|
setSelectedDstChainId,
|
|
46
49
|
setSelectedDstToken,
|
|
50
|
+
isSrcInputDirty,
|
|
47
51
|
onChangeDstAmount,
|
|
48
52
|
anyspendQuote,
|
|
49
53
|
dstTokenSymbol,
|
|
50
54
|
dstTokenLogoURI,
|
|
51
55
|
onShowPointsDetail,
|
|
56
|
+
onShowFeeDetail,
|
|
52
57
|
}: CryptoReceiveSectionProps) {
|
|
53
58
|
const featureFlags = useFeatureFlags();
|
|
54
59
|
|
|
@@ -60,7 +65,14 @@ export function CryptoReceiveSection({
|
|
|
60
65
|
className="receive-section bg-as-surface-secondary border-as-border-secondary relative flex w-full flex-col gap-2 rounded-2xl border p-4 sm:p-6"
|
|
61
66
|
>
|
|
62
67
|
<div className="flex w-full items-center justify-between">
|
|
63
|
-
<div className="text-as-primary/50 flex h-7 items-center text-sm">
|
|
68
|
+
<div className="text-as-primary/50 flex h-7 items-center gap-1.5 text-sm">
|
|
69
|
+
{isDepositMode ? "Deposit" : "Receive"}
|
|
70
|
+
{isSrcInputDirty && anyspendQuote?.data?.fee && onShowFeeDetail && (
|
|
71
|
+
<button onClick={onShowFeeDetail} className="text-as-primary/40 hover:text-as-primary/60 transition-colors">
|
|
72
|
+
<Info className="h-4 w-4" />
|
|
73
|
+
</button>
|
|
74
|
+
)}
|
|
75
|
+
</div>
|
|
64
76
|
{selectedRecipientAddress ? (
|
|
65
77
|
<button
|
|
66
78
|
className={cn("text-as-tertiarry flex h-7 items-center gap-2 rounded-lg")}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { components } from "../../../types/api";
|
|
2
|
+
|
|
3
|
+
interface FeeBreakDownProps {
|
|
4
|
+
fee: components["schemas"]["Fee"];
|
|
5
|
+
/** Number of decimals for amount display (default: 6 for USDC) */
|
|
6
|
+
decimals?: number;
|
|
7
|
+
/** Show currency symbol for amounts (default: true) */
|
|
8
|
+
showCurrency?: boolean;
|
|
9
|
+
/** Custom className for the container */
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function FeeBreakDown({ fee, decimals = 6, showCurrency = true, className = "" }: FeeBreakDownProps) {
|
|
14
|
+
const isStripeFee = fee.type === "stripeweb2_fee";
|
|
15
|
+
|
|
16
|
+
// Convert basis points to percentage
|
|
17
|
+
const bpsToPercent = (bps: number) => (bps / 100).toFixed(2);
|
|
18
|
+
|
|
19
|
+
// Format amount with optional currency
|
|
20
|
+
const formatAmount = (amount: string) => {
|
|
21
|
+
const divisor = Math.pow(10, decimals);
|
|
22
|
+
const formatted = (Number(amount) / divisor).toFixed(2);
|
|
23
|
+
return showCurrency ? `$${formatted}` : formatted;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Check if discount is active
|
|
27
|
+
const hasWhaleDiscount = fee.anyspendWhaleDiscountBps > 0;
|
|
28
|
+
const hasPartnerDiscount = fee.anyspendPartnerDiscountBps > 0;
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div className={`min-w-[240px] ${className}`}>
|
|
32
|
+
{/* Fee Breakdown Section */}
|
|
33
|
+
<div className="mb-4">
|
|
34
|
+
<h3 className="text-as-primary mb-2 text-sm font-semibold">Fee Breakdown</h3>
|
|
35
|
+
<table className="w-full">
|
|
36
|
+
<tbody className="text-as-secondary text-xs">
|
|
37
|
+
{isStripeFee && (
|
|
38
|
+
<tr>
|
|
39
|
+
<td className="py-1">Stripe Fee</td>
|
|
40
|
+
<td className="py-1 text-right">
|
|
41
|
+
{bpsToPercent(fee.stripeFeeBps)}% + ${fee.stripeFeeUsd.toFixed(2)}
|
|
42
|
+
</td>
|
|
43
|
+
</tr>
|
|
44
|
+
)}
|
|
45
|
+
<tr>
|
|
46
|
+
<td className="py-1">AnySpend Fee</td>
|
|
47
|
+
<td className="py-1 text-right">
|
|
48
|
+
{bpsToPercent(fee.anyspendFeeBps)}%
|
|
49
|
+
{isStripeFee && fee.anyspendFeeUsd > 0 && ` + $${fee.anyspendFeeUsd.toFixed(2)}`}
|
|
50
|
+
</td>
|
|
51
|
+
</tr>
|
|
52
|
+
{hasWhaleDiscount && (
|
|
53
|
+
<tr className="text-green-600">
|
|
54
|
+
<td className="py-1">Whale Discount</td>
|
|
55
|
+
<td className="py-1 text-right">-{bpsToPercent(fee.anyspendWhaleDiscountBps)}%</td>
|
|
56
|
+
</tr>
|
|
57
|
+
)}
|
|
58
|
+
{hasPartnerDiscount && (
|
|
59
|
+
<tr className="text-green-600">
|
|
60
|
+
<td className="py-1">Partner Discount</td>
|
|
61
|
+
<td className="py-1 text-right">-{bpsToPercent(fee.anyspendPartnerDiscountBps)}%</td>
|
|
62
|
+
</tr>
|
|
63
|
+
)}
|
|
64
|
+
<tr className="border-as-border-secondary border-t">
|
|
65
|
+
<td className="text-as-primary py-1.5 pt-2 font-semibold">Total Fee</td>
|
|
66
|
+
<td className="text-as-primary py-1.5 pt-2 text-right font-semibold">
|
|
67
|
+
{bpsToPercent(fee.finalFeeBps)}%{isStripeFee && ` + $${fee.finalFeeUsd.toFixed(2)}`}
|
|
68
|
+
</td>
|
|
69
|
+
</tr>
|
|
70
|
+
</tbody>
|
|
71
|
+
</table>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
{/* Amount Calculation Section (Stripe only) */}
|
|
75
|
+
{isStripeFee && (
|
|
76
|
+
<>
|
|
77
|
+
<div className="border-as-border-secondary my-3 border-t"></div>
|
|
78
|
+
<div>
|
|
79
|
+
<h3 className="text-as-primary mb-2 text-sm font-semibold">Amount Calculation</h3>
|
|
80
|
+
<table className="w-full">
|
|
81
|
+
<tbody className="text-as-secondary text-xs">
|
|
82
|
+
<tr>
|
|
83
|
+
<td className="py-1">Original Amount</td>
|
|
84
|
+
<td className="py-1 text-right font-medium">{formatAmount(fee.originalAmount)}</td>
|
|
85
|
+
</tr>
|
|
86
|
+
<tr>
|
|
87
|
+
<td className="py-1">Total Fee</td>
|
|
88
|
+
<td className="py-1 text-right text-red-600">
|
|
89
|
+
-{formatAmount((Number(fee.originalAmount) - Number(fee.finalAmount)).toString())}
|
|
90
|
+
</td>
|
|
91
|
+
</tr>
|
|
92
|
+
<tr className="border-as-border-secondary border-t">
|
|
93
|
+
<td className="text-as-primary py-1.5 pt-2 font-semibold">You Receive</td>
|
|
94
|
+
<td className="text-as-primary py-1.5 pt-2 text-right font-semibold">
|
|
95
|
+
{formatAmount(fee.finalAmount)}
|
|
96
|
+
</td>
|
|
97
|
+
</tr>
|
|
98
|
+
</tbody>
|
|
99
|
+
</table>
|
|
100
|
+
</div>
|
|
101
|
+
</>
|
|
102
|
+
)}
|
|
103
|
+
</div>
|
|
104
|
+
);
|
|
105
|
+
}
|