@b3dotfun/sdk 0.0.58-alpha.1 → 0.0.58-alpha.3
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/common/CryptoPaymentMethod.js +4 -0
- package/dist/cjs/anyspend/utils/accountStore.d.ts +7 -0
- package/dist/cjs/anyspend/utils/accountStore.js +8 -0
- package/dist/cjs/anyspend/utils/index.d.ts +1 -0
- package/dist/cjs/anyspend/utils/index.js +1 -0
- package/dist/cjs/global-account/react/components/B3DynamicModal.js +17 -0
- package/dist/cjs/shared/react/components/CurrencySelector.js +8 -3
- package/dist/cjs/shared/react/components/FormattedCurrency.d.ts +3 -3
- package/dist/cjs/shared/react/components/FormattedCurrency.js +31 -26
- package/dist/cjs/shared/react/hooks/useCurrencyConversion.d.ts +8 -5
- package/dist/cjs/shared/react/hooks/useCurrencyConversion.js +153 -94
- package/dist/cjs/shared/react/stores/currencyStore.d.ts +83 -8
- package/dist/cjs/shared/react/stores/currencyStore.js +147 -5
- package/dist/esm/anyspend/react/components/common/CryptoPaymentMethod.js +5 -1
- package/dist/esm/anyspend/utils/accountStore.d.ts +7 -0
- package/dist/esm/anyspend/utils/accountStore.js +5 -0
- package/dist/esm/anyspend/utils/index.d.ts +1 -0
- package/dist/esm/anyspend/utils/index.js +1 -0
- package/dist/esm/global-account/react/components/B3DynamicModal.js +17 -0
- package/dist/esm/shared/react/components/CurrencySelector.js +10 -5
- package/dist/esm/shared/react/components/FormattedCurrency.d.ts +3 -3
- package/dist/esm/shared/react/components/FormattedCurrency.js +31 -26
- package/dist/esm/shared/react/hooks/useCurrencyConversion.d.ts +8 -5
- package/dist/esm/shared/react/hooks/useCurrencyConversion.js +154 -95
- package/dist/esm/shared/react/stores/currencyStore.d.ts +83 -8
- package/dist/esm/shared/react/stores/currencyStore.js +143 -5
- package/dist/types/anyspend/utils/accountStore.d.ts +7 -0
- package/dist/types/anyspend/utils/index.d.ts +1 -0
- package/dist/types/shared/react/components/FormattedCurrency.d.ts +3 -3
- package/dist/types/shared/react/hooks/useCurrencyConversion.d.ts +8 -5
- package/dist/types/shared/react/stores/currencyStore.d.ts +83 -8
- package/package.json +1 -1
- package/src/anyspend/react/components/common/CryptoPaymentMethod.tsx +6 -2
- package/src/anyspend/utils/accountStore.ts +12 -0
- package/src/anyspend/utils/index.ts +1 -0
- package/src/global-account/react/components/B3DynamicModal.tsx +20 -0
- package/src/shared/react/components/CurrencySelector.tsx +36 -5
- package/src/shared/react/components/FormattedCurrency.tsx +36 -30
- package/src/shared/react/hooks/__tests__/useCurrencyConversion.test.ts +14 -14
- package/src/shared/react/hooks/useCurrencyConversion.ts +163 -96
- package/src/shared/react/stores/currencyStore.ts +216 -10
|
@@ -1,8 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Built-in supported currencies for display and conversion.
|
|
3
3
|
* Includes fiat currencies (USD, EUR, GBP, JPY, CAD, AUD, KRW) and crypto (ETH, SOL, B3).
|
|
4
4
|
*/
|
|
5
5
|
export type SupportedCurrency = "ETH" | "USD" | "EUR" | "GBP" | "JPY" | "CAD" | "AUD" | "B3" | "SOL" | "KRW";
|
|
6
|
+
/**
|
|
7
|
+
* Metadata for a custom currency including display formatting rules.
|
|
8
|
+
*/
|
|
9
|
+
export interface CurrencyMetadata {
|
|
10
|
+
/** The currency code/symbol (e.g., "BTC", "DOGE") */
|
|
11
|
+
code: string;
|
|
12
|
+
/** Display symbol for the currency (e.g., "₿", "Ð") */
|
|
13
|
+
symbol: string;
|
|
14
|
+
/** Human-readable name (e.g., "Bitcoin", "Dogecoin") */
|
|
15
|
+
name: string;
|
|
16
|
+
/** Whether to show symbol before the value (true for $100, false for 100 ETH) */
|
|
17
|
+
prefixSymbol?: boolean;
|
|
18
|
+
/** Number of decimal places to show (undefined uses smart formatting) */
|
|
19
|
+
decimals?: number;
|
|
20
|
+
/** Whether to use subscript notation for small values */
|
|
21
|
+
showSubscripts?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Exchange rate between two currencies.
|
|
25
|
+
*/
|
|
26
|
+
export interface ExchangeRate {
|
|
27
|
+
/** Currency code being converted from */
|
|
28
|
+
from: string;
|
|
29
|
+
/** Currency code being converted to */
|
|
30
|
+
to: string;
|
|
31
|
+
/** Exchange rate multiplier (amount_in_to = amount_in_from * rate) */
|
|
32
|
+
rate: number;
|
|
33
|
+
}
|
|
6
34
|
/**
|
|
7
35
|
* Currency symbols used for display formatting.
|
|
8
36
|
* Prefix currencies (USD, EUR, GBP, CAD, AUD) show symbol before the amount.
|
|
@@ -17,24 +45,51 @@ export declare const CURRENCY_NAMES: Record<SupportedCurrency, string>;
|
|
|
17
45
|
* Currency store state interface.
|
|
18
46
|
* @property selectedCurrency - The currency currently selected for display
|
|
19
47
|
* @property baseCurrency - The base currency for conversion (typically B3)
|
|
48
|
+
* @property customCurrencies - Map of custom currency codes to their metadata
|
|
49
|
+
* @property customExchangeRates - Map of "FROM-TO" pairs to exchange rates
|
|
20
50
|
* @property setSelectedCurrency - Update the selected display currency
|
|
21
51
|
* @property setBaseCurrency - Update the base currency for conversions
|
|
52
|
+
* @property addCurrency - Register a new custom currency with metadata
|
|
53
|
+
* @property removeCurrency - Remove a custom currency
|
|
54
|
+
* @property setExchangeRate - Set a custom exchange rate between two currencies
|
|
55
|
+
* @property getExchangeRate - Get exchange rate between two currencies
|
|
56
|
+
* @property getAllCurrencies - Get all available currencies (built-in + custom)
|
|
22
57
|
*/
|
|
23
58
|
interface CurrencyState {
|
|
24
|
-
selectedCurrency:
|
|
25
|
-
baseCurrency:
|
|
26
|
-
|
|
27
|
-
|
|
59
|
+
selectedCurrency: string;
|
|
60
|
+
baseCurrency: string;
|
|
61
|
+
customCurrencies: Record<string, CurrencyMetadata>;
|
|
62
|
+
customExchangeRates: Record<string, number>;
|
|
63
|
+
setSelectedCurrency: (currency: string) => void;
|
|
64
|
+
setBaseCurrency: (currency: string) => void;
|
|
65
|
+
addCurrency: (metadata: CurrencyMetadata) => void;
|
|
66
|
+
removeCurrency: (code: string) => void;
|
|
67
|
+
setExchangeRate: (from: string, to: string, rate: number) => void;
|
|
68
|
+
getExchangeRate: (from: string, to: string) => number | undefined;
|
|
69
|
+
getAllCurrencies: () => string[];
|
|
28
70
|
}
|
|
29
71
|
/**
|
|
30
72
|
* Zustand store for managing currency selection and conversion.
|
|
31
73
|
* Persists user's selected currency preference in localStorage.
|
|
74
|
+
* Supports dynamic currency registration and custom exchange rates.
|
|
32
75
|
*
|
|
33
76
|
* @example
|
|
34
77
|
* ```tsx
|
|
35
|
-
* const { selectedCurrency, setSelectedCurrency } = useCurrencyStore();
|
|
36
|
-
*
|
|
37
|
-
*
|
|
78
|
+
* const { selectedCurrency, setSelectedCurrency, addCurrency, setExchangeRate } = useCurrencyStore();
|
|
79
|
+
*
|
|
80
|
+
* // Add a new currency
|
|
81
|
+
* addCurrency({
|
|
82
|
+
* code: "BTC",
|
|
83
|
+
* symbol: "₿",
|
|
84
|
+
* name: "Bitcoin",
|
|
85
|
+
* showSubscripts: true,
|
|
86
|
+
* });
|
|
87
|
+
*
|
|
88
|
+
* // Set exchange rate: 1 BTC = 50000 USD
|
|
89
|
+
* setExchangeRate("BTC", "USD", 50000);
|
|
90
|
+
*
|
|
91
|
+
* // Change display currency
|
|
92
|
+
* setSelectedCurrency('BTC');
|
|
38
93
|
* ```
|
|
39
94
|
*/
|
|
40
95
|
export declare const useCurrencyStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<CurrencyState>, "persist"> & {
|
|
@@ -48,4 +103,24 @@ export declare const useCurrencyStore: import("zustand").UseBoundStore<Omit<impo
|
|
|
48
103
|
getOptions: () => Partial<import("zustand/middleware").PersistOptions<CurrencyState, CurrencyState>>;
|
|
49
104
|
};
|
|
50
105
|
}>;
|
|
106
|
+
/**
|
|
107
|
+
* Get the symbol for any currency (built-in or custom).
|
|
108
|
+
*/
|
|
109
|
+
export declare function getCurrencySymbol(currency: string): string;
|
|
110
|
+
/**
|
|
111
|
+
* Get the name for any currency (built-in or custom).
|
|
112
|
+
*/
|
|
113
|
+
export declare function getCurrencyName(currency: string): string;
|
|
114
|
+
/**
|
|
115
|
+
* Get metadata for a custom currency.
|
|
116
|
+
*/
|
|
117
|
+
export declare function getCurrencyMetadata(currency: string): CurrencyMetadata | undefined;
|
|
118
|
+
/**
|
|
119
|
+
* Get the number of decimal places for a currency (for converting from smallest unit).
|
|
120
|
+
* Used when parsing amounts from wei/smallest unit format.
|
|
121
|
+
*
|
|
122
|
+
* @param currency - Currency code
|
|
123
|
+
* @returns Number of decimal places (e.g., 18 for ETH/wei, 2 for USD cents, 0 for JPY)
|
|
124
|
+
*/
|
|
125
|
+
export declare function getCurrencyDecimalPlaces(currency: string): number;
|
|
51
126
|
export {};
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.useCurrencyStore = exports.CURRENCY_NAMES = exports.CURRENCY_SYMBOLS = void 0;
|
|
4
|
+
exports.getCurrencySymbol = getCurrencySymbol;
|
|
5
|
+
exports.getCurrencyName = getCurrencyName;
|
|
6
|
+
exports.getCurrencyMetadata = getCurrencyMetadata;
|
|
7
|
+
exports.getCurrencyDecimalPlaces = getCurrencyDecimalPlaces;
|
|
4
8
|
const zustand_1 = require("zustand");
|
|
5
9
|
const middleware_1 = require("zustand/middleware");
|
|
6
10
|
/**
|
|
@@ -38,20 +42,158 @@ exports.CURRENCY_NAMES = {
|
|
|
38
42
|
/**
|
|
39
43
|
* Zustand store for managing currency selection and conversion.
|
|
40
44
|
* Persists user's selected currency preference in localStorage.
|
|
45
|
+
* Supports dynamic currency registration and custom exchange rates.
|
|
41
46
|
*
|
|
42
47
|
* @example
|
|
43
48
|
* ```tsx
|
|
44
|
-
* const { selectedCurrency, setSelectedCurrency } = useCurrencyStore();
|
|
45
|
-
*
|
|
46
|
-
*
|
|
49
|
+
* const { selectedCurrency, setSelectedCurrency, addCurrency, setExchangeRate } = useCurrencyStore();
|
|
50
|
+
*
|
|
51
|
+
* // Add a new currency
|
|
52
|
+
* addCurrency({
|
|
53
|
+
* code: "BTC",
|
|
54
|
+
* symbol: "₿",
|
|
55
|
+
* name: "Bitcoin",
|
|
56
|
+
* showSubscripts: true,
|
|
57
|
+
* });
|
|
58
|
+
*
|
|
59
|
+
* // Set exchange rate: 1 BTC = 50000 USD
|
|
60
|
+
* setExchangeRate("BTC", "USD", 50000);
|
|
61
|
+
*
|
|
62
|
+
* // Change display currency
|
|
63
|
+
* setSelectedCurrency('BTC');
|
|
47
64
|
* ```
|
|
48
65
|
*/
|
|
49
|
-
exports.useCurrencyStore = (0, zustand_1.create)()((0, middleware_1.persist)(set => ({
|
|
66
|
+
exports.useCurrencyStore = (0, zustand_1.create)()((0, middleware_1.persist)((set, get) => ({
|
|
50
67
|
selectedCurrency: "B3",
|
|
51
68
|
baseCurrency: "B3",
|
|
69
|
+
customCurrencies: {},
|
|
70
|
+
customExchangeRates: {},
|
|
52
71
|
setSelectedCurrency: currency => set({ selectedCurrency: currency }),
|
|
53
72
|
setBaseCurrency: currency => set({ baseCurrency: currency }),
|
|
73
|
+
addCurrency: metadata => {
|
|
74
|
+
set(state => ({
|
|
75
|
+
customCurrencies: {
|
|
76
|
+
...state.customCurrencies,
|
|
77
|
+
[metadata.code]: metadata,
|
|
78
|
+
},
|
|
79
|
+
}));
|
|
80
|
+
},
|
|
81
|
+
removeCurrency: code => {
|
|
82
|
+
set(state => {
|
|
83
|
+
// Remove the currency
|
|
84
|
+
const { [code]: _removed, ...remaining } = state.customCurrencies;
|
|
85
|
+
// Remove all exchange rates involving this currency
|
|
86
|
+
const filteredRates = {};
|
|
87
|
+
for (const [key, rate] of Object.entries(state.customExchangeRates)) {
|
|
88
|
+
// Key format is "FROM-TO", skip if either matches the removed code
|
|
89
|
+
const [from, to] = key.split("-");
|
|
90
|
+
if (from !== code && to !== code) {
|
|
91
|
+
filteredRates[key] = rate;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
customCurrencies: remaining,
|
|
96
|
+
customExchangeRates: filteredRates,
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
setExchangeRate: (from, to, rate) => {
|
|
101
|
+
set(state => {
|
|
102
|
+
const key = `${from}-${to}`;
|
|
103
|
+
const inverseKey = `${to}-${from}`;
|
|
104
|
+
// Only set inverse rate if rate is not 0 (to avoid Infinity)
|
|
105
|
+
const newRates = {
|
|
106
|
+
...state.customExchangeRates,
|
|
107
|
+
[key]: rate,
|
|
108
|
+
};
|
|
109
|
+
if (rate !== 0) {
|
|
110
|
+
newRates[inverseKey] = 1 / rate;
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
customExchangeRates: newRates,
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
},
|
|
117
|
+
getExchangeRate: (from, to) => {
|
|
118
|
+
const key = `${from}-${to}`;
|
|
119
|
+
return get().customExchangeRates[key];
|
|
120
|
+
},
|
|
121
|
+
getAllCurrencies: () => {
|
|
122
|
+
const builtIn = Object.keys(exports.CURRENCY_SYMBOLS);
|
|
123
|
+
const custom = Object.keys(get().customCurrencies);
|
|
124
|
+
return [...builtIn, ...custom];
|
|
125
|
+
},
|
|
54
126
|
}), {
|
|
55
127
|
name: "currency-storage",
|
|
56
|
-
version:
|
|
128
|
+
version: 3,
|
|
57
129
|
}));
|
|
130
|
+
/**
|
|
131
|
+
* Get the symbol for any currency (built-in or custom).
|
|
132
|
+
*/
|
|
133
|
+
function getCurrencySymbol(currency) {
|
|
134
|
+
// Check built-in currencies first
|
|
135
|
+
if (currency in exports.CURRENCY_SYMBOLS) {
|
|
136
|
+
return exports.CURRENCY_SYMBOLS[currency];
|
|
137
|
+
}
|
|
138
|
+
// Check custom currencies
|
|
139
|
+
const customCurrencies = exports.useCurrencyStore.getState().customCurrencies;
|
|
140
|
+
const customCurrency = customCurrencies[currency];
|
|
141
|
+
if (customCurrency) {
|
|
142
|
+
return customCurrency.symbol;
|
|
143
|
+
}
|
|
144
|
+
// Fallback to currency code
|
|
145
|
+
return currency;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Get the name for any currency (built-in or custom).
|
|
149
|
+
*/
|
|
150
|
+
function getCurrencyName(currency) {
|
|
151
|
+
// Check built-in currencies first
|
|
152
|
+
if (currency in exports.CURRENCY_NAMES) {
|
|
153
|
+
return exports.CURRENCY_NAMES[currency];
|
|
154
|
+
}
|
|
155
|
+
// Check custom currencies
|
|
156
|
+
const customCurrencies = exports.useCurrencyStore.getState().customCurrencies;
|
|
157
|
+
const customCurrency = customCurrencies[currency];
|
|
158
|
+
if (customCurrency) {
|
|
159
|
+
return customCurrency.name;
|
|
160
|
+
}
|
|
161
|
+
// Fallback to currency code
|
|
162
|
+
return currency;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Get metadata for a custom currency.
|
|
166
|
+
*/
|
|
167
|
+
function getCurrencyMetadata(currency) {
|
|
168
|
+
const customCurrencies = exports.useCurrencyStore.getState().customCurrencies;
|
|
169
|
+
return customCurrencies[currency];
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Get the number of decimal places for a currency (for converting from smallest unit).
|
|
173
|
+
* Used when parsing amounts from wei/smallest unit format.
|
|
174
|
+
*
|
|
175
|
+
* @param currency - Currency code
|
|
176
|
+
* @returns Number of decimal places (e.g., 18 for ETH/wei, 2 for USD cents, 0 for JPY)
|
|
177
|
+
*/
|
|
178
|
+
function getCurrencyDecimalPlaces(currency) {
|
|
179
|
+
// Check custom currencies first
|
|
180
|
+
const customCurrencies = exports.useCurrencyStore.getState().customCurrencies;
|
|
181
|
+
const customMetadata = customCurrencies[currency];
|
|
182
|
+
if (customMetadata?.decimals !== undefined) {
|
|
183
|
+
return customMetadata.decimals;
|
|
184
|
+
}
|
|
185
|
+
// Built-in currencies with 18 decimals (wei-like)
|
|
186
|
+
if (currency === "WIN" || currency === "ETH" || currency === "SOL" || currency === "B3") {
|
|
187
|
+
return 18;
|
|
188
|
+
}
|
|
189
|
+
// Fiat currencies with cent-like decimals
|
|
190
|
+
if (currency === "USD" || currency === "EUR" || currency === "GBP" || currency === "CAD" || currency === "AUD") {
|
|
191
|
+
return 2;
|
|
192
|
+
}
|
|
193
|
+
// Currencies without fractional units
|
|
194
|
+
if (currency === "JPY" || currency === "KRW") {
|
|
195
|
+
return 0;
|
|
196
|
+
}
|
|
197
|
+
// Default to 18 decimals (wei-like)
|
|
198
|
+
return 18;
|
|
199
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useGlobalWalletState } from "../../../../anyspend/utils/index.js";
|
|
3
4
|
import { useAccountWallet } from "../../../../global-account/react/index.js";
|
|
4
5
|
import { cn } from "../../../../shared/utils/cn.js";
|
|
5
6
|
import { shortenAddress } from "../../../../shared/utils/formatAddress.js";
|
|
@@ -9,7 +10,7 @@ import { ChevronLeft, ChevronRightCircle, Wallet, X, ZapIcon } from "lucide-reac
|
|
|
9
10
|
import { useState } from "react";
|
|
10
11
|
import { createPortal } from "react-dom";
|
|
11
12
|
import { toast } from "sonner";
|
|
12
|
-
import { useSetActiveWallet, useWalletInfo } from "thirdweb/react";
|
|
13
|
+
import { useActiveWallet, useSetActiveWallet, useWalletInfo } from "thirdweb/react";
|
|
13
14
|
import { createWallet } from "thirdweb/wallets";
|
|
14
15
|
import { useAccount, useConnect, useDisconnect, useWalletClient } from "wagmi";
|
|
15
16
|
export var CryptoPaymentMethodType;
|
|
@@ -28,6 +29,8 @@ export function CryptoPaymentMethod({ selectedPaymentMethod, setSelectedPaymentM
|
|
|
28
29
|
const [showWalletModal, setShowWalletModal] = useState(false);
|
|
29
30
|
const setActiveWallet = useSetActiveWallet();
|
|
30
31
|
const { data: eoaWalletInfo } = useWalletInfo(connectedEOAWallet?.id);
|
|
32
|
+
const activeWallet = useActiveWallet();
|
|
33
|
+
const setGlobalAccountWallet = useGlobalWalletState(state => state.setGlobalAccountWallet);
|
|
31
34
|
const isConnected = !!connectedEOAWallet;
|
|
32
35
|
const globalAddress = connectedSmartWallet?.getAccount()?.address;
|
|
33
36
|
// Helper function to check if two addresses are the same
|
|
@@ -169,6 +172,7 @@ export function CryptoPaymentMethod({ selectedPaymentMethod, setSelectedPaymentM
|
|
|
169
172
|
return (_jsxs("div", { className: "crypto-payment-method mx-auto h-fit w-[460px] max-w-full", children: [_jsxs("div", { className: cn("relative flex flex-col gap-10"), children: [_jsx("button", { onClick: onBack, className: "text-as-quaternary hover:text-as-primary absolute flex h-8 w-8 items-center justify-center rounded-lg transition-colors", children: _jsx(ChevronLeft, { className: "h-6 w-6" }) }), _jsx("div", { className: "flex items-center justify-around gap-4", children: _jsx("div", { className: "flex-1 text-center", children: _jsx("h2", { className: "text-as-primary text-lg font-semibold", children: "Select a payment method" }) }) }), _jsxs("div", { className: "crypto-payment-methods flex flex-col gap-4", children: [(shouldShowConnectedEOA || shouldShowWagmiWallet || globalAddress) && (_jsxs("div", { className: "installed-wallets", children: [_jsx("h3", { className: "text-as-primary/80 mb-3 text-sm font-medium", children: "Connected wallets" }), _jsxs("div", { className: "space-y-2", children: [shouldShowConnectedEOA && (_jsx("button", { onClick: () => {
|
|
170
173
|
setSelectedPaymentMethod(CryptoPaymentMethodType.CONNECT_WALLET);
|
|
171
174
|
onSelectPaymentMethod(CryptoPaymentMethodType.CONNECT_WALLET);
|
|
175
|
+
setGlobalAccountWallet(activeWallet);
|
|
172
176
|
setActiveWallet(connectedEOAWallet);
|
|
173
177
|
toast.success(`Selected ${eoaWalletInfo?.name || connector?.name || "wallet"}`);
|
|
174
178
|
}, className: cn("crypto-payment-method-connect-wallet w-full rounded-xl border p-4 text-left transition-all hover:shadow-md", selectedPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Wallet } from "thirdweb/wallets";
|
|
2
|
+
interface GlobalWalletState {
|
|
3
|
+
globalAccountWallet?: Wallet;
|
|
4
|
+
setGlobalAccountWallet: (account?: Wallet) => void;
|
|
5
|
+
}
|
|
6
|
+
export declare const useGlobalWalletState: import("zustand").UseBoundStore<import("zustand").StoreApi<GlobalWalletState>>;
|
|
7
|
+
export {};
|
|
@@ -2,9 +2,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { AnySpend, AnySpendBondKit, AnySpendBuySpin, AnySpendNFT, AnyspendSignatureMint, AnySpendStakeB3, AnySpendTournament, OrderHistory, } from "../../../anyspend/react/index.js";
|
|
3
3
|
import { AnySpendDepositHype } from "../../../anyspend/react/components/AnyspendDepositHype.js";
|
|
4
4
|
import { AnySpendStakeUpside } from "../../../anyspend/react/components/AnySpendStakeUpside.js";
|
|
5
|
+
import { useGlobalWalletState } from "../../../anyspend/utils/index.js";
|
|
5
6
|
import { useIsMobile, useModalStore } from "../../../global-account/react/index.js";
|
|
6
7
|
import { cn } from "../../../shared/utils/cn.js";
|
|
7
8
|
import { debugB3React } from "../../../shared/utils/debug.js";
|
|
9
|
+
import { useEffect, useRef } from "react";
|
|
8
10
|
import { AvatarEditor } from "./AvatarEditor/AvatarEditor.js";
|
|
9
11
|
import { useB3 } from "./B3Provider/useB3.js";
|
|
10
12
|
import { LinkAccount } from "./LinkAccount/LinkAccount.js";
|
|
@@ -13,11 +15,26 @@ import { RequestPermissions } from "./RequestPermissions/RequestPermissions.js";
|
|
|
13
15
|
import { SignInWithB3Flow } from "./SignInWithB3/SignInWithB3Flow.js";
|
|
14
16
|
import { Dialog, DialogContent, DialogDescription, DialogTitle } from "./ui/dialog.js";
|
|
15
17
|
import { Drawer, DrawerContent, DrawerDescription, DrawerTitle } from "./ui/drawer.js";
|
|
18
|
+
import { useSetActiveWallet } from "thirdweb/react";
|
|
16
19
|
const debug = debugB3React("B3DynamicModal");
|
|
17
20
|
export function B3DynamicModal() {
|
|
18
21
|
const { isOpen, setB3ModalOpen, contentType, history, navigateBack } = useModalStore();
|
|
19
22
|
const { theme } = useB3();
|
|
20
23
|
const isMobile = useIsMobile();
|
|
24
|
+
const prevIsOpenRef = useRef(isOpen);
|
|
25
|
+
const globalAccountWallet = useGlobalWalletState(state => state.globalAccountWallet);
|
|
26
|
+
const setGlobalAccountWallet = useGlobalWalletState(state => state.setGlobalAccountWallet);
|
|
27
|
+
const setActiveWallet = useSetActiveWallet();
|
|
28
|
+
// anyspend cleanup global account chnages by setting account back
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (prevIsOpenRef.current && !isOpen) {
|
|
31
|
+
if (globalAccountWallet) {
|
|
32
|
+
setActiveWallet(globalAccountWallet);
|
|
33
|
+
setGlobalAccountWallet(undefined);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
prevIsOpenRef.current = isOpen;
|
|
37
|
+
}, [isOpen, globalAccountWallet, setActiveWallet, setGlobalAccountWallet]);
|
|
21
38
|
// Define arrays for different modal type groups
|
|
22
39
|
const fullWidthTypes = [
|
|
23
40
|
"anySpend",
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { cn } from "../../../shared/utils/index.js";
|
|
4
4
|
import { Button } from "../../../global-account/react/components/ui/button.js";
|
|
5
5
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "../../../global-account/react/components/ui/dropdown-menu.js";
|
|
6
|
-
import { CURRENCY_NAMES, CURRENCY_SYMBOLS, useCurrencyStore } from "../stores/currencyStore.js";
|
|
7
|
-
const
|
|
6
|
+
import { CURRENCY_NAMES, CURRENCY_SYMBOLS, useCurrencyStore, getCurrencyName, getCurrencySymbol, } from "../stores/currencyStore.js";
|
|
7
|
+
const builtInCurrencies = ["B3", "ETH", "SOL", "USD", "EUR", "GBP", "KRW", "JPY", "CAD", "AUD"];
|
|
8
8
|
export function CurrencySelector({ labelClassName, buttonVariant = "dark", label }) {
|
|
9
|
-
const
|
|
10
|
-
|
|
9
|
+
const selectedCurrency = useCurrencyStore(state => state.selectedCurrency);
|
|
10
|
+
const setSelectedCurrency = useCurrencyStore(state => state.setSelectedCurrency);
|
|
11
|
+
const customCurrencies = useCurrencyStore(state => state.customCurrencies);
|
|
12
|
+
// Separate built-in and custom for better organization
|
|
13
|
+
const customCurrencyCodes = Object.keys(customCurrencies);
|
|
14
|
+
const hasCustomCurrencies = customCurrencyCodes.length > 0;
|
|
15
|
+
return (_jsx("div", { className: "flex items-center gap-2", children: _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs("div", { className: "flex items-center gap-3", children: [label && (_jsx("span", { className: cn("text-foreground text-sm font-medium leading-none tracking-tight sm:text-base", labelClassName), children: label })), _jsxs(Button, { variant: buttonVariant, className: "flex items-center gap-2", children: [_jsx("span", { className: "text-sm font-medium", children: getCurrencyName(selectedCurrency) }), _jsx("svg", { className: "h-4 w-4", fill: "currentColor", viewBox: "0 0 20 20", children: _jsx("path", { fillRule: "evenodd", d: "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z", clipRule: "evenodd" }) })] })] }) }), _jsxs(DropdownMenuContent, { align: "end", className: "z-[100] min-w-[200px]", children: [builtInCurrencies.map(currency => (_jsxs("div", { children: [_jsxs(DropdownMenuItem, { onClick: () => setSelectedCurrency(currency), className: `flex cursor-pointer items-center justify-between gap-3 px-3 py-2.5 transition-colors ${selectedCurrency === currency ? "bg-accent" : "hover:bg-accent/50"}`, children: [_jsx("span", { className: "text-foreground text-sm font-medium", children: CURRENCY_NAMES[currency] }), _jsx("span", { className: "text-muted-foreground text-xs font-medium", children: CURRENCY_SYMBOLS[currency] })] }), currency === "SOL" && _jsx(DropdownMenuSeparator, { className: "bg-border my-1" }, "separator")] }, currency))), hasCustomCurrencies && (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, { className: "bg-border my-1" }), customCurrencyCodes.map(currency => (_jsxs(DropdownMenuItem, { onClick: () => setSelectedCurrency(currency), className: `flex cursor-pointer items-center justify-between gap-3 px-3 py-2.5 transition-colors ${selectedCurrency === currency ? "bg-accent" : "hover:bg-accent/50"}`, children: [_jsx("span", { className: "text-foreground text-sm font-medium", children: getCurrencyName(currency) }), _jsx("span", { className: "text-muted-foreground text-xs font-medium", children: getCurrencySymbol(currency) })] }, currency)))] }))] })] }) }));
|
|
11
16
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
interface FormattedCurrencyProps {
|
|
2
|
-
amount:
|
|
2
|
+
amount: string;
|
|
3
|
+
sourceCurrency: string;
|
|
3
4
|
showChange?: boolean;
|
|
4
5
|
showColor?: boolean;
|
|
5
6
|
className?: string;
|
|
6
7
|
subB3Icon?: boolean;
|
|
7
8
|
clickable?: boolean;
|
|
8
9
|
decimals?: number;
|
|
9
|
-
currency?: string;
|
|
10
10
|
}
|
|
11
|
-
export declare function FormattedCurrency({ amount, showChange, showColor, className, subB3Icon, clickable, decimals,
|
|
11
|
+
export declare function FormattedCurrency({ amount, sourceCurrency, showChange, showColor, className, subB3Icon, clickable, decimals, }: FormattedCurrencyProps): import("react/jsx-runtime").JSX.Element;
|
|
12
12
|
export {};
|
|
@@ -5,21 +5,30 @@ import { cn } from "../../../shared/utils/index.js";
|
|
|
5
5
|
import { Tooltip, TooltipContent, TooltipTrigger } from "../../../global-account/react/components/ui/tooltip.js";
|
|
6
6
|
import { useCurrencyConversion } from "../hooks/useCurrencyConversion.js";
|
|
7
7
|
import { useCurrencyModalStore } from "../stores/currencyModalStore.js";
|
|
8
|
-
|
|
8
|
+
import { getCurrencyDecimalPlaces } from "../stores/currencyStore.js";
|
|
9
|
+
export function FormattedCurrency({ amount, sourceCurrency, showChange = false, showColor = false, className, subB3Icon = true, clickable = true, decimals, }) {
|
|
9
10
|
const { formatCurrencyValue, formatTooltipValue, selectedCurrency, baseCurrency } = useCurrencyConversion();
|
|
10
11
|
const { openModal } = useCurrencyModalStore();
|
|
11
|
-
//
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
12
|
+
// Convert from smallest unit to human-readable using currency's decimal places
|
|
13
|
+
const decimalPlaces = getCurrencyDecimalPlaces(sourceCurrency);
|
|
14
|
+
const divisor = Math.pow(10, decimalPlaces);
|
|
15
|
+
// Parse amount - handle both string and numeric inputs, including negatives
|
|
16
|
+
let parsedAmount;
|
|
17
|
+
if (typeof amount === "string") {
|
|
18
|
+
// Handle BigInt strings and negative values
|
|
19
|
+
const numericAmount = amount.startsWith("-") ? -Math.abs(parseFloat(amount.replace("-", ""))) : parseFloat(amount);
|
|
20
|
+
parsedAmount = numericAmount / divisor;
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
parsedAmount = amount / divisor;
|
|
24
|
+
}
|
|
25
|
+
const isPositive = parsedAmount >= 0;
|
|
26
|
+
// Always format with absolute value, we'll add the sign separately
|
|
27
|
+
const absoluteAmount = Math.abs(parsedAmount);
|
|
28
|
+
// Format value with automatic conversion from source to display currency
|
|
29
|
+
const formattedValue = formatCurrencyValue(absoluteAmount, sourceCurrency, { decimals });
|
|
21
30
|
// Generate tooltip using the centralized hook function
|
|
22
|
-
const baseTooltipValue = formatTooltipValue(
|
|
31
|
+
const baseTooltipValue = formatTooltipValue(parsedAmount, sourceCurrency);
|
|
23
32
|
// Add change indicator if needed
|
|
24
33
|
const tooltipValue = showChange ? `${isPositive ? "+" : "-"}${baseTooltipValue}` : baseTooltipValue;
|
|
25
34
|
// Determine color class
|
|
@@ -32,26 +41,22 @@ export function FormattedCurrency({ amount, showChange = false, showColor = fals
|
|
|
32
41
|
colorClass = "text-red-400";
|
|
33
42
|
}
|
|
34
43
|
}
|
|
35
|
-
//
|
|
44
|
+
// Build display value with appropriate sign
|
|
36
45
|
let displayValue = formattedValue;
|
|
37
46
|
if (showChange) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
47
|
+
// Add +/- prefix for change indicators
|
|
48
|
+
displayValue = `${isPositive ? "+" : "-"}${formattedValue}`;
|
|
49
|
+
}
|
|
50
|
+
else if (!isPositive) {
|
|
51
|
+
// Add minus sign for negative values
|
|
52
|
+
displayValue = `-${formattedValue}`;
|
|
44
53
|
}
|
|
45
54
|
const handleClick = () => {
|
|
46
55
|
if (clickable) {
|
|
47
56
|
openModal();
|
|
48
57
|
}
|
|
49
58
|
};
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
? displayValue.split(" ")[0]
|
|
54
|
-
: displayValue, subB3Icon &&
|
|
55
|
-
(currency === baseCurrency || (!currency && activeCurrency === baseCurrency)) &&
|
|
56
|
-
baseCurrency === "B3" && (_jsx("img", { src: B3_COIN_IMAGE_URL, className: "inline-block h-4 w-4 align-middle", alt: "B3 coin" }))] }) }), _jsx(TooltipContent, { children: tooltipValue })] }));
|
|
59
|
+
// Check if we should show B3 icon (when displaying in B3 and baseCurrency is B3)
|
|
60
|
+
const shouldShowB3Icon = subB3Icon && selectedCurrency === "B3" && baseCurrency === "B3";
|
|
61
|
+
return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("span", { onClick: handleClick, className: cn("inline-flex items-center gap-1 whitespace-nowrap", colorClass, className, clickable && "cursor-pointer transition-opacity hover:opacity-80"), children: [shouldShowB3Icon ? displayValue.split(" ")[0] : displayValue, shouldShowB3Icon && (_jsx("img", { src: B3_COIN_IMAGE_URL, className: "inline-block h-4 w-4 align-middle", alt: "B3 coin" }))] }) }), _jsx(TooltipContent, { children: tooltipValue })] }));
|
|
57
62
|
}
|
|
@@ -16,20 +16,23 @@
|
|
|
16
16
|
*/
|
|
17
17
|
export declare function useCurrencyConversion(): {
|
|
18
18
|
/** Currently selected display currency */
|
|
19
|
-
selectedCurrency:
|
|
19
|
+
selectedCurrency: string;
|
|
20
20
|
/** Base currency used for conversion (typically B3) */
|
|
21
|
-
baseCurrency:
|
|
21
|
+
baseCurrency: string;
|
|
22
22
|
/** Current exchange rate from base to selected currency (undefined while loading) */
|
|
23
23
|
exchangeRate: number | undefined;
|
|
24
24
|
/** Format a value with currency conversion and proper symbol/decimal handling */
|
|
25
|
-
formatCurrencyValue: (value: number, options?: {
|
|
25
|
+
formatCurrencyValue: (value: number, sourceCurrency: string, options?: {
|
|
26
26
|
decimals?: number;
|
|
27
|
-
currency?: string;
|
|
28
27
|
}) => string;
|
|
29
28
|
/** Format a tooltip value showing alternate currency representation */
|
|
30
|
-
formatTooltipValue: (value: number,
|
|
29
|
+
formatTooltipValue: (value: number, sourceCurrency: string) => string;
|
|
31
30
|
/** Symbol for the currently selected currency (e.g., "$", "€", "ETH") */
|
|
32
31
|
selectedCurrencySymbol: string;
|
|
33
32
|
/** Symbol for the base currency */
|
|
34
33
|
baseCurrencySymbol: string;
|
|
34
|
+
/** Get exchange rate between any two currencies */
|
|
35
|
+
getExchangeRate: (from: string, to: string) => number | undefined;
|
|
36
|
+
/** All registered custom currencies */
|
|
37
|
+
customCurrencies: Record<string, import("../stores/currencyStore").CurrencyMetadata>;
|
|
35
38
|
};
|